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. 16
      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. 76
      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 version: 2
updates: updates:
- package-ecosystem: "npm" - package-ecosystem: "npm"

68
boa/benches/exec.rs

@ -1,6 +1,6 @@
//! Benchmarks of the whole execution engine in Boa. //! 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}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] #[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) { fn symbol_creation(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(SYMBOL_CREATION.as_bytes()).parse_all().unwrap(); 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) { fn for_loop_execution(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(FOR_LOOP.as_bytes()).parse_all().unwrap(); 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) { fn fibonacci(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(FIBONACCI.as_bytes()).parse_all().unwrap(); 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) { fn object_creation(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(OBJECT_CREATION.as_bytes()).parse_all().unwrap(); 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) { fn object_prop_access_const(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(OBJECT_PROP_ACCESS_CONST.as_bytes()) 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) { fn object_prop_access_dyn(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(OBJECT_PROP_ACCESS_DYN.as_bytes()) 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) { fn regexp_literal_creation(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(REGEXP_LITERAL_CREATION.as_bytes()) 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) { fn regexp_creation(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(REGEXP_CREATION.as_bytes()).parse_all().unwrap(); 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) { fn regexp_literal(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(REGEXP_LITERAL.as_bytes()).parse_all().unwrap(); 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) { fn regexp(c: &mut Criterion) {
// Create new Realm and interpreter. // Create new Realm and interpreter.
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Parse the AST nodes. // Parse the AST nodes.
let nodes = Parser::new(REGEXP.as_bytes()).parse_all().unwrap(); 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"); static ARRAY_ACCESS: &str = include_str!("bench_scripts/array_access.js");
fn array_access(c: &mut Criterion) { fn array_access(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(ARRAY_ACCESS.as_bytes()).parse_all().unwrap(); 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"); static ARRAY_CREATE: &str = include_str!("bench_scripts/array_create.js");
fn array_creation(c: &mut Criterion) { fn array_creation(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(ARRAY_CREATE.as_bytes()).parse_all().unwrap(); 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"); static ARRAY_POP: &str = include_str!("bench_scripts/array_pop.js");
fn array_pop(c: &mut Criterion) { fn array_pop(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(ARRAY_POP.as_bytes()).parse_all().unwrap(); 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"); static STRING_CONCAT: &str = include_str!("bench_scripts/string_concat.js");
fn string_concat(c: &mut Criterion) { fn string_concat(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(STRING_CONCAT.as_bytes()).parse_all().unwrap(); 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"); static STRING_COMPARE: &str = include_str!("bench_scripts/string_compare.js");
fn string_compare(c: &mut Criterion) { fn string_compare(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(STRING_COMPARE.as_bytes()).parse_all().unwrap(); 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"); static STRING_COPY: &str = include_str!("bench_scripts/string_copy.js");
fn string_copy(c: &mut Criterion) { fn string_copy(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(STRING_COPY.as_bytes()).parse_all().unwrap(); 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"); static NUMBER_OBJECT_ACCESS: &str = include_str!("bench_scripts/number_object_access.js");
fn number_object_access(c: &mut Criterion) { fn number_object_access(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(NUMBER_OBJECT_ACCESS.as_bytes()) let nodes = Parser::new(NUMBER_OBJECT_ACCESS.as_bytes())
.parse_all() .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"); static BOOLEAN_OBJECT_ACCESS: &str = include_str!("bench_scripts/boolean_object_access.js");
fn boolean_object_access(c: &mut Criterion) { fn boolean_object_access(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(BOOLEAN_OBJECT_ACCESS.as_bytes()) let nodes = Parser::new(BOOLEAN_OBJECT_ACCESS.as_bytes())
.parse_all() .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"); static STRING_OBJECT_ACCESS: &str = include_str!("bench_scripts/string_object_access.js");
fn string_object_access(c: &mut Criterion) { fn string_object_access(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(STRING_OBJECT_ACCESS.as_bytes()) let nodes = Parser::new(STRING_OBJECT_ACCESS.as_bytes())
.parse_all() .parse_all()
@ -306,8 +287,7 @@ fn string_object_access(c: &mut Criterion) {
static ARITHMETIC_OPERATIONS: &str = include_str!("bench_scripts/arithmetic_operations.js"); static ARITHMETIC_OPERATIONS: &str = include_str!("bench_scripts/arithmetic_operations.js");
fn arithmetic_operations(c: &mut Criterion) { fn arithmetic_operations(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(ARITHMETIC_OPERATIONS.as_bytes()) let nodes = Parser::new(ARITHMETIC_OPERATIONS.as_bytes())
.parse_all() .parse_all()
@ -321,8 +301,7 @@ fn arithmetic_operations(c: &mut Criterion) {
static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js"); static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js");
fn clean_js(c: &mut Criterion) { fn clean_js(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(CLEAN_JS.as_bytes()).parse_all().unwrap(); let nodes = Parser::new(CLEAN_JS.as_bytes()).parse_all().unwrap();
c.bench_function("Clean js (Execution)", move |b| { c.bench_function("Clean js (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap()) 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"); static MINI_JS: &str = include_str!("bench_scripts/mini_js.js");
fn mini_js(c: &mut Criterion) { fn mini_js(c: &mut Criterion) {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let nodes = Parser::new(MINI_JS.as_bytes()).parse_all().unwrap(); let nodes = Parser::new(MINI_JS.as_bytes()).parse_all().unwrap();
c.bench_function("Mini js (Execution)", move |b| { c.bench_function("Mini js (Execution)", move |b| {
b.iter(|| black_box(&nodes).run(&mut engine).unwrap()) 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. //! Benchmarks of whole program execution in Boa.
use boa::exec; use boa::Context;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] #[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) { fn symbol_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Symbols (Full)", move |b| { 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) { fn for_loop(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("For loop (Full)", move |b| { 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) { fn fibonacci(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Fibonacci (Full)", move |b| { 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) { fn object_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Object Creation (Full)", move |b| { 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) { fn object_prop_access_const(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Static Object Property Access (Full)", move |b| { 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) { fn object_prop_access_dyn(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("Dynamic Object Property Access (Full)", move |b| { 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) { fn regexp_literal_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("RegExp Literal Creation (Full)", move |b| { 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) { fn regexp_creation(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("RegExp (Full)", move |b| { 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) { fn regexp_literal(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // Execute the code by taking into account realm creation, lexing and parsing
c.bench_function("RegExp Literal (Full)", move |b| { 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) { fn regexp(c: &mut Criterion) {
// Execute the code by taking into account realm creation, lexing and parsing // 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"); static ARRAY_ACCESS: &str = include_str!("bench_scripts/array_access.js");
fn array_access(c: &mut Criterion) { fn array_access(c: &mut Criterion) {
c.bench_function("Array access (Full)", move |b| { 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) { fn array_creation(c: &mut Criterion) {
c.bench_function("Array creation (Full)", move |b| { 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) { fn array_pop(c: &mut Criterion) {
c.bench_function("Array pop (Full)", move |b| { 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) { fn string_concat(c: &mut Criterion) {
c.bench_function("String concatenation (Full)", move |b| { 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) { fn string_compare(c: &mut Criterion) {
c.bench_function("String comparison (Full)", move |b| { 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) { fn string_copy(c: &mut Criterion) {
c.bench_function("String copy (Full)", move |b| { 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) { fn number_object_access(c: &mut Criterion) {
c.bench_function("Number Object Access (Full)", move |b| { 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) { fn boolean_object_access(c: &mut Criterion) {
c.bench_function("Boolean Object Access (Full)", move |b| { 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) { fn string_object_access(c: &mut Criterion) {
c.bench_function("String Object Access (Full)", move |b| { 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) { fn arithmetic_operations(c: &mut Criterion) {
c.bench_function("Arithmetic operations (Full)", move |b| { 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) { fn clean_js(c: &mut Criterion) {
c.bench_function("Clean js (Full)", move |b| { 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) { fn mini_js(c: &mut Criterion) {
c.bench_function("Mini js (Full)", move |b| { c.bench_function("Mini js (Full)", move |b| {
b.iter(|| exec(black_box(MINI_JS))) b.iter(|| Context::new().eval(black_box(MINI_JS)))
}); });
} }

16
boa/examples/classes.rs

@ -1,10 +1,7 @@
use boa::{ use boa::{
class::{Class, ClassBuilder}, class::{Class, ClassBuilder},
exec::Interpreter,
forward_val,
property::Attribute, property::Attribute,
realm::Realm, Context, Finalize, Result, Trace, Value,
Finalize, Result, Trace, Value,
}; };
// We create a new struct that is going to represent a person. // 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. // or any function that matches that signature.
impl Person { impl Person {
/// This function says hello /// 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. // We check if this is an object.
if let Some(object) = this.as_object() { if let Some(object) = this.as_object() {
// If it is we downcast the type to type `Person`. // If it is we downcast the type to type `Person`.
@ -59,7 +56,7 @@ impl Class for Person {
const LENGTH: usize = 2; const LENGTH: usize = 2;
// This is what is called when we do `new Person()` // 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`. // we get the first arguemnt of undefined if the first one is unavalable and call `to_string`.
// //
// This is equivalent to `String(arg)`. // This is equivalent to `String(arg)`.
@ -117,14 +114,13 @@ impl Class for Person {
} }
fn main() { fn main() {
let realm = Realm::create(); let mut context = Context::new();
let mut context = Interpreter::new(realm);
// we register the global class `Person`. // we register the global class `Person`.
context.register_global_class::<Person>().unwrap(); context.register_global_class::<Person>().unwrap();
forward_val( context
&mut context, .eval(
r" r"
let person = new Person('John', 19); let person = new Person('John', 19);
person.sayHello(); person.sayHello();

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

@ -15,15 +15,11 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::object::{ObjectData, PROTOTYPE}, builtins::object::{ObjectData, PROTOTYPE},
exec::Interpreter,
property::{Attribute, Property}, property::{Attribute, Property},
value::{same_value_zero, Value}, value::{same_value_zero, Value},
BoaProfiler, Result, BoaProfiler, Context, Result,
};
use std::{
borrow::Borrow,
cmp::{max, min},
}; };
use std::cmp::{max, min};
/// JavaScript `Array` built-in implementation. /// JavaScript `Array` built-in implementation.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -37,7 +33,7 @@ impl Array {
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Creates a new `Array` instance. /// 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( let array = Value::new_object(Some(
&interpreter &interpreter
.realm() .realm()
@ -52,10 +48,9 @@ impl Array {
.environment .environment
.get_binding_value("Array") .get_binding_value("Array")
.expect("Array was not initialized") .expect("Array was not initialized")
.borrow()
.get_field(PROTOTYPE), .get_field(PROTOTYPE),
); );
array.borrow().set_field("length", Value::from(0)); array.set_field("length", Value::from(0));
Ok(array) Ok(array)
} }
@ -104,12 +99,12 @@ impl Array {
} }
/// Create a new 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 // Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype // between indices and values): this creates an Object with no prototype
// Set 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() this.as_object_mut()
.expect("this should be an array object") .expect("this should be an array object")
@ -163,7 +158,7 @@ impl Array {
pub(crate) fn is_array( pub(crate) fn is_array(
_this: &Value, _this: &Value,
args: &[Value], args: &[Value],
_interpreter: &mut Interpreter, _interpreter: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
match args.get(0).and_then(|x| x.as_object()) { match args.get(0).and_then(|x| x.as_object()) {
Some(object) => Ok(Value::from(object.is_array())), Some(object) => Ok(Value::from(object.is_array())),
@ -183,7 +178,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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 args.is_empty() {
// If concat is called with no arguments, it returns the original array // If concat is called with no arguments, it returns the original array
return Ok(this.clone()); return Ok(this.clone());
@ -220,7 +215,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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)?; let new_array = Self::add_to_array_object(this, args)?;
Ok(new_array.get_field("length")) Ok(new_array.get_field("length"))
} }
@ -235,7 +230,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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; let curr_length = this.get_field("length").as_number().unwrap() as i32;
if curr_length < 1 { if curr_length < 1 {
@ -258,11 +253,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
pub(crate) fn for_each( pub(crate) fn for_each(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from("Missing argument for Array.prototype.forEach")); return Err(Value::from("Missing argument for Array.prototype.forEach"));
} }
@ -276,7 +267,7 @@ impl Array {
let element = this.get_field(i); let element = this.get_field(i);
let arguments = [element, Value::from(i), this.clone()]; 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()) Ok(Value::undefined())
@ -294,7 +285,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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() { let separator = if args.is_empty() {
String::from(",") String::from(",")
} else { } else {
@ -327,7 +318,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
#[allow(clippy::wrong_self_convention)] #[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 method_name = "join";
let mut arguments = vec![Value::from(",")]; let mut arguments = vec![Value::from(",")];
// 2. // 2.
@ -335,8 +326,7 @@ impl Array {
// 3. // 3.
if !method.is_function() { if !method.is_function() {
method = ctx method = ctx
.realm .global_object()
.global_obj
.get_field("Object") .get_field("Object")
.get_field(PROTOTYPE) .get_field(PROTOTYPE)
.get_field("toString"); .get_field("toString");
@ -367,7 +357,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
#[allow(clippy::else_if_without_else)] #[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 len = this.get_field("length").as_number().unwrap() as i32;
let middle: i32 = len.wrapping_div(2); let middle: i32 = len.wrapping_div(2);
@ -406,7 +396,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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; let len = this.get_field("length").as_number().unwrap() as i32;
if len == 0 { if len == 0 {
@ -447,7 +437,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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 len = this.get_field("length").as_number().unwrap() as i32;
let arg_c: i32 = args.len() 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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
pub(crate) fn every( pub(crate) fn every(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing callback when calling function Array.prototype.every", "missing callback when calling function Array.prototype.every",
@ -538,11 +524,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
pub(crate) fn map( pub(crate) fn map(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing argument 0 when calling function Array.prototype.map", "missing argument 0 when calling function Array.prototype.map",
@ -589,7 +571,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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 no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
return Ok(Value::from(-1)); return Ok(Value::from(-1));
@ -642,11 +624,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf
pub(crate) fn last_index_of( pub(crate) fn last_index_of(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
_: &mut Interpreter,
) -> Result<Value> {
// If no arguments, return -1. Not described in spec, but is what chrome does. // If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
return Ok(Value::from(-1)); return Ok(Value::from(-1));
@ -696,11 +674,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
pub(crate) fn find( pub(crate) fn find(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing callback when calling function Array.prototype.find", "missing callback when calling function Array.prototype.find",
@ -735,7 +709,7 @@ impl Array {
pub(crate) fn find_index( pub(crate) fn find_index(
this: &Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
@ -774,7 +748,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/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 len: i32 = this.get_field("length").as_number().unwrap() as i32;
let default_value = Value::undefined(); let default_value = Value::undefined();
@ -814,11 +788,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
pub(crate) fn includes_value( pub(crate) fn includes_value(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
_: &mut Interpreter,
) -> Result<Value> {
let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined); let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined);
let length = this.get_field("length").as_number().unwrap() as i32; 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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
pub(crate) fn slice( pub(crate) fn slice(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
let new_array = Self::new_array(interpreter)?; let new_array = Self::new_array(interpreter)?;
let len = this.get_field("length").as_number().unwrap() as i32; 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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
pub(crate) fn filter( pub(crate) fn filter(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing argument 0 when calling function Array.prototype.filter", "missing argument 0 when calling function Array.prototype.filter",
@ -951,11 +913,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
pub(crate) fn some( pub(crate) fn some(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing callback when calling function Array.prototype.some", "missing callback when calling function Array.prototype.some",
@ -998,11 +956,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reduce /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reduce
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
pub(crate) fn reduce( pub(crate) fn reduce(this: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> Result<Value> {
let this = this.to_object(interpreter)?; let this = this.to_object(interpreter)?;
let callback = match args.get(0) { let callback = match args.get(0) {
Some(value) if value.is_function() => value, Some(value) if value.is_function() => value,
@ -1063,7 +1017,7 @@ impl Array {
pub(crate) fn reduce_right( pub(crate) fn reduce_right(
this: &Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
let this = this.to_object(interpreter)?; let this = this.to_object(interpreter)?;
let callback = match args.get(0) { let callback = match args.get(0) {
@ -1136,8 +1090,8 @@ impl Array {
/// Initialise the `Array` object on the global object. /// Initialise the `Array` object on the global object.
#[inline] #[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype // 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] #[test]
fn is_array() { fn is_array() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = []; var empty = [];
var new_arr = new Array(); var new_arr = new Array();
var many = ["a", "b", "c"]; var many = ["a", "b", "c"];
"#; "#;
eprintln!("{}", forward(&mut engine, init)); engine.eval(init).unwrap();
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");
assert_eq!( assert_eq!(
forward(&mut engine, "Array.isArray({ constructor: Array })"), engine.eval("Array.isArray(empty)").unwrap(),
"false" Value::Boolean(true)
); );
assert_eq!( assert_eq!(
forward( engine.eval("Array.isArray(new_arr)").unwrap(),
&mut engine, Value::Boolean(true)
"Array.isArray({ push: Array.prototype.push, concat: Array.prototype.concat })" );
), assert_eq!(
"false" engine.eval("Array.isArray(many)").unwrap(),
Value::Boolean(true)
); );
assert_eq!(forward(&mut engine, "Array.isArray(17)"), "false");
assert_eq!( assert_eq!(
forward(&mut engine, "Array.isArray({ __proto__: Array.prototype })"), engine.eval("Array.isArray([1, 2, 3])").unwrap(),
"false" Value::Boolean(true)
); );
assert_eq!( assert_eq!(
forward(&mut engine, "Array.isArray({ length: 0 })"), engine.eval("Array.isArray([])").unwrap(),
"false" 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] #[ignore]
fn concat() { fn concat() {
//TODO: array display formatter //TODO: array display formatter
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = new Array(); var empty = new Array();
var one = new Array(1); var one = new Array(1);
"#; "#;
eprintln!("{}", forward(&mut engine, init)); engine.eval(init).unwrap();
// Empty ++ Empty // Empty ++ Empty
let ee = forward(&mut engine, "empty.concat(empty)"); let ee = engine
assert_eq!(ee, String::from("[]")); .eval("empty.concat(empty)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(ee, "[]");
// Empty ++ NonEmpty // Empty ++ NonEmpty
let en = forward(&mut engine, "empty.concat(one)"); let en = engine
assert_eq!(en, String::from("[a]")); .eval("empty.concat(one)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(en, "[a]");
// NonEmpty ++ Empty // NonEmpty ++ Empty
let ne = forward(&mut engine, "one.concat(empty)"); let ne = engine
assert_eq!(ne, String::from("a.b.c")); .eval("one.concat(empty)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(ne, "a.b.c");
// NonEmpty ++ NonEmpty // NonEmpty ++ NonEmpty
let nn = forward(&mut engine, "one.concat(one)"); let nn = engine
assert_eq!(nn, String::from("a.b.c")); .eval("one.concat(one)")
.unwrap()
.to_string(&mut engine)
.unwrap();
assert_eq!(nn, "a.b.c");
} }
#[test] #[test]
fn join() { fn join() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ]; var empty = [ ];
var one = ["a"]; var one = ["a"];
@ -88,8 +128,7 @@ fn join() {
#[test] #[test]
fn to_string() { fn to_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ]; var empty = [ ];
var one = ["a"]; var one = ["a"];
@ -109,8 +148,7 @@ fn to_string() {
#[test] #[test]
fn every() { fn every() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
let init = r#" let init = r#"
var empty = []; var empty = [];
@ -154,8 +192,7 @@ fn every() {
#[test] #[test]
fn find() { fn find() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
function comp(a) { function comp(a) {
return a == "a"; return a == "a";
@ -169,8 +206,7 @@ fn find() {
#[test] #[test]
fn find_index() { fn find_index() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let code = r#" let code = r#"
function comp(item) { function comp(item) {
@ -195,8 +231,7 @@ fn find_index() {
#[test] #[test]
fn push() { fn push() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var arr = [1, 2]; var arr = [1, 2];
"#; "#;
@ -210,8 +245,7 @@ fn push() {
#[test] #[test]
fn pop() { fn pop() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ]; var empty = [ ];
var one = [1]; var one = [1];
@ -232,8 +266,7 @@ fn pop() {
#[test] #[test]
fn shift() { fn shift() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ]; var empty = [ ];
var one = [1]; var one = [1];
@ -254,8 +287,7 @@ fn shift() {
#[test] #[test]
fn unshift() { fn unshift() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var arr = [3, 4]; var arr = [3, 4];
"#; "#;
@ -269,8 +301,7 @@ fn unshift() {
#[test] #[test]
fn reverse() { fn reverse() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var arr = [1, 2]; var arr = [1, 2];
var reversed = arr.reverse(); var reversed = arr.reverse();
@ -284,8 +315,7 @@ fn reverse() {
#[test] #[test]
fn index_of() { fn index_of() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ]; var empty = [ ];
var one = ["a"]; var one = ["a"];
@ -348,8 +378,7 @@ fn index_of() {
#[test] #[test]
fn last_index_of() { fn last_index_of() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ]; var empty = [ ];
var one = ["a"]; var one = ["a"];
@ -412,8 +441,7 @@ fn last_index_of() {
#[test] #[test]
fn fill_obj_ref() { fn fill_obj_ref() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// test object reference // test object reference
forward(&mut engine, "a = (new Array(3)).fill({});"); forward(&mut engine, "a = (new Array(3)).fill({});");
@ -423,8 +451,7 @@ fn fill_obj_ref() {
#[test] #[test]
fn fill() { fn fill() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
forward(&mut engine, "var a = [1, 2, 3];"); forward(&mut engine, "var a = [1, 2, 3];");
assert_eq!( assert_eq!(
@ -519,8 +546,7 @@ fn fill() {
#[test] #[test]
fn includes_value() { fn includes_value() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ]; var empty = [ ];
var one = ["a"]; var one = ["a"];
@ -558,8 +584,7 @@ fn includes_value() {
#[test] #[test]
fn map() { fn map() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let js = r#" let js = r#"
var empty = []; var empty = [];
@ -622,8 +647,7 @@ fn map() {
#[test] #[test]
fn slice() { fn slice() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = [ ].slice(); var empty = [ ].slice();
var one = ["a"].slice(); var one = ["a"].slice();
@ -646,8 +670,7 @@ fn slice() {
#[test] #[test]
fn for_each() { fn for_each() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var a = [2, 3, 4, 5]; var a = [2, 3, 4, 5];
var sum = 0; var sum = 0;
@ -669,8 +692,7 @@ fn for_each() {
#[test] #[test]
fn for_each_push_value() { fn for_each_push_value() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var a = [1, 2, 3, 4]; var a = [1, 2, 3, 4];
function callingCallback(item, index, list) { function callingCallback(item, index, list) {
@ -690,8 +712,7 @@ fn for_each_push_value() {
#[test] #[test]
fn filter() { fn filter() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let js = r#" let js = r#"
var empty = []; var empty = [];
@ -760,8 +781,7 @@ fn filter() {
#[test] #[test]
fn some() { fn some() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = []; var empty = [];
@ -809,8 +829,7 @@ fn some() {
#[test] #[test]
fn reduce() { fn reduce() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var arr = [1, 2, 3, 4]; var arr = [1, 2, 3, 4];
@ -919,8 +938,7 @@ fn reduce() {
#[test] #[test]
fn reduce_right() { fn reduce_right() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var arr = [1, 2, 3, 4]; var arr = [1, 2, 3, 4];
@ -1042,8 +1060,7 @@ fn reduce_right() {
#[test] #[test]
fn call_array_constructor_with_one_argument() { fn call_array_constructor_with_one_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = new Array(0); var empty = new Array(0);

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

@ -1,6 +1,6 @@
use super::BigInt; use super::BigInt;
use crate::{builtins::Number, exec::Interpreter, Value}; use crate::{builtins::Number, Context, Value};
use num_traits::cast::{FromPrimitive, ToPrimitive}; use num_traits::cast::{FromPrimitive, ToPrimitive};
use std::convert::TryFrom; use std::convert::TryFrom;
@ -14,7 +14,7 @@ impl BigInt {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-stringtobigint /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint
#[inline] #[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() { if string.is_empty() {
return Ok(BigInt::from(0)); 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}, function::{make_builtin_fn, make_constructor_fn},
object::ObjectData, object::ObjectData,
}, },
exec::Interpreter,
value::{RcBigInt, Value}, value::{RcBigInt, Value},
BoaProfiler, Result, BoaProfiler, Context, Result,
}; };
use gc::{unsafe_empty_trace, Finalize, Trace}; use gc::{unsafe_empty_trace, Finalize, Trace};
@ -61,7 +60,7 @@ impl BigInt {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue /// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue
#[inline] #[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 { match value {
// 1. If Type(value) is BigInt, return value. // 1. If Type(value) is BigInt, return value.
Value::BigInt(ref bigint) => return Ok(bigint.clone()), Value::BigInt(ref bigint) => return Ok(bigint.clone()),
@ -91,7 +90,7 @@ impl BigInt {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-bigint-objects /// [spec]: https://tc39.es/ecma262/#sec-bigint-objects
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Result<Value> { pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let data = match args.get(0) { let data = match args.get(0) {
Some(ref value) => value.to_bigint(ctx)?, Some(ref value) => value.to_bigint(ctx)?,
None => RcBigInt::from(Self::from(0)), None => RcBigInt::from(Self::from(0)),
@ -110,7 +109,7 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
#[allow(clippy::wrong_self_convention)] #[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() { let radix = if !args.is_empty() {
args[0].to_integer(ctx)? as i32 args[0].to_integer(ctx)? as i32
} else { } else {
@ -135,7 +134,7 @@ impl BigInt {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/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)?)) Ok(Value::from(Self::this_bigint_value(this, ctx)?))
} }
@ -146,7 +145,7 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn /// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN
#[allow(clippy::wrong_self_convention)] #[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)?; let (modulo, bits) = Self::calculate_as_uint_n(args, ctx)?;
if bits > 0 && modulo >= BigInt::from(2).pow(&BigInt::from(bits as i64 - 1)) { 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 /// [spec]: https://tc39.es/ecma262/#sec-bigint.asuintn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN
#[allow(clippy::wrong_self_convention)] #[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)?; let (modulo, _) = Self::calculate_as_uint_n(args, ctx)?;
Ok(Value::from(modulo)) 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`. /// 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 /// Additionally to the wrapped unsigned value it returns the converted `bits` argument, so it
/// can be reused from the `as_int_n` method. /// 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; use std::convert::TryFrom;
let undefined_value = Value::undefined(); let undefined_value = Value::undefined();
@ -200,8 +199,8 @@ impl BigInt {
/// Initialise the `BigInt` object on the global object. /// Initialise the `BigInt` object on the global object.
#[inline] #[inline]
pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global)); 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] #[test]
fn equality() { fn equality() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "0n == 0n"), "true"); assert_eq!(forward(&mut engine, "0n == 0n"), "true");
assert_eq!(forward(&mut engine, "1n == 0n"), "false"); assert_eq!(forward(&mut engine, "1n == 0n"), "false");
@ -56,8 +55,7 @@ fn equality() {
#[test] #[test]
fn bigint_function_conversion_from_integer() { fn bigint_function_conversion_from_integer() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "BigInt(1000)"), "1000n"); assert_eq!(forward(&mut engine, "BigInt(1000)"), "1000n");
assert_eq!( assert_eq!(
@ -72,8 +70,7 @@ fn bigint_function_conversion_from_integer() {
#[test] #[test]
fn bigint_function_conversion_from_rational() { fn bigint_function_conversion_from_rational() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "BigInt(0.0)"), "0n"); assert_eq!(forward(&mut engine, "BigInt(0.0)"), "0n");
assert_eq!(forward(&mut engine, "BigInt(1.0)"), "1n"); assert_eq!(forward(&mut engine, "BigInt(1.0)"), "1n");
@ -82,8 +79,7 @@ fn bigint_function_conversion_from_rational() {
#[test] #[test]
fn bigint_function_conversion_from_rational_with_fractional_part() { fn bigint_function_conversion_from_rational_with_fractional_part() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
try { try {
@ -100,8 +96,7 @@ fn bigint_function_conversion_from_rational_with_fractional_part() {
#[test] #[test]
fn bigint_function_conversion_from_null() { fn bigint_function_conversion_from_null() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
try { try {
@ -118,8 +113,7 @@ fn bigint_function_conversion_from_null() {
#[test] #[test]
fn bigint_function_conversion_from_undefined() { fn bigint_function_conversion_from_undefined() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
try { try {
@ -136,8 +130,7 @@ fn bigint_function_conversion_from_undefined() {
#[test] #[test]
fn bigint_function_conversion_from_string() { fn bigint_function_conversion_from_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "BigInt('')"), "0n"); assert_eq!(forward(&mut engine, "BigInt('')"), "0n");
assert_eq!( assert_eq!(
@ -152,24 +145,21 @@ fn bigint_function_conversion_from_string() {
#[test] #[test]
fn add() { fn add() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "10000n + 1000n"), "11000n"); assert_eq!(forward(&mut engine, "10000n + 1000n"), "11000n");
} }
#[test] #[test]
fn sub() { fn sub() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "10000n - 1000n"), "9000n"); assert_eq!(forward(&mut engine, "10000n - 1000n"), "9000n");
} }
#[test] #[test]
fn mul() { fn mul() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!( assert_eq!(
forward(&mut engine, "123456789n * 102030n"), forward(&mut engine, "123456789n * 102030n"),
@ -179,32 +169,28 @@ fn mul() {
#[test] #[test]
fn div() { fn div() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "15000n / 50n"), "300n"); assert_eq!(forward(&mut engine, "15000n / 50n"), "300n");
} }
#[test] #[test]
fn div_with_truncation() { fn div_with_truncation() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "15001n / 50n"), "300n"); assert_eq!(forward(&mut engine, "15001n / 50n"), "300n");
} }
#[test] #[test]
fn r#mod() { fn r#mod() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "15007n % 10n"), "7n"); assert_eq!(forward(&mut engine, "15007n % 10n"), "7n");
} }
#[test] #[test]
fn pow() { fn pow() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!( assert_eq!(
forward(&mut engine, "100n ** 10n"), forward(&mut engine, "100n ** 10n"),
@ -214,8 +200,7 @@ fn pow() {
#[test] #[test]
fn to_string() { fn to_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "1000n.toString()"), "\"1000\""); assert_eq!(forward(&mut engine, "1000n.toString()"), "\"1000\"");
assert_eq!(forward(&mut engine, "1000n.toString(2)"), "\"1111101000\""); assert_eq!(forward(&mut engine, "1000n.toString(2)"), "\"1111101000\"");
@ -225,8 +210,7 @@ fn to_string() {
#[test] #[test]
fn as_int_n() { fn as_int_n() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "BigInt.asIntN(0, 1n)"), "0n"); assert_eq!(forward(&mut engine, "BigInt.asIntN(0, 1n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asIntN(1, 1n)"), "-1n"); assert_eq!(forward(&mut engine, "BigInt.asIntN(1, 1n)"), "-1n");
@ -286,8 +270,7 @@ fn as_int_n() {
#[test] #[test]
fn as_int_n_errors() { fn as_int_n_errors() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_throws(&mut engine, "BigInt.asIntN(-1, 0n)", "RangeError"); assert_throws(&mut engine, "BigInt.asIntN(-1, 0n)", "RangeError");
assert_throws(&mut engine, "BigInt.asIntN(-2.5, 0n)", "RangeError"); assert_throws(&mut engine, "BigInt.asIntN(-2.5, 0n)", "RangeError");
@ -301,8 +284,7 @@ fn as_int_n_errors() {
#[test] #[test]
fn as_uint_n() { fn as_uint_n() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "BigInt.asUintN(0, -2n)"), "0n"); assert_eq!(forward(&mut engine, "BigInt.asUintN(0, -2n)"), "0n");
assert_eq!(forward(&mut engine, "BigInt.asUintN(0, -1n)"), "0n"); assert_eq!(forward(&mut engine, "BigInt.asUintN(0, -1n)"), "0n");
@ -353,8 +335,7 @@ fn as_uint_n() {
#[test] #[test]
fn as_uint_n_errors() { fn as_uint_n_errors() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_throws(&mut engine, "BigInt.asUintN(-1, 0n)", "RangeError"); assert_throws(&mut engine, "BigInt.asUintN(-1, 0n)", "RangeError");
assert_throws(&mut engine, "BigInt.asUintN(-2.5, 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"); 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); let result = forward(engine, src);
assert!(result.contains(error_type)); assert!(result.contains(error_type));
} }

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

@ -13,7 +13,7 @@
mod tests; mod tests;
use super::function::{make_builtin_fn, make_constructor_fn}; 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. /// Boolean implementation.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -32,7 +32,7 @@ impl Boolean {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue /// [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 { match value {
Value::Boolean(boolean) => return Ok(*boolean), Value::Boolean(boolean) => return Ok(*boolean),
Value::Object(ref object) => { Value::Object(ref object) => {
@ -53,7 +53,7 @@ impl Boolean {
pub(crate) fn construct_boolean( pub(crate) fn construct_boolean(
this: &Value, this: &Value,
args: &[Value], args: &[Value],
_: &mut Interpreter, _: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
// Get the argument, if any // Get the argument, if any
let data = args.get(0).map(|x| x.to_boolean()).unwrap_or(false); 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 /// [spec]: https://tc39.es/ecma262/#sec-boolean-object
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString
#[allow(clippy::wrong_self_convention)] #[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)?; let boolean = Self::this_boolean_value(this, ctx)?;
Ok(Value::from(boolean.to_string())) Ok(Value::from(boolean.to_string()))
} }
@ -85,14 +85,14 @@ impl Boolean {
/// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof /// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf
#[inline] #[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)?)) Ok(Value::from(Self::this_boolean_value(this, ctx)?))
} }
/// Initialise the `Boolean` object on the global object. /// Initialise the `Boolean` object on the global object.
#[inline] #[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create Prototype // 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 /// Test the correct type is returned from call and construct
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
#[test] #[test]
fn construct_and_call() { fn construct_and_call() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var one = new Boolean(1); var one = new Boolean(1);
var zero = Boolean(0); var zero = Boolean(0);
@ -20,8 +19,7 @@ fn construct_and_call() {
#[test] #[test]
fn constructor_gives_true_instance() { fn constructor_gives_true_instance() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var trueVal = new Boolean(true); var trueVal = new Boolean(true);
var trueNum = new Boolean(1); var trueNum = new Boolean(1);
@ -50,8 +48,7 @@ fn constructor_gives_true_instance() {
#[test] #[test]
fn instances_have_correct_proto_set() { fn instances_have_correct_proto_set() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var boolInstance = new Boolean(true); var boolInstance = new Boolean(true);
var boolProto = Boolean.prototype; var boolProto = Boolean.prototype;

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

@ -18,9 +18,8 @@ mod tests;
use crate::{ use crate::{
builtins::function::make_builtin_fn, builtins::function::make_builtin_fn,
exec::Interpreter,
value::{display_obj, RcString, Value}, value::{display_obj, RcString, Value},
BoaProfiler, Result, BoaProfiler, Context, Result,
}; };
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::time::SystemTime; use std::time::SystemTime;
@ -57,7 +56,7 @@ pub(crate) fn logger(msg: LogMessage, console_state: &Console) {
} }
/// This represents the `console` formatter. /// 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)?; let target = data.get(0).cloned().unwrap_or_default().to_string(ctx)?;
match data.len() { match data.len() {
@ -152,7 +151,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#assert /// [spec]: https://console.spec.whatwg.org/#assert
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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(); let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default();
if !assertion { if !assertion {
@ -183,7 +182,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#clear /// [spec]: https://console.spec.whatwg.org/#clear
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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(); ctx.console_mut().groups.clear();
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -198,7 +197,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#debug /// [spec]: https://console.spec.whatwg.org/#debug
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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()); logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -213,7 +212,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#error /// [spec]: https://console.spec.whatwg.org/#error
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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()); logger(LogMessage::Error(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -228,7 +227,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#info /// [spec]: https://console.spec.whatwg.org/#info
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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()); logger(LogMessage::Info(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -243,7 +242,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#log /// [spec]: https://console.spec.whatwg.org/#log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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()); logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -258,7 +257,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#trace /// [spec]: https://console.spec.whatwg.org/#trace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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() { if !args.is_empty() {
logger(LogMessage::Log(formatter(args, ctx)?), ctx.console()); logger(LogMessage::Log(formatter(args, ctx)?), ctx.console());
@ -282,7 +281,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#warn /// [spec]: https://console.spec.whatwg.org/#warn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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()); logger(LogMessage::Warn(formatter(args, ctx)?), ctx.console());
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -297,7 +296,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#count /// [spec]: https://console.spec.whatwg.org/#count
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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) { let label = match args.get(0) {
Some(value) => value.to_string(ctx)?, Some(value) => value.to_string(ctx)?,
None => "default".into(), None => "default".into(),
@ -321,7 +320,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#countreset /// [spec]: https://console.spec.whatwg.org/#countreset
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/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) { let label = match args.get(0) {
Some(value) => value.to_string(ctx)?, Some(value) => value.to_string(ctx)?,
None => "default".into(), None => "default".into(),
@ -355,7 +354,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#time /// [spec]: https://console.spec.whatwg.org/#time
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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) { let label = match args.get(0) {
Some(value) => value.to_string(ctx)?, Some(value) => value.to_string(ctx)?,
None => "default".into(), None => "default".into(),
@ -384,7 +383,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#timelog /// [spec]: https://console.spec.whatwg.org/#timelog
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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) { let label = match args.get(0) {
Some(value) => value.to_string(ctx)?, Some(value) => value.to_string(ctx)?,
None => "default".into(), None => "default".into(),
@ -417,7 +416,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#timeend /// [spec]: https://console.spec.whatwg.org/#timeend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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) { let label = match args.get(0) {
Some(value) => value.to_string(ctx)?, Some(value) => value.to_string(ctx)?,
None => "default".into(), None => "default".into(),
@ -449,7 +448,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#group /// [spec]: https://console.spec.whatwg.org/#group
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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)?; let group_label = formatter(args, ctx)?;
logger( logger(
@ -471,7 +470,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#groupend /// [spec]: https://console.spec.whatwg.org/#groupend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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(); ctx.console_mut().groups.pop();
Ok(Value::undefined()) Ok(Value::undefined())
@ -487,7 +486,7 @@ impl Console {
/// ///
/// [spec]: https://console.spec.whatwg.org/#dir /// [spec]: https://console.spec.whatwg.org/#dir
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/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(); let undefined = Value::undefined();
logger( logger(
LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined), true)), 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. /// Initialise the `console` object on the global object.
#[inline] #[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let console = Value::new_object(Some(global)); 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] #[test]
fn formatter_no_args_is_empty_string() { fn formatter_no_args_is_empty_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(formatter(&[], &mut engine).unwrap(), ""); assert_eq!(formatter(&[], &mut engine).unwrap(), "");
} }
#[test] #[test]
fn formatter_empty_format_string_is_empty_string() { fn formatter_empty_format_string_is_empty_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let val = Value::string("".to_string()); let val = Value::string("".to_string());
assert_eq!(formatter(&[val], &mut engine).unwrap(), ""); assert_eq!(formatter(&[val], &mut engine).unwrap(), "");
} }
#[test] #[test]
fn formatter_format_without_args_renders_verbatim() { fn formatter_format_without_args_renders_verbatim() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let val = [Value::string("%d %s %% %f")]; let val = [Value::string("%d %s %% %f")];
let res = formatter(&val, &mut engine).unwrap(); let res = formatter(&val, &mut engine).unwrap();
assert_eq!(res, "%d %s %% %f"); assert_eq!(res, "%d %s %% %f");
@ -26,8 +23,7 @@ fn formatter_format_without_args_renders_verbatim() {
#[test] #[test]
fn formatter_empty_format_string_concatenates_rest_of_args() { fn formatter_empty_format_string_concatenates_rest_of_args() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let val = [ let val = [
Value::string(""), Value::string(""),
@ -40,8 +36,7 @@ fn formatter_empty_format_string_concatenates_rest_of_args() {
#[test] #[test]
fn formatter_utf_8_checks() { fn formatter_utf_8_checks() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let val = [ let val = [
Value::string("Są takie chwile %dą %są tu%sów %привет%ź".to_string()), Value::string("Są takie chwile %dą %są tu%sów %привет%ź".to_string()),
@ -55,8 +50,7 @@ fn formatter_utf_8_checks() {
#[test] #[test]
fn formatter_trailing_format_leader_renders() { fn formatter_trailing_format_leader_renders() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let val = [ let val = [
Value::string("%%%%%".to_string()), Value::string("%%%%%".to_string()),
@ -69,8 +63,7 @@ fn formatter_trailing_format_leader_renders() {
#[test] #[test]
#[allow(clippy::approx_constant)] #[allow(clippy::approx_constant)]
fn formatter_float_format_works() { fn formatter_float_format_works() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let val = [Value::string("%f".to_string()), Value::rational(3.1415)]; let val = [Value::string("%f".to_string()), Value::rational(3.1415)];
let res = formatter(&val, &mut engine).unwrap(); 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}, function::{make_builtin_fn, make_constructor_fn},
object::ObjectData, object::ObjectData,
}, },
value::PreferredType, value::{PreferredType, Value},
BoaProfiler, Interpreter, Result, Value, BoaProfiler, Context, Result,
}; };
use chrono::{prelude::*, Duration, LocalResult}; use chrono::{prelude::*, Duration, LocalResult};
use gc::{unsafe_empty_trace, Finalize, Trace}; use gc::{unsafe_empty_trace, Finalize, Trace};
@ -39,13 +39,13 @@ fn ignore_ambiguity<T>(result: LocalResult<T>) -> Option<T> {
macro_rules! getter_method { macro_rules! getter_method {
($name:ident) => {{ ($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())) Ok(Value::from(this_time_value(this, ctx)?.$name()))
} }
get_value get_value
}}; }};
(Self::$name:ident) => {{ (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())) Ok(Value::from(Date::$name()))
} }
get_value get_value
@ -54,7 +54,7 @@ macro_rules! getter_method {
macro_rules! setter_method { macro_rules! setter_method {
($name:ident($($e:expr),* $(,)?)) => {{ ($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)?; let mut result = this_time_value(this, ctx)?;
result.$name( result.$name(
$( $(
@ -245,7 +245,7 @@ impl Date {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date /// [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() { if this.is_global() {
Self::make_date_string() Self::make_date_string()
} else if args.is_empty() { } else if args.is_empty() {
@ -300,7 +300,7 @@ impl Date {
pub(crate) fn make_date_single( pub(crate) fn make_date_single(
this: &Value, this: &Value,
args: &[Value], args: &[Value],
ctx: &mut Interpreter, ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
let value = &args[0]; let value = &args[0];
let tv = match this_time_value(value, ctx) { let tv = match this_time_value(value, ctx) {
@ -337,7 +337,7 @@ impl Date {
pub(crate) fn make_date_multiple( pub(crate) fn make_date_multiple(
this: &Value, this: &Value,
args: &[Value], args: &[Value],
ctx: &mut Interpreter, ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
let year = args[0].to_number(ctx)?; let year = args[0].to_number(ctx)?;
let month = args[1].to_number(ctx)?; let month = args[1].to_number(ctx)?;
@ -1163,7 +1163,7 @@ impl Date {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-date.now /// [spec]: https://tc39.es/ecma262/#sec-date.now
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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)) Ok(Value::from(Utc::now().timestamp_millis() as f64))
} }
@ -1179,7 +1179,7 @@ impl Date {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-date.parse /// [spec]: https://tc39.es/ecma262/#sec-date.parse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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 // This method is implementation-defined and discouraged, so we just require the same format as the string
// constructor. // constructor.
@ -1203,7 +1203,7 @@ impl Date {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-date.utc /// [spec]: https://tc39.es/ecma262/#sec-date.utc
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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 let year = args
.get(0) .get(0)
.map_or(Ok(f64::NAN), |value| value.to_number(ctx))?; .map_or(Ok(f64::NAN), |value| value.to_number(ctx))?;
@ -1241,8 +1241,8 @@ impl Date {
/// Initialise the `Date` object on the global object. /// Initialise the `Date` object on the global object.
#[inline] #[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global)); let prototype = Value::new_object(Some(global));
@ -1580,7 +1580,7 @@ impl Date {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-thistimevalue /// [spec]: https://tc39.es/ecma262/#sec-thistimevalue
#[inline] #[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 Value::Object(ref object) = value {
if let ObjectData::Date(ref date) = object.borrow().data { if let ObjectData::Date(ref date) = object.borrow().data {
return Ok(*date); return Ok(*date);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -3,9 +3,8 @@
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::object::{ObjectData, PROTOTYPE}, builtins::object::{ObjectData, PROTOTYPE},
exec::Interpreter,
property::{Attribute, Property}, property::{Attribute, Property},
BoaProfiler, Result, Value, BoaProfiler, Context, Result, Value,
}; };
use ordered_map::OrderedMap; use ordered_map::OrderedMap;
@ -41,7 +40,7 @@ impl Map {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.set /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.set
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/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() { let (key, value) = match args.len() {
0 => (Value::Undefined, Value::Undefined), 0 => (Value::Undefined, Value::Undefined),
1 => (args[0].clone(), Value::Undefined), 1 => (args[0].clone(), Value::Undefined),
@ -74,7 +73,7 @@ impl Map {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.delete /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.delete
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/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 undefined = Value::Undefined;
let key = match args.len() { let key = match args.len() {
0 => &undefined, 0 => &undefined,
@ -106,7 +105,7 @@ impl Map {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.get /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.get
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/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 undefined = Value::Undefined;
let key = match args.len() { let key = match args.len() {
0 => &undefined, 0 => &undefined,
@ -137,7 +136,7 @@ impl Map {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.clear /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.clear
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/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())); this.set_data(ObjectData::Map(OrderedMap::new()));
Self::set_size(this, 0); Self::set_size(this, 0);
@ -155,7 +154,7 @@ impl Map {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/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 undefined = Value::Undefined;
let key = match args.len() { let key = match args.len() {
0 => &undefined, 0 => &undefined,
@ -185,7 +184,7 @@ impl Map {
pub(crate) fn for_each( pub(crate) fn for_each(
this: &Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
if args.is_empty() { if args.is_empty() {
return Err(Value::from("Missing argument for Map.prototype.forEach")); return Err(Value::from("Missing argument for Map.prototype.forEach"));
@ -224,12 +223,12 @@ impl Map {
} }
/// Create a new 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 // Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype // between indices and values): this creates an Object with no prototype
// Set 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() this.as_object_mut()
.expect("this is array object") .expect("this is array object")
@ -281,8 +280,8 @@ impl Map {
} }
/// Initialise the `Map` object on the global object. /// Initialise the `Map` object on the global object.
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype // 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] #[test]
fn construct_empty() { fn construct_empty() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = new Map(); var empty = new Map();
"#; "#;
@ -14,8 +13,7 @@ fn construct_empty() {
#[test] #[test]
fn construct_from_array() { fn construct_from_array() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]); let map = new Map([["1", "one"], ["2", "two"]]);
"#; "#;
@ -26,8 +24,7 @@ fn construct_from_array() {
#[test] #[test]
fn clone() { fn clone() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let original = new Map([["1", "one"], ["2", "two"]]); let original = new Map([["1", "one"], ["2", "two"]]);
let clone = new Map(original); let clone = new Map(original);
@ -48,8 +45,7 @@ fn clone() {
#[test] #[test]
fn merge() { fn merge() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let first = new Map([["1", "one"], ["2", "two"]]); let first = new Map([["1", "one"], ["2", "two"]]);
let second = new Map([["2", "second two"], ["3", "three"]]); let second = new Map([["2", "second two"], ["3", "three"]]);
@ -68,8 +64,7 @@ fn merge() {
#[test] #[test]
fn get() { fn get() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]); let map = new Map([["1", "one"], ["2", "two"]]);
"#; "#;
@ -86,8 +81,7 @@ fn get() {
#[test] #[test]
fn set() { fn set() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map(); let map = new Map();
"#; "#;
@ -105,8 +99,7 @@ fn set() {
#[test] #[test]
fn clear() { fn clear() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]); let map = new Map([["1", "one"], ["2", "two"]]);
map.clear(); map.clear();
@ -118,8 +111,7 @@ fn clear() {
#[test] #[test]
fn delete() { fn delete() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map([["1", "one"], ["2", "two"]]); let map = new Map([["1", "one"], ["2", "two"]]);
"#; "#;
@ -134,8 +126,7 @@ fn delete() {
#[test] #[test]
fn has() { fn has() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map([["1", "one"]]); let map = new Map([["1", "one"]]);
"#; "#;
@ -150,8 +141,7 @@ fn has() {
#[test] #[test]
fn for_each() { fn for_each() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map([[1, 5], [2, 10], [3, 15]]); let map = new Map([[1, 5], [2, 10], [3, 15]]);
let valueSum = 0; let valueSum = 0;
@ -172,8 +162,7 @@ fn for_each() {
#[test] #[test]
fn modify_key() { fn modify_key() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let obj = new Object(); let obj = new Object();
let map = new Map([[obj, "one"]]); let map = new Map([[obj, "one"]]);
@ -186,8 +175,7 @@ fn modify_key() {
#[test] #[test]
fn order() { fn order() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map([[1, "one"]]); let map = new Map([[1, "one"]]);
map.set(2, "two"); map.set(2, "two");
@ -213,8 +201,7 @@ fn order() {
#[test] #[test]
fn recursive_display() { fn recursive_display() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let map = new Map(); let map = new Map();
let array = new Array([map]); let array = new Array([map]);
@ -229,8 +216,7 @@ fn recursive_display() {
#[test] #[test]
fn not_a_function() { fn not_a_function() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r" let init = r"
try { try {
let map = Map() let map = Map()

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

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

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

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

6
boa/src/builtins/mod.rs

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

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

@ -13,7 +13,7 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use crate::{BoaProfiler, Interpreter, Value}; use crate::{BoaProfiler, Context, Value};
/// JavaScript global `NaN` property. /// JavaScript global `NaN` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -25,7 +25,7 @@ impl NaN {
/// Initialize the `NaN` property on the global object. /// Initialize the `NaN` property on the global object.
#[inline] #[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 _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::from(f64::NAN)) (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}, function::{make_builtin_fn, make_constructor_fn},
object::ObjectData, object::ObjectData,
}; };
use crate::{exec::Interpreter, value::AbstractRelation, BoaProfiler, Result, Value}; use crate::{
value::{AbstractRelation, Value},
BoaProfiler, Context, Result,
};
use num_traits::float::FloatCore; use num_traits::float::FloatCore;
mod conversions; mod conversions;
@ -101,7 +104,7 @@ impl Number {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue /// [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 { match *value {
Value::Integer(integer) => return Ok(f64::from(integer)), Value::Integer(integer) => return Ok(f64::from(integer)),
Value::Rational(rational) => return Ok(rational), Value::Rational(rational) => return Ok(rational),
@ -128,11 +131,7 @@ impl Number {
/// `[[Construct]]` - Creates a Number instance /// `[[Construct]]` - Creates a Number instance
/// ///
/// `[[Call]]` - Creates a number primitive /// `[[Call]]` - Creates a number primitive
pub(crate) fn make_number( pub(crate) fn make_number(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
let data = match args.get(0) { let data = match args.get(0) {
Some(ref value) => value.to_numeric_number(ctx)?, Some(ref value) => value.to_numeric_number(ctx)?,
None => 0.0, None => 0.0,
@ -156,7 +155,7 @@ impl Number {
pub(crate) fn to_exponential( pub(crate) fn to_exponential(
this: &Value, this: &Value,
_args: &[Value], _args: &[Value],
ctx: &mut Interpreter, ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?; let this_num = Self::this_number_value(this, ctx)?;
let this_str_num = Self::num_to_exponential(this_num); 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 /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
#[allow(clippy::wrong_self_convention)] #[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 this_num = Self::this_number_value(this, ctx)?;
let precision = match args.get(0) { let precision = match args.get(0) {
Some(n) => match n.to_integer(ctx)? as i32 { Some(n) => match n.to_integer(ctx)? as i32 {
@ -204,7 +203,7 @@ impl Number {
pub(crate) fn to_locale_string( pub(crate) fn to_locale_string(
this: &Value, this: &Value,
_args: &[Value], _args: &[Value],
ctx: &mut Interpreter, ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?; let this_num = Self::this_number_value(this, ctx)?;
let this_str_num = format!("{}", this_num); let this_str_num = format!("{}", this_num);
@ -222,11 +221,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_precision( pub(crate) fn to_precision(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
let this_num = Self::this_number_value(this, ctx)?; let this_num = Self::this_number_value(this, ctx)?;
let _num_str_len = format!("{}", this_num).len(); let _num_str_len = format!("{}", this_num).len();
let _precision = match args.get(0) { let _precision = match args.get(0) {
@ -380,7 +375,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString
#[allow(clippy::wrong_self_convention)] #[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). // 1. Let x be ? thisNumberValue(this value).
let x = Self::this_number_value(this, ctx)?; let x = Self::this_number_value(this, ctx)?;
@ -435,7 +430,7 @@ impl Number {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/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)?)) Ok(Value::from(Self::this_number_value(this, ctx)?))
} }
@ -453,11 +448,7 @@ impl Number {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix /// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
pub(crate) fn parse_int( pub(crate) fn parse_int(_this: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
_this: &Value,
args: &[Value],
_ctx: &mut Interpreter,
) -> Result<Value> {
if let (Some(val), r) = (args.get(0), args.get(1)) { if let (Some(val), r) = (args.get(0), args.get(1)) {
let mut radix = if let Some(rx) = r { let mut radix = if let Some(rx) = r {
if let Value::Integer(i) = rx { if let Value::Integer(i) = rx {
@ -523,11 +514,7 @@ impl Number {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string /// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
pub(crate) fn parse_float( pub(crate) fn parse_float(_this: &Value, args: &[Value], _ctx: &mut Context) -> Result<Value> {
_this: &Value,
args: &[Value],
_ctx: &mut Interpreter,
) -> Result<Value> {
if let Some(val) = args.get(0) { if let Some(val) = args.get(0) {
match val { match val {
Value::String(s) => { Value::String(s) => {
@ -572,7 +559,7 @@ impl Number {
pub(crate) fn global_is_finite( pub(crate) fn global_is_finite(
_this: &Value, _this: &Value,
args: &[Value], args: &[Value],
ctx: &mut Interpreter, ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
if let Some(value) = args.get(0) { if let Some(value) = args.get(0) {
let number = value.to_number(ctx)?; let number = value.to_number(ctx)?;
@ -596,11 +583,7 @@ impl Number {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-isnan-number /// [spec]: https://tc39.es/ecma262/#sec-isnan-number
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN
pub(crate) fn global_is_nan( pub(crate) fn global_is_nan(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
_this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
if let Some(value) = args.get(0) { if let Some(value) = args.get(0) {
let number = value.to_number(ctx)?; let number = value.to_number(ctx)?;
Ok(number.is_nan().into()) Ok(number.is_nan().into())
@ -626,7 +609,7 @@ impl Number {
pub(crate) fn number_is_finite( pub(crate) fn number_is_finite(
_this: &Value, _this: &Value,
args: &[Value], args: &[Value],
_ctx: &mut Interpreter, _ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
Ok(Value::from(if let Some(val) = args.get(0) { Ok(Value::from(if let Some(val) = args.get(0) {
match val { match val {
@ -652,7 +635,7 @@ impl Number {
pub(crate) fn number_is_integer( pub(crate) fn number_is_integer(
_this: &Value, _this: &Value,
args: &[Value], args: &[Value],
_ctx: &mut Interpreter, _ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
Ok(args.get(0).map_or(false, Self::is_integer).into()) Ok(args.get(0).map_or(false, Self::is_integer).into())
} }
@ -674,7 +657,7 @@ impl Number {
pub(crate) fn number_is_nan( pub(crate) fn number_is_nan(
_this: &Value, _this: &Value,
args: &[Value], args: &[Value],
_ctx: &mut Interpreter, _ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
Ok(Value::from(if let Some(val) = args.get(0) { Ok(Value::from(if let Some(val) = args.get(0) {
match val { match val {
@ -704,7 +687,7 @@ impl Number {
pub(crate) fn is_safe_integer( pub(crate) fn is_safe_integer(
_this: &Value, _this: &Value,
args: &[Value], args: &[Value],
_ctx: &mut Interpreter, _ctx: &mut Context,
) -> Result<Value> { ) -> Result<Value> {
Ok(Value::from(match args.get(0) { Ok(Value::from(match args.get(0) {
Some(Value::Integer(_)) => true, Some(Value::Integer(_)) => true,
@ -739,8 +722,8 @@ impl Number {
/// Initialise the `Number` object on the global object. /// Initialise the `Number` object on the global object.
#[inline] #[inline]
pub(crate) fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global)); let prototype = Value::new_object(Some(global));

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

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

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

@ -11,7 +11,7 @@ use crate::{
function_environment_record::BindingStatus, lexical_environment::new_function_environment, function_environment_record::BindingStatus, lexical_environment::new_function_environment,
}, },
syntax::ast::node::RcStatementList, syntax::ast::node::RcStatementList,
Executable, Interpreter, Result, Value, Context, Executable, Result, Value,
}; };
use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace}; use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace};
use std::{ use std::{
@ -107,7 +107,7 @@ impl GcObject {
// <https://tc39.es/ecma262/#sec-prepareforordinarycall> // <https://tc39.es/ecma262/#sec-prepareforordinarycall>
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist> // <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
#[track_caller] #[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 this_function_object = self.clone();
let f_body = if let Some(function) = self.borrow().as_function() { let f_body = if let Some(function) = self.borrow().as_function() {
if function.is_callable() { if function.is_callable() {
@ -160,7 +160,7 @@ impl GcObject {
.borrow_mut() .borrow_mut()
.initialize_binding("arguments", arguments_obj); .initialize_binding("arguments", arguments_obj);
ctx.realm.environment.push(local_env); ctx.realm_mut().environment.push(local_env);
FunctionBody::Ordinary(body.clone()) FunctionBody::Ordinary(body.clone())
} }
@ -176,7 +176,7 @@ impl GcObject {
FunctionBody::BuiltIn(func) => func(this, args, ctx), FunctionBody::BuiltIn(func) => func(this, args, ctx),
FunctionBody::Ordinary(body) => { FunctionBody::Ordinary(body) => {
let result = body.run(ctx); let result = body.run(ctx);
ctx.realm.environment.pop(); ctx.realm_mut().environment.pop();
result result
} }
@ -189,7 +189,7 @@ impl GcObject {
/// Panics if the object is currently mutably borrowed. /// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget> // <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
#[track_caller] #[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 = Object::create(self.borrow().get(&PROTOTYPE.into())).into();
let this_function_object = self.clone(); let this_function_object = self.clone();
@ -242,13 +242,13 @@ impl GcObject {
.borrow_mut() .borrow_mut()
.initialize_binding("arguments", arguments_obj); .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 // Call body should be set before reaching here
let _ = body.run(ctx); let _ = body.run(ctx);
// local_env gets dropped here, its no longer needed // 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) Ok(binding)
} }
} }

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

@ -19,10 +19,9 @@ use crate::{
map::ordered_map::OrderedMap, map::ordered_map::OrderedMap,
BigInt, Date, RegExp, BigInt, Date, RegExp,
}, },
exec::Interpreter,
property::{Property, PropertyKey}, property::{Property, PropertyKey},
value::{same_value, RcBigInt, RcString, RcSymbol, Value}, value::{same_value, RcBigInt, RcString, RcSymbol, Value},
BoaProfiler, Result, BoaProfiler, Context, Result,
}; };
use gc::{Finalize, Trace}; use gc::{Finalize, Trace};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -469,14 +468,15 @@ impl Object {
} }
/// Create a new 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 let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() { if !arg.is_null_or_undefined() {
return arg.to_object(ctx); 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] )` /// `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 /// [spec]: https://tc39.es/ecma262/#sec-object.create
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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 prototype = args.get(0).cloned().unwrap_or_else(Value::undefined);
let properties = args.get(1).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 /// 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 x = args.get(0).cloned().unwrap_or_else(Value::undefined);
let y = args.get(1).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. /// 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"); let obj = args.get(0).expect("Cannot get object");
Ok(obj Ok(obj
.as_object() .as_object()
@ -526,7 +526,7 @@ pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> Resul
} }
/// Set the `prototype` of an object. /// 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 obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone();
obj.as_object_mut().unwrap().prototype = proto; 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 /// 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 obj = args.get(0).expect("Cannot get object");
let prop = args.get(1).expect("Cannot get object").to_string(ctx)?; let prop = args.get(1).expect("Cannot get object").to_string(ctx)?;
let desc = Property::from(args.get(2).expect("Cannot get object")); 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 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/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. // FIXME: it should not display the object.
Ok(this.display().to_string().into()) 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 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/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() { let prop = if args.is_empty() {
None None
} else { } else {
@ -586,11 +586,7 @@ pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Interpreter) ->
} }
} }
pub fn property_is_enumerable( pub fn property_is_enumerable(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
let key = match args.get(0) { let key = match args.get(0) {
None => return Ok(Value::from(false)), None => return Ok(Value::from(false)),
Some(key) => key, Some(key) => key,
@ -610,8 +606,8 @@ pub fn property_is_enumerable(
/// Initialise the `Object` object on the global object. /// Initialise the `Object` object on the global object.
#[inline] #[inline]
pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global(); let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("object", "init"); let _timer = BoaProfiler::global().start_event("object", "init");
let prototype = Value::new_object(None); 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] #[test]
fn object_create_with_regular_object() { fn object_create_with_regular_object() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
const foo = { a: 5 }; const foo = { a: 5 };
@ -18,8 +17,7 @@ fn object_create_with_regular_object() {
#[test] #[test]
fn object_create_with_undefined() { fn object_create_with_undefined() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
try { try {
@ -38,8 +36,7 @@ fn object_create_with_undefined() {
#[test] #[test]
fn object_create_with_number() { fn object_create_with_number() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
try { try {
@ -60,8 +57,7 @@ fn object_create_with_number() {
#[ignore] #[ignore]
// to test on __proto__ somehow. __proto__ getter is not working as expected currently // to test on __proto__ somehow. __proto__ getter is not working as expected currently
fn object_create_with_function() { fn object_create_with_function() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
const x = function (){}; const x = function (){};
@ -75,8 +71,7 @@ fn object_create_with_function() {
#[test] #[test]
fn object_is() { fn object_is() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var foo = { a: 1}; 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(NaN, 0/0)"), "true");
assert_eq!(forward(&mut engine, "Object.is()"), "true"); assert_eq!(forward(&mut engine, "Object.is()"), "true");
assert_eq!(forward(&mut engine, "Object.is(undefined)"), "true"); assert_eq!(forward(&mut engine, "Object.is(undefined)"), "true");
assert!(engine.realm.global_obj.is_global()); assert!(engine.global_object().is_global());
assert!(!engine.realm.global_obj.get_field("Object").is_global()); assert!(!engine.global_object().get_field("Object").is_global());
} }
#[test] #[test]
fn object_has_own_property() { fn object_has_own_property() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let x = { someProp: 1, undefinedProp: undefined, nullProp: null }; let x = { someProp: 1, undefinedProp: undefined, nullProp: null };
"#; "#;
@ -122,8 +116,7 @@ fn object_has_own_property() {
#[test] #[test]
fn object_property_is_enumerable() { fn object_property_is_enumerable() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let x = { enumerableProp: 'yes' }; 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 super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::object::ObjectData, builtins::object::ObjectData,
exec::Interpreter,
property::Property, property::Property,
value::{RcString, Value}, value::{RcString, Value},
BoaProfiler, Result, BoaProfiler, Context, Result,
}; };
use gc::{unsafe_empty_trace, Finalize, Trace}; use gc::{unsafe_empty_trace, Finalize, Trace};
@ -70,7 +69,7 @@ impl RegExp {
pub(crate) const LENGTH: usize = 2; pub(crate) const LENGTH: usize = 2;
/// Create a new `RegExp` /// 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 arg = args.get(0).ok_or_else(Value::undefined)?;
let mut regex_body = String::new(); let mut regex_body = String::new();
let mut regex_flags = String::new(); let mut regex_flags = String::new();
@ -167,7 +166,7 @@ impl RegExp {
// /// // ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll // /// [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 // /// [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))) // 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 // /// [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 // /// [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 // /// [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()))) // 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 // /// [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 // /// [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))) // 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 // /// [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 // /// [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))) // 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 // /// [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 // /// [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))) // 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 // /// [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 // /// [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")) // Ok(this.get_internal_slot("OriginalSource"))
// } // }
@ -253,7 +252,7 @@ impl RegExp {
// /// // ///
// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky // /// [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 // /// [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))) // 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 // /// [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 // /// [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))) // 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 /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/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 let arg_str = args
.get(0) .get(0)
.expect("could not get argument") .expect("could not get argument")
@ -323,7 +322,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/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 let arg_str = args
.get(0) .get(0)
.expect("could not get argument") .expect("could not get argument")
@ -379,7 +378,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@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 (matcher, flags) = if let Some(object) = this.as_object() {
let regex = object.as_regexp().unwrap(); let regex = object.as_regexp().unwrap();
(regex.matcher.clone(), regex.flags.clone()) (regex.matcher.clone(), regex.flags.clone())
@ -411,7 +410,7 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
#[allow(clippy::wrong_self_convention)] #[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 (body, flags) = if let Some(object) = this.as_object() {
let regex = object.as_regexp().unwrap(); let regex = object.as_regexp().unwrap();
(regex.original_source.clone(), regex.flags.clone()) (regex.original_source.clone(), regex.flags.clone())
@ -478,9 +477,9 @@ impl RegExp {
/// Initialise the `RegExp` object on the global object. /// Initialise the `RegExp` object on the global object.
#[inline] #[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 _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let global = interpreter.global(); let global = interpreter.global_object();
// Create prototype // Create prototype
let prototype = Value::new_object(Some(global)); 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] #[test]
fn constructors() { fn constructors() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var constructed = new RegExp("[0-9]+(\\.[0-9]+)?"); var constructed = new RegExp("[0-9]+(\\.[0-9]+)?");
var literal = /[0-9]+(\.[0-9]+)?/; var literal = /[0-9]+(\.[0-9]+)?/;
@ -20,7 +19,7 @@ fn constructors() {
// #[test] // #[test]
// fn flags() { // fn flags() {
// let mut engine = Interpreter::new(); // let mut engine = Context::new();
// let init = r#" // let init = r#"
// var re_gi = /test/gi; // var re_gi = /test/gi;
// var re_sm = /test/sm; // var re_sm = /test/sm;
@ -46,8 +45,7 @@ fn constructors() {
#[test] #[test]
fn last_index() { fn last_index() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var regex = /[0-9]+(\.[0-9]+)?/g; var regex = /[0-9]+(\.[0-9]+)?/g;
"#; "#;
@ -62,8 +60,7 @@ fn last_index() {
#[test] #[test]
fn exec() { fn exec() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var re = /quick\s(brown).+?(jumps)/ig; var re = /quick\s(brown).+?(jumps)/ig;
var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog'); var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
@ -85,8 +82,7 @@ fn exec() {
#[test] #[test]
fn to_string() { fn to_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!( assert_eq!(
forward(&mut engine, "(new RegExp('a+b+c')).toString()"), forward(&mut engine, "(new RegExp('a+b+c')).toString()"),

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

@ -18,10 +18,9 @@ use crate::{
object::{Object, ObjectData}, object::{Object, ObjectData},
RegExp, RegExp,
}, },
exec::Interpreter,
property::Property, property::Property,
value::{RcString, Value}, value::{RcString, Value},
BoaProfiler, Result, BoaProfiler, Context, Result,
}; };
use regex::Regex; use regex::Regex;
use std::string::String as StdString; 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` /// which can differ in JavaScript engines. In Boa it is `2^32 - 1`
pub(crate) const MAX_STRING_LENGTH: f64 = u32::MAX as f64; 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 { match this {
Value::String(ref string) => return Ok(string.clone()), Value::String(ref string) => return Ok(string.clone()),
Value::Object(ref object) => { Value::Object(ref object) => {
@ -67,11 +66,7 @@ impl String {
/// ///
/// [[Call]] - Returns a new native `string` /// [[Call]] - Returns a new native `string`
/// <https://tc39.es/ecma262/#sec-string-constructor-string-value> /// <https://tc39.es/ecma262/#sec-string-constructor-string-value>
pub(crate) fn make_string( pub(crate) fn make_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
// This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe // 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) // to its Javascript Identifier (global constructor method name)
let string = match args.get(0) { let string = match args.get(0) {
@ -91,7 +86,7 @@ impl String {
/// Get the string value to a primitive string /// Get the string value to a primitive string
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[inline] #[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 // Get String from String Object and send it back as a new value
Ok(Value::from(Self::this_string_value(this, ctx)?)) Ok(Value::from(Self::this_string_value(this, ctx)?))
} }
@ -112,7 +107,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -154,11 +149,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
pub(crate) fn char_code_at( pub(crate) fn char_code_at(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -198,7 +189,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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 object = this.require_object_coercible(ctx)?;
let mut string = object.to_string(ctx)?.to_string(); let mut string = object.to_string(ctx)?.to_string();
@ -220,7 +211,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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 object = this.require_object_coercible(ctx)?;
let string = object.to_string(ctx)?; let string = object.to_string(ctx)?;
@ -254,7 +245,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -304,11 +295,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
pub(crate) fn starts_with( pub(crate) fn starts_with(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
// First we get it the actual string a private field stored on the object only the engine has access to. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -351,7 +338,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -397,7 +384,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -459,7 +446,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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 // TODO: Support Symbol replacer
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
if args.is_empty() { if args.is_empty() {
@ -617,7 +604,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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 this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?; let string = this.to_string(ctx)?;
@ -660,11 +647,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
pub(crate) fn last_index_of( pub(crate) fn last_index_of(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> Result<Value> {
let this = this.require_object_coercible(ctx)?; let this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?; let string = this.to_string(ctx)?;
@ -705,7 +688,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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 /// [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)?; let re = RegExp::make_regexp(&Value::from(Object::default()), &[args[0].clone()], ctx)?;
RegExp::r#match(&re, this.to_string(ctx)?, 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 /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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)?; let primitive = this.to_string(ctx)?;
if args.is_empty() { if args.is_empty() {
return Err(Value::from("padEnd requires maxLength argument")); return Err(Value::from("padEnd requires maxLength argument"));
@ -783,7 +766,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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)?; let primitive = this.to_string(ctx)?;
if args.is_empty() { if args.is_empty() {
return Err(Value::from("padStart requires maxLength argument")); return Err(Value::from("padStart requires maxLength argument"));
@ -830,7 +813,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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 this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?; let string = this.to_string(ctx)?;
Ok(Value::from( Ok(Value::from(
@ -850,8 +833,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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> { pub(crate) fn trim_start(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?; let string = this.to_string(ctx)?;
Ok(Value::from( Ok(Value::from(
string.trim_start_matches(Self::is_trimmable_whitespace), string.trim_start_matches(Self::is_trimmable_whitespace),
@ -870,7 +852,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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 this = this.require_object_coercible(ctx)?;
let string = this.to_string(ctx)?; let string = this.to_string(ctx)?;
Ok(Value::from( Ok(Value::from(
@ -889,7 +871,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
#[allow(clippy::wrong_self_convention)] #[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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let this_str = this.to_string(ctx)?; let this_str = this.to_string(ctx)?;
@ -911,7 +893,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
#[allow(clippy::wrong_self_convention)] #[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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let this_str = this.to_string(ctx)?; let this_str = this.to_string(ctx)?;
@ -930,7 +912,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -979,7 +961,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
/// <https://tc39.es/ecma262/#sec-string.prototype.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. // 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 // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = this.to_string(ctx)?; let primitive_val = this.to_string(ctx)?;
@ -1035,7 +1017,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of /// [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 /// [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 // Use the to_string method because it is specified to do the same thing in this case
Self::to_string(this, args, ctx) Self::to_string(this, args, ctx)
} }
@ -1053,7 +1035,7 @@ impl String {
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions /// [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 /// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges
// TODO: update this method to return iterator // 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) { let re: Value = match args.get(0) {
Some(arg) => { Some(arg) => {
if arg.is_null() { if arg.is_null() {
@ -1084,12 +1066,12 @@ impl String {
/// Initialise the `String` object on the global object. /// Initialise the `String` object on the global object.
#[inline] #[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 _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create `String` `prototype` // Create `String` `prototype`
let global = interpreter.global(); let global = interpreter.global_object();
let prototype = Value::new_object(Some(global)); let prototype = Value::new_object(Some(global));
let length = Property::default().value(Value::from(0)); 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; ///TODO: re-enable when getProperty() is finished;
#[test] #[test]
#[ignore] #[ignore]
fn length() { fn length() {
//TEST262: https://github.com/tc39/test262/blob/master/test/built-ins/String/length.js //TEST262: https://github.com/tc39/test262/blob/master/test/built-ins/String/length.js
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
const a = new String(' '); const a = new String(' ');
const b = new String('\ud834\udf06'); const b = new String('\ud834\udf06');
@ -30,8 +29,7 @@ fn length() {
#[test] #[test]
fn new_string_has_length() { fn new_string_has_length() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let a = new String("1234"); let a = new String("1234");
a a
@ -43,8 +41,7 @@ fn new_string_has_length() {
#[test] #[test]
fn new_utf8_string_has_length() { fn new_utf8_string_has_length() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let a = new String("中文"); let a = new String("中文");
a a
@ -56,8 +53,7 @@ fn new_utf8_string_has_length() {
#[test] #[test]
fn concat() { fn concat() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var hello = new String('Hello, '); var hello = new String('Hello, ');
var world = new String('world! '); var world = new String('world! ');
@ -74,8 +70,7 @@ fn concat() {
#[test] #[test]
fn generic_concat() { fn generic_concat() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
Number.prototype.concat = String.prototype.concat; Number.prototype.concat = String.prototype.concat;
let number = new Number(100); let number = new Number(100);
@ -90,8 +85,7 @@ fn generic_concat() {
#[test] #[test]
/// Test the correct type is returned from call and construct /// Test the correct type is returned from call and construct
fn construct_and_call() { fn construct_and_call() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var hello = new String('Hello'); var hello = new String('Hello');
var world = String('world'); var world = String('world');
@ -108,8 +102,7 @@ fn construct_and_call() {
#[test] #[test]
fn repeat() { fn repeat() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = new String(''); var empty = new String('');
var en = new String('english'); var en = new String('english');
@ -130,8 +123,7 @@ fn repeat() {
#[test] #[test]
fn repeat_throws_when_count_is_negative() { fn repeat_throws_when_count_is_negative() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!( assert_eq!(
forward( forward(
@ -150,8 +142,7 @@ fn repeat_throws_when_count_is_negative() {
#[test] #[test]
fn repeat_throws_when_count_is_infinity() { fn repeat_throws_when_count_is_infinity() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!( assert_eq!(
forward( forward(
@ -170,8 +161,7 @@ fn repeat_throws_when_count_is_infinity() {
#[test] #[test]
fn repeat_throws_when_count_overflows_max_length() { fn repeat_throws_when_count_overflows_max_length() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!( assert_eq!(
forward( forward(
@ -190,8 +180,7 @@ fn repeat_throws_when_count_overflows_max_length() {
#[test] #[test]
fn repeat_generic() { fn repeat_generic() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = "Number.prototype.repeat = String.prototype.repeat;"; let init = "Number.prototype.repeat = String.prototype.repeat;";
forward(&mut engine, init); forward(&mut engine, init);
@ -205,8 +194,7 @@ fn repeat_generic() {
#[test] #[test]
fn replace() { fn replace() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var a = "abc"; var a = "abc";
a = a.replace("a", "2"); a = a.replace("a", "2");
@ -220,8 +208,7 @@ fn replace() {
#[test] #[test]
fn replace_no_match() { fn replace_no_match() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var a = "abc"; var a = "abc";
a = a.replace(/d/, "$&$&"); a = a.replace(/d/, "$&$&");
@ -234,8 +221,7 @@ fn replace_no_match() {
#[test] #[test]
fn replace_with_capture_groups() { fn replace_with_capture_groups() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var re = /(\w+)\s(\w+)/; var re = /(\w+)\s(\w+)/;
var a = "John Smith"; var a = "John Smith";
@ -250,8 +236,7 @@ fn replace_with_capture_groups() {
#[test] #[test]
fn replace_with_tenth_capture_group() { fn replace_with_tenth_capture_group() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var re = /(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/; var re = /(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/;
var a = "0123456789"; var a = "0123456789";
@ -265,8 +250,7 @@ fn replace_with_tenth_capture_group() {
#[test] #[test]
fn replace_substitutions() { fn replace_substitutions() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var re = / two /; var re = / two /;
var a = "one two three"; var a = "one two three";
@ -288,8 +272,7 @@ fn replace_substitutions() {
#[test] #[test]
fn replace_with_function() { fn replace_with_function() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var a = "ecmascript is cool"; var a = "ecmascript is cool";
var p1, p2, p3; var p1, p2, p3;
@ -315,8 +298,7 @@ fn replace_with_function() {
#[test] #[test]
fn starts_with() { fn starts_with() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = new String(''); var empty = new String('');
var en = new String('english'); var en = new String('english');
@ -340,8 +322,7 @@ fn starts_with() {
#[test] #[test]
fn ends_with() { fn ends_with() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var empty = new String(''); var empty = new String('');
var en = new String('english'); var en = new String('english');
@ -365,8 +346,7 @@ fn ends_with() {
#[test] #[test]
fn match_all() { fn match_all() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "'aa'.matchAll(null).length"), "0"); assert_eq!(forward(&mut engine, "'aa'.matchAll(null).length"), "0");
assert_eq!(forward(&mut engine, "'aa'.matchAll(/b/).length"), "0"); assert_eq!(forward(&mut engine, "'aa'.matchAll(/b/).length"), "0");
@ -408,8 +388,7 @@ fn match_all() {
#[test] #[test]
fn test_match() { fn test_match() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var str = new String('The Quick Brown Fox Jumps Over The Lazy Dog'); var str = new String('The Quick Brown Fox Jumps Over The Lazy Dog');
var result1 = str.match(/quick\s(brown).+?(jumps)/i); var result1 = str.match(/quick\s(brown).+?(jumps)/i);
@ -453,8 +432,7 @@ fn test_match() {
#[test] #[test]
fn trim() { fn trim() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "'Hello'.trim()"), "\"Hello\""); assert_eq!(forward(&mut engine, "'Hello'.trim()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trim()"), "\"Hello\""); assert_eq!(forward(&mut engine, "' \nHello'.trim()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "'Hello \n\r'.trim()"), "\"Hello\""); assert_eq!(forward(&mut engine, "'Hello \n\r'.trim()"), "\"Hello\"");
@ -463,8 +441,7 @@ fn trim() {
#[test] #[test]
fn trim_start() { fn trim_start() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "'Hello'.trimStart()"), "\"Hello\""); assert_eq!(forward(&mut engine, "'Hello'.trimStart()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trimStart()"), "\"Hello\""); assert_eq!(forward(&mut engine, "' \nHello'.trimStart()"), "\"Hello\"");
assert_eq!( assert_eq!(
@ -476,8 +453,7 @@ fn trim_start() {
#[test] #[test]
fn trim_end() { fn trim_end() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "'Hello'.trimEnd()"), "\"Hello\""); assert_eq!(forward(&mut engine, "'Hello'.trimEnd()"), "\"Hello\"");
assert_eq!(forward(&mut engine, "' \nHello'.trimEnd()"), "\" \nHello\""); assert_eq!(forward(&mut engine, "' \nHello'.trimEnd()"), "\" \nHello\"");
assert_eq!(forward(&mut engine, "'Hello \n'.trimEnd()"), "\"Hello\""); assert_eq!(forward(&mut engine, "'Hello \n'.trimEnd()"), "\"Hello\"");
@ -486,8 +462,7 @@ fn trim_end() {
#[test] #[test]
fn index_of_with_no_arguments() { fn index_of_with_no_arguments() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf()"), "-1"); assert_eq!(forward(&mut engine, "''.indexOf()"), "-1");
assert_eq!(forward(&mut engine, "'undefined'.indexOf()"), "0"); assert_eq!(forward(&mut engine, "'undefined'.indexOf()"), "0");
assert_eq!(forward(&mut engine, "'a1undefined'.indexOf()"), "2"); assert_eq!(forward(&mut engine, "'a1undefined'.indexOf()"), "2");
@ -498,8 +473,7 @@ fn index_of_with_no_arguments() {
#[test] #[test]
fn index_of_with_string_search_string_argument() { fn index_of_with_string_search_string_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf('hello')"), "-1"); assert_eq!(forward(&mut engine, "''.indexOf('hello')"), "-1");
assert_eq!( assert_eq!(
forward(&mut engine, "'undefined'.indexOf('undefined')"), forward(&mut engine, "'undefined'.indexOf('undefined')"),
@ -525,8 +499,7 @@ fn index_of_with_string_search_string_argument() {
#[test] #[test]
fn index_of_with_non_string_search_string_argument() { fn index_of_with_non_string_search_string_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf(1)"), "-1"); assert_eq!(forward(&mut engine, "''.indexOf(1)"), "-1");
assert_eq!(forward(&mut engine, "'1'.indexOf(1)"), "0"); assert_eq!(forward(&mut engine, "'1'.indexOf(1)"), "0");
assert_eq!(forward(&mut engine, "'true'.indexOf(true)"), "0"); assert_eq!(forward(&mut engine, "'true'.indexOf(true)"), "0");
@ -537,8 +510,7 @@ fn index_of_with_non_string_search_string_argument() {
#[test] #[test]
fn index_of_with_from_index_argument() { fn index_of_with_from_index_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf('x', 2)"), "-1"); assert_eq!(forward(&mut engine, "''.indexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'x'.indexOf('x', 2)"), "-1"); assert_eq!(forward(&mut engine, "'x'.indexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'abcx'.indexOf('x', 2)"), "3"); assert_eq!(forward(&mut engine, "'abcx'.indexOf('x', 2)"), "3");
@ -553,8 +525,7 @@ fn index_of_with_from_index_argument() {
#[test] #[test]
fn generic_index_of() { fn generic_index_of() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
forward_val( forward_val(
&mut engine, &mut engine,
"Number.prototype.indexOf = String.prototype.indexOf", "Number.prototype.indexOf = String.prototype.indexOf",
@ -568,8 +539,7 @@ fn generic_index_of() {
#[test] #[test]
fn index_of_empty_search_string() { fn index_of_empty_search_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.indexOf('')"), "0"); assert_eq!(forward(&mut engine, "''.indexOf('')"), "0");
assert_eq!(forward(&mut engine, "''.indexOf('', 10)"), "0"); assert_eq!(forward(&mut engine, "''.indexOf('', 10)"), "0");
@ -580,8 +550,7 @@ fn index_of_empty_search_string() {
#[test] #[test]
fn last_index_of_with_no_arguments() { fn last_index_of_with_no_arguments() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.lastIndexOf()"), "-1"); assert_eq!(forward(&mut engine, "''.lastIndexOf()"), "-1");
assert_eq!(forward(&mut engine, "'undefined'.lastIndexOf()"), "0"); assert_eq!(forward(&mut engine, "'undefined'.lastIndexOf()"), "0");
assert_eq!(forward(&mut engine, "'a1undefined'.lastIndexOf()"), "2"); assert_eq!(forward(&mut engine, "'a1undefined'.lastIndexOf()"), "2");
@ -601,8 +570,7 @@ fn last_index_of_with_no_arguments() {
#[test] #[test]
fn last_index_of_with_string_search_string_argument() { fn last_index_of_with_string_search_string_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.lastIndexOf('hello')"), "-1"); assert_eq!(forward(&mut engine, "''.lastIndexOf('hello')"), "-1");
assert_eq!( assert_eq!(
forward(&mut engine, "'undefined'.lastIndexOf('undefined')"), forward(&mut engine, "'undefined'.lastIndexOf('undefined')"),
@ -637,8 +605,7 @@ fn last_index_of_with_string_search_string_argument() {
#[test] #[test]
fn last_index_of_with_non_string_search_string_argument() { fn last_index_of_with_non_string_search_string_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.lastIndexOf(1)"), "-1"); assert_eq!(forward(&mut engine, "''.lastIndexOf(1)"), "-1");
assert_eq!(forward(&mut engine, "'1'.lastIndexOf(1)"), "0"); assert_eq!(forward(&mut engine, "'1'.lastIndexOf(1)"), "0");
assert_eq!(forward(&mut engine, "'11'.lastIndexOf(1)"), "1"); assert_eq!(forward(&mut engine, "'11'.lastIndexOf(1)"), "1");
@ -653,8 +620,7 @@ fn last_index_of_with_non_string_search_string_argument() {
#[test] #[test]
fn last_index_of_with_from_index_argument() { fn last_index_of_with_from_index_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.lastIndexOf('x', 2)"), "-1"); assert_eq!(forward(&mut engine, "''.lastIndexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'x'.lastIndexOf('x', 2)"), "-1"); assert_eq!(forward(&mut engine, "'x'.lastIndexOf('x', 2)"), "-1");
assert_eq!(forward(&mut engine, "'abcxx'.lastIndexOf('x', 2)"), "4"); assert_eq!(forward(&mut engine, "'abcxx'.lastIndexOf('x', 2)"), "4");
@ -669,8 +635,7 @@ fn last_index_of_with_from_index_argument() {
#[test] #[test]
fn last_index_with_empty_search_string() { fn last_index_with_empty_search_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(forward(&mut engine, "''.lastIndexOf('')"), "0"); assert_eq!(forward(&mut engine, "''.lastIndexOf('')"), "0");
assert_eq!(forward(&mut engine, "'x'.lastIndexOf('', 2)"), "1"); assert_eq!(forward(&mut engine, "'x'.lastIndexOf('', 2)"), "1");
assert_eq!(forward(&mut engine, "'abcxx'.lastIndexOf('', 4)"), "4"); assert_eq!(forward(&mut engine, "'abcxx'.lastIndexOf('', 4)"), "4");
@ -681,8 +646,7 @@ fn last_index_with_empty_search_string() {
#[test] #[test]
fn generic_last_index_of() { fn generic_last_index_of() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
forward_val( forward_val(
&mut engine, &mut engine,
"Number.prototype.lastIndexOf = String.prototype.lastIndexOf", "Number.prototype.lastIndexOf = String.prototype.lastIndexOf",
@ -696,8 +660,7 @@ fn generic_last_index_of() {
#[test] #[test]
fn last_index_non_integer_position_argument() { fn last_index_non_integer_position_argument() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!( assert_eq!(
forward(&mut engine, "''.lastIndexOf('x', new Number(4))"), forward(&mut engine, "''.lastIndexOf('x', new Number(4))"),
"-1" "-1"

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

@ -20,10 +20,9 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
exec::Interpreter,
property::{Attribute, Property}, property::{Attribute, Property},
value::{RcString, RcSymbol, Value}, value::{RcString, RcSymbol, Value},
BoaProfiler, Result, BoaProfiler, Context, Result,
}; };
use gc::{Finalize, Trace}; use gc::{Finalize, Trace};
@ -56,7 +55,7 @@ impl Symbol {
self.hash 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 { match value {
Value::Symbol(ref symbol) => return Ok(symbol.clone()), Value::Symbol(ref symbol) => return Ok(symbol.clone()),
Value::Object(ref object) => { Value::Object(ref object) => {
@ -82,7 +81,7 @@ impl Symbol {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-description /// [spec]: https://tc39.es/ecma262/#sec-symbol-description
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol /// [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) { let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?), Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?),
_ => None, _ => None,
@ -102,7 +101,7 @@ impl Symbol {
/// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString
#[allow(clippy::wrong_self_convention)] #[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 symbol = Self::this_symbol_value(this, ctx)?;
let description = symbol.description().unwrap_or(""); let description = symbol.description().unwrap_or("");
Ok(Value::from(format!("Symbol({})", description))) Ok(Value::from(format!("Symbol({})", description)))
@ -110,7 +109,7 @@ impl Symbol {
/// Initialise the `Symbol` object on the global object. /// Initialise the `Symbol` object on the global object.
#[inline] #[inline]
pub fn init(interpreter: &mut Interpreter) -> (&'static str, Value) { pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
// Define the Well-Known Symbols // Define the Well-Known Symbols
// https://tc39.es/ecma262/#sec-well-known-symbols // https://tc39.es/ecma262/#sec-well-known-symbols
let symbol_async_iterator = let symbol_async_iterator =
@ -129,7 +128,7 @@ impl Symbol {
let symbol_to_string_tag = interpreter.construct_symbol(Some("Symbol.toStringTag".into())); let symbol_to_string_tag = interpreter.construct_symbol(Some("Symbol.toStringTag".into()));
let symbol_unscopables = interpreter.construct_symbol(Some("Symbol.unscopables".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"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype object // 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] #[test]
fn call_symbol_and_check_return_type() { fn call_symbol_and_check_return_type() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var sym = Symbol(); var sym = Symbol();
"#; "#;
@ -14,8 +13,7 @@ fn call_symbol_and_check_return_type() {
#[test] #[test]
fn print_symbol_expect_description() { fn print_symbol_expect_description() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var sym = Symbol("Hello"); var sym = Symbol("Hello");
"#; "#;
@ -26,8 +24,7 @@ fn print_symbol_expect_description() {
#[test] #[test]
fn symbol_access() { fn symbol_access() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var x = {}; var x = {};
var sym1 = Symbol("Hello"); var sym1 = Symbol("Hello");

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

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

27
boa/src/class.rs

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

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

@ -1,13 +1,13 @@
//! Block statement execution. //! Block statement execution.
use super::{Executable, Interpreter, InterpreterState}; use super::{Context, Executable, InterpreterState};
use crate::{ use crate::{
environment::lexical_environment::new_declarative_environment, syntax::ast::node::Block, environment::lexical_environment::new_declarative_environment, syntax::ast::node::Block,
BoaProfiler, Result, Value, BoaProfiler, Result, Value,
}; };
impl Executable for Block { 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 _timer = BoaProfiler::global().start_event("Block", "exec");
{ {
let env = &mut interpreter.realm_mut().environment; let env = &mut interpreter.realm_mut().environment;
@ -22,7 +22,7 @@ impl Executable for Block {
for statement in self.statements() { for statement in self.statements() {
obj = statement.run(interpreter)?; obj = statement.run(interpreter)?;
match interpreter.get_current_state() { match interpreter.executor().get_current_state() {
InterpreterState::Return => { InterpreterState::Return => {
// Early return. // Early return.
break; 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::{ use crate::{
syntax::ast::node::{Break, Continue}, syntax::ast::node::{Break, Continue},
Result, Value, Result, Value,
@ -8,16 +8,20 @@ use crate::{
mod tests; mod tests;
impl Executable for Break { impl Executable for Break {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter.set_current_state(InterpreterState::Break(self.label().map(String::from))); interpreter
.executor()
.set_current_state(InterpreterState::Break(self.label().map(String::from)));
Ok(Value::undefined()) Ok(Value::undefined())
} }
} }
impl Executable for Continue { impl Executable for Continue {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter.set_current_state(InterpreterState::Continue(self.label().map(String::from))); interpreter
.executor()
.set_current_state(InterpreterState::Continue(self.label().map(String::from)));
Ok(Value::undefined()) Ok(Value::undefined())
} }

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

@ -1,17 +1,16 @@
use super::{Interpreter, InterpreterState}; use super::{Context, InterpreterState};
use crate::{exec::Executable, syntax::ast::node::Break, Realm}; use crate::{exec::Executable, syntax::ast::node::Break};
#[test] #[test]
fn check_post_state() { fn check_post_state() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let brk: Break = Break::new("label"); let brk: Break = Break::new("label");
brk.run(&mut engine).unwrap(); brk.run(&mut engine).unwrap();
assert_eq!( assert_eq!(
engine.get_current_state(), engine.executor().get_current_state(),
&InterpreterState::Break(Some("label".to_string())) &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::{ use crate::{
syntax::ast::node::{Call, Node}, syntax::ast::node::{Call, Node},
value::{Type, Value}, value::{Type, Value},
@ -6,7 +6,7 @@ use crate::{
}; };
impl Executable for Call { 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 _timer = BoaProfiler::global().start_event("Call", "exec");
let (this, func) = match self.expr() { let (this, func) = match self.expr() {
Node::GetConstField(ref get_const_field) => { Node::GetConstField(ref get_const_field) => {
@ -44,7 +44,9 @@ impl Executable for Call {
let fnct_result = interpreter.call(&func, &this, &v_args); let fnct_result = interpreter.call(&func, &this, &v_args);
// unset the early return flag // unset the early return flag
interpreter.set_current_state(InterpreterState::Executing); interpreter
.executor()
.set_current_state(InterpreterState::Executing);
fnct_result fnct_result
} }

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

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

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

@ -1,6 +1,6 @@
//! Declaration execution. //! Declaration execution.
use super::{Executable, Interpreter}; use super::{Context, Executable};
use crate::{ use crate::{
builtins::function::FunctionFlags, builtins::function::FunctionFlags,
environment::lexical_environment::VariableScope, environment::lexical_environment::VariableScope,
@ -11,7 +11,7 @@ use crate::{
}; };
impl Executable for FunctionDecl { 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 _timer = BoaProfiler::global().start_event("FunctionDecl", "exec");
let val = interpreter.create_function( let val = interpreter.create_function(
self.parameters().to_vec(), self.parameters().to_vec(),
@ -37,7 +37,7 @@ impl Executable for FunctionDecl {
} }
impl Executable for FunctionExpr { 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( let val = interpreter.create_function(
self.parameters().to_vec(), self.parameters().to_vec(),
self.body().to_vec(), self.body().to_vec(),
@ -53,7 +53,7 @@ impl Executable for FunctionExpr {
} }
impl Executable for VarDeclList { 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() { for var in self.as_ref() {
let val = match var.init() { let val = match var.init() {
Some(v) => v.run(interpreter)?, Some(v) => v.run(interpreter)?,
@ -79,7 +79,7 @@ impl Executable for VarDeclList {
} }
impl Executable for ConstDeclList { 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() { for decl in self.as_ref() {
let val = decl.init().run(interpreter)?; let val = decl.init().run(interpreter)?;
@ -98,7 +98,7 @@ impl Executable for ConstDeclList {
} }
impl Executable for LetDeclList { 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() { for var in self.as_ref() {
let val = match var.init() { let val = match var.init() {
Some(v) => v.run(interpreter)?, Some(v) => v.run(interpreter)?,
@ -119,7 +119,7 @@ impl Executable for LetDeclList {
} }
impl Executable for ArrowFunctionDecl { impl Executable for ArrowFunctionDecl {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { fn run(&self, interpreter: &mut Context) -> Result<Value> {
Ok(interpreter.create_function( Ok(interpreter.create_function(
self.params().to_vec(), self.params().to_vec(),
self.body().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::{ use crate::{
syntax::ast::node::{GetConstField, GetField}, syntax::ast::node::{GetConstField, GetField},
value::{Type, Value}, value::{Type, Value},
@ -6,7 +6,7 @@ use crate::{
}; };
impl Executable for GetConstField { 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)?; let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object { if obj.get_type() != Type::Object {
obj = obj.to_object(interpreter)?; obj = obj.to_object(interpreter)?;
@ -17,7 +17,7 @@ impl Executable for GetConstField {
} }
impl Executable for GetField { 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)?; let mut obj = self.obj().run(interpreter)?;
if obj.get_type() != Type::Object { if obj.get_type() != Type::Object {
obj = obj.to_object(interpreter)?; 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}; use crate::{syntax::ast::node::identifier::Identifier, Result, Value};
impl Executable for Identifier { impl Executable for Identifier {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { fn run(&self, interpreter: &mut Context) -> Result<Value> {
interpreter interpreter
.realm() .realm()
.environment .environment

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

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

346
boa/src/exec/mod.rs

@ -6,7 +6,6 @@ mod break_node;
mod call; mod call;
mod conditional; mod conditional;
mod declaration; mod declaration;
mod exception;
mod field; mod field;
mod identifier; mod identifier;
mod iteration; mod iteration;
@ -17,33 +16,20 @@ mod return_smt;
mod spread; mod spread;
mod statement_list; mod statement_list;
mod switch; mod switch;
#[cfg(test)]
mod tests;
mod throw; mod throw;
mod try_node; mod try_node;
#[cfg(test)]
mod tests;
use crate::{ use crate::{
builtins, syntax::ast::{constant::Const, node::Node},
builtins::{ BoaProfiler, Context, Result, Value,
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,
}; };
use std::result::Result as StdResult;
pub trait Executable { pub trait Executable {
/// Runs this executable in the given executor. /// Runs this executable in the given context.
fn run(&self, interpreter: &mut Interpreter) -> Result<Value>; fn run(&self, interpreter: &mut Context) -> Result<Value>;
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
@ -59,269 +45,19 @@ pub(crate) enum InterpreterState {
pub struct Interpreter { pub struct Interpreter {
/// the current state of the interpreter. /// the current state of the interpreter.
state: InterpreterState, state: InterpreterState,
}
/// realm holds both the global object and the environment impl Default for Interpreter {
pub realm: Realm, fn default() -> Self {
Self::new()
/// This is for generating an unique internal `Symbol` hash. }
symbol_count: u32,
/// console object state.
console: Console,
} }
impl Interpreter { impl Interpreter {
/// Creates a new interpreter. /// Creates a new interpreter.
pub fn new(realm: Realm) -> Self { pub fn new() -> Self {
let mut interpreter = Self { Self {
state: InterpreterState::Executing, 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 { pub(crate) fn get_current_state(&self) -> &InterpreterState {
&self.state &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 { 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"); let _timer = BoaProfiler::global().start_event("Executable", "exec");
match *self { match *self {
Node::Const(Const::Null) => Ok(Value::null()), 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}; use crate::{syntax::ast::node::New, BoaProfiler, Result, Value};
impl Executable for New { 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 _timer = BoaProfiler::global().start_event("New", "exec");
let func_object = self.expr().run(interpreter)?; let func_object = self.expr().run(interpreter)?;

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

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

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

@ -2,7 +2,7 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use super::{Executable, Interpreter}; use super::{Context, Executable};
use crate::{ use crate::{
environment::lexical_environment::VariableScope, environment::lexical_environment::VariableScope,
syntax::ast::{ syntax::ast::{
@ -13,7 +13,7 @@ use crate::{
}; };
impl Executable for Assign { 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 _timer = BoaProfiler::global().start_event("Assign", "exec");
let val = self.rhs().run(interpreter)?; let val = self.rhs().run(interpreter)?;
match self.lhs() { match self.lhs() {
@ -49,7 +49,7 @@ impl Executable for Assign {
} }
impl Executable for BinOp { impl Executable for BinOp {
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { fn run(&self, interpreter: &mut Context) -> Result<Value> {
match self.op() { match self.op() {
op::BinOp::Num(op) => { op::BinOp::Num(op) => {
let x = self.lhs().run(interpreter)?; let x = self.lhs().run(interpreter)?;
@ -133,7 +133,7 @@ impl Executable for BinOp {
.ok_or_else(|| interpreter.construct_reference_error(name.as_ref()))?; .ok_or_else(|| interpreter.construct_reference_error(name.as_ref()))?;
let v_b = self.rhs().run(interpreter)?; let v_b = self.rhs().run(interpreter)?;
let value = Self::run_assign(op, v_a, v_b, 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(), name.as_ref(),
value.clone(), value.clone(),
true, true,
@ -160,12 +160,7 @@ impl Executable for BinOp {
impl BinOp { impl BinOp {
/// Runs the assignment operators. /// Runs the assignment operators.
fn run_assign( fn run_assign(op: AssignOp, x: Value, y: Value, interpreter: &mut Context) -> Result<Value> {
op: AssignOp,
x: Value,
y: Value,
interpreter: &mut Interpreter,
) -> Result<Value> {
match op { match op {
AssignOp::Add => x.add(&y, interpreter), AssignOp::Add => x.add(&y, interpreter),
AssignOp::Sub => x.sub(&y, interpreter), AssignOp::Sub => x.sub(&y, interpreter),
@ -183,7 +178,7 @@ impl BinOp {
} }
impl Executable for UnaryOp { 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)?; let x = self.target().run(interpreter)?;
Ok(match self.op() { 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}; use crate::{syntax::ast::node::Return, Result, Value};
impl Executable for Return { 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() { let result = match self.expr() {
Some(ref v) => v.run(interpreter), Some(ref v) => v.run(interpreter),
None => Ok(Value::undefined()), None => Ok(Value::undefined()),
}; };
// Set flag for return // Set flag for return
interpreter.set_current_state(InterpreterState::Return); interpreter
.executor()
.set_current_state(InterpreterState::Return);
result 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}; use crate::{syntax::ast::node::Spread, Result, Value};
impl Executable for Spread { 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 // TODO: for now we can do nothing but return the value as-is
self.val().run(interpreter) self.val().run(interpreter)
} }

10
boa/src/exec/statement_list.rs

@ -1,19 +1,21 @@
//! Statement list execution. //! Statement list execution.
use super::{Executable, Interpreter, InterpreterState}; use super::{Context, Executable, InterpreterState};
use crate::{syntax::ast::node::StatementList, BoaProfiler, Result, Value}; use crate::{syntax::ast::node::StatementList, BoaProfiler, Result, Value};
impl Executable for StatementList { 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"); let _timer = BoaProfiler::global().start_event("StatementList", "exec");
// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
// The return value is uninitialized, which means it defaults to Value::Undefined // The return value is uninitialized, which means it defaults to Value::Undefined
let mut obj = Value::default(); 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() { for (i, item) in self.statements().iter().enumerate() {
let val = item.run(interpreter)?; let val = item.run(interpreter)?;
match interpreter.get_current_state() { match interpreter.executor().get_current_state() {
InterpreterState::Return => { InterpreterState::Return => {
// Early return. // Early return.
obj = val; 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}; use crate::{syntax::ast::node::Switch, Result, Value};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
impl Executable for Switch { 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 val = self.val().run(interpreter)?;
let mut result = Value::null(); let mut result = Value::null();
let mut matched = false; 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 // 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. // 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)?) { if fall_through || val.strict_equals(&cond.run(interpreter)?) {
matched = true; matched = true;
let result = block.run(interpreter)?; let result = block.run(interpreter)?;
match interpreter.get_current_state() { match interpreter.executor().get_current_state() {
InterpreterState::Return => { InterpreterState::Return => {
// Early return. // Early return.
return Ok(result); return Ok(result);
@ -29,7 +31,9 @@ impl Executable for Switch {
InterpreterState::Break(_label) => { InterpreterState::Break(_label) => {
// TODO, break to a label. // TODO, break to a label.
// Break statement encountered so therefore end switch statement. // Break statement encountered so therefore end switch statement.
interpreter.set_current_state(InterpreterState::Executing); interpreter
.executor()
.set_current_state(InterpreterState::Executing);
break; break;
} }
InterpreterState::Continue(_label) => { InterpreterState::Continue(_label) => {
@ -46,10 +50,12 @@ impl Executable for Switch {
if !matched { if !matched {
if let Some(default) = self.default() { 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() { for (i, item) in default.iter().enumerate() {
let val = item.run(interpreter)?; let val = item.run(interpreter)?;
match interpreter.get_current_state() { match interpreter.executor().get_current_state() {
InterpreterState::Return => { InterpreterState::Return => {
// Early return. // Early return.
result = val; 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] #[test]
fn function_declaration_returns_undefined() { fn function_declaration_returns_undefined() {
@ -109,8 +109,7 @@ fn object_field_set() {
#[test] #[test]
fn spread_with_arguments() { fn spread_with_arguments() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
const a = [1, "test", 3, 4]; const a = [1, "test", 3, 4];
@ -136,8 +135,7 @@ fn spread_with_arguments() {
#[test] #[test]
fn array_rest_with_arguments() { fn array_rest_with_arguments() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
var b = [4, 5, 6] var b = [4, 5, 6]
@ -715,8 +713,7 @@ mod in_operator {
#[test] #[test]
fn should_type_error_when_rhs_not_object() { fn should_type_error_when_rhs_not_object() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
var x = false; var x = false;
@ -733,8 +730,7 @@ mod in_operator {
#[test] #[test]
fn should_set_this_value() { fn should_set_this_value() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
function Foo() { function Foo() {
@ -752,8 +748,7 @@ mod in_operator {
#[test] #[test]
fn new_instance_should_point_to_prototype() { fn new_instance_should_point_to_prototype() {
// A new instance should point to a prototype object created with the constructor function // A new instance should point to a prototype object created with the constructor function
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
function Foo() {} function Foo() {}
@ -880,8 +875,7 @@ fn function_decl_hoisting() {
#[test] #[test]
fn to_bigint() { fn to_bigint() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert!(Value::null().to_bigint(&mut engine).is_err()); assert!(Value::null().to_bigint(&mut engine).is_err());
assert!(Value::undefined().to_bigint(&mut engine).is_err()); assert!(Value::undefined().to_bigint(&mut engine).is_err());
@ -892,8 +886,7 @@ fn to_bigint() {
#[test] #[test]
fn to_index() { fn to_index() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(Value::undefined().to_index(&mut engine).unwrap(), 0); assert_eq!(Value::undefined().to_index(&mut engine).unwrap(), 0);
assert!(Value::integer(-1).to_index(&mut engine).is_err()); assert!(Value::integer(-1).to_index(&mut engine).is_err());
@ -901,8 +894,7 @@ fn to_index() {
#[test] #[test]
fn to_integer() { fn to_integer() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert!(Number::equal( assert!(Number::equal(
Value::number(f64::NAN).to_integer(&mut engine).unwrap(), Value::number(f64::NAN).to_integer(&mut engine).unwrap(),
@ -939,8 +931,7 @@ fn to_integer() {
#[test] #[test]
fn to_length() { fn to_length() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(Value::number(f64::NAN).to_length(&mut engine).unwrap(), 0); assert_eq!(Value::number(f64::NAN).to_length(&mut engine).unwrap(), 0);
assert_eq!( assert_eq!(
@ -971,8 +962,7 @@ fn to_length() {
#[test] #[test]
fn to_int32() { fn to_int32() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
macro_rules! check_to_int32 { macro_rules! check_to_int32 {
($from:expr => $to:expr) => { ($from:expr => $to:expr) => {
@ -1085,8 +1075,7 @@ fn to_int32() {
#[test] #[test]
fn to_string() { fn to_string() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert_eq!(Value::null().to_string(&mut engine).unwrap(), "null"); assert_eq!(Value::null().to_string(&mut engine).unwrap(), "null");
assert_eq!( assert_eq!(
@ -1103,8 +1092,7 @@ fn to_string() {
#[test] #[test]
fn calling_function_with_unspecified_arguments() { fn calling_function_with_unspecified_arguments() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let scenario = r#" let scenario = r#"
function test(a, b) { function test(a, b) {
return b; return b;
@ -1118,8 +1106,7 @@ fn calling_function_with_unspecified_arguments() {
#[test] #[test]
fn to_object() { fn to_object() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert!(Value::undefined() assert!(Value::undefined()
.to_object(&mut engine) .to_object(&mut engine)
@ -1133,8 +1120,7 @@ fn to_object() {
#[test] #[test]
fn check_this_binding_in_object_literal() { fn check_this_binding_in_object_literal() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
var foo = { var foo = {
a: 3, a: 3,
@ -1149,8 +1135,7 @@ fn check_this_binding_in_object_literal() {
#[test] #[test]
fn array_creation_benchmark() { fn array_creation_benchmark() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
(function(){ (function(){
let testArr = []; let testArr = [];
@ -1167,8 +1152,7 @@ fn array_creation_benchmark() {
#[test] #[test]
fn array_pop_benchmark() { fn array_pop_benchmark() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
(function(){ (function(){
let testArray = [83, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, let testArray = [83, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32,
@ -1201,8 +1185,7 @@ fn array_pop_benchmark() {
#[test] #[test]
fn number_object_access_benchmark() { fn number_object_access_benchmark() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
new Number( new Number(
new Number( new Number(
@ -1218,8 +1201,7 @@ fn number_object_access_benchmark() {
#[test] #[test]
fn not_a_function() { fn not_a_function() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let init = r#" let init = r#"
let a = {}; let a = {};
let b = true; let b = true;
@ -1283,8 +1265,7 @@ fn comma_operator() {
fn assignment_to_non_assignable() { fn assignment_to_non_assignable() {
// Relates to the behaviour described at // Relates to the behaviour described at
// https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors // https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Tests all assignment operators as per [spec] and [mdn] // Tests all assignment operators as per [spec] and [mdn]
// //
@ -1306,8 +1287,7 @@ fn assignment_to_non_assignable() {
fn multicharacter_assignment_to_non_assignable() { fn multicharacter_assignment_to_non_assignable() {
// Relates to the behaviour described at // Relates to the behaviour described at
// https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors // https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let test_cases = ["3 **= 5", "3 <<= 5", "3 >>= 5"]; let test_cases = ["3 **= 5", "3 <<= 5", "3 >>= 5"];
@ -1322,8 +1302,7 @@ fn multicharacter_assignment_to_non_assignable() {
#[test] #[test]
#[ignore] #[ignore]
fn multicharacter_bitwise_assignment_to_non_assignable() { fn multicharacter_bitwise_assignment_to_non_assignable() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
// Disabled - awaiting implementation. // Disabled - awaiting implementation.
let test_cases = ["3 >>>= 5", "3 &&= 5", "3 ||= 5", "3 ??= 5"]; let test_cases = ["3 >>>= 5", "3 &&= 5", "3 ||= 5", "3 ??= 5"];
@ -1338,8 +1317,7 @@ fn multicharacter_bitwise_assignment_to_non_assignable() {
#[test] #[test]
fn assign_to_array_decl() { fn assign_to_array_decl() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
assert!(forward(&mut engine, "[1] = [2]").starts_with("Uncaught \"SyntaxError\": ")); assert!(forward(&mut engine, "[1] = [2]").starts_with("Uncaught \"SyntaxError\": "));
assert!(forward(&mut engine, "[3, 5] = [7, 8]").starts_with("Uncaught \"SyntaxError\": ")); assert!(forward(&mut engine, "[3, 5] = [7, 8]").starts_with("Uncaught \"SyntaxError\": "));
@ -1349,8 +1327,7 @@ fn assign_to_array_decl() {
#[test] #[test]
fn assign_to_object_decl() { fn assign_to_object_decl() {
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
const ERR_MSG: &str = const ERR_MSG: &str =
"Uncaught \"SyntaxError\": \"expected token \';\', got \':\' in expression statement at line 1, col 3\""; "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}; use crate::{syntax::ast::node::Throw, Result, Value};
impl Executable for Throw { impl Executable for Throw {
#[inline] #[inline]
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { fn run(&self, interpreter: &mut Context) -> Result<Value> {
Err(self.expr().run(interpreter)?) Err(self.expr().run(interpreter)?)
} }
} }

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

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

49
boa/src/lib.rs

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

3
boa/src/syntax/mod.rs

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

4
boa/src/value/equality.rs

@ -1,5 +1,5 @@
use super::*; use super::*;
use crate::{builtins::Number, Interpreter}; use crate::{builtins::Number, Context};
impl Value { impl Value {
/// Strict equality comparison. /// Strict equality comparison.
@ -37,7 +37,7 @@ impl Value {
/// This method is executed when doing abstract equality comparisons with the `==` operator. /// This method is executed when doing abstract equality comparisons with the `==` operator.
/// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison> /// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
#[allow(clippy::float_cmp)] #[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 // 1. If Type(x) is the same as Type(y), then
// a. Return the result of performing Strict Equality Comparison x === y. // a. Return the result of performing Strict Equality Comparison x === y.
if self.get_type() == other.get_type() { if self.get_type() == other.get_type() {

56
boa/src/value/mod.rs

@ -5,7 +5,6 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use crate::exec::Interpreter;
use crate::{ use crate::{
builtins::{ builtins::{
number::{f64_to_int32, f64_to_uint32}, number::{f64_to_int32, f64_to_uint32},
@ -13,8 +12,8 @@ use crate::{
BigInt, Number, BigInt, Number,
}, },
property::{Attribute, Property, PropertyKey}, property::{Attribute, Property, PropertyKey},
BoaProfiler, Context, Result,
}; };
use crate::{BoaProfiler, Result};
use gc::{Finalize, GcCellRef, GcCellRefMut, Trace}; use gc::{Finalize, GcCellRef, GcCellRefMut, Trace};
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue};
use std::{ use std::{
@ -174,7 +173,7 @@ impl Value {
} }
/// Convert from a JSON value to a JS 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 { match json {
JSONValue::Number(v) => { JSONValue::Number(v) => {
if let Some(Ok(integer_32)) = v.as_i64().map(i32::try_from) { 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::Bool(v) => Self::boolean(v),
JSONValue::Array(vs) => { JSONValue::Array(vs) => {
let global_array_prototype = interpreter let global_array_prototype = interpreter
.realm .global_object()
.global_obj
.get_field("Array") .get_field("Array")
.get_field(PROTOTYPE); .get_field(PROTOTYPE);
let new_obj = let new_obj =
@ -210,7 +208,7 @@ impl Value {
new_obj new_obj
} }
JSONValue::Object(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() { for (key, json) in obj.into_iter() {
let value = Self::from_json(json, interpreter); let value = Self::from_json(json, interpreter);
new_obj.set_property( new_obj.set_property(
@ -228,7 +226,7 @@ impl Value {
} }
/// Converts the `Value` to `JSON`. /// 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"); let to_json = self.get_field("toJSON");
if to_json.is_function() { if to_json.is_function() {
let json_value = interpreter.call(&to_json, self, &[])?; 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. /// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
/// ///
/// <https://tc39.es/ecma262/#sec-toprimitive> /// <https://tc39.es/ecma262/#sec-toprimitive>
pub fn to_primitive( pub fn to_primitive(&self, ctx: &mut Context, preferred_type: PreferredType) -> Result<Value> {
&self,
ctx: &mut Interpreter,
preferred_type: PreferredType,
) -> Result<Value> {
// 1. Assert: input is an ECMAScript language value. (always a value not need to check) // 1. Assert: input is an ECMAScript language value. (always a value not need to check)
// 2. If Type(input) is Object, then // 2. If Type(input) is Object, then
if let Value::Object(_) = self { if let Value::Object(_) = self {
@ -610,7 +604,7 @@ impl Value {
/// Converts the value to a `BigInt`. /// Converts the value to a `BigInt`.
/// ///
/// This function is equivelent to `BigInt(value)` in JavaScript. /// 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 { match self {
Value::Null => Err(ctx.construct_type_error("cannot convert null to a BigInt")), Value::Null => Err(ctx.construct_type_error("cannot convert null to a BigInt")),
Value::Undefined => { Value::Undefined => {
@ -657,7 +651,7 @@ impl Value {
/// Converts the value to a string. /// Converts the value to a string.
/// ///
/// This function is equivalent to `String(value)` in JavaScript. /// 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 { match self {
Value::Null => Ok("null".into()), Value::Null => Ok("null".into()),
Value::Undefined => Ok("undefined".into()), Value::Undefined => Ok("undefined".into()),
@ -679,14 +673,14 @@ impl Value {
/// This function is equivalent to `Object(value)` in JavaScript /// This function is equivalent to `Object(value)` in JavaScript
/// ///
/// See: <https://tc39.es/ecma262/#sec-toobject> /// 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 { match self {
Value::Undefined | Value::Null => { Value::Undefined | Value::Null => {
ctx.throw_type_error("cannot convert 'null' or 'undefined' to object") ctx.throw_type_error("cannot convert 'null' or 'undefined' to object")
} }
Value::Boolean(boolean) => { Value::Boolean(boolean) => {
let proto = ctx let proto = ctx
.realm .realm()
.environment .environment
.get_binding_value("Boolean") .get_binding_value("Boolean")
.expect("Boolean was not initialized") .expect("Boolean was not initialized")
@ -699,7 +693,7 @@ impl Value {
} }
Value::Integer(integer) => { Value::Integer(integer) => {
let proto = ctx let proto = ctx
.realm .realm()
.environment .environment
.get_binding_value("Number") .get_binding_value("Number")
.expect("Number was not initialized") .expect("Number was not initialized")
@ -711,7 +705,7 @@ impl Value {
} }
Value::Rational(rational) => { Value::Rational(rational) => {
let proto = ctx let proto = ctx
.realm .realm()
.environment .environment
.get_binding_value("Number") .get_binding_value("Number")
.expect("Number was not initialized") .expect("Number was not initialized")
@ -724,7 +718,7 @@ impl Value {
} }
Value::String(ref string) => { Value::String(ref string) => {
let proto = ctx let proto = ctx
.realm .realm()
.environment .environment
.get_binding_value("String") .get_binding_value("String")
.expect("String was not initialized") .expect("String was not initialized")
@ -737,7 +731,7 @@ impl Value {
} }
Value::Symbol(ref symbol) => { Value::Symbol(ref symbol) => {
let proto = ctx let proto = ctx
.realm .realm()
.environment .environment
.get_binding_value("Symbol") .get_binding_value("Symbol")
.expect("Symbol was not initialized") .expect("Symbol was not initialized")
@ -750,7 +744,7 @@ impl Value {
} }
Value::BigInt(ref bigint) => { Value::BigInt(ref bigint) => {
let proto = ctx let proto = ctx
.realm .realm()
.environment .environment
.get_binding_value("BigInt") .get_binding_value("BigInt")
.expect("BigInt was not initialized") .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. /// Converts the value to a `PropertyKey`, that can be used as a key for properties.
/// ///
/// See <https://tc39.es/ecma262/#sec-topropertykey> /// 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 { Ok(match self {
// Fast path: // Fast path:
Value::String(string) => string.clone().into(), 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`. /// It returns value converted to a numeric value of type `Number` or `BigInt`.
/// ///
/// See: <https://tc39.es/ecma262/#sec-tonumeric> /// 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)?; let primitive = self.to_primitive(ctx, PreferredType::Number)?;
if let Some(bigint) = primitive.as_bigint() { if let Some(bigint) = primitive.as_bigint() {
return Ok(bigint.clone().into()); return Ok(bigint.clone().into());
@ -796,7 +790,7 @@ impl Value {
/// This function is equivalent to `value | 0` in JavaScript /// This function is equivalent to `value | 0` in JavaScript
/// ///
/// See: <https://tc39.es/ecma262/#sec-toint32> /// 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. // This is the fast path, if the value is Integer we can just return it.
if let Value::Integer(number) = *self { if let Value::Integer(number) = *self {
return Ok(number as u32); return Ok(number as u32);
@ -809,7 +803,7 @@ impl Value {
/// Converts a value to an integral 32 bit signed integer. /// Converts a value to an integral 32 bit signed integer.
/// ///
/// See: <https://tc39.es/ecma262/#sec-toint32> /// 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. // This is the fast path, if the value is Integer we can just return it.
if let Value::Integer(number) = *self { if let Value::Integer(number) = *self {
return Ok(number); 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. /// Converts a value to a non-negative integer if it is a valid integer index value.
/// ///
/// See: <https://tc39.es/ecma262/#sec-toindex> /// 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() { if self.is_undefined() {
return Ok(0); 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. /// Converts argument to an integer suitable for use as the length of an array-like object.
/// ///
/// See: <https://tc39.es/ecma262/#sec-tolength> /// 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). // 1. Let len be ? ToInteger(argument).
let len = self.to_integer(ctx)?; let len = self.to_integer(ctx)?;
@ -859,7 +853,7 @@ impl Value {
/// Converts a value to an integral Number value. /// Converts a value to an integral Number value.
/// ///
/// See: <https://tc39.es/ecma262/#sec-tointeger> /// 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). // 1. Let number be ? ToNumber(argument).
let number = self.to_number(ctx)?; let number = self.to_number(ctx)?;
@ -883,7 +877,7 @@ impl Value {
/// This function is equivalent to the unary `+` operator (`+value`) in JavaScript /// This function is equivalent to the unary `+` operator (`+value`) in JavaScript
/// ///
/// See: https://tc39.es/ecma262/#sec-tonumber /// 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 { match *self {
Value::Null => Ok(0.0), Value::Null => Ok(0.0),
Value::Undefined => Ok(f64::NAN), Value::Undefined => Ok(f64::NAN),
@ -911,7 +905,7 @@ impl Value {
/// This function is equivalent to `Number(value)` in JavaScript /// This function is equivalent to `Number(value)` in JavaScript
/// ///
/// See: <https://tc39.es/ecma262/#sec-tonumeric> /// 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)?; let primitive = self.to_primitive(ctx, PreferredType::Number)?;
if let Some(ref bigint) = primitive.as_bigint() { if let Some(ref bigint) = primitive.as_bigint() {
return Ok(bigint.to_f64()); return Ok(bigint.to_f64());
@ -931,7 +925,7 @@ impl Value {
/// [table]: https://tc39.es/ecma262/#table-14 /// [table]: https://tc39.es/ecma262/#table-14
/// [spec]: https://tc39.es/ecma262/#sec-requireobjectcoercible /// [spec]: https://tc39.es/ecma262/#sec-requireobjectcoercible
#[inline] #[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() { if self.is_null_or_undefined() {
Err(ctx.construct_type_error("cannot convert null or undefined to Object")) Err(ctx.construct_type_error("cannot convert null or undefined to Object"))
} else { } else {

38
boa/src/value/operations.rs

@ -3,7 +3,7 @@ use crate::builtins::number::{f64_to_int32, f64_to_uint32, Number};
impl Value { impl Value {
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) + f64::from(*y)), (Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) + f64::from(*y)),
@ -40,7 +40,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) - f64::from(*y)), (Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) - f64::from(*y)),
@ -68,7 +68,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) * f64::from(*y)), (Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) * f64::from(*y)),
@ -96,7 +96,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) / f64::from(*y)), (Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) / f64::from(*y)),
@ -124,7 +124,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x % *y), (Self::Integer(x), Self::Integer(y)) => Self::integer(x % *y),
@ -152,7 +152,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x).powi(*y)), (Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x).powi(*y)),
@ -178,7 +178,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x & y), (Self::Integer(x), Self::Integer(y)) => Self::integer(x & y),
@ -210,7 +210,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x | y), (Self::Integer(x), Self::Integer(y)) => Self::integer(x | y),
@ -242,7 +242,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x ^ y), (Self::Integer(x), Self::Integer(y)) => Self::integer(x ^ y),
@ -274,7 +274,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shl(*y as u32)), (Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shl(*y as u32)),
@ -310,7 +310,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shr(*y as u32)), (Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shr(*y as u32)),
@ -346,7 +346,7 @@ impl Value {
} }
#[inline] #[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) { Ok(match (self, other) {
// Fast path: // Fast path:
(Self::Integer(x), Self::Integer(y)) => { (Self::Integer(x), Self::Integer(y)) => {
@ -381,7 +381,7 @@ impl Value {
} }
#[inline] #[inline]
pub fn neg(&self, interpreter: &mut Interpreter) -> Result<Value> { pub fn neg(&self, interpreter: &mut Context) -> Result<Value> {
Ok(match *self { Ok(match *self {
Self::Symbol(_) | Self::Undefined => Self::rational(NAN), Self::Symbol(_) | Self::Undefined => Self::rational(NAN),
Self::Object(_) => Self::rational(match self.to_numeric_number(interpreter) { Self::Object(_) => Self::rational(match self.to_numeric_number(interpreter) {
@ -401,7 +401,7 @@ impl Value {
} }
#[inline] #[inline]
pub fn not(&self, _: &mut Interpreter) -> Result<bool> { pub fn not(&self, _: &mut Context) -> Result<bool> {
Ok(!self.to_boolean()) Ok(!self.to_boolean())
} }
@ -426,7 +426,7 @@ impl Value {
&self, &self,
other: &Self, other: &Self,
left_first: bool, left_first: bool,
ctx: &mut Interpreter, ctx: &mut Context,
) -> Result<AbstractRelation> { ) -> Result<AbstractRelation> {
Ok(match (self, other) { Ok(match (self, other) {
// Fast path (for some common operations): // 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 /// [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 /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline] #[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)? { match self.abstract_relation(other, true, ctx)? {
AbstractRelation::True => Ok(true), AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false), 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 /// [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 /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline] #[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)? { match other.abstract_relation(self, false, ctx)? {
AbstractRelation::False => Ok(true), AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false), 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 /// [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 /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline] #[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)? { match other.abstract_relation(self, false, ctx)? {
AbstractRelation::True => Ok(true), AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false), 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 /// [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 /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline] #[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)? { match self.abstract_relation(other, true, ctx)? {
AbstractRelation::False => Ok(true), AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false), AbstractRelation::True | AbstractRelation::Undefined => Ok(false),

260
boa/src/value/tests.rs

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

10
boa_cli/src/main.rs

@ -25,7 +25,7 @@
clippy::as_conversions 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 colored::*;
use rustyline::{config::Config, error::ReadlineError, EditMode, Editor}; use rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
use std::{fs::read_to_string, path::PathBuf}; 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> { pub fn main() -> Result<(), std::io::Error> {
let args = Opt::from_args(); let args = Opt::from_args();
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
for file in &args.files { for file in &args.files {
let buffer = read_to_string(file)?; let buffer = read_to_string(file)?;
@ -151,7 +149,7 @@ pub fn main() -> Result<(), std::io::Error> {
eprintln!("{}", e); eprintln!("{}", e);
} }
} else { } else {
match forward_val(&mut engine, &buffer) { match engine.eval(&buffer) {
Ok(v) => println!("{}", v.display()), Ok(v) => println!("{}", v.display()),
Err(v) => eprintln!("Uncaught {}", v.display()), Err(v) => eprintln!("Uncaught {}", v.display()),
} }
@ -187,7 +185,7 @@ pub fn main() -> Result<(), std::io::Error> {
eprintln!("{}", e); eprintln!("{}", e);
} }
} else { } else {
match forward_val(&mut engine, line.trim_end()) { match engine.eval(line.trim_end()) {
Ok(v) => println!("{}", v.display()), Ok(v) => println!("{}", v.display()),
Err(v) => { Err(v) => {
eprintln!("{}: {}", "Uncaught".red(), v.display().to_string().red()) 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::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]
pub fn evaluate(src: &str) -> Result<String, JsValue> { pub fn evaluate(src: &str) -> Result<String, JsValue> {
// Setup executor // Setup executor
let realm = Realm::create(); let mut engine = Context::new();
let mut engine = Interpreter::new(realm);
let expr = match parse(src) { let expr = match parse(src) {
Ok(res) => res, Ok(res) => res,

26
tester/src/exec.rs

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

Loading…
Cancel
Save