Browse Source

Feature `Context` (#656)

- Move `Console` to `Context`
 - Change `Context::global()` to `Context::global_object()`
 - Remove some `use std::borrow::Borrow`
 - Add some pub exports
 - Add `Context::eval()`
 - Deprecate forward_val, forward, exec
 - Make boa_cli use Context::eval()
 - Deprecated forward forward_val and exec
 - Make deprecated functions
pull/688/head
Halid Odat 4 years ago committed by GitHub
parent
commit
edfafc4e03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/dependabot.yml
  2. 68
      boa/benches/exec.rs
  3. 48
      boa/benches/full.rs
  4. 22
      boa/examples/classes.rs
  5. 112
      boa/src/builtins/array/mod.rs
  6. 183
      boa/src/builtins/array/tests.rs
  7. 4
      boa/src/builtins/bigint/conversions.rs
  8. 21
      boa/src/builtins/bigint/mod.rs
  9. 61
      boa/src/builtins/bigint/tests.rs
  10. 14
      boa/src/builtins/boolean/mod.rs
  11. 11
      boa/src/builtins/boolean/tests.rs
  12. 41
      boa/src/builtins/console/mod.rs
  13. 23
      boa/src/builtins/console/tests.rs
  14. 28
      boa/src/builtins/date/mod.rs
  15. 183
      boa/src/builtins/date/tests.rs
  16. 11
      boa/src/builtins/error/mod.rs
  17. 11
      boa/src/builtins/error/range.rs
  18. 11
      boa/src/builtins/error/reference.rs
  19. 11
      boa/src/builtins/error/syntax.rs
  20. 11
      boa/src/builtins/error/type.rs
  21. 21
      boa/src/builtins/function/mod.rs
  22. 9
      boa/src/builtins/function/tests.rs
  23. 6
      boa/src/builtins/global_this/mod.rs
  24. 4
      boa/src/builtins/infinity/mod.rs
  25. 13
      boa/src/builtins/json/mod.rs
  26. 74
      boa/src/builtins/json/tests.rs
  27. 23
      boa/src/builtins/map/mod.rs
  28. 44
      boa/src/builtins/map/tests.rs
  29. 78
      boa/src/builtins/math/mod.rs
  30. 101
      boa/src/builtins/math/tests.rs
  31. 6
      boa/src/builtins/mod.rs
  32. 4
      boa/src/builtins/nan/mod.rs
  33. 61
      boa/src/builtins/number/mod.rs
  34. 119
      boa/src/builtins/number/tests.rs
  35. 14
      boa/src/builtins/object/gcobject.rs
  36. 32
      boa/src/builtins/object/mod.rs
  37. 27
      boa/src/builtins/object/tests.rs
  38. 33
      boa/src/builtins/regexp/mod.rs
  39. 16
      boa/src/builtins/regexp/tests.rs
  40. 82
      boa/src/builtins/string/mod.rs
  41. 113
      boa/src/builtins/string/tests.rs
  42. 13
      boa/src/builtins/symbol/mod.rs
  43. 11
      boa/src/builtins/symbol/tests.rs
  44. 4
      boa/src/builtins/undefined/mod.rs
  45. 27
      boa/src/class.rs
  46. 496
      boa/src/context.rs
  47. 4
      boa/src/exec/array/mod.rs
  48. 6
      boa/src/exec/block/mod.rs
  49. 14
      boa/src/exec/break_node/mod.rs
  50. 9
      boa/src/exec/break_node/tests.rs
  51. 8
      boa/src/exec/call/mod.rs
  52. 11
      boa/src/exec/conditional/mod.rs
  53. 14
      boa/src/exec/declaration/mod.rs
  54. 96
      boa/src/exec/exception.rs
  55. 6
      boa/src/exec/field/mod.rs
  56. 4
      boa/src/exec/identifier/mod.rs
  57. 53
      boa/src/exec/iteration/mod.rs
  58. 346
      boa/src/exec/mod.rs
  59. 4
      boa/src/exec/new/mod.rs
  60. 10
      boa/src/exec/object/mod.rs
  61. 17
      boa/src/exec/operator/mod.rs
  62. 8
      boa/src/exec/return_smt/mod.rs
  63. 4
      boa/src/exec/spread/mod.rs
  64. 10
      boa/src/exec/statement_list.rs
  65. 20
      boa/src/exec/switch/mod.rs
  66. 71
      boa/src/exec/tests.rs
  67. 4
      boa/src/exec/throw/mod.rs
  68. 4
      boa/src/exec/try_node/mod.rs
  69. 49
      boa/src/lib.rs
  70. 3
      boa/src/syntax/mod.rs
  71. 4
      boa/src/value/equality.rs
  72. 56
      boa/src/value/mod.rs
  73. 38
      boa/src/value/operations.rs
  74. 260
      boa/src/value/tests.rs
  75. 10
      boa_cli/src/main.rs
  76. 5
      boa_wasm/src/lib.rs
  77. 26
      tester/src/exec.rs

1
.github/dependabot.yml

@ -1,4 +1,3 @@
---
version: 2
updates:
- package-ecosystem: "npm"

68
boa/benches/exec.rs

@ -1,6 +1,6 @@
//! Benchmarks of the whole execution engine in Boa.
use boa::{exec::Interpreter, realm::Realm, Executable, Parser};
use boa::{exec::Executable, realm::Realm, syntax::Parser, Context};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
@ -18,8 +18,7 @@ static SYMBOL_CREATION: &str = include_str!("bench_scripts/symbol_creation.js");
fn symbol_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(SYMBOL_CREATION.as_bytes()).parse_all().unwrap();
@ -34,8 +33,7 @@ static FOR_LOOP: &str = include_str!("bench_scripts/for_loop.js");
fn for_loop_execution(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(FOR_LOOP.as_bytes()).parse_all().unwrap();
@ -50,8 +48,7 @@ static FIBONACCI: &str = include_str!("bench_scripts/fibonacci.js");
fn fibonacci(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(FIBONACCI.as_bytes()).parse_all().unwrap();
@ -66,8 +63,7 @@ static OBJECT_CREATION: &str = include_str!("bench_scripts/object_creation.js");
fn object_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(OBJECT_CREATION.as_bytes()).parse_all().unwrap();
@ -82,8 +78,7 @@ static OBJECT_PROP_ACCESS_CONST: &str = include_str!("bench_scripts/object_prop_
fn object_prop_access_const(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(OBJECT_PROP_ACCESS_CONST.as_bytes())
@ -100,8 +95,7 @@ static OBJECT_PROP_ACCESS_DYN: &str = include_str!("bench_scripts/object_prop_ac
fn object_prop_access_dyn(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(OBJECT_PROP_ACCESS_DYN.as_bytes())
@ -118,8 +112,7 @@ static REGEXP_LITERAL_CREATION: &str = include_str!("bench_scripts/regexp_litera
fn regexp_literal_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP_LITERAL_CREATION.as_bytes())
@ -136,8 +129,7 @@ static REGEXP_CREATION: &str = include_str!("bench_scripts/regexp_creation.js");
fn regexp_creation(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP_CREATION.as_bytes()).parse_all().unwrap();
@ -152,8 +144,7 @@ static REGEXP_LITERAL: &str = include_str!("bench_scripts/regexp_literal.js");
fn regexp_literal(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP_LITERAL.as_bytes()).parse_all().unwrap();
@ -168,8 +159,7 @@ static REGEXP: &str = include_str!("bench_scripts/regexp.js");
fn regexp(c: &mut Criterion) {
// Create new Realm and interpreter.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Parse the AST nodes.
let nodes = Parser::new(REGEXP.as_bytes()).parse_all().unwrap();
@ -183,8 +173,7 @@ fn regexp(c: &mut Criterion) {
static ARRAY_ACCESS: &str = include_str!("bench_scripts/array_access.js");
fn array_access(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(ARRAY_ACCESS.as_bytes()).parse_all().unwrap();
@ -196,8 +185,7 @@ fn array_access(c: &mut Criterion) {
static ARRAY_CREATE: &str = include_str!("bench_scripts/array_create.js");
fn array_creation(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(ARRAY_CREATE.as_bytes()).parse_all().unwrap();
@ -209,8 +197,7 @@ fn array_creation(c: &mut Criterion) {
static ARRAY_POP: &str = include_str!("bench_scripts/array_pop.js");
fn array_pop(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(ARRAY_POP.as_bytes()).parse_all().unwrap();
@ -222,8 +209,7 @@ fn array_pop(c: &mut Criterion) {
static STRING_CONCAT: &str = include_str!("bench_scripts/string_concat.js");
fn string_concat(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(STRING_CONCAT.as_bytes()).parse_all().unwrap();
@ -235,8 +221,7 @@ fn string_concat(c: &mut Criterion) {
static STRING_COMPARE: &str = include_str!("bench_scripts/string_compare.js");
fn string_compare(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(STRING_COMPARE.as_bytes()).parse_all().unwrap();
@ -248,8 +233,7 @@ fn string_compare(c: &mut Criterion) {
static STRING_COPY: &str = include_str!("bench_scripts/string_copy.js");
fn string_copy(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(STRING_COPY.as_bytes()).parse_all().unwrap();
@ -261,8 +245,7 @@ fn string_copy(c: &mut Criterion) {
static NUMBER_OBJECT_ACCESS: &str = include_str!("bench_scripts/number_object_access.js");
fn number_object_access(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(NUMBER_OBJECT_ACCESS.as_bytes())
.parse_all()
@ -276,8 +259,7 @@ fn number_object_access(c: &mut Criterion) {
static BOOLEAN_OBJECT_ACCESS: &str = include_str!("bench_scripts/boolean_object_access.js");
fn boolean_object_access(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(BOOLEAN_OBJECT_ACCESS.as_bytes())
.parse_all()
@ -291,8 +273,7 @@ fn boolean_object_access(c: &mut Criterion) {
static STRING_OBJECT_ACCESS: &str = include_str!("bench_scripts/string_object_access.js");
fn string_object_access(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(STRING_OBJECT_ACCESS.as_bytes())
.parse_all()
@ -306,8 +287,7 @@ fn string_object_access(c: &mut Criterion) {
static ARITHMETIC_OPERATIONS: &str = include_str!("bench_scripts/arithmetic_operations.js");
fn arithmetic_operations(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(ARITHMETIC_OPERATIONS.as_bytes())
.parse_all()
@ -321,8 +301,7 @@ fn arithmetic_operations(c: &mut Criterion) {
static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js");
fn clean_js(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(CLEAN_JS.as_bytes()).parse_all().unwrap();
c.bench_function("Clean js (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())
@ -332,8 +311,7 @@ fn clean_js(c: &mut Criterion) {
static MINI_JS: &str = include_str!("bench_scripts/mini_js.js");
fn mini_js(c: &mut Criterion) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let nodes = Parser::new(MINI_JS.as_bytes()).parse_all().unwrap();
c.bench_function("Mini js (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap())

48
boa/benches/full.rs

@ -1,6 +1,6 @@
//! Benchmarks of whole program execution in Boa.
use boa::exec;
use boa::Context;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
@ -15,7 +15,7 @@ static SYMBOL_CREATION: &str = include_str!("bench_scripts/symbol_creation.js");
fn symbol_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Symbols (Full)", move |b| {
b.iter(|| exec(black_box(SYMBOL_CREATION)))
b.iter(|| Context::new().eval(black_box(SYMBOL_CREATION)))
});
}
@ -24,7 +24,7 @@ static FOR_LOOP: &str = include_str!("bench_scripts/for_loop.js");
fn for_loop(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("For loop (Full)", move |b| {
b.iter(|| exec(black_box(FOR_LOOP)))
b.iter(|| Context::new().eval(black_box(FOR_LOOP)))
});
}
@ -33,7 +33,7 @@ static FIBONACCI: &str = include_str!("bench_scripts/fibonacci.js");
fn fibonacci(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Fibonacci (Full)", move |b| {
b.iter(|| exec(black_box(FIBONACCI)))
b.iter(|| Context::new().eval(black_box(FIBONACCI)))
});
}
@ -42,7 +42,7 @@ static OBJECT_CREATION: &str = include_str!("bench_scripts/object_creation.js");
fn object_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Object Creation (Full)", move |b| {
b.iter(|| exec(black_box(OBJECT_CREATION)))
b.iter(|| Context::new().eval(black_box(OBJECT_CREATION)))
});
}
@ -51,7 +51,7 @@ static OBJECT_PROP_ACCESS_CONST: &str = include_str!("bench_scripts/object_prop_
fn object_prop_access_const(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Static Object Property Access (Full)", move |b| {
b.iter(|| exec(black_box(OBJECT_PROP_ACCESS_CONST)))
b.iter(|| Context::new().eval(black_box(OBJECT_PROP_ACCESS_CONST)))
});
}
@ -60,7 +60,7 @@ static OBJECT_PROP_ACCESS_DYN: &str = include_str!("bench_scripts/object_prop_ac
fn object_prop_access_dyn(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Dynamic Object Property Access (Full)", move |b| {
b.iter(|| exec(black_box(OBJECT_PROP_ACCESS_DYN)))
b.iter(|| Context::new().eval(black_box(OBJECT_PROP_ACCESS_DYN)))
});
}
@ -69,7 +69,7 @@ static REGEXP_LITERAL_CREATION: &str = include_str!("bench_scripts/regexp_litera
fn regexp_literal_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("RegExp Literal Creation (Full)", move |b| {
b.iter(|| exec(black_box(REGEXP_LITERAL_CREATION)))
b.iter(|| Context::new().eval(black_box(REGEXP_LITERAL_CREATION)))
});
}
@ -78,7 +78,7 @@ static REGEXP_CREATION: &str = include_str!("bench_scripts/regexp_creation.js");
fn regexp_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("RegExp (Full)", move |b| {
b.iter(|| exec(black_box(REGEXP_CREATION)))
b.iter(|| Context::new().eval(black_box(REGEXP_CREATION)))
});
}
@ -87,7 +87,7 @@ static REGEXP_LITERAL: &str = include_str!("bench_scripts/regexp_literal.js");
fn regexp_literal(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("RegExp Literal (Full)", move |b| {
b.iter(|| exec(black_box(REGEXP_LITERAL)))
b.iter(|| Context::new().eval(black_box(REGEXP_LITERAL)))
});
}
@ -95,14 +95,16 @@ static REGEXP: &str = include_str!("bench_scripts/regexp.js");
fn regexp(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("RegExp (Full)", move |b| b.iter(|| exec(black_box(REGEXP))));
c.bench_function("RegExp (Full)", move |b| {
b.iter(|| Context::new().eval(black_box(REGEXP)))
});
}
static ARRAY_ACCESS: &str = include_str!("bench_scripts/array_access.js");
fn array_access(c: &mut Criterion) {
c.bench_function("Array access (Full)", move |b| {
b.iter(|| exec(black_box(ARRAY_ACCESS)))
b.iter(|| Context::new().eval(black_box(ARRAY_ACCESS)))
});
}
@ -110,7 +112,7 @@ static ARRAY_CREATE: &str = include_str!("bench_scripts/array_create.js");
fn array_creation(c: &mut Criterion) {
c.bench_function("Array creation (Full)", move |b| {
b.iter(|| exec(black_box(ARRAY_CREATE)))
b.iter(|| Context::new().eval(black_box(ARRAY_CREATE)))
});
}
@ -118,7 +120,7 @@ static ARRAY_POP: &str = include_str!("bench_scripts/array_pop.js");
fn array_pop(c: &mut Criterion) {
c.bench_function("Array pop (Full)", move |b| {
b.iter(|| exec(black_box(ARRAY_POP)))
b.iter(|| Context::new().eval(black_box(ARRAY_POP)))
});
}
@ -126,7 +128,7 @@ static STRING_CONCAT: &str = include_str!("bench_scripts/string_concat.js");
fn string_concat(c: &mut Criterion) {
c.bench_function("String concatenation (Full)", move |b| {
b.iter(|| exec(black_box(STRING_CONCAT)))
b.iter(|| Context::new().eval(black_box(STRING_CONCAT)))
});
}
@ -134,7 +136,7 @@ static STRING_COMPARE: &str = include_str!("bench_scripts/string_compare.js");
fn string_compare(c: &mut Criterion) {
c.bench_function("String comparison (Full)", move |b| {
b.iter(|| exec(black_box(STRING_COMPARE)))
b.iter(|| Context::new().eval(black_box(STRING_COMPARE)))
});
}
@ -142,7 +144,7 @@ static STRING_COPY: &str = include_str!("bench_scripts/string_copy.js");
fn string_copy(c: &mut Criterion) {
c.bench_function("String copy (Full)", move |b| {
b.iter(|| exec(black_box(STRING_COPY)))
b.iter(|| Context::new().eval(black_box(STRING_COPY)))
});
}
@ -150,7 +152,7 @@ static NUMBER_OBJECT_ACCESS: &str = include_str!("bench_scripts/number_object_ac
fn number_object_access(c: &mut Criterion) {
c.bench_function("Number Object Access (Full)", move |b| {
b.iter(|| exec(black_box(NUMBER_OBJECT_ACCESS)))
b.iter(|| Context::new().eval(black_box(NUMBER_OBJECT_ACCESS)))
});
}
@ -158,7 +160,7 @@ static BOOLEAN_OBJECT_ACCESS: &str = include_str!("bench_scripts/boolean_object_
fn boolean_object_access(c: &mut Criterion) {
c.bench_function("Boolean Object Access (Full)", move |b| {
b.iter(|| exec(black_box(BOOLEAN_OBJECT_ACCESS)))
b.iter(|| Context::new().eval(black_box(BOOLEAN_OBJECT_ACCESS)))
});
}
@ -166,7 +168,7 @@ static STRING_OBJECT_ACCESS: &str = include_str!("bench_scripts/string_object_ac
fn string_object_access(c: &mut Criterion) {
c.bench_function("String Object Access (Full)", move |b| {
b.iter(|| exec(black_box(STRING_OBJECT_ACCESS)))
b.iter(|| Context::new().eval(black_box(STRING_OBJECT_ACCESS)))
});
}
@ -174,7 +176,7 @@ static ARITHMETIC_OPERATIONS: &str = include_str!("bench_scripts/arithmetic_oper
fn arithmetic_operations(c: &mut Criterion) {
c.bench_function("Arithmetic operations (Full)", move |b| {
b.iter(|| exec(black_box(ARITHMETIC_OPERATIONS)))
b.iter(|| Context::new().eval(black_box(ARITHMETIC_OPERATIONS)))
});
}
@ -182,7 +184,7 @@ static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js");
fn clean_js(c: &mut Criterion) {
c.bench_function("Clean js (Full)", move |b| {
b.iter(|| exec(black_box(CLEAN_JS)))
b.iter(|| Context::new().eval(black_box(CLEAN_JS)))
});
}
@ -190,7 +192,7 @@ static MINI_JS: &str = include_str!("bench_scripts/mini_js.js");
fn mini_js(c: &mut Criterion) {
c.bench_function("Mini js (Full)", move |b| {
b.iter(|| exec(black_box(MINI_JS)))
b.iter(|| Context::new().eval(black_box(MINI_JS)))
});
}

22
boa/examples/classes.rs

@ -1,10 +1,7 @@
use boa::{
class::{Class, ClassBuilder},
exec::Interpreter,
forward_val,
property::Attribute,
realm::Realm,
Finalize, Result, Trace, Value,
Context, Finalize, Result, Trace, Value,
};
// We create a new struct that is going to represent a person.
@ -28,7 +25,7 @@ struct Person {
// or any function that matches that signature.
impl Person {
/// This function says hello
fn say_hello(this: &Value, _: &[Value], ctx: &mut Interpreter) -> Result<Value> {
fn say_hello(this: &Value, _: &[Value], ctx: &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`.
@ -59,7 +56,7 @@ 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 Interpreter) -> Result<Self> {
fn constructor(_this: &Value, args: &[Value], ctx: &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)`.
@ -117,15 +114,14 @@ impl Class for Person {
}
fn main() {
let realm = Realm::create();
let mut context = Interpreter::new(realm);
let mut context = Context::new();
// we register the global class `Person`.
context.register_global_class::<Person>().unwrap();
forward_val(
&mut context,
r"
context
.eval(
r"
let person = new Person('John', 19);
person.sayHello();
@ -140,6 +136,6 @@ fn main() {
console.log(person.inheritedProperty);
console.log(Person.prototype.inheritedProperty === person.inheritedProperty);
",
)
.unwrap();
)
.unwrap();
}

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

@ -15,15 +15,11 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::object::{ObjectData, PROTOTYPE},
exec::Interpreter,
property::{Attribute, Property},
value::{same_value_zero, Value},
BoaProfiler, Result,
};
use std::{
borrow::Borrow,
cmp::{max, min},
BoaProfiler, Context, Result,
};
use std::cmp::{max, min};
/// JavaScript `Array` built-in implementation.
#[derive(Debug, Clone, Copy)]
@ -37,7 +33,7 @@ impl Array {
pub(crate) const LENGTH: usize = 1;
/// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Interpreter) -> Result<Value> {
pub(crate) fn new_array(interpreter: &Context) -> Result<Value> {
let array = Value::new_object(Some(
&interpreter
.realm()
@ -52,10 +48,9 @@ impl Array {
.environment
.get_binding_value("Array")
.expect("Array was not initialized")
.borrow()
.get_field(PROTOTYPE),
);
array.borrow().set_field("length", Value::from(0));
array.set_field("length", Value::from(0));
Ok(array)
}
@ -104,12 +99,12 @@ impl Array {
}
/// Create a new array
pub(crate) fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_array(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype
// Set Prototype
let prototype = ctx.realm.global_obj.get_field("Array").get_field(PROTOTYPE);
let prototype = ctx.global_object().get_field("Array").get_field(PROTOTYPE);
this.as_object_mut()
.expect("this should be an array object")
@ -163,7 +158,7 @@ impl Array {
pub(crate) fn is_array(
_this: &Value,
args: &[Value],
_interpreter: &mut Interpreter,
_interpreter: &mut Context,
) -> Result<Value> {
match args.get(0).and_then(|x| x.as_object()) {
Some(object) => Ok(Value::from(object.is_array())),
@ -183,7 +178,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
pub(crate) fn concat(this: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn concat(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
if args.is_empty() {
// If concat is called with no arguments, it returns the original array
return Ok(this.clone());
@ -220,7 +215,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
pub(crate) fn push(this: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn push(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let new_array = Self::add_to_array_object(this, args)?;
Ok(new_array.get_field("length"))
}
@ -235,7 +230,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop
pub(crate) fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn pop(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
let curr_length = this.get_field("length").as_number().unwrap() as i32;
if curr_length < 1 {
@ -258,11 +253,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],
interpreter: &mut Interpreter,
) -> Result<Value> {
pub(crate) fn for_each(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from("Missing argument for Array.prototype.forEach"));
}
@ -276,7 +267,7 @@ impl Array {
let element = this.get_field(i);
let arguments = [element, Value::from(i), this.clone()];
interpreter.call(callback_arg, &this_arg, &arguments)?;
ctx.call(callback_arg, &this_arg, &arguments)?;
}
Ok(Value::undefined())
@ -294,7 +285,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn join(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let separator = if args.is_empty() {
String::from(",")
} else {
@ -327,7 +318,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
let method_name = "join";
let mut arguments = vec![Value::from(",")];
// 2.
@ -335,8 +326,7 @@ impl Array {
// 3.
if !method.is_function() {
method = ctx
.realm
.global_obj
.global_object()
.get_field("Object")
.get_field(PROTOTYPE)
.get_field("toString");
@ -367,7 +357,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
#[allow(clippy::else_if_without_else)]
pub(crate) fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn reverse(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
let len = this.get_field("length").as_number().unwrap() as i32;
let middle: i32 = len.wrapping_div(2);
@ -406,7 +396,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
pub(crate) fn shift(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn shift(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
let len = this.get_field("length").as_number().unwrap() as i32;
if len == 0 {
@ -447,7 +437,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
pub(crate) fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn unshift(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let len = this.get_field("length").as_number().unwrap() as i32;
let arg_c: i32 = args.len() as i32;
@ -492,11 +482,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 Interpreter,
) -> Result<Value> {
pub(crate) fn every(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing callback when calling function Array.prototype.every",
@ -538,11 +524,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
pub(crate) fn map(
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
pub(crate) fn map(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing argument 0 when calling function Array.prototype.map",
@ -589,7 +571,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
pub(crate) fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn index_of(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
// If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() {
return Ok(Value::from(-1));
@ -642,11 +624,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf
pub(crate) fn last_index_of(
this: &Value,
args: &[Value],
_: &mut Interpreter,
) -> Result<Value> {
pub(crate) fn last_index_of(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
// If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() {
return Ok(Value::from(-1));
@ -696,11 +674,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 Interpreter,
) -> Result<Value> {
pub(crate) fn find(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing callback when calling function Array.prototype.find",
@ -735,7 +709,7 @@ impl Array {
pub(crate) fn find_index(
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
interpreter: &mut Context,
) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
@ -774,7 +748,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn fill(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let len: i32 = this.get_field("length").as_number().unwrap() as i32;
let default_value = Value::undefined();
@ -814,11 +788,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
pub(crate) fn includes_value(
this: &Value,
args: &[Value],
_: &mut Interpreter,
) -> Result<Value> {
pub(crate) fn includes_value(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined);
let length = this.get_field("length").as_number().unwrap() as i32;
@ -848,11 +818,7 @@ 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 Interpreter,
) -> Result<Value> {
pub(crate) fn slice(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
let new_array = Self::new_array(interpreter)?;
let len = this.get_field("length").as_number().unwrap() as i32;
@ -897,11 +863,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 Interpreter,
) -> Result<Value> {
pub(crate) fn filter(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing argument 0 when calling function Array.prototype.filter",
@ -951,11 +913,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 Interpreter,
) -> Result<Value> {
pub(crate) fn some(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
if args.is_empty() {
return Err(Value::from(
"missing callback when calling function Array.prototype.some",
@ -998,11 +956,7 @@ 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 Interpreter,
) -> Result<Value> {
pub(crate) fn reduce(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
let this = this.to_object(interpreter)?;
let callback = match args.get(0) {
Some(value) if value.is_function() => value,
@ -1063,7 +1017,7 @@ impl Array {
pub(crate) fn reduce_right(
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
interpreter: &mut Context,
) -> Result<Value> {
let this = this.to_object(interpreter)?;
let callback = match args.get(0) {
@ -1136,8 +1090,8 @@ impl Array {
/// Initialise the `Array` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype

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

@ -1,42 +1,68 @@
use crate::{exec::Interpreter, forward, realm::Realm};
use crate::{forward, Context, Value};
#[test]
fn is_array() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [];
var new_arr = new Array();
var many = ["a", "b", "c"];
"#;
eprintln!("{}", forward(&mut engine, init));
assert_eq!(forward(&mut engine, "Array.isArray(empty)"), "true");
assert_eq!(forward(&mut engine, "Array.isArray(new_arr)"), "true");
assert_eq!(forward(&mut engine, "Array.isArray(many)"), "true");
assert_eq!(forward(&mut engine, "Array.isArray([1, 2, 3])"), "true");
assert_eq!(forward(&mut engine, "Array.isArray([])"), "true");
assert_eq!(forward(&mut engine, "Array.isArray({})"), "false");
// assert_eq!(forward(&mut engine, "Array.isArray(new Array)"), "true");
assert_eq!(forward(&mut engine, "Array.isArray()"), "false");
engine.eval(init).unwrap();
assert_eq!(
forward(&mut engine, "Array.isArray({ constructor: Array })"),
"false"
engine.eval("Array.isArray(empty)").unwrap(),
Value::Boolean(true)
);
assert_eq!(
forward(
&mut engine,
"Array.isArray({ push: Array.prototype.push, concat: Array.prototype.concat })"
),
"false"
engine.eval("Array.isArray(new_arr)").unwrap(),
Value::Boolean(true)
);
assert_eq!(
engine.eval("Array.isArray(many)").unwrap(),
Value::Boolean(true)
);
assert_eq!(forward(&mut engine, "Array.isArray(17)"), "false");
assert_eq!(
forward(&mut engine, "Array.isArray({ __proto__: Array.prototype })"),
"false"
engine.eval("Array.isArray([1, 2, 3])").unwrap(),
Value::Boolean(true)
);
assert_eq!(
forward(&mut engine, "Array.isArray({ length: 0 })"),
"false"
engine.eval("Array.isArray([])").unwrap(),
Value::Boolean(true)
);
assert_eq!(
engine.eval("Array.isArray({})").unwrap(),
Value::Boolean(false)
);
// assert_eq!(engine.eval("Array.isArray(new Array)"), "true");
assert_eq!(
engine.eval("Array.isArray()").unwrap(),
Value::Boolean(false)
);
assert_eq!(
engine
.eval("Array.isArray({ constructor: Array })")
.unwrap(),
Value::Boolean(false)
);
assert_eq!(
engine
.eval("Array.isArray({ push: Array.prototype.push, concat: Array.prototype.concat })")
.unwrap(),
Value::Boolean(false)
);
assert_eq!(
engine.eval("Array.isArray(17)").unwrap(),
Value::Boolean(false)
);
assert_eq!(
engine
.eval("Array.isArray({ __proto__: Array.prototype })")
.unwrap(),
Value::Boolean(false)
);
assert_eq!(
engine.eval("Array.isArray({ length: 0 })").unwrap(),
Value::Boolean(false)
);
}
@ -44,31 +70,45 @@ fn is_array() {
#[ignore]
fn concat() {
//TODO: array display formatter
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = new Array();
var one = new Array(1);
"#;
eprintln!("{}", forward(&mut engine, init));
engine.eval(init).unwrap();
// Empty ++ Empty
let ee = forward(&mut engine, "empty.concat(empty)");
assert_eq!(ee, String::from("[]"));
let ee = engine
.eval("empty.concat(empty)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(ee, "[]");
// Empty ++ NonEmpty
let en = forward(&mut engine, "empty.concat(one)");
assert_eq!(en, String::from("[a]"));
let en = engine
.eval("empty.concat(one)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(en, "[a]");
// NonEmpty ++ Empty
let ne = forward(&mut engine, "one.concat(empty)");
assert_eq!(ne, String::from("a.b.c"));
let ne = engine
.eval("one.concat(empty)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(ne, "a.b.c");
// NonEmpty ++ NonEmpty
let nn = forward(&mut engine, "one.concat(one)");
assert_eq!(nn, String::from("a.b.c"));
let nn = engine
.eval("one.concat(one)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(nn, "a.b.c");
}
#[test]
fn join() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ];
var one = ["a"];
@ -88,8 +128,7 @@ fn join() {
#[test]
fn to_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ];
var one = ["a"];
@ -109,8 +148,7 @@ fn to_string() {
#[test]
fn every() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
let init = r#"
var empty = [];
@ -154,8 +192,7 @@ fn every() {
#[test]
fn find() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
function comp(a) {
return a == "a";
@ -169,8 +206,7 @@ fn find() {
#[test]
fn find_index() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let code = r#"
function comp(item) {
@ -195,8 +231,7 @@ fn find_index() {
#[test]
fn push() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var arr = [1, 2];
"#;
@ -210,8 +245,7 @@ fn push() {
#[test]
fn pop() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ];
var one = [1];
@ -232,8 +266,7 @@ fn pop() {
#[test]
fn shift() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ];
var one = [1];
@ -254,8 +287,7 @@ fn shift() {
#[test]
fn unshift() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var arr = [3, 4];
"#;
@ -269,8 +301,7 @@ fn unshift() {
#[test]
fn reverse() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var arr = [1, 2];
var reversed = arr.reverse();
@ -284,8 +315,7 @@ fn reverse() {
#[test]
fn index_of() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ];
var one = ["a"];
@ -348,8 +378,7 @@ fn index_of() {
#[test]
fn last_index_of() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ];
var one = ["a"];
@ -412,8 +441,7 @@ fn last_index_of() {
#[test]
fn fill_obj_ref() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// test object reference
forward(&mut engine, "a = (new Array(3)).fill({});");
@ -423,8 +451,7 @@ fn fill_obj_ref() {
#[test]
fn fill() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
forward(&mut engine, "var a = [1, 2, 3];");
assert_eq!(
@ -519,8 +546,7 @@ fn fill() {
#[test]
fn includes_value() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ];
var one = ["a"];
@ -558,8 +584,7 @@ fn includes_value() {
#[test]
fn map() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let js = r#"
var empty = [];
@ -622,8 +647,7 @@ fn map() {
#[test]
fn slice() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [ ].slice();
var one = ["a"].slice();
@ -646,8 +670,7 @@ fn slice() {
#[test]
fn for_each() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = [2, 3, 4, 5];
var sum = 0;
@ -669,8 +692,7 @@ fn for_each() {
#[test]
fn for_each_push_value() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = [1, 2, 3, 4];
function callingCallback(item, index, list) {
@ -690,8 +712,7 @@ fn for_each_push_value() {
#[test]
fn filter() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let js = r#"
var empty = [];
@ -760,8 +781,7 @@ fn filter() {
#[test]
fn some() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = [];
@ -809,8 +829,7 @@ fn some() {
#[test]
fn reduce() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var arr = [1, 2, 3, 4];
@ -919,8 +938,7 @@ fn reduce() {
#[test]
fn reduce_right() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var arr = [1, 2, 3, 4];
@ -1042,8 +1060,7 @@ fn reduce_right() {
#[test]
fn call_array_constructor_with_one_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = new Array(0);

4
boa/src/builtins/bigint/conversions.rs

@ -1,6 +1,6 @@
use super::BigInt;
use crate::{builtins::Number, exec::Interpreter, Value};
use crate::{builtins::Number, Context, Value};
use num_traits::cast::{FromPrimitive, ToPrimitive};
use std::convert::TryFrom;
@ -14,7 +14,7 @@ impl BigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-stringtobigint
#[inline]
pub(crate) fn from_string(string: &str, _ctx: &mut Interpreter) -> Result<Self, Value> {
pub(crate) fn from_string(string: &str, _ctx: &mut Context) -> Result<Self, Value> {
if string.is_empty() {
return Ok(BigInt::from(0));
}

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

@ -17,9 +17,8 @@ use crate::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
},
exec::Interpreter,
value::{RcBigInt, Value},
BoaProfiler, Result,
BoaProfiler, Context, Result,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
@ -61,7 +60,7 @@ impl BigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue
#[inline]
fn this_bigint_value(value: &Value, ctx: &mut Interpreter) -> Result<RcBigInt> {
fn this_bigint_value(value: &Value, ctx: &mut Context) -> Result<RcBigInt> {
match value {
// 1. If Type(value) is BigInt, return value.
Value::BigInt(ref bigint) => return Ok(bigint.clone()),
@ -91,7 +90,7 @@ impl BigInt {
///
/// [spec]: https://tc39.es/ecma262/#sec-bigint-objects
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_bigint(ctx)?,
None => RcBigInt::from(Self::from(0)),
@ -110,7 +109,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let radix = if !args.is_empty() {
args[0].to_integer(ctx)? as i32
} else {
@ -135,7 +134,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_bigint_value(this, ctx)?))
}
@ -146,7 +145,7 @@ 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 Interpreter) -> Result<Value> {
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)?;
if bits > 0 && modulo >= BigInt::from(2).pow(&BigInt::from(bits as i64 - 1)) {
@ -165,7 +164,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn as_uint_n(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let (modulo, _) = Self::calculate_as_uint_n(args, ctx)?;
Ok(Value::from(modulo))
@ -176,7 +175,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 Interpreter) -> Result<(BigInt, u32)> {
fn calculate_as_uint_n(args: &[Value], ctx: &mut Context) -> Result<(BigInt, u32)> {
use std::convert::TryFrom;
let undefined_value = Value::undefined();
@ -200,8 +199,8 @@ impl BigInt {
/// Initialise the `BigInt` object on the global object.
#[inline]
pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));

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

@ -1,9 +1,8 @@
use crate::{forward, Interpreter, Realm};
use crate::{forward, Context};
#[test]
fn equality() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "0n == 0n"), "true");
assert_eq!(forward(&mut engine, "1n == 0n"), "false");
@ -56,8 +55,7 @@ fn equality() {
#[test]
fn bigint_function_conversion_from_integer() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "BigInt(1000)"), "1000n");
assert_eq!(
@ -72,8 +70,7 @@ fn bigint_function_conversion_from_integer() {
#[test]
fn bigint_function_conversion_from_rational() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "BigInt(0.0)"), "0n");
assert_eq!(forward(&mut engine, "BigInt(1.0)"), "1n");
@ -82,8 +79,7 @@ fn bigint_function_conversion_from_rational() {
#[test]
fn bigint_function_conversion_from_rational_with_fractional_part() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
try {
@ -100,8 +96,7 @@ fn bigint_function_conversion_from_rational_with_fractional_part() {
#[test]
fn bigint_function_conversion_from_null() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
try {
@ -118,8 +113,7 @@ fn bigint_function_conversion_from_null() {
#[test]
fn bigint_function_conversion_from_undefined() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
try {
@ -136,8 +130,7 @@ fn bigint_function_conversion_from_undefined() {
#[test]
fn bigint_function_conversion_from_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "BigInt('')"), "0n");
assert_eq!(
@ -152,24 +145,21 @@ fn bigint_function_conversion_from_string() {
#[test]
fn add() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "10000n + 1000n"), "11000n");
}
#[test]
fn sub() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "10000n - 1000n"), "9000n");
}
#[test]
fn mul() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(&mut engine, "123456789n * 102030n"),
@ -179,32 +169,28 @@ fn mul() {
#[test]
fn div() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "15000n / 50n"), "300n");
}
#[test]
fn div_with_truncation() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "15001n / 50n"), "300n");
}
#[test]
fn r#mod() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "15007n % 10n"), "7n");
}
#[test]
fn pow() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(&mut engine, "100n ** 10n"),
@ -214,8 +200,7 @@ fn pow() {
#[test]
fn to_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "1000n.toString()"), "\"1000\"");
assert_eq!(forward(&mut engine, "1000n.toString(2)"), "\"1111101000\"");
@ -225,8 +210,7 @@ fn to_string() {
#[test]
fn as_int_n() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "BigInt.asIntN(0, 1n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asIntN(1, 1n)"), "-1n");
@ -286,8 +270,7 @@ fn as_int_n() {
#[test]
fn as_int_n_errors() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_throws(&mut engine, "BigInt.asIntN(-1, 0n)", "RangeError");
assert_throws(&mut engine, "BigInt.asIntN(-2.5, 0n)", "RangeError");
@ -301,8 +284,7 @@ fn as_int_n_errors() {
#[test]
fn as_uint_n() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
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");
@ -353,8 +335,7 @@ fn as_uint_n() {
#[test]
fn as_uint_n_errors() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_throws(&mut engine, "BigInt.asUintN(-1, 0n)", "RangeError");
assert_throws(&mut engine, "BigInt.asUintN(-2.5, 0n)", "RangeError");
@ -366,7 +347,7 @@ fn as_uint_n_errors() {
assert_throws(&mut engine, "BigInt.asUintN(0n, 0n)", "TypeError");
}
fn assert_throws(engine: &mut Interpreter, src: &str, error_type: &str) {
fn assert_throws(engine: &mut Context, src: &str, error_type: &str) {
let result = forward(engine, src);
assert!(result.contains(error_type));
}

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

@ -13,7 +13,7 @@
mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{builtins::object::ObjectData, exec::Interpreter, BoaProfiler, Result, Value};
use crate::{builtins::object::ObjectData, BoaProfiler, Context, Result, Value};
/// Boolean implementation.
#[derive(Debug, Clone, Copy)]
@ -32,7 +32,7 @@ impl Boolean {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
fn this_boolean_value(value: &Value, ctx: &mut Interpreter) -> Result<bool> {
fn this_boolean_value(value: &Value, ctx: &mut Context) -> Result<bool> {
match value {
Value::Boolean(boolean) => return Ok(*boolean),
Value::Object(ref object) => {
@ -53,7 +53,7 @@ impl Boolean {
pub(crate) fn construct_boolean(
this: &Value,
args: &[Value],
_: &mut Interpreter,
_: &mut Context,
) -> Result<Value> {
// Get the argument, if any
let data = args.get(0).map(|x| x.to_boolean()).unwrap_or(false);
@ -71,7 +71,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let boolean = Self::this_boolean_value(this, ctx)?;
Ok(Value::from(boolean.to_string()))
}
@ -85,14 +85,14 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn value_of(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_boolean_value(this, ctx)?))
}
/// Initialise the `Boolean` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create Prototype

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

@ -1,11 +1,10 @@
use crate::{exec::Interpreter, forward, forward_val, realm::Realm, value::same_value};
use crate::{forward, forward_val, value::same_value, Context};
/// Test the correct type is returned from call and construct
#[allow(clippy::unwrap_used)]
#[test]
fn construct_and_call() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var one = new Boolean(1);
var zero = Boolean(0);
@ -20,8 +19,7 @@ fn construct_and_call() {
#[test]
fn constructor_gives_true_instance() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var trueVal = new Boolean(true);
var trueNum = new Boolean(1);
@ -50,8 +48,7 @@ fn constructor_gives_true_instance() {
#[test]
fn instances_have_correct_proto_set() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var boolInstance = new Boolean(true);
var boolProto = Boolean.prototype;

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

@ -18,9 +18,8 @@ mod tests;
use crate::{
builtins::function::make_builtin_fn,
exec::Interpreter,
value::{display_obj, RcString, Value},
BoaProfiler, Result,
BoaProfiler, Context, Result,
};
use rustc_hash::FxHashMap;
use std::time::SystemTime;
@ -57,7 +56,7 @@ pub(crate) fn logger(msg: LogMessage, console_state: &Console) {
}
/// This represents the `console` formatter.
pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result<String> {
pub fn formatter(data: &[Value], ctx: &mut Context) -> Result<String> {
let target = data.get(0).cloned().unwrap_or_default().to_string(ctx)?;
match data.len() {
@ -152,7 +151,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 Interpreter) -> Result<Value> {
pub(crate) fn assert(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default();
if !assertion {
@ -183,7 +182,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn clear(_: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ctx.console_mut().groups.clear();
Ok(Value::undefined())
}
@ -198,7 +197,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn debug(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined())
}
@ -213,7 +212,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn error(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Error(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined())
}
@ -228,7 +227,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn info(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Info(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined())
}
@ -243,7 +242,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn log(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined())
}
@ -258,7 +257,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn trace(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if !args.is_empty() {
logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
@ -282,7 +281,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn warn(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
logger(LogMessage::Warn(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined())
}
@ -297,7 +296,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn count(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
None => "default".into(),
@ -321,7 +320,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn count_reset(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
None => "default".into(),
@ -355,7 +354,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn time(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
None => "default".into(),
@ -384,7 +383,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn time_log(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
None => "default".into(),
@ -417,7 +416,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn time_end(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let label = match args.get(0) {
Some(value) => value.to_string(ctx)?,
None => "default".into(),
@ -449,7 +448,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn group(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let group_label = formatter(args, ctx)?;
logger(
@ -471,7 +470,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn group_end(_: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ctx.console_mut().groups.pop();
Ok(Value::undefined())
@ -487,7 +486,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn dir(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let undefined = Value::undefined();
logger(
LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined), true)),
@ -499,8 +498,8 @@ impl Console {
/// Initialise the `console` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let console = Value::new_object(Some(global));

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

@ -1,24 +1,21 @@
use crate::{builtins::console::formatter, exec::Interpreter, realm::Realm, Value};
use crate::{builtins::console::formatter, Context, Value};
#[test]
fn formatter_no_args_is_empty_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(formatter(&[], &mut engine).unwrap(), "");
}
#[test]
fn formatter_empty_format_string_is_empty_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let val = Value::string("".to_string());
assert_eq!(formatter(&[val], &mut engine).unwrap(), "");
}
#[test]
fn formatter_format_without_args_renders_verbatim() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let val = [Value::string("%d %s %% %f")];
let res = formatter(&val, &mut engine).unwrap();
assert_eq!(res, "%d %s %% %f");
@ -26,8 +23,7 @@ fn formatter_format_without_args_renders_verbatim() {
#[test]
fn formatter_empty_format_string_concatenates_rest_of_args() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let val = [
Value::string(""),
@ -40,8 +36,7 @@ fn formatter_empty_format_string_concatenates_rest_of_args() {
#[test]
fn formatter_utf_8_checks() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let val = [
Value::string("Są takie chwile %dą %są tu%sów %привет%ź".to_string()),
@ -55,8 +50,7 @@ fn formatter_utf_8_checks() {
#[test]
fn formatter_trailing_format_leader_renders() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let val = [
Value::string("%%%%%".to_string()),
@ -69,8 +63,7 @@ fn formatter_trailing_format_leader_renders() {
#[test]
#[allow(clippy::approx_constant)]
fn formatter_float_format_works() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let val = [Value::string("%f".to_string()), Value::rational(3.1415)];
let res = formatter(&val, &mut engine).unwrap();

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

@ -6,8 +6,8 @@ use crate::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
},
value::PreferredType,
BoaProfiler, Interpreter, Result, Value,
value::{PreferredType, Value},
BoaProfiler, Context, Result,
};
use chrono::{prelude::*, Duration, LocalResult};
use gc::{unsafe_empty_trace, Finalize, Trace};
@ -39,13 +39,13 @@ fn ignore_ambiguity<T>(result: LocalResult<T>) -> Option<T> {
macro_rules! getter_method {
($name:ident) => {{
fn get_value(this: &Value, _: &[Value], ctx: &mut Interpreter) -> Result<Value> {
fn get_value(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(this_time_value(this, ctx)?.$name()))
}
get_value
}};
(Self::$name:ident) => {{
fn get_value(_: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
fn get_value(_: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
Ok(Value::from(Date::$name()))
}
get_value
@ -54,7 +54,7 @@ macro_rules! getter_method {
macro_rules! setter_method {
($name:ident($($e:expr),* $(,)?)) => {{
fn set_value(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
fn set_value(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let mut result = this_time_value(this, ctx)?;
result.$name(
$(
@ -245,7 +245,7 @@ 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 make_date(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_date(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if this.is_global() {
Self::make_date_string()
} else if args.is_empty() {
@ -300,7 +300,7 @@ impl Date {
pub(crate) fn make_date_single(
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
ctx: &mut Context,
) -> Result<Value> {
let value = &args[0];
let tv = match this_time_value(value, ctx) {
@ -337,7 +337,7 @@ impl Date {
pub(crate) fn make_date_multiple(
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
ctx: &mut Context,
) -> Result<Value> {
let year = args[0].to_number(ctx)?;
let month = args[1].to_number(ctx)?;
@ -1163,7 +1163,7 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-date.now
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now
pub(crate) fn now(_: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn now(_: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
Ok(Value::from(Utc::now().timestamp_millis() as f64))
}
@ -1179,7 +1179,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 Interpreter) -> Result<Value> {
pub(crate) fn parse(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// This method is implementation-defined and discouraged, so we just require the same format as the string
// constructor.
@ -1203,7 +1203,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn utc(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let year = args
.get(0)
.map_or(Ok(f64::NAN), |value| value.to_number(ctx))?;
@ -1241,8 +1241,8 @@ impl Date {
/// Initialise the `Date` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
@ -1580,7 +1580,7 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-thistimevalue
#[inline]
pub fn this_time_value(value: &Value, ctx: &mut Interpreter) -> Result<Date> {
pub fn this_time_value(value: &Value, ctx: &mut Context) -> Result<Date> {
if let Value::Object(ref object) = value {
if let ObjectData::Date(ref date) = object.borrow().data {
return Ok(*date);

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

@ -1,12 +1,12 @@
#![allow(clippy::zero_prefixed_literal)]
use crate::{builtins::object::ObjectData, forward, forward_val, Interpreter, Realm, Value};
use crate::{builtins::object::ObjectData, forward, forward_val, Context, Value};
use chrono::prelude::*;
// NOTE: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of
// this.
fn forward_dt_utc(engine: &mut Interpreter, src: &str) -> Option<NaiveDateTime> {
fn forward_dt_utc(engine: &mut Context, src: &str) -> Option<NaiveDateTime> {
let date_time = if let Ok(v) = forward_val(engine, src) {
v
} else {
@ -24,7 +24,7 @@ fn forward_dt_utc(engine: &mut Interpreter, src: &str) -> Option<NaiveDateTime>
}
}
fn forward_dt_local(engine: &mut Interpreter, src: &str) -> Option<NaiveDateTime> {
fn forward_dt_local(engine: &mut Context, src: &str) -> Option<NaiveDateTime> {
let date_time = forward_dt_utc(engine, src);
// The timestamp is converted to UTC for internal representation
@ -53,8 +53,7 @@ fn date_display() {
#[test]
fn date_this_time_value() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let error = forward_val(
&mut engine,
@ -74,8 +73,7 @@ fn date_this_time_value() {
#[test]
fn date_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let dt1 = forward(&mut engine, "Date()");
@ -89,8 +87,7 @@ fn date_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let dt1 = forward_dt_local(&mut engine, "new Date()");
@ -104,8 +101,7 @@ fn date_ctor_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call_string() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_dt_utc(&mut engine, "new Date('2020-06-08T09:16:15.779-06:30')");
@ -119,8 +115,7 @@ fn date_ctor_call_string() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call_string_invalid() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_dt_local(&mut engine, "new Date('nope')");
assert_eq!(None, date_time);
@ -129,8 +124,7 @@ fn date_ctor_call_string_invalid() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call_number() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_dt_utc(&mut engine, "new Date(1594199775779)");
assert_eq!(
@ -142,8 +136,7 @@ fn date_ctor_call_number() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call_date() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_dt_utc(&mut engine, "new Date(new Date(1594199775779))");
@ -156,8 +149,7 @@ fn date_ctor_call_date() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call_multiple() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_dt_local(&mut engine, "new Date(2020, 06, 08, 09, 16, 15, 779)");
@ -170,8 +162,7 @@ fn date_ctor_call_multiple() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call_multiple_90s() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_dt_local(&mut engine, "new Date(99, 06, 08, 09, 16, 15, 779)");
@ -185,8 +176,7 @@ fn date_ctor_call_multiple_90s() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_call_multiple_nan() -> Result<(), Box<dyn std::error::Error>> {
fn check(src: &str) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_dt_local(&mut engine, src);
assert_eq!(None, date_time);
}
@ -204,8 +194,7 @@ fn date_ctor_call_multiple_nan() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_now_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward(&mut engine, "Date.now()");
let dt1 = u64::from_str_radix(&date_time, 10)?;
@ -221,8 +210,7 @@ fn date_ctor_now_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_parse_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_val(&mut engine, "Date.parse('2020-06-08T09:16:15.779-07:30')");
@ -232,8 +220,7 @@ fn date_ctor_parse_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_utc_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_val(&mut engine, "Date.UTC(2020, 06, 08, 09, 16, 15, 779)");
@ -244,8 +231,7 @@ fn date_ctor_utc_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_ctor_utc_call_nan() -> Result<(), Box<dyn std::error::Error>> {
fn check(src: &str) {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let date_time = forward_val(&mut engine, src).expect("Expected Success");
assert_eq!(Value::Rational(f64::NAN), date_time);
}
@ -263,8 +249,7 @@ fn date_ctor_utc_call_nan() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_date_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -280,8 +265,7 @@ fn date_proto_get_date_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_day_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -296,8 +280,7 @@ fn date_proto_get_day_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_full_year_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -312,8 +295,7 @@ fn date_proto_get_full_year_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_hours_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -328,8 +310,7 @@ fn date_proto_get_hours_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_milliseconds_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -344,8 +325,7 @@ fn date_proto_get_milliseconds_call() -> Result<(), Box<dyn std::error::Error>>
#[test]
fn date_proto_get_minutes_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -360,8 +340,7 @@ fn date_proto_get_minutes_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_month() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -377,8 +356,7 @@ fn date_proto_get_month() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_seconds() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -393,8 +371,7 @@ fn date_proto_get_seconds() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_time() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -414,8 +391,7 @@ fn date_proto_get_time() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_year() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -430,8 +406,7 @@ fn date_proto_get_year() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_timezone_offset() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -461,8 +436,7 @@ fn date_proto_get_timezone_offset() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_utc_date_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -478,8 +452,7 @@ fn date_proto_get_utc_date_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_utc_day_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -494,8 +467,7 @@ fn date_proto_get_utc_day_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_utc_full_year_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -510,8 +482,7 @@ fn date_proto_get_utc_full_year_call() -> Result<(), Box<dyn std::error::Error>>
#[test]
fn date_proto_get_utc_hours_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -526,8 +497,7 @@ fn date_proto_get_utc_hours_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_utc_milliseconds_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -542,8 +512,7 @@ fn date_proto_get_utc_milliseconds_call() -> Result<(), Box<dyn std::error::Erro
#[test]
fn date_proto_get_utc_minutes_call() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -558,8 +527,7 @@ fn date_proto_get_utc_minutes_call() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_utc_month() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -575,8 +543,7 @@ fn date_proto_get_utc_month() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_get_utc_seconds() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -591,8 +558,7 @@ fn date_proto_get_utc_seconds() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_date() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -624,8 +590,7 @@ fn date_proto_set_date() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_full_year() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -697,8 +662,7 @@ fn date_proto_set_full_year() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_hours() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -752,8 +716,7 @@ fn date_proto_set_hours() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_milliseconds() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -781,8 +744,7 @@ fn date_proto_set_milliseconds() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_minutes() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -828,8 +790,7 @@ fn date_proto_set_minutes() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_month() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -866,8 +827,7 @@ fn date_proto_set_month() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_seconds() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -904,8 +864,7 @@ fn date_proto_set_seconds() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn set_year() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -930,8 +889,7 @@ fn set_year() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_time() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_local(
&mut engine,
@ -947,8 +905,7 @@ fn date_proto_set_time() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_utc_date() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_utc(
&mut engine,
@ -980,8 +937,7 @@ fn date_proto_set_utc_date() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_utc_full_year() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_utc(
&mut engine,
@ -1053,8 +1009,7 @@ fn date_proto_set_utc_full_year() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_utc_hours() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_utc(
&mut engine,
@ -1108,8 +1063,7 @@ fn date_proto_set_utc_hours() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_utc_milliseconds() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_utc(
&mut engine,
@ -1137,8 +1091,7 @@ fn date_proto_set_utc_milliseconds() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_utc_minutes() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_utc(
&mut engine,
@ -1184,8 +1137,7 @@ fn date_proto_set_utc_minutes() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_utc_month() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_utc(
&mut engine,
@ -1222,8 +1174,7 @@ fn date_proto_set_utc_month() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_set_utc_seconds() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_dt_utc(
&mut engine,
@ -1260,8 +1211,7 @@ fn date_proto_set_utc_seconds() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_to_date_string() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1275,8 +1225,7 @@ fn date_proto_to_date_string() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_to_gmt_string() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1290,8 +1239,7 @@ fn date_proto_to_gmt_string() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_to_iso_string() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1305,8 +1253,7 @@ fn date_proto_to_iso_string() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_to_json() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1320,8 +1267,7 @@ fn date_proto_to_json() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_to_string() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1343,8 +1289,7 @@ fn date_proto_to_string() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_to_time_string() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1364,8 +1309,7 @@ fn date_proto_to_time_string() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_to_utc_string() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1379,8 +1323,7 @@ fn date_proto_to_utc_string() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_proto_value_of() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1394,8 +1337,7 @@ fn date_proto_value_of() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_neg() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,
@ -1409,8 +1351,7 @@ fn date_neg() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn date_json() -> Result<(), Box<dyn std::error::Error>> {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward_val(
&mut engine,

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

@ -15,9 +15,8 @@ use crate::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
},
exec::Interpreter,
profiler::BoaProfiler,
Result, Value,
Context, Result, Value,
};
pub(crate) mod range;
@ -46,7 +45,7 @@ impl Error {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
}
@ -68,7 +67,7 @@ impl Error {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
let name = this.get_field("name");
let message = this.get_field("message");
Ok(Value::from(format!(
@ -80,8 +79,8 @@ impl Error {
/// Initialise the global object with the `Error` object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));

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

@ -11,9 +11,8 @@
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData},
exec::Interpreter,
profiler::BoaProfiler,
Result, Value,
Context, Result, Value,
};
/// JavaScript `RangeError` impleentation.
@ -28,7 +27,7 @@ impl RangeError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
}
@ -50,7 +49,7 @@ impl RangeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let name = this.get_field("name").to_string(ctx)?;
let message = this.get_field("message").to_string(ctx)?;
@ -59,8 +58,8 @@ impl RangeError {
/// Initialise the global object with the `RangeError` object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));

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

@ -11,9 +11,8 @@
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData},
exec::Interpreter,
profiler::BoaProfiler,
Result, Value,
Context, Result, Value,
};
#[derive(Debug, Clone, Copy)]
@ -27,7 +26,7 @@ impl ReferenceError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
}
@ -49,7 +48,7 @@ impl ReferenceError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let name = this.get_field("name").to_string(ctx)?;
let message = this.get_field("message").to_string(ctx)?;
@ -57,8 +56,8 @@ impl ReferenceError {
}
/// Initialise the global object with the `ReferenceError` object.
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));

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

@ -13,9 +13,8 @@
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData},
exec::Interpreter,
profiler::BoaProfiler,
Result, Value,
Context, Result, Value,
};
/// JavaScript `SyntaxError` impleentation.
@ -30,7 +29,7 @@ impl SyntaxError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
}
@ -52,7 +51,7 @@ impl SyntaxError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
let name = this.get_field("name");
let message = this.get_field("message");
// FIXME: This should not use `.display()`
@ -61,8 +60,8 @@ impl SyntaxError {
/// Initialise the global object with the `SyntaxError` object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));

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

@ -17,8 +17,7 @@
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn, object::ObjectData},
exec::Interpreter,
BoaProfiler, Result, Value,
BoaProfiler, Context, Result, Value,
};
/// JavaScript `TypeError` implementation.
@ -33,7 +32,7 @@ impl TypeError {
pub(crate) const LENGTH: usize = 1;
/// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?);
}
@ -55,7 +54,7 @@ impl TypeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let name = this.get_field("name").to_string(ctx)?;
let message = this.get_field("message").to_string(ctx)?;
@ -64,8 +63,8 @@ impl TypeError {
/// Initialise the global object with the `RangeError` object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));

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

@ -17,10 +17,9 @@ use crate::{
Array,
},
environment::lexical_environment::Environment,
exec::Interpreter,
property::{Attribute, Property},
syntax::ast::node::{statement_list::RcStatementList, FormalParameter},
BoaProfiler, Result, Value,
syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, Result, Value,
};
use bitflags::bitflags;
use gc::{unsafe_empty_trace, Finalize, Trace};
@ -29,8 +28,8 @@ use std::fmt::{self, Debug};
#[cfg(test)]
mod tests;
/// _fn(this, arguments, ctx) -> Result<Value>_ - The signature of a built-in function
pub type NativeFunction = fn(&Value, &[Value], &mut Interpreter) -> Result<Value>;
/// _fn(this, arguments, ctx) -> 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);
@ -117,7 +116,7 @@ impl Function {
param: &FormalParameter,
index: usize,
args_list: &[Value],
interpreter: &mut Interpreter,
interpreter: &mut Context,
local_env: &Environment,
) {
// Create array of values
@ -201,7 +200,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
/// Create new function `[[Construct]]`
///
// This gets called when a new Function() is created.
pub fn make_function(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub fn make_function(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
this.set_data(ObjectData::Function(Function::BuiltIn(
BuiltInFunction(|_, _, _| Ok(Value::undefined())),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
@ -286,7 +285,7 @@ pub fn make_builtin_fn<N>(
name: N,
parent: &Value,
length: usize,
interpreter: &Interpreter,
interpreter: &Context,
) where
N: Into<String>,
{
@ -296,7 +295,7 @@ pub fn make_builtin_fn<N>(
let mut function = Object::function(
Function::BuiltIn(function.into(), FunctionFlags::CALLABLE),
interpreter
.global()
.global_object()
.get_field("Function")
.get_field("prototype"),
);
@ -310,8 +309,8 @@ pub fn make_builtin_fn<N>(
/// Initialise the `Function` object on the global object.
#[inline]
pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("function", "init");
let prototype = Value::new_object(Some(global));

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

@ -1,10 +1,10 @@
use crate::{exec::Interpreter, forward, forward_val, realm::Realm};
use crate::{forward, forward_val, Context};
#[allow(clippy::float_cmp)]
#[test]
fn check_arguments_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
function jason(a, b) {
return arguments[0];
@ -26,8 +26,7 @@ fn check_arguments_object() {
#[test]
fn check_self_mutating_func() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let func = r#"
function x() {
x.y = 3;

6
boa/src/builtins/global_this/mod.rs

@ -10,7 +10,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-globalthis
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis
use crate::{BoaProfiler, Interpreter, Value};
use crate::{BoaProfiler, Context, Value};
#[cfg(test)]
mod tests;
@ -24,8 +24,8 @@ impl GlobalThis {
/// Initialize the `globalThis` property on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, global.clone())

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

@ -12,7 +12,7 @@
#[cfg(test)]
mod tests;
use crate::{BoaProfiler, Interpreter, Value};
use crate::{BoaProfiler, Context, Value};
/// JavaScript global `Infinity` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -24,7 +24,7 @@ impl Infinity {
/// Initialize the `Infinity` property on the global object.
#[inline]
pub(crate) fn init(_interpreter: &mut Interpreter) -> (&'static str, Value) {
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::from(f64::INFINITY))

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

@ -15,9 +15,8 @@
use crate::{
builtins::function::make_builtin_fn,
exec::Interpreter,
property::{Property, PropertyKey},
BoaProfiler, Result, Value,
BoaProfiler, Context, Result, Value,
};
use serde_json::{self, Value as JSONValue};
@ -44,7 +43,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn parse(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
match serde_json::from_str::<JSONValue>(
&args
.get(0)
@ -74,7 +73,7 @@ impl Json {
/// [polyfill]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
fn walk(
reviver: &Value,
ctx: &mut Interpreter,
ctx: &mut Context,
holder: &mut Value,
key: &PropertyKey,
) -> Result<Value> {
@ -115,7 +114,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 Interpreter) -> Result<Value> {
pub(crate) fn stringify(_: &Value, args: &[Value], ctx: &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())
@ -179,8 +178,8 @@ impl Json {
/// Initialise the `JSON` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let json = Value::new_object(Some(global));

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

@ -1,12 +1,8 @@
use crate::{
builtins::object::PROTOTYPE, exec::Interpreter, forward, forward_val, realm::Realm,
value::same_value,
};
use crate::{builtins::object::PROTOTYPE, forward, forward_val, value::same_value, Context};
#[test]
fn json_sanity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(&mut engine, r#"JSON.parse('{"aaa":"bbb"}').aaa == 'bbb'"#),
"true"
@ -22,8 +18,7 @@ fn json_sanity() {
#[test]
fn json_stringify_remove_undefined_values_from_objects() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(
&mut engine,
@ -36,8 +31,7 @@ fn json_stringify_remove_undefined_values_from_objects() {
#[test]
fn json_stringify_remove_function_values_from_objects() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(
&mut engine,
@ -50,8 +44,7 @@ fn json_stringify_remove_function_values_from_objects() {
#[test]
fn json_stringify_remove_symbols_from_objects() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(
&mut engine,
@ -64,8 +57,7 @@ fn json_stringify_remove_symbols_from_objects() {
#[test]
fn json_stringify_replacer_array_strings() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(
&mut engine,
r#"JSON.stringify({aaa: 'bbb', bbb: 'ccc', ccc: 'ddd'}, ['aaa', 'bbb'])"#,
@ -76,8 +68,7 @@ fn json_stringify_replacer_array_strings() {
#[test]
fn json_stringify_replacer_array_numbers() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(
&mut engine,
r#"JSON.stringify({ 0: 'aaa', 1: 'bbb', 2: 'ccc'}, [1, 2])"#,
@ -88,8 +79,7 @@ fn json_stringify_replacer_array_numbers() {
#[test]
fn json_stringify_replacer_function() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(
&mut engine,
r#"JSON.stringify({ aaa: 1, bbb: 2}, (key, value) => {
@ -106,8 +96,7 @@ fn json_stringify_replacer_function() {
#[test]
fn json_stringify_arrays() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify(['a', 'b'])"#);
let expected = forward(&mut engine, r#"'["a","b"]'"#);
@ -116,8 +105,7 @@ fn json_stringify_arrays() {
#[test]
fn json_stringify_object_array() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
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"}]'"#);
@ -126,8 +114,7 @@ fn json_stringify_object_array() {
#[test]
fn json_stringify_array_converts_undefined_to_null() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify([undefined])"#);
let expected = forward(&mut engine, r#"'[null]'"#);
@ -136,8 +123,7 @@ fn json_stringify_array_converts_undefined_to_null() {
#[test]
fn json_stringify_array_converts_function_to_null() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify([() => {}])"#);
let expected = forward(&mut engine, r#"'[null]'"#);
@ -146,8 +132,7 @@ fn json_stringify_array_converts_function_to_null() {
#[test]
fn json_stringify_array_converts_symbol_to_null() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(&mut engine, r#"JSON.stringify([Symbol()])"#);
let expected = forward(&mut engine, r#"'[null]'"#);
@ -155,8 +140,7 @@ fn json_stringify_array_converts_symbol_to_null() {
}
#[test]
fn json_stringify_function_replacer_propogate_error() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual = forward(
&mut engine,
@ -177,8 +161,7 @@ fn json_stringify_function_replacer_propogate_error() {
#[test]
fn json_stringify_function() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual_function = forward(&mut engine, r#"JSON.stringify(() => {})"#);
let expected = forward(&mut engine, r#"undefined"#);
@ -188,8 +171,7 @@ fn json_stringify_function() {
#[test]
fn json_stringify_undefined() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual_undefined = forward(&mut engine, r#"JSON.stringify(undefined)"#);
let expected = forward(&mut engine, r#"undefined"#);
@ -198,8 +180,7 @@ fn json_stringify_undefined() {
#[test]
fn json_stringify_symbol() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual_symbol = forward(&mut engine, r#"JSON.stringify(Symbol())"#);
let expected = forward(&mut engine, r#"undefined"#);
@ -209,8 +190,7 @@ fn json_stringify_symbol() {
#[test]
fn json_stringify_no_args() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual_no_args = forward(&mut engine, r#"JSON.stringify()"#);
let expected = forward(&mut engine, r#"undefined"#);
@ -220,8 +200,7 @@ fn json_stringify_no_args() {
#[test]
fn json_parse_array_with_reviver() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let result = forward_val(
&mut engine,
r#"JSON.parse('[1,2,3,4]', function(k, v){
@ -252,8 +231,7 @@ fn json_parse_array_with_reviver() {
#[test]
fn json_parse_object_with_reviver() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let result = forward(
&mut engine,
r#"
@ -279,8 +257,7 @@ fn json_parse_object_with_reviver() {
#[test]
fn json_parse_sets_prototypes() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
const jsonString = "{
\"ob\":{\"ject\":1},
@ -302,13 +279,11 @@ fn json_parse_sets_prototypes() {
.prototype()
.clone();
let global_object_prototype = engine
.realm
.global_obj
.global_object()
.get_field("Object")
.get_field(PROTOTYPE);
let global_array_prototype = engine
.realm
.global_obj
.global_object()
.get_field("Array")
.get_field(PROTOTYPE);
assert_eq!(
@ -320,8 +295,7 @@ fn json_parse_sets_prototypes() {
#[test]
fn json_fields_should_be_enumerable() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let actual_object = forward(
&mut engine,
r#"

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

@ -3,9 +3,8 @@
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::object::{ObjectData, PROTOTYPE},
exec::Interpreter,
property::{Attribute, Property},
BoaProfiler, Result, Value,
BoaProfiler, Context, Result, Value,
};
use ordered_map::OrderedMap;
@ -41,7 +40,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 Interpreter) -> Result<Value> {
pub(crate) fn set(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let (key, value) = match args.len() {
0 => (Value::Undefined, Value::Undefined),
1 => (args[0].clone(), Value::Undefined),
@ -74,7 +73,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 Interpreter) -> Result<Value> {
pub(crate) fn delete(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let undefined = Value::Undefined;
let key = match args.len() {
0 => &undefined,
@ -106,7 +105,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 Interpreter) -> Result<Value> {
pub(crate) fn get(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let undefined = Value::Undefined;
let key = match args.len() {
0 => &undefined,
@ -137,7 +136,7 @@ impl Map {
///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.clear
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
pub(crate) fn clear(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn clear(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
this.set_data(ObjectData::Map(OrderedMap::new()));
Self::set_size(this, 0);
@ -155,7 +154,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 Interpreter) -> Result<Value> {
pub(crate) fn has(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let undefined = Value::Undefined;
let key = match args.len() {
0 => &undefined,
@ -185,7 +184,7 @@ impl Map {
pub(crate) fn for_each(
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
interpreter: &mut Context,
) -> Result<Value> {
if args.is_empty() {
return Err(Value::from("Missing argument for Map.prototype.forEach"));
@ -224,12 +223,12 @@ impl Map {
}
/// Create a new map
pub(crate) fn make_map(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_map(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype
// Set Prototype
let prototype = ctx.realm.global_obj.get_field("Map").get_field(PROTOTYPE);
let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE);
this.as_object_mut()
.expect("this is array object")
@ -281,8 +280,8 @@ impl Map {
}
/// Initialise the `Map` object on the global object.
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype

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

@ -1,9 +1,8 @@
use crate::{exec::Interpreter, forward, realm::Realm};
use crate::{forward, Context};
#[test]
fn construct_empty() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = new Map();
"#;
@ -14,8 +13,7 @@ fn construct_empty() {
#[test]
fn construct_from_array() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
"#;
@ -26,8 +24,7 @@ fn construct_from_array() {
#[test]
fn clone() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let original = new Map([["1", "one"], ["2", "two"]]);
let clone = new Map(original);
@ -48,8 +45,7 @@ fn clone() {
#[test]
fn merge() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let first = new Map([["1", "one"], ["2", "two"]]);
let second = new Map([["2", "second two"], ["3", "three"]]);
@ -68,8 +64,7 @@ fn merge() {
#[test]
fn get() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
"#;
@ -86,8 +81,7 @@ fn get() {
#[test]
fn set() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map();
"#;
@ -105,8 +99,7 @@ fn set() {
#[test]
fn clear() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
map.clear();
@ -118,8 +111,7 @@ fn clear() {
#[test]
fn delete() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]);
"#;
@ -134,8 +126,7 @@ fn delete() {
#[test]
fn has() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map([["1", "one"]]);
"#;
@ -150,8 +141,7 @@ fn has() {
#[test]
fn for_each() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map([[1, 5], [2, 10], [3, 15]]);
let valueSum = 0;
@ -172,8 +162,7 @@ fn for_each() {
#[test]
fn modify_key() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let obj = new Object();
let map = new Map([[obj, "one"]]);
@ -186,8 +175,7 @@ fn modify_key() {
#[test]
fn order() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map([[1, "one"]]);
map.set(2, "two");
@ -213,8 +201,7 @@ fn order() {
#[test]
fn recursive_display() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let map = new Map();
let array = new Array([map]);
@ -229,8 +216,7 @@ fn recursive_display() {
#[test]
fn not_a_function() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r"
try {
let map = Map()

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

@ -11,7 +11,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-math-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
use crate::{builtins::function::make_builtin_fn, exec::Interpreter, BoaProfiler, Result, Value};
use crate::{builtins::function::make_builtin_fn, BoaProfiler, Context, Result, Value};
use std::f64;
#[cfg(test)]
@ -33,7 +33,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn abs(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -50,7 +50,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn acos(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -67,7 +67,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn acosh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -84,7 +84,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn asin(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -101,7 +101,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn asinh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -118,7 +118,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn atan(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -135,7 +135,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn atanh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -152,7 +152,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn atan2(_: &Value, args: &[Value], ctx: &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()?,
@ -171,7 +171,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn cbrt(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -188,7 +188,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn ceil(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -205,7 +205,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn clz32(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_u32(ctx))
@ -223,7 +223,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn cos(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -240,7 +240,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn cosh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -257,7 +257,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn exp(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -276,7 +276,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn expm1(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -293,7 +293,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn floor(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -310,7 +310,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn fround(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -327,7 +327,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn hypot(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let mut result = 0f64;
for arg in args {
let x = arg.to_number(ctx)?;
@ -344,7 +344,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn imul(_: &Value, args: &[Value], ctx: &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()?,
@ -363,7 +363,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn log(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -380,7 +380,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn log1p(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -397,7 +397,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn log10(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -414,7 +414,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn log2(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -431,7 +431,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn max(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let mut max = f64::NEG_INFINITY;
for arg in args {
let num = arg.to_number(ctx)?;
@ -448,7 +448,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn min(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let mut min = f64::INFINITY;
for arg in args {
let num = arg.to_number(ctx)?;
@ -465,7 +465,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn pow(_: &Value, args: &[Value], ctx: &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()?,
@ -484,7 +484,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-math.random
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
pub(crate) fn random(_: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn random(_: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
Ok(rand::random::<f64>().into())
}
@ -496,7 +496,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn round(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -513,7 +513,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn sign(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -539,7 +539,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn sin(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -556,7 +556,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn sinh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -573,7 +573,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn sqrt(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -590,7 +590,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn tan(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -607,7 +607,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn tanh(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -624,7 +624,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn trunc(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(args
.get(0)
.map(|x| x.to_number(ctx))
@ -634,8 +634,8 @@ impl Math {
}
/// Create a new `Math` object
pub(crate) fn create(interpreter: &mut Interpreter) -> Value {
let global = interpreter.global();
pub(crate) fn create(interpreter: &mut Context) -> Value {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("math:create", "init");
let math = Value::new_object(Some(global));
@ -692,7 +692,7 @@ impl Math {
/// Initialise the `Math` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Self::create(interpreter))

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

@ -1,12 +1,11 @@
#![allow(clippy::float_cmp)]
use crate::{exec::Interpreter, forward, forward_val, realm::Realm};
use crate::{forward, forward_val, Context};
use std::f64;
#[test]
fn abs() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.abs(3 - 5);
var b = Math.abs(1.23456 - 7.89012);
@ -23,8 +22,7 @@ fn abs() {
#[test]
fn acos() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.acos(8 / 10);
var b = Math.acos(5 / 3);
@ -47,8 +45,7 @@ fn acos() {
#[test]
fn acosh() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.acosh(2);
var b = Math.acosh(-1);
@ -68,8 +65,7 @@ fn acosh() {
#[test]
fn asin() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.asin(6 / 10);
var b = Math.asin(5 / 3);
@ -86,8 +82,7 @@ fn asin() {
#[test]
fn asinh() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.asinh(1);
var b = Math.asinh(0);
@ -104,8 +99,7 @@ fn asinh() {
#[test]
fn atan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.atan(1);
var b = Math.atan(0);
@ -125,8 +119,7 @@ fn atan() {
#[test]
fn atan2() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.atan2(90, 15);
var b = Math.atan2(15, 90);
@ -143,8 +136,7 @@ fn atan2() {
#[test]
fn cbrt() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.cbrt(64);
var b = Math.cbrt(-1);
@ -164,8 +156,7 @@ fn cbrt() {
#[test]
fn ceil() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.ceil(1.95);
var b = Math.ceil(4);
@ -186,8 +177,7 @@ fn ceil() {
#[test]
#[allow(clippy::many_single_char_names)]
fn clz32() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.clz32();
var b = Math.clz32({});
@ -222,8 +212,7 @@ fn clz32() {
#[test]
fn cos() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.cos(0);
var b = Math.cos(1);
@ -240,8 +229,7 @@ fn cos() {
#[test]
fn cosh() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.cosh(0);
var b = Math.cosh(1);
@ -261,8 +249,7 @@ fn cosh() {
#[test]
fn exp() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.exp(0);
var b = Math.exp(-1);
@ -283,8 +270,7 @@ fn exp() {
#[test]
#[allow(clippy::many_single_char_names)]
fn expm1() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.expm1();
var b = Math.expm1({});
@ -329,8 +315,7 @@ fn expm1() {
#[test]
fn floor() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.floor(1.95);
var b = Math.floor(-3.01);
@ -351,8 +336,7 @@ fn floor() {
#[test]
#[allow(clippy::many_single_char_names)]
fn fround() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.fround(NaN);
var b = Math.fround(Infinity);
@ -385,8 +369,7 @@ fn fround() {
#[test]
#[allow(clippy::many_single_char_names)]
fn hypot() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.hypot();
var b = Math.hypot(3, 4);
@ -419,8 +402,7 @@ fn hypot() {
#[test]
#[allow(clippy::many_single_char_names)]
fn imul() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.imul(3, 4);
var b = Math.imul(-5, 12);
@ -449,8 +431,7 @@ fn imul() {
#[test]
fn log() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.log(1);
var b = Math.log(10);
@ -471,8 +452,7 @@ fn log() {
#[test]
#[allow(clippy::many_single_char_names)]
fn log1p() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.log1p(1);
var b = Math.log1p(0);
@ -504,8 +484,7 @@ fn log1p() {
#[test]
fn log10() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.log10(2);
var b = Math.log10(1);
@ -525,8 +504,7 @@ fn log10() {
#[test]
fn log2() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.log2(3);
var b = Math.log2(1);
@ -546,8 +524,7 @@ fn log2() {
#[test]
fn max() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.max(10, 20);
var b = Math.max(-10, -20);
@ -567,8 +544,7 @@ fn max() {
#[test]
fn min() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.min(10, 20);
var b = Math.min(-10, -20);
@ -588,8 +564,7 @@ fn min() {
#[test]
fn pow() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.pow(2, 10);
var b = Math.pow(-7, 2);
@ -612,8 +587,7 @@ fn pow() {
#[test]
fn round() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.round(20.5);
var b = Math.round(-20.3);
@ -630,8 +604,7 @@ fn round() {
#[test]
fn sign() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.sign(3);
var b = Math.sign(-3);
@ -651,8 +624,7 @@ fn sign() {
#[test]
fn sin() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.sin(0);
var b = Math.sin(1);
@ -669,8 +641,7 @@ fn sin() {
#[test]
fn sinh() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.sinh(0);
var b = Math.sinh(1);
@ -687,8 +658,7 @@ fn sinh() {
#[test]
fn sqrt() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.sqrt(0);
var b = Math.sqrt(2);
@ -708,8 +678,7 @@ fn sqrt() {
#[test]
fn tan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.tan(1.1);
"#;
@ -727,8 +696,7 @@ fn tan() {
#[test]
fn tanh() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.tanh(1);
var b = Math.tanh(0);
@ -745,8 +713,7 @@ fn tanh() {
#[test]
fn trunc() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = Math.trunc(13.37);
var b = Math.trunc(0.123);

6
boa/src/builtins/mod.rs

@ -39,11 +39,11 @@ pub(crate) use self::{
symbol::Symbol,
undefined::Undefined,
};
use crate::{Interpreter, Value};
use crate::{Context, Value};
/// Initializes builtin objects and functions
#[inline]
pub fn init(interpreter: &mut Interpreter) {
pub fn init(interpreter: &mut Context) {
let globals = [
// The `Function` global must be initialized before other types.
function::init,
@ -75,7 +75,7 @@ pub fn init(interpreter: &mut Interpreter) {
for init in &globals {
let (name, value) = init(interpreter);
let global = interpreter.global();
let global = interpreter.global_object();
match global {
Value::Object(ref global_object) => {
global_object.borrow_mut().insert_field(name, value);

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

@ -13,7 +13,7 @@
#[cfg(test)]
mod tests;
use crate::{BoaProfiler, Interpreter, Value};
use crate::{BoaProfiler, Context, Value};
/// JavaScript global `NaN` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -25,7 +25,7 @@ impl NaN {
/// Initialize the `NaN` property on the global object.
#[inline]
pub(crate) fn init(_interpreter: &mut Interpreter) -> (&'static str, Value) {
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::from(f64::NAN))

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

@ -17,7 +17,10 @@ use super::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
};
use crate::{exec::Interpreter, value::AbstractRelation, BoaProfiler, Result, Value};
use crate::{
value::{AbstractRelation, Value},
BoaProfiler, Context, Result,
};
use num_traits::float::FloatCore;
mod conversions;
@ -101,7 +104,7 @@ impl Number {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue
fn this_number_value(value: &Value, ctx: &mut Interpreter) -> Result<f64> {
fn this_number_value(value: &Value, ctx: &mut Context) -> Result<f64> {
match *value {
Value::Integer(integer) => return Ok(f64::from(integer)),
Value::Rational(rational) => return Ok(rational),
@ -128,11 +131,7 @@ impl Number {
/// `[[Construct]]` - Creates a Number instance
///
/// `[[Call]]` - Creates a number primitive
pub(crate) fn make_number(
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
pub(crate) fn make_number(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_numeric_number(ctx)?,
None => 0.0,
@ -156,7 +155,7 @@ impl Number {
pub(crate) fn to_exponential(
this: &Value,
_args: &[Value],
ctx: &mut Interpreter,
ctx: &mut Context,
) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
let this_str_num = Self::num_to_exponential(this_num);
@ -174,7 +173,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn to_fixed(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
let precision = match args.get(0) {
Some(n) => match n.to_integer(ctx)? as i32 {
@ -204,7 +203,7 @@ impl Number {
pub(crate) fn to_locale_string(
this: &Value,
_args: &[Value],
ctx: &mut Interpreter,
ctx: &mut Context,
) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
let this_str_num = format!("{}", this_num);
@ -222,11 +221,7 @@ 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 Interpreter,
) -> Result<Value> {
pub(crate) fn to_precision(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?;
let _num_str_len = format!("{}", this_num).len();
let _precision = match args.get(0) {
@ -380,7 +375,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// 1. Let x be ? thisNumberValue(this value).
let x = Self::this_number_value(this, ctx)?;
@ -435,7 +430,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_number_value(this, ctx)?))
}
@ -453,11 +448,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 Interpreter,
) -> Result<Value> {
pub(crate) fn parse_int(_this: &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 {
@ -523,11 +514,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 Interpreter,
) -> Result<Value> {
pub(crate) fn parse_float(_this: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
if let Some(val) = args.get(0) {
match val {
Value::String(s) => {
@ -572,7 +559,7 @@ impl Number {
pub(crate) fn global_is_finite(
_this: &Value,
args: &[Value],
ctx: &mut Interpreter,
ctx: &mut Context,
) -> Result<Value> {
if let Some(value) = args.get(0) {
let number = value.to_number(ctx)?;
@ -596,11 +583,7 @@ 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 Interpreter,
) -> Result<Value> {
pub(crate) fn global_is_nan(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(value) = args.get(0) {
let number = value.to_number(ctx)?;
Ok(number.is_nan().into())
@ -626,7 +609,7 @@ impl Number {
pub(crate) fn number_is_finite(
_this: &Value,
args: &[Value],
_ctx: &mut Interpreter,
_ctx: &mut Context,
) -> Result<Value> {
Ok(Value::from(if let Some(val) = args.get(0) {
match val {
@ -652,7 +635,7 @@ impl Number {
pub(crate) fn number_is_integer(
_this: &Value,
args: &[Value],
_ctx: &mut Interpreter,
_ctx: &mut Context,
) -> Result<Value> {
Ok(args.get(0).map_or(false, Self::is_integer).into())
}
@ -674,7 +657,7 @@ impl Number {
pub(crate) fn number_is_nan(
_this: &Value,
args: &[Value],
_ctx: &mut Interpreter,
_ctx: &mut Context,
) -> Result<Value> {
Ok(Value::from(if let Some(val) = args.get(0) {
match val {
@ -704,7 +687,7 @@ impl Number {
pub(crate) fn is_safe_integer(
_this: &Value,
args: &[Value],
_ctx: &mut Interpreter,
_ctx: &mut Context,
) -> Result<Value> {
Ok(Value::from(match args.get(0) {
Some(Value::Integer(_)) => true,
@ -739,8 +722,8 @@ impl Number {
/// Initialise the `Number` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));

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

@ -1,11 +1,10 @@
#![allow(clippy::float_cmp)]
use crate::{builtins::Number, exec::Interpreter, forward, forward_val, realm::Realm};
use crate::{builtins::Number, forward, forward_val, Context};
#[test]
fn integer_number_primitive_to_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
(100).toString() === "100"
@ -16,8 +15,7 @@ fn integer_number_primitive_to_number_object() {
#[test]
fn call_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var default_zero = Number();
var int_one = Number(1);
@ -51,8 +49,7 @@ fn call_number() {
#[test]
fn to_exponential() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var default_exp = Number().toExponential();
var int_exp = Number(5).toExponential();
@ -80,8 +77,7 @@ fn to_exponential() {
#[test]
fn to_fixed() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var default_fixed = Number().toFixed();
var pos_fixed = Number("3.456e+4").toFixed();
@ -106,8 +102,7 @@ fn to_fixed() {
#[test]
fn to_locale_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var default_locale = Number().toLocaleString();
var small_locale = Number(5).toLocaleString();
@ -133,8 +128,7 @@ fn to_locale_string() {
#[test]
#[ignore]
fn to_precision() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var default_precision = Number().toPrecision();
var low_precision = Number(123456789).toPrecision(1);
@ -165,8 +159,7 @@ fn to_precision() {
#[test]
fn to_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("\"NaN\"", &forward(&mut engine, "Number(NaN).toString()"));
assert_eq!(
@ -338,8 +331,7 @@ fn to_string() {
#[test]
fn num_to_string_exponential() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("\"0\"", forward(&mut engine, "(0).toString()"));
assert_eq!("\"0\"", forward(&mut engine, "(-0).toString()"));
@ -377,8 +369,7 @@ fn num_to_string_exponential() {
#[test]
fn value_of() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// TODO: In addition to parsing numbers from strings, parse them bare As of October 2019
// the parser does not understand scientific e.g., Xe+Y or -Xe-Y notation.
let init = r#"
@ -436,8 +427,7 @@ fn same_value_zero() {
#[test]
fn from_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "Number(0n)"), "0",);
assert_eq!(&forward(&mut engine, "Number(100000n)"), "100000",);
@ -447,8 +437,7 @@ fn from_bigint() {
#[test]
fn number_constants() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert!(!forward_val(&mut engine, "Number.EPSILON")
.unwrap()
@ -475,48 +464,42 @@ fn number_constants() {
#[test]
fn parse_int_simple() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(\"6\")"), "6");
}
#[test]
fn parse_int_negative() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(\"-9\")"), "-9");
}
#[test]
fn parse_int_already_int() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(100)"), "100");
}
#[test]
fn parse_int_float() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(100.5)"), "100");
}
#[test]
fn parse_int_float_str() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(\"100.5\")"), "NaN");
}
#[test]
fn parse_int_inferred_hex() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(\"0xA\")"), "10");
}
@ -525,16 +508,14 @@ fn parse_int_inferred_hex() {
/// a radix 10 if no radix is specified. Some alternative implementations default to a radix of 8.
#[test]
fn parse_int_zero_start() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(\"018\")"), "18");
}
#[test]
fn parse_int_varying_radix() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let base_str = "1000";
@ -553,8 +534,7 @@ fn parse_int_varying_radix() {
#[test]
fn parse_int_negative_varying_radix() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let base_str = "-1000";
@ -573,16 +553,14 @@ fn parse_int_negative_varying_radix() {
#[test]
fn parse_int_malformed_str() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(\"hello\")"), "NaN");
}
#[test]
fn parse_int_undefined() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(undefined)"), "NaN");
}
@ -591,8 +569,7 @@ fn parse_int_undefined() {
/// passed as the first argument.
#[test]
fn parse_int_no_args() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt()"), "NaN");
}
@ -600,64 +577,56 @@ fn parse_int_no_args() {
/// Shows that extra arguments to parseInt are ignored.
#[test]
fn parse_int_too_many_args() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseInt(\"100\", 10, 10)"), "100");
}
#[test]
fn parse_float_simple() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(\"6.5\")"), "6.5");
}
#[test]
fn parse_float_int() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(10)"), "10");
}
#[test]
fn parse_float_int_str() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(\"8\")"), "8");
}
#[test]
fn parse_float_already_float() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(17.5)"), "17.5");
}
#[test]
fn parse_float_negative() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(\"-99.7\")"), "-99.7");
}
#[test]
fn parse_float_malformed_str() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(\"hello\")"), "NaN");
}
#[test]
fn parse_float_undefined() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(undefined)"), "NaN");
}
@ -665,8 +634,7 @@ fn parse_float_undefined() {
/// No arguments to parseFloat is treated the same as passing undefined as the first argument.
#[test]
fn parse_float_no_args() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat()"), "NaN");
}
@ -674,16 +642,14 @@ fn parse_float_no_args() {
/// Shows that the parseFloat function ignores extra arguments.
#[test]
fn parse_float_too_many_args() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(&forward(&mut engine, "parseFloat(\"100.5\", 10)"), "100.5");
}
#[test]
fn global_is_finite() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("false", &forward(&mut engine, "isFinite(Infinity)"));
assert_eq!("false", &forward(&mut engine, "isFinite(NaN)"));
@ -698,8 +664,7 @@ fn global_is_finite() {
#[test]
fn global_is_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("true", &forward(&mut engine, "isNaN(NaN)"));
assert_eq!("true", &forward(&mut engine, "isNaN('NaN')"));
@ -720,8 +685,7 @@ fn global_is_nan() {
#[test]
fn number_is_finite() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("false", &forward(&mut engine, "Number.isFinite(Infinity)"));
assert_eq!("false", &forward(&mut engine, "Number.isFinite(NaN)"));
@ -747,8 +711,7 @@ fn number_is_finite() {
#[test]
fn number_is_integer() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("true", &forward(&mut engine, "Number.isInteger(0)"));
assert_eq!("true", &forward(&mut engine, "Number.isInteger(1)"));
@ -795,8 +758,7 @@ fn number_is_integer() {
#[test]
fn number_is_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("true", &forward(&mut engine, "Number.isNaN(NaN)"));
assert_eq!("true", &forward(&mut engine, "Number.isNaN(Number.NaN)"));
@ -829,8 +791,7 @@ fn number_is_nan() {
#[test]
fn number_is_safe_integer() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!("true", &forward(&mut engine, "Number.isSafeInteger(3)"));
assert_eq!(

14
boa/src/builtins/object/gcobject.rs

@ -11,7 +11,7 @@ use crate::{
function_environment_record::BindingStatus, lexical_environment::new_function_environment,
},
syntax::ast::node::RcStatementList,
Executable, Interpreter, Result, Value,
Context, Executable, Result, Value,
};
use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace};
use std::{
@ -107,7 +107,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 Interpreter) -> Result<Value> {
pub fn call(&self, this: &Value, args: &[Value], ctx: &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() {
@ -160,7 +160,7 @@ impl GcObject {
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
ctx.realm.environment.push(local_env);
ctx.realm_mut().environment.push(local_env);
FunctionBody::Ordinary(body.clone())
}
@ -176,7 +176,7 @@ impl GcObject {
FunctionBody::BuiltIn(func) => func(this, args, ctx),
FunctionBody::Ordinary(body) => {
let result = body.run(ctx);
ctx.realm.environment.pop();
ctx.realm_mut().environment.pop();
result
}
@ -189,7 +189,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 Interpreter) -> Result<Value> {
pub fn construct(&self, args: &[Value], ctx: &mut Context) -> Result<Value> {
let this = Object::create(self.borrow().get(&PROTOTYPE.into())).into();
let this_function_object = self.clone();
@ -242,13 +242,13 @@ impl GcObject {
.borrow_mut()
.initialize_binding("arguments", arguments_obj);
ctx.realm.environment.push(local_env);
ctx.realm_mut().environment.push(local_env);
// Call body should be set before reaching here
let _ = body.run(ctx);
// local_env gets dropped here, its no longer needed
let binding = ctx.realm.environment.get_this_binding();
let binding = ctx.realm_mut().environment.get_this_binding();
Ok(binding)
}
}

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

@ -19,10 +19,9 @@ use crate::{
map::ordered_map::OrderedMap,
BigInt, Date, RegExp,
},
exec::Interpreter,
property::{Property, PropertyKey},
value::{same_value, RcBigInt, RcString, RcSymbol, Value},
BoaProfiler, Result,
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap;
@ -469,14 +468,15 @@ impl Object {
}
/// Create a new object.
pub fn make_object(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub fn make_object(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() {
return arg.to_object(ctx);
}
}
let global = ctx.global_object();
Ok(Value::new_object(Some(ctx.global())))
Ok(Value::new_object(Some(global)))
}
/// `Object.create( proto, [propertiesObject] )`
@ -489,7 +489,7 @@ pub fn make_object(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<V
///
/// [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], interpreter: &mut Interpreter) -> Result<Value> {
pub fn create(_: &Value, args: &[Value], interpreter: &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);
@ -510,7 +510,7 @@ pub fn create(_: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul
}
/// Uses the SameValue algorithm to check equality of objects
pub fn is(_: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub fn is(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let x = args.get(0).cloned().unwrap_or_else(Value::undefined);
let y = args.get(1).cloned().unwrap_or_else(Value::undefined);
@ -518,7 +518,7 @@ pub fn is(_: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
}
/// Get the `prototype` of an object.
pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object");
Ok(obj
.as_object()
@ -526,7 +526,7 @@ pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> Resul
}
/// Set the `prototype` of an object.
pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).expect("Cannot get object").clone();
obj.as_object_mut().unwrap().prototype = proto;
@ -534,7 +534,7 @@ pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> Resul
}
/// Define a property in an object
pub fn define_property(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub fn define_property(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object");
let prop = args.get(1).expect("Cannot get object").to_string(ctx)?;
let desc = Property::from(args.get(2).expect("Cannot get object"));
@ -552,7 +552,7 @@ pub fn define_property(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Resu
///
/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// FIXME: it should not display the object.
Ok(this.display().to_string().into())
}
@ -568,7 +568,7 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value
///
/// [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 Interpreter) -> Result<Value> {
pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let prop = if args.is_empty() {
None
} else {
@ -586,11 +586,7 @@ pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Interpreter) ->
}
}
pub fn property_is_enumerable(
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
pub fn property_is_enumerable(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let key = match args.get(0) {
None => return Ok(Value::from(false)),
Some(key) => key,
@ -610,8 +606,8 @@ pub fn property_is_enumerable(
/// Initialise the `Object` object on the global object.
#[inline]
pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
let global = interpreter.global();
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("object", "init");
let prototype = Value::new_object(None);

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

@ -1,9 +1,8 @@
use crate::{exec::Interpreter, forward, realm::Realm};
use crate::{forward, Context};
#[test]
fn object_create_with_regular_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
const foo = { a: 5 };
@ -18,8 +17,7 @@ fn object_create_with_regular_object() {
#[test]
fn object_create_with_undefined() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
try {
@ -38,8 +36,7 @@ fn object_create_with_undefined() {
#[test]
fn object_create_with_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
try {
@ -60,8 +57,7 @@ fn object_create_with_number() {
#[ignore]
// to test on __proto__ somehow. __proto__ getter is not working as expected currently
fn object_create_with_function() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
const x = function (){};
@ -75,8 +71,7 @@ fn object_create_with_function() {
#[test]
fn object_is() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var foo = { a: 1};
@ -96,13 +91,12 @@ fn object_is() {
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.realm.global_obj.is_global());
assert!(!engine.realm.global_obj.get_field("Object").is_global());
assert!(engine.global_object().is_global());
assert!(!engine.global_object().get_field("Object").is_global());
}
#[test]
fn object_has_own_property() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let x = { someProp: 1, undefinedProp: undefined, nullProp: null };
"#;
@ -122,8 +116,7 @@ fn object_has_own_property() {
#[test]
fn object_property_is_enumerable() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let x = { enumerableProp: 'yes' };
"#;

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

@ -14,10 +14,9 @@ use regex::Regex;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::object::ObjectData,
exec::Interpreter,
property::Property,
value::{RcString, Value},
BoaProfiler, Result,
BoaProfiler, Context, Result,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
@ -70,7 +69,7 @@ impl RegExp {
pub(crate) const LENGTH: usize = 2;
/// Create a new `RegExp`
pub(crate) fn make_regexp(this: &Value, args: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn make_regexp(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let arg = args.get(0).ok_or_else(Value::undefined)?;
let mut regex_body = String::new();
let mut regex_flags = String::new();
@ -167,7 +166,7 @@ impl RegExp {
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll
// fn get_dot_all(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_dot_all(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all)))
// }
@ -182,7 +181,7 @@ impl RegExp {
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags
// /// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2
// fn get_flags(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_flags(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone())))
// }
@ -196,7 +195,7 @@ impl RegExp {
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global
// fn get_global(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_global(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global)))
// }
@ -210,7 +209,7 @@ impl RegExp {
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase
// fn get_ignore_case(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_ignore_case(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case)))
// }
@ -224,7 +223,7 @@ impl RegExp {
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline
// fn get_multiline(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_multiline(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline)))
// }
@ -239,7 +238,7 @@ impl RegExp {
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
// fn get_source(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_source(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// Ok(this.get_internal_slot("OriginalSource"))
// }
@ -253,7 +252,7 @@ impl RegExp {
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky
// fn get_sticky(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_sticky(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky)))
// }
@ -268,7 +267,7 @@ impl RegExp {
// ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode
// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode
// fn get_unicode(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
// fn get_unicode(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
// this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode)))
// }
@ -284,7 +283,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn test(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let arg_str = args
.get(0)
.expect("could not get argument")
@ -323,7 +322,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn exec(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let arg_str = args
.get(0)
.expect("could not get argument")
@ -379,7 +378,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 Interpreter) -> Result<Value> {
pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Context) -> Result<Value> {
let (matcher, flags) = if let Some(object) = this.as_object() {
let regex = object.as_regexp().unwrap();
(regex.matcher.clone(), regex.flags.clone())
@ -411,7 +410,7 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
let (body, flags) = if let Some(object) = this.as_object() {
let regex = object.as_regexp().unwrap();
(regex.original_source.clone(), regex.flags.clone())
@ -478,9 +477,9 @@ impl RegExp {
/// Initialise the `RegExp` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let global = interpreter.global();
let global = interpreter.global_object();
// Create prototype
let prototype = Value::new_object(Some(global));

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

@ -1,9 +1,8 @@
use crate::{exec::Interpreter, forward, realm::Realm};
use crate::{forward, Context};
#[test]
fn constructors() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var constructed = new RegExp("[0-9]+(\\.[0-9]+)?");
var literal = /[0-9]+(\.[0-9]+)?/;
@ -20,7 +19,7 @@ fn constructors() {
// #[test]
// fn flags() {
// let mut engine = Interpreter::new();
// let mut engine = Context::new();
// let init = r#"
// var re_gi = /test/gi;
// var re_sm = /test/sm;
@ -46,8 +45,7 @@ fn constructors() {
#[test]
fn last_index() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var regex = /[0-9]+(\.[0-9]+)?/g;
"#;
@ -62,8 +60,7 @@ fn last_index() {
#[test]
fn exec() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = 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');
@ -85,8 +82,7 @@ fn exec() {
#[test]
fn to_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(&mut engine, "(new RegExp('a+b+c')).toString()"),

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

@ -18,10 +18,9 @@ use crate::{
object::{Object, ObjectData},
RegExp,
},
exec::Interpreter,
property::Property,
value::{RcString, Value},
BoaProfiler, Result,
BoaProfiler, Context, Result,
};
use regex::Regex;
use std::string::String as StdString;
@ -48,7 +47,7 @@ impl String {
/// which can differ in JavaScript engines. In Boa it is `2^32 - 1`
pub(crate) const MAX_STRING_LENGTH: f64 = u32::MAX as f64;
fn this_string_value(this: &Value, ctx: &mut Interpreter) -> Result<RcString> {
fn this_string_value(this: &Value, ctx: &mut Context) -> Result<RcString> {
match this {
Value::String(ref string) => return Ok(string.clone()),
Value::Object(ref object) => {
@ -67,11 +66,7 @@ impl String {
///
/// [[Call]] - Returns a new native `string`
/// <https://tc39.es/ecma262/#sec-string-constructor-string-value>
pub(crate) fn make_string(
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
pub(crate) fn make_string(this: &Value, args: &[Value], ctx: &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) {
@ -91,7 +86,7 @@ impl 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 Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &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)?))
}
@ -112,7 +107,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -154,11 +149,7 @@ 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 Interpreter,
) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -198,7 +189,7 @@ 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 Interpreter) -> Result<Value> {
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();
@ -220,7 +211,7 @@ 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 Interpreter) -> Result<Value> {
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)?;
@ -254,7 +245,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -304,11 +295,7 @@ 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 Interpreter,
) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -351,7 +338,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -397,7 +384,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -459,7 +446,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn replace(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// TODO: Support Symbol replacer
let primitive_val = this.to_string(ctx)?;
if args.is_empty() {
@ -617,7 +604,7 @@ 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 Interpreter) -> Result<Value> {
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)?;
@ -660,11 +647,7 @@ 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 Interpreter,
) -> Result<Value> {
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)?;
@ -705,7 +688,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn r#match(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let re = RegExp::make_regexp(&Value::from(Object::default()), &[args[0].clone()], ctx)?;
RegExp::r#match(&re, this.to_string(ctx)?, ctx)
}
@ -756,7 +739,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn pad_end(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let primitive = this.to_string(ctx)?;
if args.is_empty() {
return Err(Value::from("padEnd requires maxLength argument"));
@ -783,7 +766,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn pad_start(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let primitive = this.to_string(ctx)?;
if args.is_empty() {
return Err(Value::from("padStart requires maxLength argument"));
@ -808,11 +791,11 @@ impl String {
// And does not include:
// '\u{FEFF}' (zero width non-breaking space)
match c {
// Explicit whitespace: https://tc39.es/ecma262/#sec-white-space
// Explicit whitespace: https://tc39.es/ecma262/#sec-white-space
'\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{0020}' | '\u{00A0}' | '\u{FEFF}' |
// Unicode Space_Seperator category
// Unicode Space_Seperator category
'\u{1680}' | '\u{2000}'..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' |
// Line terminators: https://tc39.es/ecma262/#sec-line-terminators
// Line terminators: https://tc39.es/ecma262/#sec-line-terminators
'\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => true,
_ => false,
}
@ -830,7 +813,7 @@ 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 Interpreter) -> Result<Value> {
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)?;
Ok(Value::from(
@ -850,8 +833,7 @@ 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 Interpreter) -> Result<Value> {
let this = this.require_object_coercible(ctx)?;
pub(crate) fn trim_start(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let string = this.to_string(ctx)?;
Ok(Value::from(
string.trim_start_matches(Self::is_trimmable_whitespace),
@ -870,7 +852,7 @@ 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 Interpreter) -> Result<Value> {
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)?;
Ok(Value::from(
@ -889,7 +871,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str = this.to_string(ctx)?;
@ -911,7 +893,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str = this.to_string(ctx)?;
@ -930,7 +912,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -979,7 +961,7 @@ 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 Interpreter) -> Result<Value> {
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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?;
@ -1035,7 +1017,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn value_of(this: &Value, args: &[Value], ctx: &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)
}
@ -1053,7 +1035,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn match_all(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let re: Value = match args.get(0) {
Some(arg) => {
if arg.is_null() {
@ -1084,12 +1066,12 @@ impl String {
/// Initialise the `String` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create `String` `prototype`
let global = interpreter.global();
let global = interpreter.global_object();
let prototype = Value::new_object(Some(global));
let length = Property::default().value(Value::from(0));

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

@ -1,12 +1,11 @@
use crate::{exec::Interpreter, forward, forward_val, realm::Realm};
use crate::{forward, forward_val, Context};
///TODO: re-enable when getProperty() is finished;
#[test]
#[ignore]
fn length() {
//TEST262: https://github.com/tc39/test262/blob/master/test/built-ins/String/length.js
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
const a = new String(' ');
const b = new String('\ud834\udf06');
@ -30,8 +29,7 @@ fn length() {
#[test]
fn new_string_has_length() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let a = new String("1234");
a
@ -43,8 +41,7 @@ fn new_string_has_length() {
#[test]
fn new_utf8_string_has_length() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let a = new String("中文");
a
@ -56,8 +53,7 @@ fn new_utf8_string_has_length() {
#[test]
fn concat() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var hello = new String('Hello, ');
var world = new String('world! ');
@ -74,8 +70,7 @@ fn concat() {
#[test]
fn generic_concat() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
Number.prototype.concat = String.prototype.concat;
let number = new Number(100);
@ -90,8 +85,7 @@ fn generic_concat() {
#[test]
/// Test the correct type is returned from call and construct
fn construct_and_call() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var hello = new String('Hello');
var world = String('world');
@ -108,8 +102,7 @@ fn construct_and_call() {
#[test]
fn repeat() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = new String('');
var en = new String('english');
@ -130,8 +123,7 @@ fn repeat() {
#[test]
fn repeat_throws_when_count_is_negative() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(
@ -150,8 +142,7 @@ fn repeat_throws_when_count_is_negative() {
#[test]
fn repeat_throws_when_count_is_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(
@ -170,8 +161,7 @@ fn repeat_throws_when_count_is_infinity() {
#[test]
fn repeat_throws_when_count_overflows_max_length() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(
@ -190,8 +180,7 @@ fn repeat_throws_when_count_overflows_max_length() {
#[test]
fn repeat_generic() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = "Number.prototype.repeat = String.prototype.repeat;";
forward(&mut engine, init);
@ -205,8 +194,7 @@ fn repeat_generic() {
#[test]
fn replace() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = "abc";
a = a.replace("a", "2");
@ -220,8 +208,7 @@ fn replace() {
#[test]
fn replace_no_match() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = "abc";
a = a.replace(/d/, "$&$&");
@ -234,8 +221,7 @@ fn replace_no_match() {
#[test]
fn replace_with_capture_groups() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var re = /(\w+)\s(\w+)/;
var a = "John Smith";
@ -250,8 +236,7 @@ fn replace_with_capture_groups() {
#[test]
fn replace_with_tenth_capture_group() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var re = /(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/;
var a = "0123456789";
@ -265,8 +250,7 @@ fn replace_with_tenth_capture_group() {
#[test]
fn replace_substitutions() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var re = / two /;
var a = "one two three";
@ -288,8 +272,7 @@ fn replace_substitutions() {
#[test]
fn replace_with_function() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var a = "ecmascript is cool";
var p1, p2, p3;
@ -315,8 +298,7 @@ fn replace_with_function() {
#[test]
fn starts_with() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = new String('');
var en = new String('english');
@ -340,8 +322,7 @@ fn starts_with() {
#[test]
fn ends_with() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var empty = new String('');
var en = new String('english');
@ -365,8 +346,7 @@ fn ends_with() {
#[test]
fn match_all() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "'aa'.matchAll(null).length"), "0");
assert_eq!(forward(&mut engine, "'aa'.matchAll(/b/).length"), "0");
@ -408,8 +388,7 @@ fn match_all() {
#[test]
fn test_match() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var str = new String('The Quick Brown Fox Jumps Over The Lazy Dog');
var result1 = str.match(/quick\s(brown).+?(jumps)/i);
@ -453,8 +432,7 @@ fn test_match() {
#[test]
fn trim() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "'Hello'.trim()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trim()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "'Hello \n\r'.trim()"), "\"Hello\"");
@ -463,8 +441,7 @@ fn trim() {
#[test]
fn trim_start() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "'Hello'.trimStart()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trimStart()"), "\"Hello\"");
assert_eq!(
@ -476,8 +453,7 @@ fn trim_start() {
#[test]
fn trim_end() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "'Hello'.trimEnd()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trimEnd()"), "\" \nHello\"");
assert_eq!(forward(&mut engine, "'Hello \n'.trimEnd()"), "\"Hello\"");
@ -486,8 +462,7 @@ fn trim_end() {
#[test]
fn index_of_with_no_arguments() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.indexOf()"), "-1");
assert_eq!(forward(&mut engine, "'undefined'.indexOf()"), "0");
assert_eq!(forward(&mut engine, "'a1undefined'.indexOf()"), "2");
@ -498,8 +473,7 @@ fn index_of_with_no_arguments() {
#[test]
fn index_of_with_string_search_string_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.indexOf('hello')"), "-1");
assert_eq!(
forward(&mut engine, "'undefined'.indexOf('undefined')"),
@ -525,8 +499,7 @@ fn index_of_with_string_search_string_argument() {
#[test]
fn index_of_with_non_string_search_string_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.indexOf(1)"), "-1");
assert_eq!(forward(&mut engine, "'1'.indexOf(1)"), "0");
assert_eq!(forward(&mut engine, "'true'.indexOf(true)"), "0");
@ -537,8 +510,7 @@ fn index_of_with_non_string_search_string_argument() {
#[test]
fn index_of_with_from_index_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.indexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'x'.indexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'abcx'.indexOf('x', 2)"), "3");
@ -553,8 +525,7 @@ fn index_of_with_from_index_argument() {
#[test]
fn generic_index_of() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
forward_val(
&mut engine,
"Number.prototype.indexOf = String.prototype.indexOf",
@ -568,8 +539,7 @@ fn generic_index_of() {
#[test]
fn index_of_empty_search_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.indexOf('')"), "0");
assert_eq!(forward(&mut engine, "''.indexOf('', 10)"), "0");
@ -580,8 +550,7 @@ fn index_of_empty_search_string() {
#[test]
fn last_index_of_with_no_arguments() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.lastIndexOf()"), "-1");
assert_eq!(forward(&mut engine, "'undefined'.lastIndexOf()"), "0");
assert_eq!(forward(&mut engine, "'a1undefined'.lastIndexOf()"), "2");
@ -601,8 +570,7 @@ fn last_index_of_with_no_arguments() {
#[test]
fn last_index_of_with_string_search_string_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.lastIndexOf('hello')"), "-1");
assert_eq!(
forward(&mut engine, "'undefined'.lastIndexOf('undefined')"),
@ -637,8 +605,7 @@ fn last_index_of_with_string_search_string_argument() {
#[test]
fn last_index_of_with_non_string_search_string_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.lastIndexOf(1)"), "-1");
assert_eq!(forward(&mut engine, "'1'.lastIndexOf(1)"), "0");
assert_eq!(forward(&mut engine, "'11'.lastIndexOf(1)"), "1");
@ -653,8 +620,7 @@ fn last_index_of_with_non_string_search_string_argument() {
#[test]
fn last_index_of_with_from_index_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.lastIndexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'x'.lastIndexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'abcxx'.lastIndexOf('x', 2)"), "4");
@ -669,8 +635,7 @@ fn last_index_of_with_from_index_argument() {
#[test]
fn last_index_with_empty_search_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "''.lastIndexOf('')"), "0");
assert_eq!(forward(&mut engine, "'x'.lastIndexOf('', 2)"), "1");
assert_eq!(forward(&mut engine, "'abcxx'.lastIndexOf('', 4)"), "4");
@ -681,8 +646,7 @@ fn last_index_with_empty_search_string() {
#[test]
fn generic_last_index_of() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
forward_val(
&mut engine,
"Number.prototype.lastIndexOf = String.prototype.lastIndexOf",
@ -696,8 +660,7 @@ fn generic_last_index_of() {
#[test]
fn last_index_non_integer_position_argument() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward(&mut engine, "''.lastIndexOf('x', new Number(4))"),
"-1"

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

@ -20,10 +20,9 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
exec::Interpreter,
property::{Attribute, Property},
value::{RcString, RcSymbol, Value},
BoaProfiler, Result,
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
@ -56,7 +55,7 @@ impl Symbol {
self.hash
}
fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result<RcSymbol> {
fn this_symbol_value(value: &Value, ctx: &mut Context) -> Result<RcSymbol> {
match value {
Value::Symbol(ref symbol) => return Ok(symbol.clone()),
Value::Object(ref object) => {
@ -82,7 +81,7 @@ 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 call(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> {
pub(crate) fn call(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?),
_ => None,
@ -102,7 +101,7 @@ 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 Interpreter) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let symbol = Self::this_symbol_value(this, ctx)?;
let description = symbol.description().unwrap_or("");
Ok(Value::from(format!("Symbol({})", description)))
@ -110,7 +109,7 @@ impl Symbol {
/// Initialise the `Symbol` object on the global object.
#[inline]
pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) {
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
// Define the Well-Known Symbols
// https://tc39.es/ecma262/#sec-well-known-symbols
let symbol_async_iterator =
@ -129,7 +128,7 @@ impl Symbol {
let symbol_to_string_tag = interpreter.construct_symbol(Some("Symbol.toStringTag".into()));
let symbol_unscopables = interpreter.construct_symbol(Some("Symbol.unscopables".into()));
let global = interpreter.global();
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype object

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

@ -1,9 +1,8 @@
use crate::{exec::Interpreter, forward, forward_val, realm::Realm};
use crate::{forward, forward_val, Context};
#[test]
fn call_symbol_and_check_return_type() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var sym = Symbol();
"#;
@ -14,8 +13,7 @@ fn call_symbol_and_check_return_type() {
#[test]
fn print_symbol_expect_description() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var sym = Symbol("Hello");
"#;
@ -26,8 +24,7 @@ fn print_symbol_expect_description() {
#[test]
fn symbol_access() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var x = {};
var sym1 = Symbol("Hello");

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

@ -12,7 +12,7 @@
#[cfg(test)]
mod tests;
use crate::{BoaProfiler, Interpreter, Value};
use crate::{BoaProfiler, Context, Value};
/// JavaScript global `undefined` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -24,7 +24,7 @@ impl Undefined {
/// Initialize the `undefined` property on the global object.
#[inline]
pub(crate) fn init(_interpreter: &mut Interpreter) -> (&'static str, Value) {
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::undefined())

27
boa/src/class.rs

@ -5,10 +5,7 @@
//!# use boa::{
//!# property::Attribute,
//!# class::{Class, ClassBuilder},
//!# exec::Interpreter,
//!# forward_val,
//!# realm::Realm,
//!# Finalize, Value, Result, Trace,
//!# Context, Finalize, Result, Trace, Value,
//!# };
//!#
//! // This does not have to be an enum it can also be a struct.
@ -27,7 +24,7 @@
//! const LENGTH: usize = 1;
//!
//! // This is what is called when we do `new Animal()`
//! fn constructor(_this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Self> {
//! fn constructor(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Self> {
//! // This is equivalent to `String(arg)`.
//! let kind = args.get(0).cloned().unwrap_or_default().to_string(ctx)?;
//!
@ -68,7 +65,7 @@ use crate::{
object::{GcObject, NativeObject, Object, ObjectData, PROTOTYPE},
},
property::{Attribute, Property, PropertyKey},
Interpreter, Result, Value,
Context, Result, Value,
};
use std::fmt::Debug;
@ -82,7 +79,7 @@ pub trait Class: NativeObject + Sized {
const ATTRIBUTE: Attribute = Attribute::all();
/// The constructor of the class.
fn constructor(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Self>;
fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Self>;
/// Initializes the internals and the methods of the class.
fn init(class: &mut ClassBuilder<'_>) -> Result<()>;
@ -93,13 +90,13 @@ 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 Interpreter) -> Result<Value>
fn raw_constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value>
where
Self: Sized;
}
impl<T: Class> ClassConstructor for T {
fn raw_constructor(this: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value>
fn raw_constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value>
where
Self: Sized,
{
@ -113,7 +110,7 @@ impl<T: Class> ClassConstructor for T {
#[derive(Debug)]
pub struct ClassBuilder<'context> {
/// The current context.
context: &'context mut Interpreter,
context: &'context mut Context,
/// The constructor object.
object: GcObject,
@ -123,11 +120,11 @@ pub struct ClassBuilder<'context> {
}
impl<'context> ClassBuilder<'context> {
pub(crate) fn new<T>(context: &'context mut Interpreter) -> Self
pub(crate) fn new<T>(context: &'context mut Context) -> Self
where
T: ClassConstructor,
{
let global = context.global();
let global = context.global_object();
let prototype = {
let object_prototype = global.get_field("Object").get_field(PROTOTYPE);
@ -190,7 +187,7 @@ impl<'context> ClassBuilder<'context> {
let mut function = Object::function(
Function::BuiltIn(function.into(), FunctionFlags::CALLABLE),
self.context
.global()
.global_object()
.get_field("Function")
.get_field("prototype"),
);
@ -214,7 +211,7 @@ impl<'context> ClassBuilder<'context> {
let mut function = Object::function(
Function::BuiltIn(function.into(), FunctionFlags::CALLABLE),
self.context
.global()
.global_object()
.get_field("Function")
.get_field("prototype"),
);
@ -262,7 +259,7 @@ impl<'context> ClassBuilder<'context> {
}
/// Return the current context.
pub fn context(&mut self) -> &'_ mut Interpreter {
pub fn context(&mut self) -> &'_ mut Context {
self.context
}
}

496
boa/src/context.rs

@ -0,0 +1,496 @@
//! Javascript context.
use crate::{
builtins::{
self,
function::{Function, FunctionFlags, NativeFunction},
object::ObjectData,
object::{GcObject, Object, PROTOTYPE},
Console, Symbol,
},
class::{Class, ClassBuilder},
exec::Interpreter,
property::{Property, PropertyKey},
realm::Realm,
syntax::{
ast::{
node::{
statement_list::RcStatementList, Call, FormalParameter, Identifier, New,
StatementList,
},
Const, Node,
},
Parser,
},
value::{PreferredType, RcString, RcSymbol, Type, Value},
BoaProfiler, Executable, Result,
};
use std::result::Result as StdResult;
/// Javascript context. It is the primary way to interact with the runtime.
///
/// For each `Context` instance a new instance of runtime is created.
/// It means that it is safe to use different contexts in different threads,
/// but each `Context` instance must be used only from a single thread.
#[derive(Debug)]
pub struct Context {
/// realm holds both the global object and the environment
realm: Realm,
/// The current executor.
executor: Interpreter,
/// Symbol hash.
///
/// For now this is an incremented u32 number.
symbol_count: u32,
/// console object state.
console: Console,
}
impl Default for Context {
fn default() -> Self {
let realm = Realm::create();
let executor = Interpreter::new();
let mut context = Self {
realm,
executor,
symbol_count: 0,
console: Console::default(),
};
// Add new builtIns to Context Realm
// At a later date this can be removed from here and called explicitly,
// but for now we almost always want these default builtins
context.create_intrinsics();
context
}
}
impl Context {
/// Create a new `Context`.
pub fn new() -> Self {
Default::default()
}
pub fn realm(&self) -> &Realm {
&self.realm
}
pub fn realm_mut(&mut self) -> &mut Realm {
&mut self.realm
}
pub fn executor(&mut self) -> &mut Interpreter {
&mut self.executor
}
/// A helper function for getting a immutable reference to the `console` object.
pub(crate) fn console(&self) -> &Console {
&self.console
}
/// A helper function for getting a mutable reference to the `console` object.
pub(crate) fn console_mut(&mut self) -> &mut Console {
&mut self.console
}
/// Sets up the default global objects within Global
fn create_intrinsics(&mut self) {
let _timer = BoaProfiler::global().start_event("create_intrinsics", "interpreter");
// Create intrinsics, add global objects here
builtins::init(self);
}
/// Generates a new `Symbol` internal hash.
///
/// This currently is an incremented value.
#[inline]
fn generate_hash(&mut self) -> u32 {
let hash = self.symbol_count;
self.symbol_count += 1;
hash
}
/// Construct a new `Symbol` with an optional description.
#[inline]
pub fn construct_symbol(&mut self, description: Option<RcString>) -> RcSymbol {
RcSymbol::from(Symbol::new(self.generate_hash(), description))
}
/// Construct an empty object.
#[inline]
pub fn construct_object(&self) -> GcObject {
let object_prototype = self
.global_object()
.get_field("Object")
.get_field(PROTOTYPE);
GcObject::new(Object::create(object_prototype))
}
/// <https://tc39.es/ecma262/#sec-call>
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),
_ => self.throw_type_error("not a function"),
}
}
/// Return the global object.
pub fn global_object(&self) -> &Value {
&self.realm.global_obj
}
/// Constructs a `RangeError` with the specified message.
pub fn construct_range_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
// Runs a `new RangeError(message)`.
New::from(Call::new(
Identifier::from("RangeError"),
vec![Const::from(message.into()).into()],
))
.run(self)
.expect_err("RangeError should always throw")
}
/// Throws a `RangeError` with the specified message.
pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_range_error(message))
}
/// Constructs a `TypeError` with the specified message.
pub fn construct_type_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
// Runs a `new TypeError(message)`.
New::from(Call::new(
Identifier::from("TypeError"),
vec![Const::from(message.into()).into()],
))
.run(self)
.expect_err("TypeError should always throw")
}
/// Throws a `TypeError` with the specified message.
pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_type_error(message))
}
/// Constructs a `ReferenceError` with the specified message.
pub fn construct_reference_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
New::from(Call::new(
Identifier::from("ReferenceError"),
vec![Const::from(message.into() + " is not defined").into()],
))
.run(self)
.expect_err("ReferenceError should always throw")
}
/// Throws a `ReferenceError` with the specified message.
pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_reference_error(message))
}
/// Constructs a `SyntaxError` with the specified message.
pub fn construct_syntax_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
New::from(Call::new(
Identifier::from("SyntaxError"),
vec![Const::from(message.into()).into()],
))
.run(self)
.expect_err("SyntaxError should always throw")
}
/// Throws a `SyntaxError` with the specified message.
pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_syntax_error(message))
}
/// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions
pub(crate) fn create_function<P, B>(
&mut self,
params: P,
body: B,
flags: FunctionFlags,
) -> Value
where
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
let function_prototype = self
.global_object()
.get_field("Function")
.get_field(PROTOTYPE);
// Every new function has a prototype property pre-made
let proto = Value::new_object(Some(self.global_object()));
let params = params.into();
let params_len = params.len();
let func = Function::Ordinary {
flags,
body: RcStatementList::from(body.into()),
params,
environment: self.realm.environment.get_current_environment().clone(),
};
let new_func = Object::function(func, function_prototype);
let val = Value::from(new_func);
// Set constructor field to the newly created Value (function object)
proto.set_field("constructor", val.clone());
val.set_field(PROTOTYPE, proto);
val.set_field("length", Value::from(params_len));
val
}
/// Create a new builin function.
pub fn create_builtin_function(
&mut self,
name: &str,
length: usize,
body: NativeFunction,
) -> Result<GcObject> {
let function_prototype = self
.global_object()
.get_field("Function")
.get_field(PROTOTYPE);
// Every new function has a prototype property pre-made
let proto = Value::new_object(Some(self.global_object()));
let mut function = Object::function(
Function::BuiltIn(body.into(), FunctionFlags::CALLABLE),
function_prototype,
);
function.set(PROTOTYPE.into(), proto);
function.set("length".into(), length.into());
function.set("name".into(), name.into());
Ok(GcObject::new(function))
}
/// Register a global function.
pub fn register_global_function(
&mut self,
name: &str,
length: usize,
body: NativeFunction,
) -> Result<()> {
let function = self.create_builtin_function(name, length, body)?;
self.global_object().set_field(name, function);
Ok(())
}
/// Converts an array object into a rust vector of values.
///
/// This is useful for the spread operator, for any other object an `Err` is returned
pub(crate) fn extract_array_properties(&mut self, value: &Value) -> StdResult<Vec<Value>, ()> {
if let Value::Object(ref x) = value {
// Check if object is array
if let ObjectData::Array = x.borrow().data {
let length = value.get_field("length").as_number().unwrap() as i32;
let values = (0..length)
.map(|idx| value.get_field(idx.to_string()))
.collect();
return Ok(values);
}
// Check if object is a Map
else if let ObjectData::Map(ref map) = x.borrow().data {
let values = map
.iter()
.map(|(key, value)| {
// Construct a new array containing the key-value pair
let array = Value::new_object(Some(
&self
.realm()
.environment
.get_global_object()
.expect("Could not get global object"),
));
array.set_data(ObjectData::Array);
array.as_object_mut().expect("object").set_prototype(
self.realm()
.environment
.get_binding_value("Array")
.expect("Array was not initialized")
.get_field(PROTOTYPE),
);
array.set_field("0", key);
array.set_field("1", value);
array.set_field("length", Value::from(2));
array
})
.collect();
return Ok(values);
}
return Err(());
}
Err(())
}
/// Converts an object to a primitive.
///
/// More information:
/// - [ECMAScript][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive
pub(crate) fn ordinary_to_primitive(
&mut self,
o: &Value,
hint: PreferredType,
) -> Result<Value> {
// 1. Assert: Type(O) is Object.
debug_assert!(o.get_type() == Type::Object);
// 2. Assert: Type(hint) is String and its value is either "string" or "number".
debug_assert!(hint == PreferredType::String || hint == PreferredType::Number);
// 3. If hint is "string", then
// a. Let methodNames be « "toString", "valueOf" ».
// 4. Else,
// a. Let methodNames be « "valueOf", "toString" ».
let method_names = if hint == PreferredType::String {
["toString", "valueOf"]
} else {
["valueOf", "toString"]
};
// 5. For each name in methodNames in List order, do
for name in &method_names {
// a. Let method be ? Get(O, name).
let method: Value = o.get_field(*name);
// b. If IsCallable(method) is true, then
if method.is_function() {
// i. Let result be ? Call(method, O).
let result = self.call(&method, &o, &[])?;
// ii. If Type(result) is not Object, return result.
if !result.is_object() {
return Ok(result);
}
}
}
// 6. Throw a TypeError exception.
self.throw_type_error("cannot convert object to primitive value")
}
/// https://tc39.es/ecma262/#sec-hasproperty
pub(crate) fn has_property(&self, obj: &Value, key: &PropertyKey) -> bool {
if let Some(obj) = obj.as_object() {
obj.has_property(key)
} else {
false
}
}
pub(crate) fn set_value(&mut self, node: &Node, value: Value) -> Result<Value> {
match node {
Node::Identifier(ref name) => {
self.realm
.environment
.set_mutable_binding(name.as_ref(), value.clone(), true);
Ok(value)
}
Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node
.obj()
.run(self)?
.set_field(get_const_field_node.field(), value)),
Node::GetField(ref get_field) => {
let field = get_field.field().run(self)?;
let key = field.to_property_key(self)?;
Ok(get_field.obj().run(self)?.set_field(key, value))
}
_ => panic!("TypeError: invalid assignment to {}", node),
}
}
/// Register a global class of type `T`, where `T` implemets `Class`.
///
/// # Example
/// ```ignore
/// #[derive(Debug, Trace, Finalize)]
/// struct MyClass;
///
/// impl Class for MyClass {
/// // ...
/// }
///
/// context.register_global_class::<MyClass>();
/// ```
pub fn register_global_class<T>(&mut self) -> Result<()>
where
T: Class,
{
let mut class_builder = ClassBuilder::new::<T>(self);
T::init(&mut class_builder)?;
let class = class_builder.build();
let property = Property::data_descriptor(class.into(), T::ATTRIBUTE);
self.global_object()
.as_object_mut()
.unwrap()
.insert_property(T::NAME, property);
Ok(())
}
fn parser_expr(src: &str) -> StdResult<StatementList, String> {
Parser::new(src.as_bytes())
.parse_all()
.map_err(|e| e.to_string())
}
/// Evaluates the given code.
///
/// # Examples
/// ```
///# use boa::Context;
/// let mut context = Context::new();
///
/// let value = context.eval("1 + 3").unwrap();
///
/// assert!(value.is_number());
/// assert_eq!(value.as_number().unwrap(), 4.0);
/// ```
#[allow(clippy::unit_arg, clippy::drop_copy)]
pub fn eval(&mut self, src: &str) -> Result<Value> {
let main_timer = BoaProfiler::global().start_event("Main", "Main");
let result = match Self::parser_expr(src) {
Ok(expr) => expr.run(self),
Err(e) => self.throw_type_error(e),
};
// The main_timer needs to be dropped before the BoaProfiler is.
drop(main_timer);
BoaProfiler::global().drop();
result
}
}

4
boa/src/exec/array/mod.rs

@ -1,6 +1,6 @@
//! Array declaration execution.
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{
builtins::Array,
syntax::ast::node::{ArrayDecl, Node},
@ -8,7 +8,7 @@ use crate::{
};
impl Executable for ArrayDecl {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec");
let array = Array::new_array(interpreter)?;
let mut elements = Vec::new();

6
boa/src/exec/block/mod.rs

@ -1,13 +1,13 @@
//! Block statement execution.
use super::{Executable, Interpreter, InterpreterState};
use super::{Context, Executable, InterpreterState};
use crate::{
environment::lexical_environment::new_declarative_environment, syntax::ast::node::Block,
BoaProfiler, Result, Value,
};
impl Executable for Block {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Block", "exec");
{
let env = &mut interpreter.realm_mut().environment;
@ -22,7 +22,7 @@ impl Executable for Block {
for statement in self.statements() {
obj = statement.run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
break;

14
boa/src/exec/break_node/mod.rs

@ -1,4 +1,4 @@
use super::{Executable, Interpreter, InterpreterState};
use super::{Context, Executable, InterpreterState};
use crate::{
syntax::ast::node::{Break, Continue},
Result, Value,
@ -8,16 +8,20 @@ use crate::{
mod tests;
impl Executable for Break {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
interpreter.set_current_state(InterpreterState::Break(self.label().map(String::from)));
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.executor()
.set_current_state(InterpreterState::Break(self.label().map(String::from)));
Ok(Value::undefined())
}
}
impl Executable for Continue {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
interpreter.set_current_state(InterpreterState::Continue(self.label().map(String::from)));
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.executor()
.set_current_state(InterpreterState::Continue(self.label().map(String::from)));
Ok(Value::undefined())
}

9
boa/src/exec/break_node/tests.rs

@ -1,17 +1,16 @@
use super::{Interpreter, InterpreterState};
use crate::{exec::Executable, syntax::ast::node::Break, Realm};
use super::{Context, InterpreterState};
use crate::{exec::Executable, syntax::ast::node::Break};
#[test]
fn check_post_state() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let brk: Break = Break::new("label");
brk.run(&mut engine).unwrap();
assert_eq!(
engine.get_current_state(),
engine.executor().get_current_state(),
&InterpreterState::Break(Some("label".to_string()))
);
}

8
boa/src/exec/call/mod.rs

@ -1,4 +1,4 @@
use super::{Executable, Interpreter, InterpreterState};
use super::{Context, Executable, InterpreterState};
use crate::{
syntax::ast::node::{Call, Node},
value::{Type, Value},
@ -6,7 +6,7 @@ use crate::{
};
impl Executable for Call {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Call", "exec");
let (this, func) = match self.expr() {
Node::GetConstField(ref get_const_field) => {
@ -44,7 +44,9 @@ impl Executable for Call {
let fnct_result = interpreter.call(&func, &this, &v_args);
// unset the early return flag
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
fnct_result
}

11
boa/src/exec/conditional/mod.rs

@ -1,13 +1,12 @@
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{
syntax::ast::node::{ConditionalOp, If},
Result, Value,
};
use std::borrow::Borrow;
impl Executable for If {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.borrow().to_boolean() {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.body().run(interpreter)?
} else if let Some(ref else_e) = self.else_node() {
else_e.run(interpreter)?
@ -18,8 +17,8 @@ impl Executable for If {
}
impl Executable for ConditionalOp {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.borrow().to_boolean() {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(if self.cond().run(interpreter)?.to_boolean() {
self.if_true().run(interpreter)?
} else {
self.if_false().run(interpreter)?

14
boa/src/exec/declaration/mod.rs

@ -1,6 +1,6 @@
//! Declaration execution.
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{
builtins::function::FunctionFlags,
environment::lexical_environment::VariableScope,
@ -11,7 +11,7 @@ use crate::{
};
impl Executable for FunctionDecl {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec");
let val = interpreter.create_function(
self.parameters().to_vec(),
@ -37,7 +37,7 @@ impl Executable for FunctionDecl {
}
impl Executable for FunctionExpr {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = interpreter.create_function(
self.parameters().to_vec(),
self.body().to_vec(),
@ -53,7 +53,7 @@ impl Executable for FunctionExpr {
}
impl Executable for VarDeclList {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
@ -79,7 +79,7 @@ impl Executable for VarDeclList {
}
impl Executable for ConstDeclList {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for decl in self.as_ref() {
let val = decl.init().run(interpreter)?;
@ -98,7 +98,7 @@ impl Executable for ConstDeclList {
}
impl Executable for LetDeclList {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
for var in self.as_ref() {
let val = match var.init() {
Some(v) => v.run(interpreter)?,
@ -119,7 +119,7 @@ impl Executable for LetDeclList {
}
impl Executable for ArrowFunctionDecl {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(interpreter.create_function(
self.params().to_vec(),
self.body().to_vec(),

96
boa/src/exec/exception.rs

@ -1,96 +0,0 @@
use super::*;
use crate::{
exec::Executable,
syntax::ast::{
node::{Call, Identifier, New},
Const,
},
};
impl Interpreter {
/// Constructs a `RangeError` with the specified message.
pub fn construct_range_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
// Runs a `new RangeError(message)`.
New::from(Call::new(
Identifier::from("RangeError"),
vec![Const::from(message.into()).into()],
))
.run(self)
.expect_err("RangeError should always throw")
}
/// Throws a `RangeError` with the specified message.
pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_range_error(message))
}
/// Constructs a `TypeError` with the specified message.
pub fn construct_type_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
// Runs a `new TypeError(message)`.
New::from(Call::new(
Identifier::from("TypeError"),
vec![Const::from(message.into()).into()],
))
.run(self)
.expect_err("TypeError should always throw")
}
/// Throws a `TypeError` with the specified message.
pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_type_error(message))
}
/// Constructs a `ReferenceError` with the specified message.
pub fn construct_reference_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
New::from(Call::new(
Identifier::from("ReferenceError"),
vec![Const::from(message.into() + " is not defined").into()],
))
.run(self)
.expect_err("ReferenceError should always throw")
}
/// Throws a `ReferenceError` with the specified message.
pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_reference_error(message))
}
/// Constructs a `SyntaxError` with the specified message.
pub fn construct_syntax_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
{
New::from(Call::new(
Identifier::from("SyntaxError"),
vec![Const::from(message.into()).into()],
))
.run(self)
.expect_err("SyntaxError should always throw")
}
/// Throws a `SyntaxError` with the specified message.
pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
{
Err(self.construct_syntax_error(message))
}
}

6
boa/src/exec/field/mod.rs

@ -1,4 +1,4 @@
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{
syntax::ast::node::{GetConstField, GetField},
value::{Type, Value},
@ -6,7 +6,7 @@ use crate::{
};
impl Executable for GetConstField {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = obj.to_object(interpreter)?;
@ -17,7 +17,7 @@ impl Executable for GetConstField {
}
impl Executable for GetField {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object {
obj = obj.to_object(interpreter)?;

4
boa/src/exec/identifier/mod.rs

@ -1,8 +1,8 @@
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{syntax::ast::node::identifier::Identifier, Result, Value};
impl Executable for Identifier {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter
.realm()
.environment

53
boa/src/exec/iteration/mod.rs

@ -1,18 +1,17 @@
//! Iteration node execution.
use super::{Executable, Interpreter, InterpreterState};
use super::{Context, Executable, InterpreterState};
use crate::{
environment::lexical_environment::new_declarative_environment,
syntax::ast::node::{DoWhileLoop, ForLoop, WhileLoop},
BoaProfiler, Result, Value,
};
use std::borrow::Borrow;
#[cfg(test)]
mod tests;
impl Executable for ForLoop {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
// Create the block environment.
let _timer = BoaProfiler::global().start_event("ForLoop", "exec");
{
@ -34,17 +33,21 @@ impl Executable for ForLoop {
{
let result = self.body().run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
@ -68,21 +71,25 @@ impl Executable for ForLoop {
}
impl Executable for WhileLoop {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut result = Value::undefined();
while self.cond().run(interpreter)?.borrow().to_boolean() {
while self.cond().run(interpreter)?.to_boolean() {
result = self.expr().run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
@ -98,19 +105,23 @@ impl Executable for WhileLoop {
}
impl Executable for DoWhileLoop {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let mut result = self.body().run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
return Ok(result);
}
InterpreterState::Continue(_label) => {
// TODO continue to label;
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {
@ -121,19 +132,23 @@ impl Executable for DoWhileLoop {
}
}
while self.cond().run(interpreter)?.borrow().to_boolean() {
while self.cond().run(interpreter)?.to_boolean() {
result = self.body().run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Break(_label) => {
// TODO break to label.
// Loops 'consume' breaks.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
// TODO continue to label.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// after breaking out of the block, continue execution of the loop
}
InterpreterState::Return => {

346
boa/src/exec/mod.rs

@ -6,7 +6,6 @@ mod break_node;
mod call;
mod conditional;
mod declaration;
mod exception;
mod field;
mod identifier;
mod iteration;
@ -17,33 +16,20 @@ mod return_smt;
mod spread;
mod statement_list;
mod switch;
#[cfg(test)]
mod tests;
mod throw;
mod try_node;
#[cfg(test)]
mod tests;
use crate::{
builtins,
builtins::{
function::{Function, FunctionFlags, NativeFunction},
object::{GcObject, Object, ObjectData, PROTOTYPE},
Console, Symbol,
},
class::{Class, ClassBuilder},
property::{Property, PropertyKey},
realm::Realm,
syntax::ast::{
constant::Const,
node::{FormalParameter, Node, RcStatementList, StatementList},
},
value::{PreferredType, RcString, RcSymbol, Type, Value},
BoaProfiler, Result,
syntax::ast::{constant::Const, node::Node},
BoaProfiler, Context, Result, Value,
};
use std::result::Result as StdResult;
pub trait Executable {
/// Runs this executable in the given executor.
fn run(&self, interpreter: &mut Interpreter) -> Result<Value>;
/// Runs this executable in the given context.
fn run(&self, interpreter: &mut Context) -> Result<Value>;
}
#[derive(Debug, Eq, PartialEq)]
@ -59,269 +45,19 @@ pub(crate) enum InterpreterState {
pub struct Interpreter {
/// the current state of the interpreter.
state: InterpreterState,
}
/// realm holds both the global object and the environment
pub realm: Realm,
/// This is for generating an unique internal `Symbol` hash.
symbol_count: u32,
/// console object state.
console: Console,
impl Default for Interpreter {
fn default() -> Self {
Self::new()
}
}
impl Interpreter {
/// Creates a new interpreter.
pub fn new(realm: Realm) -> Self {
let mut interpreter = Self {
pub fn new() -> Self {
Self {
state: InterpreterState::Executing,
realm,
symbol_count: 0,
console: Console::default(),
};
// Add new builtIns to Interpreter Realm
// At a later date this can be removed from here and called explicitly, but for now we almost always want these default builtins
interpreter.create_intrinsics();
interpreter
}
/// Sets up the default global objects within Global
fn create_intrinsics(&mut self) {
let _timer = BoaProfiler::global().start_event("create_intrinsics", "interpreter");
// Create intrinsics, add global objects here
builtins::init(self);
}
/// Retrieves the `Realm` of this executor.
#[inline]
pub(crate) fn realm(&self) -> &Realm {
&self.realm
}
/// Retrieves the `Realm` of this executor as a mutable reference.
#[inline]
pub(crate) fn realm_mut(&mut self) -> &mut Realm {
&mut self.realm
}
/// Retrieves the global object of the `Realm` of this executor.
#[inline]
pub fn global(&self) -> &Value {
&self.realm.global_obj
}
/// Generates a new `Symbol` internal hash.
///
/// This currently is an incremented value.
#[inline]
pub(crate) fn generate_hash(&mut self) -> u32 {
let hash = self.symbol_count;
self.symbol_count += 1;
hash
}
/// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions
pub(crate) fn create_function<P, B>(
&mut self,
params: P,
body: B,
flags: FunctionFlags,
) -> Value
where
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
let function_prototype = self.global().get_field("Function").get_field(PROTOTYPE);
// Every new function has a prototype property pre-made
let proto = Value::new_object(Some(self.global()));
let params = params.into();
let params_len = params.len();
let func = Function::Ordinary {
flags,
body: RcStatementList::from(body.into()),
params,
environment: self.realm.environment.get_current_environment().clone(),
};
let new_func = Object::function(func, function_prototype);
let val = Value::from(new_func);
// Set constructor field to the newly created Value (function object)
proto.set_field("constructor", val.clone());
val.set_field(PROTOTYPE, proto);
val.set_field("length", Value::from(params_len));
val
}
/// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions
pub fn create_builtin_function(
&mut self,
name: &str,
length: usize,
body: NativeFunction,
) -> Result<GcObject> {
let function_prototype = self.global().get_field("Function").get_field(PROTOTYPE);
// Every new function has a prototype property pre-made
let proto = Value::new_object(Some(self.global()));
let mut function = Object::function(
Function::BuiltIn(body.into(), FunctionFlags::CALLABLE),
function_prototype,
);
function.set(PROTOTYPE.into(), proto);
function.set("length".into(), length.into());
function.set("name".into(), name.into());
Ok(GcObject::new(function))
}
pub fn register_global_function(
&mut self,
name: &str,
length: usize,
body: NativeFunction,
) -> Result<()> {
let function = self.create_builtin_function(name, length, body)?;
self.global().set_field(name, function);
Ok(())
}
/// <https://tc39.es/ecma262/#sec-call>
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),
_ => self.throw_type_error("not a function"),
}
}
/// Converts an array object into a rust vector of values.
///
/// This is useful for the spread operator, for any other object an `Err` is returned
pub(crate) fn extract_array_properties(&mut self, value: &Value) -> StdResult<Vec<Value>, ()> {
if let Value::Object(ref x) = value {
// Check if object is array
if let ObjectData::Array = x.borrow().data {
let length = value.get_field("length").as_number().unwrap() as i32;
let values = (0..length)
.map(|idx| value.get_field(idx.to_string()))
.collect();
return Ok(values);
}
// Check if object is a Map
else if let ObjectData::Map(ref map) = x.borrow().data {
let values = map
.iter()
.map(|(key, value)| {
// Construct a new array containing the key-value pair
let array = Value::new_object(Some(
&self
.realm()
.environment
.get_global_object()
.expect("Could not get global object"),
));
array.set_data(ObjectData::Array);
array.as_object_mut().expect("object").set_prototype(
self.realm()
.environment
.get_binding_value("Array")
.expect("Array was not initialized")
.get_field(PROTOTYPE),
);
array.set_field("0", key);
array.set_field("1", value);
array.set_field("length", Value::from(2));
array
})
.collect();
return Ok(values);
}
return Err(());
}
Err(())
}
/// Converts an object to a primitive.
///
/// More information:
/// - [ECMAScript][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive
pub(crate) fn ordinary_to_primitive(
&mut self,
o: &Value,
hint: PreferredType,
) -> Result<Value> {
// 1. Assert: Type(O) is Object.
debug_assert!(o.get_type() == Type::Object);
// 2. Assert: Type(hint) is String and its value is either "string" or "number".
debug_assert!(hint == PreferredType::String || hint == PreferredType::Number);
// 3. If hint is "string", then
// a. Let methodNames be « "toString", "valueOf" ».
// 4. Else,
// a. Let methodNames be « "valueOf", "toString" ».
let method_names = if hint == PreferredType::String {
["toString", "valueOf"]
} else {
["valueOf", "toString"]
};
// 5. For each name in methodNames in List order, do
for name in &method_names {
// a. Let method be ? Get(O, name).
let method: Value = o.get_field(*name);
// b. If IsCallable(method) is true, then
if method.is_function() {
// i. Let result be ? Call(method, O).
let result = self.call(&method, &o, &[])?;
// ii. If Type(result) is not Object, return result.
if !result.is_object() {
return Ok(result);
}
}
}
// 6. Throw a TypeError exception.
self.throw_type_error("cannot convert object to primitive value")
}
/// https://tc39.es/ecma262/#sec-hasproperty
pub(crate) fn has_property(&self, obj: &Value, key: &PropertyKey) -> bool {
if let Some(obj) = obj.as_object() {
obj.has_property(key)
} else {
false
}
}
fn set_value(&mut self, node: &Node, value: Value) -> Result<Value> {
match node {
Node::Identifier(ref name) => {
self.realm
.environment
.set_mutable_binding(name.as_ref(), value.clone(), true);
Ok(value)
}
Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node
.obj()
.run(self)?
.set_field(get_const_field_node.field(), value)),
Node::GetField(ref get_field) => {
let field = get_field.field().run(self)?;
let key = field.to_property_key(self)?;
Ok(get_field.obj().run(self)?.set_field(key, value))
}
_ => panic!("TypeError: invalid assignment to {}", node),
}
}
@ -334,62 +70,10 @@ impl Interpreter {
pub(crate) fn get_current_state(&self) -> &InterpreterState {
&self.state
}
/// A helper function for getting a immutable reference to the `console` object.
pub(crate) fn console(&self) -> &Console {
&self.console
}
/// A helper function for getting a mutable reference to the `console` object.
pub(crate) fn console_mut(&mut self) -> &mut Console {
&mut self.console
}
/// Construct a new `Symbol` with an optional description.
#[inline]
pub fn construct_symbol(&mut self, description: Option<RcString>) -> RcSymbol {
RcSymbol::from(Symbol::new(self.generate_hash(), description))
}
/// Construct an empty object.
#[inline]
pub fn construct_object(&self) -> GcObject {
let object_prototype = self.global().get_field("Object").get_field(PROTOTYPE);
GcObject::new(Object::create(object_prototype))
}
/// Register a global class of type `T`, where `T` implemets `Class`.
///
/// # Example
/// ```ignore
/// #[derive(Debug, Trace, Finalize)]
/// struct MyClass;
///
/// impl Class for MyClass {
/// // ...
/// }
///
/// context.register_global_class::<MyClass>();
/// ```
pub fn register_global_class<T>(&mut self) -> Result<()>
where
T: Class,
{
let mut class_builder = ClassBuilder::new::<T>(self);
T::init(&mut class_builder)?;
let class = class_builder.build();
let property = Property::data_descriptor(class.into(), T::ATTRIBUTE);
self.global()
.as_object_mut()
.unwrap()
.insert_property(T::NAME, property);
Ok(())
}
}
impl Executable for Node {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Executable", "exec");
match *self {
Node::Const(Const::Null) => Ok(Value::null()),

4
boa/src/exec/new/mod.rs

@ -1,8 +1,8 @@
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{syntax::ast::node::New, BoaProfiler, Result, Value};
impl Executable for New {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("New", "exec");
let func_object = self.expr().run(interpreter)?;

10
boa/src/exec/object/mod.rs

@ -1,16 +1,14 @@
//! Object execution.
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{
syntax::ast::node::MethodDefinitionKind,
syntax::ast::node::{Object, PropertyDefinition},
Result, Value,
};
use std::borrow::Borrow;
impl Executable for Object {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let global_val = &interpreter
.realm()
.environment
@ -22,11 +20,11 @@ impl Executable for Object {
for property in self.properties().iter() {
match property {
PropertyDefinition::Property(key, value) => {
obj.borrow().set_field(key.clone(), value.run(interpreter)?);
obj.set_field(key.clone(), value.run(interpreter)?);
}
PropertyDefinition::MethodDefinition(kind, name, func) => {
if let MethodDefinitionKind::Ordinary = kind {
obj.borrow().set_field(name.clone(), func.run(interpreter)?);
obj.set_field(name.clone(), func.run(interpreter)?);
} else {
// TODO: Implement other types of MethodDefinitionKinds.
unimplemented!("other types of property method definitions.");

17
boa/src/exec/operator/mod.rs

@ -2,7 +2,7 @@
#[cfg(test)]
mod tests;
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{
environment::lexical_environment::VariableScope,
syntax::ast::{
@ -13,7 +13,7 @@ use crate::{
};
impl Executable for Assign {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Assign", "exec");
let val = self.rhs().run(interpreter)?;
match self.lhs() {
@ -49,7 +49,7 @@ impl Executable for Assign {
}
impl Executable for BinOp {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
match self.op() {
op::BinOp::Num(op) => {
let x = self.lhs().run(interpreter)?;
@ -133,7 +133,7 @@ impl Executable for BinOp {
.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.environment.set_mutable_binding(
interpreter.realm_mut().environment.set_mutable_binding(
name.as_ref(),
value.clone(),
true,
@ -160,12 +160,7 @@ impl Executable for BinOp {
impl BinOp {
/// Runs the assignment operators.
fn run_assign(
op: AssignOp,
x: Value,
y: Value,
interpreter: &mut Interpreter,
) -> Result<Value> {
fn run_assign(op: AssignOp, x: Value, y: Value, interpreter: &mut Context) -> Result<Value> {
match op {
AssignOp::Add => x.add(&y, interpreter),
AssignOp::Sub => x.sub(&y, interpreter),
@ -183,7 +178,7 @@ impl BinOp {
}
impl Executable for UnaryOp {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let x = self.target().run(interpreter)?;
Ok(match self.op() {

8
boa/src/exec/return_smt/mod.rs

@ -1,14 +1,16 @@
use super::{Executable, Interpreter, InterpreterState};
use super::{Context, Executable, InterpreterState};
use crate::{syntax::ast::node::Return, Result, Value};
impl Executable for Return {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let result = match self.expr() {
Some(ref v) => v.run(interpreter),
None => Ok(Value::undefined()),
};
// Set flag for return
interpreter.set_current_state(InterpreterState::Return);
interpreter
.executor()
.set_current_state(InterpreterState::Return);
result
}
}

4
boa/src/exec/spread/mod.rs

@ -1,8 +1,8 @@
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{syntax::ast::node::Spread, Result, Value};
impl Executable for Spread {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
// TODO: for now we can do nothing but return the value as-is
self.val().run(interpreter)
}

10
boa/src/exec/statement_list.rs

@ -1,19 +1,21 @@
//! Statement list execution.
use super::{Executable, Interpreter, InterpreterState};
use super::{Context, Executable, InterpreterState};
use crate::{syntax::ast::node::StatementList, BoaProfiler, Result, Value};
impl Executable for StatementList {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &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.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in self.statements().iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
obj = val;

20
boa/src/exec/switch/mod.rs

@ -1,15 +1,17 @@
use super::{Executable, Interpreter, InterpreterState};
use super::{Context, Executable, InterpreterState};
use crate::{syntax::ast::node::Switch, Result, Value};
#[cfg(test)]
mod tests;
impl Executable for Switch {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let val = self.val().run(interpreter)?;
let mut result = Value::null();
let mut matched = false;
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
// If a case block does not end with a break statement then subsequent cases will be run without
// checking their conditions until a break is encountered.
@ -21,7 +23,7 @@ impl Executable for Switch {
if fall_through || val.strict_equals(&cond.run(interpreter)?) {
matched = true;
let result = block.run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
return Ok(result);
@ -29,7 +31,9 @@ impl Executable for Switch {
InterpreterState::Break(_label) => {
// TODO, break to a label.
// Break statement encountered so therefore end switch statement.
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break;
}
InterpreterState::Continue(_label) => {
@ -46,10 +50,12 @@ impl Executable for Switch {
if !matched {
if let Some(default) = self.default() {
interpreter.set_current_state(InterpreterState::Executing);
interpreter
.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in default.iter().enumerate() {
let val = item.run(interpreter)?;
match interpreter.get_current_state() {
match interpreter.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
result = val;

71
boa/src/exec/tests.rs

@ -1,4 +1,4 @@
use crate::{builtins::Number, exec, exec::Interpreter, forward, forward_val, realm::Realm, Value};
use crate::{builtins::Number, exec, forward, forward_val, Context, Value};
#[test]
fn function_declaration_returns_undefined() {
@ -109,8 +109,7 @@ fn object_field_set() {
#[test]
fn spread_with_arguments() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
const a = [1, "test", 3, 4];
@ -136,8 +135,7 @@ fn spread_with_arguments() {
#[test]
fn array_rest_with_arguments() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
var b = [4, 5, 6]
@ -715,8 +713,7 @@ mod in_operator {
#[test]
fn should_type_error_when_rhs_not_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
var x = false;
@ -733,8 +730,7 @@ mod in_operator {
#[test]
fn should_set_this_value() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
function Foo() {
@ -752,8 +748,7 @@ mod in_operator {
#[test]
fn new_instance_should_point_to_prototype() {
// A new instance should point to a prototype object created with the constructor function
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
function Foo() {}
@ -880,8 +875,7 @@ fn function_decl_hoisting() {
#[test]
fn to_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert!(Value::null().to_bigint(&mut engine).is_err());
assert!(Value::undefined().to_bigint(&mut engine).is_err());
@ -892,8 +886,7 @@ fn to_bigint() {
#[test]
fn to_index() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(Value::undefined().to_index(&mut engine).unwrap(), 0);
assert!(Value::integer(-1).to_index(&mut engine).is_err());
@ -901,8 +894,7 @@ fn to_index() {
#[test]
fn to_integer() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert!(Number::equal(
Value::number(f64::NAN).to_integer(&mut engine).unwrap(),
@ -939,8 +931,7 @@ fn to_integer() {
#[test]
fn to_length() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(Value::number(f64::NAN).to_length(&mut engine).unwrap(), 0);
assert_eq!(
@ -971,8 +962,7 @@ fn to_length() {
#[test]
fn to_int32() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
macro_rules! check_to_int32 {
($from:expr => $to:expr) => {
@ -1085,8 +1075,7 @@ fn to_int32() {
#[test]
fn to_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(Value::null().to_string(&mut engine).unwrap(), "null");
assert_eq!(
@ -1103,8 +1092,7 @@ fn to_string() {
#[test]
fn calling_function_with_unspecified_arguments() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let scenario = r#"
function test(a, b) {
return b;
@ -1118,8 +1106,7 @@ fn calling_function_with_unspecified_arguments() {
#[test]
fn to_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert!(Value::undefined()
.to_object(&mut engine)
@ -1133,8 +1120,7 @@ fn to_object() {
#[test]
fn check_this_binding_in_object_literal() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
var foo = {
a: 3,
@ -1149,8 +1135,7 @@ fn check_this_binding_in_object_literal() {
#[test]
fn array_creation_benchmark() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
(function(){
let testArr = [];
@ -1167,8 +1152,7 @@ fn array_creation_benchmark() {
#[test]
fn array_pop_benchmark() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
(function(){
let testArray = [83, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32,
@ -1201,8 +1185,7 @@ fn array_pop_benchmark() {
#[test]
fn number_object_access_benchmark() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
new Number(
new Number(
@ -1218,8 +1201,7 @@ fn number_object_access_benchmark() {
#[test]
fn not_a_function() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let init = r#"
let a = {};
let b = true;
@ -1283,8 +1265,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 realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Tests all assignment operators as per [spec] and [mdn]
//
@ -1306,8 +1287,7 @@ 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 realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let test_cases = ["3 **= 5", "3 <<= 5", "3 >>= 5"];
@ -1322,8 +1302,7 @@ fn multicharacter_assignment_to_non_assignable() {
#[test]
#[ignore]
fn multicharacter_bitwise_assignment_to_non_assignable() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// Disabled - awaiting implementation.
let test_cases = ["3 >>>= 5", "3 &&= 5", "3 ||= 5", "3 ??= 5"];
@ -1338,8 +1317,7 @@ fn multicharacter_bitwise_assignment_to_non_assignable() {
#[test]
fn assign_to_array_decl() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert!(forward(&mut engine, "[1] = [2]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut engine, "[3, 5] = [7, 8]").starts_with("Uncaught \"SyntaxError\": "));
@ -1349,8 +1327,7 @@ fn assign_to_array_decl() {
#[test]
fn assign_to_object_decl() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
const ERR_MSG: &str =
"Uncaught \"SyntaxError\": \"expected token \';\', got \':\' in expression statement at line 1, col 3\"";

4
boa/src/exec/throw/mod.rs

@ -1,9 +1,9 @@
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{syntax::ast::node::Throw, Result, Value};
impl Executable for Throw {
#[inline]
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
Err(self.expr().run(interpreter)?)
}
}

4
boa/src/exec/try_node/mod.rs

@ -1,6 +1,6 @@
//! Try..catch node execution.
use super::{Executable, Interpreter};
use super::{Context, Executable};
use crate::{
environment::lexical_environment::{new_declarative_environment, VariableScope},
syntax::ast::node::Try,
@ -11,7 +11,7 @@ use crate::{
mod tests;
impl Executable for Try {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> {
fn run(&self, interpreter: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Try", "exec");
let res = self.block().run(interpreter).map_or_else(
|err| {

49
boa/src/lib.rs

@ -44,21 +44,21 @@ pub mod realm;
pub mod syntax;
pub mod value;
use crate::syntax::ast::node::StatementList;
pub use crate::{
exec::{Executable, Interpreter},
profiler::BoaProfiler,
realm::Realm,
syntax::{
lexer::Lexer,
parser::{ParseError, Parser},
},
value::Value,
};
mod context;
use std::result::Result as StdResult;
pub(crate) use crate::{exec::Executable, profiler::BoaProfiler};
pub use gc::{custom_trace, unsafe_empty_trace, Finalize, Trace};
// Export things to root level
pub use crate::{context::Context, value::Value};
use crate::syntax::{
ast::node::StatementList,
parser::{ParseError, Parser},
};
/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
#[must_use]
pub type Result<T> = StdResult<T, Value>;
@ -72,9 +72,10 @@ pub fn parse(src: &str) -> StdResult<StatementList, ParseError> {
Parser::new(src.as_bytes()).parse_all()
}
/// Execute the code using an existing Interpreter
/// The str is consumed and the state of the Interpreter is changed
pub fn forward(engine: &mut Interpreter, src: &str) -> String {
/// 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 {
// Setup executor
let expr = match parse(src) {
Ok(res) => res,
@ -94,12 +95,13 @@ pub fn forward(engine: &mut Interpreter, src: &str) -> String {
)
}
/// Execute the code using an existing Interpreter.
/// The str is consumed and the state of the Interpreter is changed
/// Execute the code using an existing Context.
/// The str is consumed and the state of the Context is changed
/// Similar to `forward`, except the current value is returned instad of the string
/// If the interpreter fails parsing an error value is returned instead (error object)
#[allow(clippy::unit_arg, clippy::drop_copy)]
pub fn forward_val(engine: &mut Interpreter, src: &str) -> Result<Value> {
#[cfg(test)]
pub(crate) fn forward_val(engine: &mut Context, src: &str) -> Result<Value> {
let main_timer = BoaProfiler::global().start_event("Main", "Main");
// Setup executor
let result = parse(src)
@ -117,10 +119,11 @@ pub fn forward_val(engine: &mut Interpreter, src: &str) -> Result<Value> {
result
}
/// Create a clean Interpreter and execute the code
pub fn exec(src: &str) -> String {
// Create new Realm
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
forward(&mut engine, src)
/// Create a clean Context and execute the code
#[cfg(test)]
pub(crate) fn exec(src: &str) -> String {
match Context::new().eval(src) {
Ok(value) => value.display().to_string(),
Err(error) => error.display().to_string(),
}
}

3
boa/src/syntax/mod.rs

@ -3,3 +3,6 @@
pub mod ast;
pub mod lexer;
pub mod parser;
pub use lexer::Lexer;
pub use parser::Parser;

4
boa/src/value/equality.rs

@ -1,5 +1,5 @@
use super::*;
use crate::{builtins::Number, Interpreter};
use crate::{builtins::Number, Context};
impl Value {
/// Strict equality comparison.
@ -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 Interpreter) -> Result<bool> {
pub fn equals(&self, other: &Self, interpreter: &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() {

56
boa/src/value/mod.rs

@ -5,7 +5,6 @@
#[cfg(test)]
mod tests;
use crate::exec::Interpreter;
use crate::{
builtins::{
number::{f64_to_int32, f64_to_uint32},
@ -13,8 +12,8 @@ use crate::{
BigInt, Number,
},
property::{Attribute, Property, PropertyKey},
BoaProfiler, Context, Result,
};
use crate::{BoaProfiler, Result};
use gc::{Finalize, GcCellRef, GcCellRefMut, Trace};
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue};
use std::{
@ -174,7 +173,7 @@ impl Value {
}
/// Convert from a JSON value to a JS value
pub fn from_json(json: JSONValue, interpreter: &mut Interpreter) -> Self {
pub fn from_json(json: JSONValue, interpreter: &mut Context) -> Self {
match json {
JSONValue::Number(v) => {
if let Some(Ok(integer_32)) = v.as_i64().map(i32::try_from) {
@ -187,8 +186,7 @@ impl Value {
JSONValue::Bool(v) => Self::boolean(v),
JSONValue::Array(vs) => {
let global_array_prototype = interpreter
.realm
.global_obj
.global_object()
.get_field("Array")
.get_field(PROTOTYPE);
let new_obj =
@ -210,7 +208,7 @@ impl Value {
new_obj
}
JSONValue::Object(obj) => {
let new_obj = Value::new_object(Some(interpreter.global()));
let new_obj = Value::new_object(Some(interpreter.global_object()));
for (key, json) in obj.into_iter() {
let value = Self::from_json(json, interpreter);
new_obj.set_property(
@ -228,7 +226,7 @@ impl Value {
}
/// Converts the `Value` to `JSON`.
pub fn to_json(&self, interpreter: &mut Interpreter) -> Result<JSONValue> {
pub fn to_json(&self, interpreter: &mut Context) -> Result<JSONValue> {
let to_json = self.get_field("toJSON");
if to_json.is_function() {
let json_value = interpreter.call(&to_json, self, &[])?;
@ -582,11 +580,7 @@ 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 Interpreter,
preferred_type: PreferredType,
) -> Result<Value> {
pub fn to_primitive(&self, ctx: &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(_) = self {
@ -610,7 +604,7 @@ impl Value {
/// Converts the value to a `BigInt`.
///
/// This function is equivelent to `BigInt(value)` in JavaScript.
pub fn to_bigint(&self, ctx: &mut Interpreter) -> Result<RcBigInt> {
pub fn to_bigint(&self, ctx: &mut Context) -> Result<RcBigInt> {
match self {
Value::Null => Err(ctx.construct_type_error("cannot convert null to a BigInt")),
Value::Undefined => {
@ -657,7 +651,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 Interpreter) -> Result<RcString> {
pub fn to_string(&self, ctx: &mut Context) -> Result<RcString> {
match self {
Value::Null => Ok("null".into()),
Value::Undefined => Ok("undefined".into()),
@ -679,14 +673,14 @@ 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 Interpreter) -> Result<Value> {
pub fn to_object(&self, ctx: &mut Context) -> Result<Value> {
match self {
Value::Undefined | Value::Null => {
ctx.throw_type_error("cannot convert 'null' or 'undefined' to object")
}
Value::Boolean(boolean) => {
let proto = ctx
.realm
.realm()
.environment
.get_binding_value("Boolean")
.expect("Boolean was not initialized")
@ -699,7 +693,7 @@ impl Value {
}
Value::Integer(integer) => {
let proto = ctx
.realm
.realm()
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
@ -711,7 +705,7 @@ impl Value {
}
Value::Rational(rational) => {
let proto = ctx
.realm
.realm()
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
@ -724,7 +718,7 @@ impl Value {
}
Value::String(ref string) => {
let proto = ctx
.realm
.realm()
.environment
.get_binding_value("String")
.expect("String was not initialized")
@ -737,7 +731,7 @@ impl Value {
}
Value::Symbol(ref symbol) => {
let proto = ctx
.realm
.realm()
.environment
.get_binding_value("Symbol")
.expect("Symbol was not initialized")
@ -750,7 +744,7 @@ impl Value {
}
Value::BigInt(ref bigint) => {
let proto = ctx
.realm
.realm()
.environment
.get_binding_value("BigInt")
.expect("BigInt was not initialized")
@ -766,7 +760,7 @@ 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 Interpreter) -> Result<PropertyKey> {
pub fn to_property_key(&self, ctx: &mut Context) -> Result<PropertyKey> {
Ok(match self {
// Fast path:
Value::String(string) => string.clone().into(),
@ -783,7 +777,7 @@ 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 Interpreter) -> Result<Numeric> {
pub fn to_numeric(&self, ctx: &mut Context) -> Result<Numeric> {
let primitive = self.to_primitive(ctx, PreferredType::Number)?;
if let Some(bigint) = primitive.as_bigint() {
return Ok(bigint.clone().into());
@ -796,7 +790,7 @@ 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 Interpreter) -> Result<u32> {
pub fn to_u32(&self, ctx: &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);
@ -809,7 +803,7 @@ 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 Interpreter) -> Result<i32> {
pub fn to_i32(&self, ctx: &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);
@ -822,7 +816,7 @@ 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 Interpreter) -> Result<usize> {
pub fn to_index(&self, ctx: &mut Context) -> Result<usize> {
if self.is_undefined() {
return Ok(0);
}
@ -843,7 +837,7 @@ 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 Interpreter) -> Result<usize> {
pub fn to_length(&self, ctx: &mut Context) -> Result<usize> {
// 1. Let len be ? ToInteger(argument).
let len = self.to_integer(ctx)?;
@ -859,7 +853,7 @@ impl Value {
/// Converts a value to an integral Number value.
///
/// See: <https://tc39.es/ecma262/#sec-tointeger>
pub fn to_integer(&self, ctx: &mut Interpreter) -> Result<f64> {
pub fn to_integer(&self, ctx: &mut Context) -> Result<f64> {
// 1. Let number be ? ToNumber(argument).
let number = self.to_number(ctx)?;
@ -883,7 +877,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 Interpreter) -> Result<f64> {
pub fn to_number(&self, ctx: &mut Context) -> Result<f64> {
match *self {
Value::Null => Ok(0.0),
Value::Undefined => Ok(f64::NAN),
@ -911,7 +905,7 @@ 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 Interpreter) -> Result<f64> {
pub fn to_numeric_number(&self, ctx: &mut Context) -> Result<f64> {
let primitive = self.to_primitive(ctx, PreferredType::Number)?;
if let Some(ref bigint) = primitive.as_bigint() {
return Ok(bigint.to_f64());
@ -931,7 +925,7 @@ impl Value {
/// [table]: https://tc39.es/ecma262/#table-14
/// [spec]: https://tc39.es/ecma262/#sec-requireobjectcoercible
#[inline]
pub fn require_object_coercible<'a>(&'a self, ctx: &mut Interpreter) -> Result<&'a Value> {
pub fn require_object_coercible<'a>(&'a self, ctx: &mut Context) -> Result<&'a Value> {
if self.is_null_or_undefined() {
Err(ctx.construct_type_error("cannot convert null or undefined to Object"))
} else {

38
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 Interpreter) -> Result<Value> {
pub fn add(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) + f64::from(*y)),
@ -40,7 +40,7 @@ impl Value {
}
#[inline]
pub fn sub(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn sub(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) - f64::from(*y)),
@ -68,7 +68,7 @@ impl Value {
}
#[inline]
pub fn mul(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn mul(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) * f64::from(*y)),
@ -96,7 +96,7 @@ impl Value {
}
#[inline]
pub fn div(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn div(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) / f64::from(*y)),
@ -124,7 +124,7 @@ impl Value {
}
#[inline]
pub fn rem(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn rem(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x % *y),
@ -152,7 +152,7 @@ impl Value {
}
#[inline]
pub fn pow(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn pow(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x).powi(*y)),
@ -178,7 +178,7 @@ impl Value {
}
#[inline]
pub fn bitand(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn bitand(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x & y),
@ -210,7 +210,7 @@ impl Value {
}
#[inline]
pub fn bitor(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn bitor(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x | y),
@ -242,7 +242,7 @@ impl Value {
}
#[inline]
pub fn bitxor(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn bitxor(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x ^ y),
@ -274,7 +274,7 @@ impl Value {
}
#[inline]
pub fn shl(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn shl(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shl(*y as u32)),
@ -310,7 +310,7 @@ impl Value {
}
#[inline]
pub fn shr(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn shr(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shr(*y as u32)),
@ -346,7 +346,7 @@ impl Value {
}
#[inline]
pub fn ushr(&self, other: &Self, ctx: &mut Interpreter) -> Result<Value> {
pub fn ushr(&self, other: &Self, ctx: &mut Context) -> Result<Value> {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => {
@ -381,7 +381,7 @@ impl Value {
}
#[inline]
pub fn neg(&self, interpreter: &mut Interpreter) -> Result<Value> {
pub fn neg(&self, interpreter: &mut Context) -> Result<Value> {
Ok(match *self {
Self::Symbol(_) | Self::Undefined => Self::rational(NAN),
Self::Object(_) => Self::rational(match self.to_numeric_number(interpreter) {
@ -401,7 +401,7 @@ impl Value {
}
#[inline]
pub fn not(&self, _: &mut Interpreter) -> Result<bool> {
pub fn not(&self, _: &mut Context) -> Result<bool> {
Ok(!self.to_boolean())
}
@ -426,7 +426,7 @@ impl Value {
&self,
other: &Self,
left_first: bool,
ctx: &mut Interpreter,
ctx: &mut Context,
) -> Result<AbstractRelation> {
Ok(match (self, other) {
// Fast path (for some common operations):
@ -525,7 +525,7 @@ 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 Interpreter) -> Result<bool> {
pub fn lt(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match self.abstract_relation(other, true, ctx)? {
AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
@ -542,7 +542,7 @@ 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 Interpreter) -> Result<bool> {
pub fn le(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match other.abstract_relation(self, false, ctx)? {
AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false),
@ -559,7 +559,7 @@ 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 Interpreter) -> Result<bool> {
pub fn gt(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match other.abstract_relation(self, false, ctx)? {
AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
@ -576,7 +576,7 @@ 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 Interpreter) -> Result<bool> {
pub fn ge(&self, other: &Self, ctx: &mut Context) -> Result<bool> {
match self.abstract_relation(other, true, ctx)? {
AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false),

260
boa/src/value/tests.rs

@ -1,5 +1,5 @@
use super::*;
use crate::{forward, forward_val, Interpreter, Realm};
use crate::{forward, forward_val, Context};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
@ -54,8 +54,7 @@ fn number_is_true() {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
#[test]
fn abstract_equality_comparison() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(forward(&mut engine, "undefined == undefined"), "true");
assert_eq!(forward(&mut engine, "null == null"), "true");
@ -159,8 +158,7 @@ fn hash_object() {
#[test]
fn get_types() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
assert_eq!(
forward_val(&mut engine, "undefined").unwrap().get_type(),
@ -250,8 +248,7 @@ fn to_string() {
#[test]
fn add_number_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "1 + 2").unwrap();
let value = value.to_i32(&mut engine).unwrap();
@ -260,8 +257,7 @@ fn add_number_and_number() {
#[test]
fn add_number_and_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "1 + \" + 2 = 3\"").unwrap();
let value = value.to_string(&mut engine).unwrap();
@ -270,8 +266,7 @@ fn add_number_and_string() {
#[test]
fn add_string_and_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "\"Hello\" + \", world\"").unwrap();
let value = value.to_string(&mut engine).unwrap();
@ -280,8 +275,7 @@ fn add_string_and_string() {
#[test]
fn add_number_object_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "new Number(10) + 6").unwrap();
let value = value.to_i32(&mut engine).unwrap();
@ -290,8 +284,7 @@ fn add_number_object_and_number() {
#[test]
fn add_number_object_and_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "new Number(10) + new String(\"0\")").unwrap();
let value = value.to_string(&mut engine).unwrap();
@ -300,8 +293,7 @@ fn add_number_object_and_string_object() {
#[test]
fn sub_number_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "1 - 999").unwrap();
let value = value.to_i32(&mut engine).unwrap();
@ -310,8 +302,7 @@ fn sub_number_and_number() {
#[test]
fn sub_number_object_and_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "new Number(1) - new Number(999)").unwrap();
let value = value.to_i32(&mut engine).unwrap();
@ -320,8 +311,7 @@ fn sub_number_object_and_number_object() {
#[test]
fn sub_string_and_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "'Hello' - new Number(999)").unwrap();
let value = value.to_number(&mut engine).unwrap();
@ -330,8 +320,7 @@ fn sub_string_and_number_object() {
#[test]
fn bitand_integer_and_integer() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "0xFFFF & 0xFF").unwrap();
let value = value.to_i32(&mut engine).unwrap();
@ -340,8 +329,7 @@ fn bitand_integer_and_integer() {
#[test]
fn bitand_integer_and_rational() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "0xFFFF & 255.5").unwrap();
let value = value.to_i32(&mut engine).unwrap();
@ -350,8 +338,7 @@ fn bitand_integer_and_rational() {
#[test]
fn bitand_rational_and_rational() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "255.772 & 255.5").unwrap();
let value = value.to_i32(&mut engine).unwrap();
@ -361,8 +348,7 @@ fn bitand_rational_and_rational() {
#[test]
#[allow(clippy::float_cmp)]
fn pow_number_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "3 ** 3").unwrap();
let value = value.to_number(&mut engine).unwrap();
@ -371,8 +357,7 @@ fn pow_number_and_number() {
#[test]
fn pow_number_and_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "3 ** 'Hello'").unwrap();
let value = value.to_number(&mut engine).unwrap();
@ -381,8 +366,7 @@ fn pow_number_and_string() {
#[test]
fn assign_pow_number_and_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(
&mut engine,
@ -406,8 +390,7 @@ fn display_string() {
#[test]
fn display_array_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "[\"Hello\"]").unwrap();
assert_eq!(value.display().to_string(), "[ \"Hello\" ]");
@ -415,8 +398,7 @@ fn display_array_string() {
#[test]
fn display_boolean_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let d_obj = r#"
let bool = new Boolean(0);
bool
@ -427,8 +409,7 @@ fn display_boolean_object() {
#[test]
fn display_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let d_obj = r#"
let num = new Number(3.14);
num
@ -439,8 +420,7 @@ fn display_number_object() {
#[test]
fn display_negative_zero_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let d_obj = r#"
let num = new Number(-0);
num
@ -451,8 +431,7 @@ fn display_negative_zero_object() {
#[test]
fn debug_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let value = forward_val(&mut engine, "new Array([new Date()])").unwrap();
// We don't care about the contents of the debug display (it is *debug* after all). In the commit that this test was
@ -466,8 +445,7 @@ fn debug_object() {
#[test]
#[ignore] // TODO: Once objects are printed in a simpler way this test can be simplified and used
fn display_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let d_obj = r#"
let o = {a: 'a'};
o
@ -531,8 +509,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 < 2" => true);
check_comparison!(engine, "2 < 2" => false);
check_comparison!(engine, "3 < 2" => false);
@ -542,8 +519,7 @@ mod abstract_relational_comparison {
#[test]
fn string_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1' < 2" => true);
check_comparison!(engine, "'2' < 2" => false);
check_comparison!(engine, "'3' < 2" => false);
@ -553,8 +529,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 < '2'" => true);
check_comparison!(engine, "2 < '2'" => false);
check_comparison!(engine, "3 < '2'" => false);
@ -564,8 +539,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) < '2'" => true);
check_comparison!(engine, "new Number(2) < '2'" => false);
check_comparison!(engine, "new Number(3) < '2'" => false);
@ -575,8 +549,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_less_than_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) < new Number(2)" => true);
check_comparison!(engine, "new Number(2) < new Number(2)" => false);
check_comparison!(engine, "new Number(3) < new Number(2)" => false);
@ -586,8 +559,7 @@ mod abstract_relational_comparison {
#[test]
fn string_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'hello' < 'hello'" => false);
check_comparison!(engine, "'hell' < 'hello'" => true);
check_comparison!(engine, "'hello, world' < 'world'" => true);
@ -596,8 +568,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') < 'hello'" => false);
check_comparison!(engine, "new String('hell') < 'hello'" => true);
check_comparison!(engine, "new String('hello, world') < 'world'" => true);
@ -606,8 +577,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_less_than_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') < new String('hello')" => false);
check_comparison!(engine, "new String('hell') < new String('hello')" => true);
check_comparison!(engine, "new String('hello, world') < new String('world')" => true);
@ -616,8 +586,7 @@ mod abstract_relational_comparison {
#[test]
fn bigint_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1n < 10" => true);
check_comparison!(engine, "10n < 10" => false);
check_comparison!(engine, "100n < 10" => false);
@ -626,8 +595,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "10 < 1n" => false);
check_comparison!(engine, "1 < 1n" => false);
check_comparison!(engine, "-1 < -1n" => false);
@ -636,40 +604,35 @@ mod abstract_relational_comparison {
#[test]
fn negative_infnity_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "-Infinity < -10000000000n" => true);
check_comparison!(engine, "-Infinity < (-1n << 100n)" => true);
}
#[test]
fn bigint_less_than_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n < NaN" => false);
check_comparison!(engine, "(1n << 100n) < NaN" => false);
}
#[test]
fn nan_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "NaN < -10000000000n" => false);
check_comparison!(engine, "NaN < (-1n << 100n)" => false);
}
#[test]
fn bigint_less_than_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n < Infinity" => true);
check_comparison!(engine, "(1n << 100n) < Infinity" => true);
}
#[test]
fn bigint_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n < '1000'" => false);
check_comparison!(engine, "1000n < '2000'" => true);
check_comparison!(engine, "1n < '-1'" => false);
@ -679,8 +642,7 @@ mod abstract_relational_comparison {
#[test]
fn string_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1000' < 1000n" => false);
check_comparison!(engine, "'2000' < 1000n" => false);
check_comparison!(engine, "'500' < 1000n" => true);
@ -693,8 +655,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 <= 2" => true);
check_comparison!(engine, "2 <= 2" => true);
check_comparison!(engine, "3 <= 2" => false);
@ -704,8 +665,7 @@ mod abstract_relational_comparison {
#[test]
fn string_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1' <= 2" => true);
check_comparison!(engine, "'2' <= 2" => true);
check_comparison!(engine, "'3' <= 2" => false);
@ -715,8 +675,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 <= '2'" => true);
check_comparison!(engine, "2 <= '2'" => true);
check_comparison!(engine, "3 <= '2'" => false);
@ -726,8 +685,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) <= '2'" => true);
check_comparison!(engine, "new Number(2) <= '2'" => true);
check_comparison!(engine, "new Number(3) <= '2'" => false);
@ -737,8 +695,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_less_than_number_or_equal_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) <= new Number(2)" => true);
check_comparison!(engine, "new Number(2) <= new Number(2)" => true);
check_comparison!(engine, "new Number(3) <= new Number(2)" => false);
@ -748,8 +705,7 @@ mod abstract_relational_comparison {
#[test]
fn string_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'hello' <= 'hello'" => true);
check_comparison!(engine, "'hell' <= 'hello'" => true);
check_comparison!(engine, "'hello, world' <= 'world'" => true);
@ -758,8 +714,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') <= 'hello'" => true);
check_comparison!(engine, "new String('hell') <= 'hello'" => true);
check_comparison!(engine, "new String('hello, world') <= 'world'" => true);
@ -768,8 +723,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_less_than_string_or_equal_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') <= new String('hello')" => true);
check_comparison!(engine, "new String('hell') <= new String('hello')" => true);
check_comparison!(engine, "new String('hello, world') <= new String('world')" => true);
@ -778,8 +732,7 @@ mod abstract_relational_comparison {
#[test]
fn bigint_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1n <= 10" => true);
check_comparison!(engine, "10n <= 10" => true);
check_comparison!(engine, "100n <= 10" => false);
@ -788,8 +741,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "10 <= 1n" => false);
check_comparison!(engine, "1 <= 1n" => true);
check_comparison!(engine, "-1 <= -1n" => true);
@ -798,40 +750,35 @@ mod abstract_relational_comparison {
#[test]
fn negative_infnity_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "-Infinity <= -10000000000n" => true);
check_comparison!(engine, "-Infinity <= (-1n << 100n)" => true);
}
#[test]
fn bigint_less_than_or_equal_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n <= NaN" => false);
check_comparison!(engine, "(1n << 100n) <= NaN" => false);
}
#[test]
fn nan_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "NaN <= -10000000000n" => false);
check_comparison!(engine, "NaN <= (-1n << 100n)" => false);
}
#[test]
fn bigint_less_than_or_equal_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n <= Infinity" => true);
check_comparison!(engine, "(1n << 100n) <= Infinity" => true);
}
#[test]
fn bigint_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n <= '1000'" => true);
check_comparison!(engine, "1000n <= '2000'" => true);
check_comparison!(engine, "1n <= '-1'" => false);
@ -841,8 +788,7 @@ mod abstract_relational_comparison {
#[test]
fn string_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1000' <= 1000n" => true);
check_comparison!(engine, "'2000' <= 1000n" => false);
check_comparison!(engine, "'500' <= 1000n" => true);
@ -855,8 +801,7 @@ mod abstract_relational_comparison {
#[test]
fn number_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 > 2" => false);
check_comparison!(engine, "2 > 2" => false);
check_comparison!(engine, "3 > 2" => true);
@ -866,8 +811,7 @@ mod abstract_relational_comparison {
#[test]
fn string_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1' > 2" => false);
check_comparison!(engine, "'2' > 2" => false);
check_comparison!(engine, "'3' > 2" => true);
@ -877,8 +821,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_greater_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 > '2'" => false);
check_comparison!(engine, "2 > '2'" => false);
check_comparison!(engine, "3 > '2'" => true);
@ -888,8 +831,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) > '2'" => false);
check_comparison!(engine, "new Number(2) > '2'" => false);
check_comparison!(engine, "new Number(3) > '2'" => true);
@ -899,8 +841,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_greater_than_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) > new Number(2)" => false);
check_comparison!(engine, "new Number(2) > new Number(2)" => false);
check_comparison!(engine, "new Number(3) > new Number(2)" => true);
@ -910,8 +851,7 @@ mod abstract_relational_comparison {
#[test]
fn string_greater_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'hello' > 'hello'" => false);
check_comparison!(engine, "'hell' > 'hello'" => false);
check_comparison!(engine, "'hello, world' > 'world'" => false);
@ -921,8 +861,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_greater_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') > 'hello'" => false);
check_comparison!(engine, "new String('hell') > 'hello'" => false);
check_comparison!(engine, "new String('hello, world') > 'world'" => false);
@ -932,8 +871,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_greater_than_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') > new String('hello')" => false);
check_comparison!(engine, "new String('hell') > new String('hello')" => false);
check_comparison!(engine, "new String('hello, world') > new String('world')" => false);
@ -943,8 +881,7 @@ mod abstract_relational_comparison {
#[test]
fn bigint_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1n > 10" => false);
check_comparison!(engine, "10n > 10" => false);
check_comparison!(engine, "100n > 10" => true);
@ -953,8 +890,7 @@ mod abstract_relational_comparison {
#[test]
fn number_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "10 > 1n" => true);
check_comparison!(engine, "1 > 1n" => false);
check_comparison!(engine, "-1 > -1n" => false);
@ -963,40 +899,35 @@ mod abstract_relational_comparison {
#[test]
fn negative_infnity_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "-Infinity > -10000000000n" => false);
check_comparison!(engine, "-Infinity > (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n > NaN" => false);
check_comparison!(engine, "(1n << 100n) > NaN" => false);
}
#[test]
fn nan_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "NaN > -10000000000n" => false);
check_comparison!(engine, "NaN > (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n > Infinity" => false);
check_comparison!(engine, "(1n << 100n) > Infinity" => false);
}
#[test]
fn bigint_greater_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n > '1000'" => false);
check_comparison!(engine, "1000n > '2000'" => false);
check_comparison!(engine, "1n > '-1'" => true);
@ -1006,8 +937,7 @@ mod abstract_relational_comparison {
#[test]
fn string_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1000' > 1000n" => false);
check_comparison!(engine, "'2000' > 1000n" => true);
check_comparison!(engine, "'500' > 1000n" => false);
@ -1020,8 +950,7 @@ mod abstract_relational_comparison {
#[test]
fn number_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 >= 2" => false);
check_comparison!(engine, "2 >= 2" => true);
check_comparison!(engine, "3 >= 2" => true);
@ -1031,8 +960,7 @@ mod abstract_relational_comparison {
#[test]
fn string_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1' >= 2" => false);
check_comparison!(engine, "'2' >= 2" => true);
check_comparison!(engine, "'3' >= 2" => true);
@ -1042,8 +970,7 @@ mod abstract_relational_comparison {
#[test]
fn number_less_greater_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1 >= '2'" => false);
check_comparison!(engine, "2 >= '2'" => true);
check_comparison!(engine, "3 >= '2'" => true);
@ -1053,8 +980,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) >= '2'" => false);
check_comparison!(engine, "new Number(2) >= '2'" => true);
check_comparison!(engine, "new Number(3) >= '2'" => true);
@ -1064,8 +990,7 @@ mod abstract_relational_comparison {
#[test]
fn number_object_greater_than_or_equal_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new Number(1) >= new Number(2)" => false);
check_comparison!(engine, "new Number(2) >= new Number(2)" => true);
check_comparison!(engine, "new Number(3) >= new Number(2)" => true);
@ -1075,8 +1000,7 @@ mod abstract_relational_comparison {
#[test]
fn string_greater_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'hello' >= 'hello'" => true);
check_comparison!(engine, "'hell' >= 'hello'" => false);
check_comparison!(engine, "'hello, world' >= 'world'" => false);
@ -1086,8 +1010,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_greater_or_equal_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') >= 'hello'" => true);
check_comparison!(engine, "new String('hell') >= 'hello'" => false);
check_comparison!(engine, "new String('hello, world') >= 'world'" => false);
@ -1097,8 +1020,7 @@ mod abstract_relational_comparison {
#[test]
fn string_object_greater_than_or_equal_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "new String('hello') >= new String('hello')" => true);
check_comparison!(engine, "new String('hell') >= new String('hello')" => false);
check_comparison!(engine, "new String('hello, world') >= new String('world')" => false);
@ -1108,8 +1030,7 @@ mod abstract_relational_comparison {
#[test]
fn bigint_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1n >= 10" => false);
check_comparison!(engine, "10n >= 10" => true);
check_comparison!(engine, "100n >= 10" => true);
@ -1118,8 +1039,7 @@ mod abstract_relational_comparison {
#[test]
fn number_greater_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "10 >= 1n" => true);
check_comparison!(engine, "1 >= 1n" => true);
check_comparison!(engine, "-1 >= -1n" => true);
@ -1128,40 +1048,35 @@ mod abstract_relational_comparison {
#[test]
fn negative_infnity_greater_or_equal_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "-Infinity >= -10000000000n" => false);
check_comparison!(engine, "-Infinity >= (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_or_equal_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n >= NaN" => false);
check_comparison!(engine, "(1n << 100n) >= NaN" => false);
}
#[test]
fn nan_greater_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "NaN >= -10000000000n" => false);
check_comparison!(engine, "NaN >= (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_or_equal_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n >= Infinity" => false);
check_comparison!(engine, "(1n << 100n) >= Infinity" => false);
}
#[test]
fn bigint_greater_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "1000n >= '1000'" => true);
check_comparison!(engine, "1000n >= '2000'" => false);
check_comparison!(engine, "1n >= '-1'" => true);
@ -1171,8 +1086,7 @@ mod abstract_relational_comparison {
#[test]
fn string_greater_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
check_comparison!(engine, "'1000' >= 1000n" => true);
check_comparison!(engine, "'2000' >= 1000n" => true);
check_comparison!(engine, "'500' >= 1000n" => false);

10
boa_cli/src/main.rs

@ -25,7 +25,7 @@
clippy::as_conversions
)]
use boa::{exec::Interpreter, forward_val, realm::Realm, syntax::ast::node::StatementList};
use boa::{syntax::ast::node::StatementList, Context};
use colored::*;
use rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
use std::{fs::read_to_string, path::PathBuf};
@ -139,9 +139,7 @@ fn dump(src: &str, args: &Opt) -> Result<(), String> {
pub fn main() -> Result<(), std::io::Error> {
let args = Opt::from_args();
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
for file in &args.files {
let buffer = read_to_string(file)?;
@ -151,7 +149,7 @@ pub fn main() -> Result<(), std::io::Error> {
eprintln!("{}", e);
}
} else {
match forward_val(&mut engine, &buffer) {
match engine.eval(&buffer) {
Ok(v) => println!("{}", v.display()),
Err(v) => eprintln!("Uncaught {}", v.display()),
}
@ -187,7 +185,7 @@ pub fn main() -> Result<(), std::io::Error> {
eprintln!("{}", e);
}
} else {
match forward_val(&mut engine, line.trim_end()) {
match engine.eval(line.trim_end()) {
Ok(v) => println!("{}", v.display()),
Err(v) => {
eprintln!("{}: {}", "Uncaught".red(), v.display().to_string().red())

5
boa_wasm/src/lib.rs

@ -1,11 +1,10 @@
use boa::{parse, Executable, Interpreter, Realm};
use boa::{exec::Executable, parse, Context};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn evaluate(src: &str) -> Result<String, JsValue> {
// Setup executor
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
let expr = match parse(src) {
Ok(res) => res,

26
tester/src/exec.rs

@ -1,7 +1,7 @@
//! Execution module for the test runner.
use super::{Harness, Outcome, Phase, SuiteResult, Test, TestFlags, TestResult, TestSuite, CLI};
use boa::{forward_val, parse, Interpreter, Realm};
use boa::{parse, Context};
use colored::Colorize;
use fxhash::FxHashSet;
use once_cell::sync::Lazy;
@ -94,20 +94,20 @@ impl Test {
if self.flags.contains(TestFlags::RAW) {
let mut engine = self.set_up_env(&harness, false);
let res = forward_val(&mut engine, &self.content);
let res = engine.eval(&self.content);
passed = res.is_ok()
} else {
if self.flags.contains(TestFlags::STRICT) {
let mut engine = self.set_up_env(&harness, true);
let res = forward_val(&mut engine, &self.content);
let res = engine.eval(&self.content);
passed = res.is_ok()
}
if passed && self.flags.contains(TestFlags::NO_STRICT) {
let mut engine = self.set_up_env(&harness, false);
let res = forward_val(&mut engine, &self.content);
let res = engine.eval(&self.content);
passed = res.is_ok()
}
@ -160,24 +160,26 @@ impl Test {
}
/// Sets the environment up to run the test.
fn set_up_env(&self, harness: &Harness, strict: bool) -> Interpreter {
fn set_up_env(&self, harness: &Harness, strict: bool) -> Context {
// Create new Realm
// TODO: in parallel.
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let mut engine = Context::new();
// TODO: set up the environment.
if strict {
forward_val(&mut engine, r#""use strict";"#).expect("could not set strict mode");
engine
.eval(r#""use strict";"#)
.expect("could not set strict mode");
}
forward_val(&mut engine, &harness.assert).expect("could not run assert.js");
forward_val(&mut engine, &harness.sta).expect("could not run sta.js");
engine
.eval(&harness.assert)
.expect("could not run assert.js");
engine.eval(&harness.sta).expect("could not run sta.js");
self.includes.iter().for_each(|include| {
let res = forward_val(
&mut engine,
let res = engine.eval(
&harness
.includes
.get(include)

Loading…
Cancel
Save