From dfb3df5bf2c920262a0250d4b924201e78373541 Mon Sep 17 00:00:00 2001 From: Jason Williams <936006+jasonwilliams@users.noreply.github.com> Date: Sat, 25 Dec 2021 17:56:36 +0000 Subject: [PATCH] Start removing non-VM path (#1747) --- .github/workflows/bors.yml | 22 - .github/workflows/rust.yml | 22 - .github/workflows/test262.yml | 20 - .vscode/tasks.json | 2 +- boa/Cargo.toml | 11 - boa/benches/README.md | 14 +- boa/benches/bench_scripts/expression.js | 1 - .../bench_scripts/goal_symbol_switch.js | 7 - boa/benches/bench_scripts/hello_world.js | 2 - boa/benches/bench_scripts/long_repetition.js | 9 - boa/benches/exec.rs | 359 --------------- boa/benches/full.rs | 290 ++++-------- boa/benches/parser.rs | 99 ---- boa/src/builtins/console/mod.rs | 7 - boa/src/builtins/function/mod.rs | 121 ----- boa/src/builtins/iterable/mod.rs | 2 - boa/src/builtins/json/mod.rs | 2 - boa/src/context.rs | 241 ++-------- boa/src/environment/lexical_environment.rs | 1 - boa/src/exec/mod.rs | 51 --- boa/src/lib.rs | 25 +- boa/src/object/internal_methods/function.rs | 278 +---------- boa/src/syntax/ast/node/array/mod.rs | 38 +- boa/src/syntax/ast/node/await_expr/mod.rs | 9 - boa/src/syntax/ast/node/block/mod.rs | 55 +-- boa/src/syntax/ast/node/break_node/mod.rs | 17 +- boa/src/syntax/ast/node/break_node/tests.rs | 20 - boa/src/syntax/ast/node/call/mod.rs | 65 --- .../node/conditional/conditional_op/mod.rs | 12 - .../ast/node/conditional/if_node/mod.rs | 14 - .../declaration/arrow_function_decl/mod.rs | 15 - .../declaration/async_function_decl/mod.rs | 14 +- .../declaration/async_function_expr/mod.rs | 13 +- .../declaration/async_generator_decl/mod.rs | 14 +- .../declaration/async_generator_expr/mod.rs | 9 - .../ast/node/declaration/function_decl/mod.rs | 26 -- .../ast/node/declaration/function_expr/mod.rs | 17 - .../node/declaration/generator_decl/mod.rs | 11 - .../node/declaration/generator_expr/mod.rs | 10 - boa/src/syntax/ast/node/declaration/mod.rs | 430 ------------------ .../ast/node/field/get_const_field/mod.rs | 13 - .../syntax/ast/node/field/get_field/mod.rs | 14 - boa/src/syntax/ast/node/identifier/mod.rs | 9 - .../ast/node/iteration/continue_node/mod.rs | 12 - .../ast/node/iteration/do_while_loop/mod.rs | 30 -- .../ast/node/iteration/for_in_loop/mod.rs | 156 +------ .../syntax/ast/node/iteration/for_loop/mod.rs | 53 --- .../ast/node/iteration/for_of_loop/mod.rs | 143 +----- boa/src/syntax/ast/node/iteration/mod.rs | 21 - .../ast/node/iteration/while_loop/mod.rs | 27 -- boa/src/syntax/ast/node/mod.rs | 91 +--- boa/src/syntax/ast/node/new/mod.rs | 40 -- boa/src/syntax/ast/node/object/mod.rs | 143 +----- .../syntax/ast/node/operator/assign/mod.rs | 46 -- .../syntax/ast/node/operator/bin_op/mod.rs | 149 +----- .../syntax/ast/node/operator/unary_op/mod.rs | 82 ---- boa/src/syntax/ast/node/return_smt/mod.rs | 16 - boa/src/syntax/ast/node/spread/mod.rs | 9 - boa/src/syntax/ast/node/statement_list/mod.rs | 58 --- boa/src/syntax/ast/node/switch/mod.rs | 80 ---- boa/src/syntax/ast/node/template/mod.rs | 78 ---- boa/src/syntax/ast/node/throw/mod.rs | 9 - boa/src/syntax/ast/node/try_node/mod.rs | 63 --- boa/src/syntax/ast/node/yield/mod.rs | 9 - boa/src/{exec => }/tests.rs | 23 +- boa/src/vm/code_block.rs | 24 +- boa/src/vm/mod.rs | 21 +- boa_cli/Cargo.toml | 3 - boa_cli/src/main.rs | 2 - boa_tester/Cargo.toml | 3 - boa_tester/src/exec/js262.rs | 6 - boa_tester/src/exec/mod.rs | 8 +- boa_tester/src/results.rs | 3 - boa_wasm/src/lib.rs | 17 +- 74 files changed, 183 insertions(+), 3653 deletions(-) delete mode 100644 boa/benches/bench_scripts/expression.js delete mode 100644 boa/benches/bench_scripts/goal_symbol_switch.js delete mode 100644 boa/benches/bench_scripts/hello_world.js delete mode 100644 boa/benches/bench_scripts/long_repetition.js delete mode 100644 boa/benches/exec.rs delete mode 100644 boa/benches/parser.rs delete mode 100644 boa/src/exec/mod.rs rename boa/src/{exec => }/tests.rs (98%) diff --git a/.github/workflows/bors.yml b/.github/workflows/bors.yml index 9d43d0d312..d9970ad54d 100644 --- a/.github/workflows/bors.yml +++ b/.github/workflows/bors.yml @@ -29,28 +29,6 @@ jobs: with: command: test args: -v - test_vm_on_linux: - name: Tests on Linux with vm enabled - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2.4.0 - - uses: actions-rs/toolchain@v1.0.7 - with: - toolchain: stable - override: true - profile: minimal - - name: Cache cargo - uses: actions/cache@v2.1.7 - with: - path: | - target - ~/.cargo/git - ~/.cargo/registry - key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} - - uses: actions-rs/cargo@v1 - with: - command: test - args: ---package Boa --lib --features=vm -- vm --nocapture test_on_windows: name: Tests on Windows diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 779bd385f1..46089d3945 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -33,28 +33,6 @@ jobs: args: --ignore-tests - name: Upload to codecov.io uses: codecov/codecov-action@v2.1.0 - test_vm_on_linux: - name: Tests on Linux with vm enabled - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2.4.0 - - uses: actions-rs/toolchain@v1.0.7 - with: - toolchain: stable - override: true - profile: minimal - - name: Cache cargo - uses: actions/cache@v2.1.7 - with: - path: | - target - ~/.cargo/git - ~/.cargo/registry - key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} - - uses: actions-rs/cargo@v1 - with: - command: test - args: ---package Boa --lib --features=vm -- vm --nocapture test_on_windows: name: Tests on Windows diff --git a/.github/workflows/test262.yml b/.github/workflows/test262.yml index 3157a35260..63044d0f26 100644 --- a/.github/workflows/test262.yml +++ b/.github/workflows/test262.yml @@ -61,26 +61,6 @@ jobs: comment="${comment//$'\r'/'%0D'}" echo "::set-output name=comment::$comment" - - name: Run the test262 test suite for VM - run: | - cd boa - mkdir -p ../results - cargo run --release --features vm --bin boa_tester -- run -v -o ../results/test262/vm - cd .. - - # Run the results comparison for VM - - name: Compare results for VM - if: github.event_name == 'pull_request' - id: compare-vm - shell: bash - run: | - cd boa - comment="$(./target/release/boa_tester compare ../gh-pages/test262/vm/refs/heads/main/latest.json ../results/test262/vm/pull/latest.json -m)" - comment="${comment//'%'/'%25'}" - comment="${comment//$'\n'/'%0A'}" - comment="${comment//$'\r'/'%0D'}" - echo "::set-output name=comment::$comment" - - name: Get the PR number if: github.event_name == 'pull_request' id: pr-number diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 075f5c8a7e..71c4d1ac6f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -26,7 +26,7 @@ "type": "process", "label": "Cargo Run (VM)", "command": "cargo", - "args": ["run", "--features", "vm", "--", "-t", "../tests/js/test.js"], + "args": ["run", "--", "-t", "../tests/js/test.js"], "group": { "kind": "build", "isDefault": true diff --git a/boa/Cargo.toml b/boa/Cargo.toml index c4ad9caf05..50de7a83d6 100644 --- a/boa/Cargo.toml +++ b/boa/Cargo.toml @@ -15,9 +15,6 @@ rust-version = "1.57" profiler = ["measureme"] deser = [] -# Enable Bytecode generation & execution instead of tree walking -vm = [] - # Enable Boa's WHATWG console object implementation. console = [] @@ -59,14 +56,6 @@ crate-type = ["cdylib", "lib"] name = "boa" bench = false -[[bench]] -name = "parser" -harness = false - -[[bench]] -name = "exec" -harness = false - [[bench]] name = "full" harness = false diff --git a/boa/benches/README.md b/boa/benches/README.md index 47f8ae90f8..a84190ff02 100644 --- a/boa/benches/README.md +++ b/boa/benches/README.md @@ -1,10 +1,10 @@ -# Boa Benchmarks. +# Boa Benchmarks -We divide the benchmarks in 3 sections: +For each js script in the `bench_scripts` folder, we create three benchmarks: -- Full engine benchmarks (lexing + parsing + realm creation + execution) -- Execution benchmarks -- Parsing benchmarks (lexing + parse - these are tightly coupled so must be benchmarked together) +- Parser => lexing and parsing of the source code +- Compiler => compilation of the parsed statement list into bytecode +- Execution => execution of the bytecode in the vm -The idea is to check the performance of Boa in different scenarios and dividing the Boa execution -process in its different parts. +The idea is to check the performance of Boa in different scenarios. +Different parts of Boa are benchmarked separately to make the impact of local changes visible. diff --git a/boa/benches/bench_scripts/expression.js b/boa/benches/bench_scripts/expression.js deleted file mode 100644 index 2c78c4eab7..0000000000 --- a/boa/benches/bench_scripts/expression.js +++ /dev/null @@ -1 +0,0 @@ -1 + 1 + 1 + 1 + 1 + 1 / 1 + 1 + 1 * 1 + 1 + 1 + 1; diff --git a/boa/benches/bench_scripts/goal_symbol_switch.js b/boa/benches/bench_scripts/goal_symbol_switch.js deleted file mode 100644 index b14461c036..0000000000 --- a/boa/benches/bench_scripts/goal_symbol_switch.js +++ /dev/null @@ -1,7 +0,0 @@ -function foo(regex, num) {} - -let i = 0; -while (i < 1000000) { - foo(/ab+c/, 5.0 / 5); - i++; -} diff --git a/boa/benches/bench_scripts/hello_world.js b/boa/benches/bench_scripts/hello_world.js deleted file mode 100644 index 3597f5047f..0000000000 --- a/boa/benches/bench_scripts/hello_world.js +++ /dev/null @@ -1,2 +0,0 @@ -let foo = "hello world!"; -foo; diff --git a/boa/benches/bench_scripts/long_repetition.js b/boa/benches/bench_scripts/long_repetition.js deleted file mode 100644 index 59a5c981ac..0000000000 --- a/boa/benches/bench_scripts/long_repetition.js +++ /dev/null @@ -1,9 +0,0 @@ -for (let a = 10; a < 100; a++) { - if (a < 10) { - console.log("impossible D:"); - } else if (a < 50) { - console.log("starting"); - } else { - console.log("finishing"); - } -} diff --git a/boa/benches/exec.rs b/boa/benches/exec.rs deleted file mode 100644 index 99789f4af8..0000000000 --- a/boa/benches/exec.rs +++ /dev/null @@ -1,359 +0,0 @@ -//! Benchmarks of the whole execution engine in Boa. - -use boa::{exec::Executable, realm::Realm, syntax::Parser, Context}; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] -#[cfg_attr( - all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"), - global_allocator -)] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -fn create_realm(c: &mut Criterion) { - c.bench_function("Create Realm", move |b| b.iter(Realm::create)); -} - -static SYMBOL_CREATION: &str = include_str!("bench_scripts/symbol_creation.js"); - -fn symbol_creation(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(SYMBOL_CREATION.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("Symbols (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static FOR_LOOP: &str = include_str!("bench_scripts/for_loop.js"); - -fn for_loop_execution(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(FOR_LOOP.as_bytes(), false).parse_all().unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("For loop (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static FIBONACCI: &str = include_str!("bench_scripts/fibonacci.js"); - -fn fibonacci(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(FIBONACCI.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("Fibonacci (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static OBJECT_CREATION: &str = include_str!("bench_scripts/object_creation.js"); - -fn object_creation(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(OBJECT_CREATION.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("Object Creation (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static OBJECT_PROP_ACCESS_CONST: &str = include_str!("bench_scripts/object_prop_access_const.js"); - -fn object_prop_access_const(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(OBJECT_PROP_ACCESS_CONST.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("Static Object Property Access (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static OBJECT_PROP_ACCESS_DYN: &str = include_str!("bench_scripts/object_prop_access_dyn.js"); - -fn object_prop_access_dyn(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(OBJECT_PROP_ACCESS_DYN.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("Dynamic Object Property Access (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static REGEXP_LITERAL_CREATION: &str = include_str!("bench_scripts/regexp_literal_creation.js"); - -fn regexp_literal_creation(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(REGEXP_LITERAL_CREATION.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("RegExp Literal Creation (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static REGEXP_CREATION: &str = include_str!("bench_scripts/regexp_creation.js"); - -fn regexp_creation(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(REGEXP_CREATION.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("RegExp (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static REGEXP_LITERAL: &str = include_str!("bench_scripts/regexp_literal.js"); - -fn regexp_literal(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(REGEXP_LITERAL.as_bytes(), false) - .parse_all() - .unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("RegExp Literal (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static REGEXP: &str = include_str!("bench_scripts/regexp.js"); - -fn regexp(c: &mut Criterion) { - let mut context = Context::new(); - - // Parse the AST nodes. - let nodes = Parser::new(REGEXP.as_bytes(), false).parse_all().unwrap(); - - // Execute the parsed nodes, passing them through a black box, to avoid over-optimizing by the compiler - c.bench_function("RegExp (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static ARRAY_ACCESS: &str = include_str!("bench_scripts/array_access.js"); - -fn array_access(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(ARRAY_ACCESS.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("Array access (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static ARRAY_CREATE: &str = include_str!("bench_scripts/array_create.js"); - -fn array_creation(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(ARRAY_CREATE.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("Array creation (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static ARRAY_POP: &str = include_str!("bench_scripts/array_pop.js"); - -fn array_pop(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(ARRAY_POP.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("Array pop (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static STRING_CONCAT: &str = include_str!("bench_scripts/string_concat.js"); - -fn string_concat(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(STRING_CONCAT.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("String concatenation (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static STRING_COMPARE: &str = include_str!("bench_scripts/string_compare.js"); - -fn string_compare(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(STRING_COMPARE.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("String comparison (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static STRING_COPY: &str = include_str!("bench_scripts/string_copy.js"); - -fn string_copy(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(STRING_COPY.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("String copy (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static NUMBER_OBJECT_ACCESS: &str = include_str!("bench_scripts/number_object_access.js"); - -fn number_object_access(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(NUMBER_OBJECT_ACCESS.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("Number Object Access (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static BOOLEAN_OBJECT_ACCESS: &str = include_str!("bench_scripts/boolean_object_access.js"); - -fn boolean_object_access(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(BOOLEAN_OBJECT_ACCESS.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("Boolean Object Access (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static STRING_OBJECT_ACCESS: &str = include_str!("bench_scripts/string_object_access.js"); - -fn string_object_access(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(STRING_OBJECT_ACCESS.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("String Object Access (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static ARITHMETIC_OPERATIONS: &str = include_str!("bench_scripts/arithmetic_operations.js"); - -fn arithmetic_operations(c: &mut Criterion) { - let mut context = Context::new(); - - let nodes = Parser::new(ARITHMETIC_OPERATIONS.as_bytes(), false) - .parse_all() - .unwrap(); - - c.bench_function("Arithmetic operations (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js"); - -fn clean_js(c: &mut Criterion) { - let mut context = Context::new(); - let nodes = Parser::new(CLEAN_JS.as_bytes(), false).parse_all().unwrap(); - c.bench_function("Clean js (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -static MINI_JS: &str = include_str!("bench_scripts/mini_js.js"); - -fn mini_js(c: &mut Criterion) { - let mut context = Context::new(); - let nodes = Parser::new(MINI_JS.as_bytes(), false).parse_all().unwrap(); - c.bench_function("Mini js (Execution)", move |b| { - b.iter(|| black_box(&nodes).run(&mut context).unwrap()) - }); -} - -criterion_group!( - execution, - create_realm, - symbol_creation, - for_loop_execution, - fibonacci, - array_access, - array_creation, - array_pop, - object_creation, - object_prop_access_const, - object_prop_access_dyn, - regexp_literal_creation, - regexp_creation, - regexp_literal, - regexp, - string_concat, - string_compare, - string_copy, - number_object_access, - boolean_object_access, - string_object_access, - arithmetic_operations, - clean_js, - mini_js, -); -criterion_main!(execution); diff --git a/boa/benches/full.rs b/boa/benches/full.rs index a46682db38..1d67b0d826 100644 --- a/boa/benches/full.rs +++ b/boa/benches/full.rs @@ -1,6 +1,6 @@ -//! Benchmarks of whole program execution in Boa. +//! Benchmarks of the whole execution engine in Boa. -use boa::Context; +use boa::{parse, realm::Realm, Context}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; #[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] @@ -10,215 +10,83 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; )] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -static SYMBOL_CREATION: &str = include_str!("bench_scripts/symbol_creation.js"); - -fn symbol_creation(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("Symbols (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(SYMBOL_CREATION))) - }); -} - -static FOR_LOOP: &str = include_str!("bench_scripts/for_loop.js"); - -fn for_loop(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("For loop (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(FOR_LOOP))) - }); -} - -static FIBONACCI: &str = include_str!("bench_scripts/fibonacci.js"); - -fn fibonacci(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("Fibonacci (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(FIBONACCI))) - }); -} - -static OBJECT_CREATION: &str = include_str!("bench_scripts/object_creation.js"); - -fn object_creation(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("Object Creation (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(OBJECT_CREATION))) - }); -} - -static OBJECT_PROP_ACCESS_CONST: &str = include_str!("bench_scripts/object_prop_access_const.js"); - -fn object_prop_access_const(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("Static Object Property Access (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(OBJECT_PROP_ACCESS_CONST))) - }); -} - -static OBJECT_PROP_ACCESS_DYN: &str = include_str!("bench_scripts/object_prop_access_dyn.js"); - -fn object_prop_access_dyn(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("Dynamic Object Property Access (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(OBJECT_PROP_ACCESS_DYN))) - }); -} - -static REGEXP_LITERAL_CREATION: &str = include_str!("bench_scripts/regexp_literal_creation.js"); - -fn regexp_literal_creation(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("RegExp Literal Creation (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(REGEXP_LITERAL_CREATION))) - }); -} - -static REGEXP_CREATION: &str = include_str!("bench_scripts/regexp_creation.js"); - -fn regexp_creation(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("RegExp (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(REGEXP_CREATION))) - }); -} - -static REGEXP_LITERAL: &str = include_str!("bench_scripts/regexp_literal.js"); - -fn regexp_literal(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("RegExp Literal (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(REGEXP_LITERAL))) - }); -} - -static REGEXP: &str = include_str!("bench_scripts/regexp.js"); - -fn regexp(c: &mut Criterion) { - // Execute the code by taking into account realm creation, lexing and parsing - c.bench_function("RegExp (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(REGEXP))) - }); -} - -static ARRAY_ACCESS: &str = include_str!("bench_scripts/array_access.js"); - -fn array_access(c: &mut Criterion) { - c.bench_function("Array access (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(ARRAY_ACCESS))) - }); -} - -static ARRAY_CREATE: &str = include_str!("bench_scripts/array_create.js"); - -fn array_creation(c: &mut Criterion) { - c.bench_function("Array creation (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(ARRAY_CREATE))) - }); -} - -static ARRAY_POP: &str = include_str!("bench_scripts/array_pop.js"); - -fn array_pop(c: &mut Criterion) { - c.bench_function("Array pop (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(ARRAY_POP))) - }); -} - -static STRING_CONCAT: &str = include_str!("bench_scripts/string_concat.js"); - -fn string_concat(c: &mut Criterion) { - c.bench_function("String concatenation (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(STRING_CONCAT))) - }); -} - -static STRING_COMPARE: &str = include_str!("bench_scripts/string_compare.js"); - -fn string_compare(c: &mut Criterion) { - c.bench_function("String comparison (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(STRING_COMPARE))) - }); -} - -static STRING_COPY: &str = include_str!("bench_scripts/string_copy.js"); - -fn string_copy(c: &mut Criterion) { - c.bench_function("String copy (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(STRING_COPY))) - }); -} - -static NUMBER_OBJECT_ACCESS: &str = include_str!("bench_scripts/number_object_access.js"); - -fn number_object_access(c: &mut Criterion) { - c.bench_function("Number Object Access (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(NUMBER_OBJECT_ACCESS))) - }); -} - -static BOOLEAN_OBJECT_ACCESS: &str = include_str!("bench_scripts/boolean_object_access.js"); - -fn boolean_object_access(c: &mut Criterion) { - c.bench_function("Boolean Object Access (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(BOOLEAN_OBJECT_ACCESS))) - }); -} - -static STRING_OBJECT_ACCESS: &str = include_str!("bench_scripts/string_object_access.js"); - -fn string_object_access(c: &mut Criterion) { - c.bench_function("String Object Access (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(STRING_OBJECT_ACCESS))) - }); -} - -static ARITHMETIC_OPERATIONS: &str = include_str!("bench_scripts/arithmetic_operations.js"); - -fn arithmetic_operations(c: &mut Criterion) { - c.bench_function("Arithmetic operations (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(ARITHMETIC_OPERATIONS))) - }); -} - -static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js"); - -fn clean_js(c: &mut Criterion) { - c.bench_function("Clean js (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(CLEAN_JS))) - }); -} - -static MINI_JS: &str = include_str!("bench_scripts/mini_js.js"); - -fn mini_js(c: &mut Criterion) { - c.bench_function("Mini js (Full)", move |b| { - b.iter(|| Context::new().eval(black_box(MINI_JS))) - }); -} +fn create_realm(c: &mut Criterion) { + c.bench_function("Create Realm", move |b| b.iter(Realm::create)); +} + +macro_rules! full_benchmarks { + ($({$id:literal, $name:ident}),*) => { + fn bench_parser(c: &mut Criterion) { + $( + { + static CODE: &str = include_str!(concat!("bench_scripts/", stringify!($name), ".js")); + c.bench_function(concat!($id, " (Parser)"), move |b| { + b.iter(|| parse(black_box(CODE), false)) + }); + } + )* + } + fn bench_compile(c: &mut Criterion) { + $( + { + static CODE: &str = include_str!(concat!("bench_scripts/", stringify!($name), ".js")); + let statement_list = parse(CODE, false).unwrap(); + c.bench_function(concat!($id, " (Compiler)"), move |b| { + b.iter(|| { + Context::compile(black_box(statement_list.clone())); + }) + }); + } + )* + } + fn bench_execution(c: &mut Criterion) { + $( + { + static CODE: &str = include_str!(concat!("bench_scripts/", stringify!($name), ".js")); + let statement_list = parse(CODE, false).unwrap(); + let code_block = Context::compile(statement_list); + let mut context = Context::new(); + c.bench_function(concat!($id, " (Execution)"), move |b| { + b.iter(|| { + context.execute(black_box(code_block.clone())).unwrap(); + }) + }); + } + )* + } + }; +} + +full_benchmarks!( + {"Symbols", symbol_creation}, + {"For loop", for_loop}, + {"Fibonacci", fibonacci}, + {"Object Creation", object_creation}, + {"Static Object Property Access", object_prop_access_const}, + {"Dynamic Object Property Access", object_prop_access_dyn}, + {"RegExp Literal Creation", regexp_literal_creation}, + {"RegExp Creation", regexp_creation}, + {"RegExp Literal", regexp_literal}, + {"RegExp", regexp}, + {"Array access", array_access}, + {"Array creation", array_create}, + {"Array pop", array_pop}, + {"String concatenation", string_concat}, + {"String comparison", string_compare}, + {"String copy", string_copy}, + {"Number Object Access", number_object_access}, + {"Boolean Object Access", boolean_object_access}, + {"String Object Access", string_object_access}, + {"Arithmetic operations", arithmetic_operations}, + {"Clean js", clean_js}, + {"Mini js", mini_js} +); criterion_group!( - full, - symbol_creation, - for_loop, - fibonacci, - array_access, - array_creation, - array_pop, - object_creation, - object_prop_access_const, - object_prop_access_dyn, - regexp_literal_creation, - regexp_creation, - regexp_literal, - regexp, - string_concat, - string_compare, - string_copy, - number_object_access, - boolean_object_access, - string_object_access, - arithmetic_operations, - clean_js, - mini_js, + benches, + create_realm, + bench_parser, + bench_compile, + bench_execution, ); -criterion_main!(full); +criterion_main!(benches); diff --git a/boa/benches/parser.rs b/boa/benches/parser.rs deleted file mode 100644 index 464a362bdd..0000000000 --- a/boa/benches/parser.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Benchmarks of the parsing process in Boa. - -use boa::syntax::parser::Parser; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))] -#[cfg_attr( - all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"), - global_allocator -)] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -static EXPRESSION: &str = include_str!("bench_scripts/expression.js"); - -fn expression_parser(c: &mut Criterion) { - c.bench_function("Expression (Parser)", move |b| { - b.iter(|| Parser::new(black_box(EXPRESSION.as_bytes()), false).parse_all()) - }); -} - -static HELLO_WORLD: &str = include_str!("bench_scripts/hello_world.js"); - -fn hello_world_parser(c: &mut Criterion) { - c.bench_function("Hello World (Parser)", move |b| { - b.iter(|| Parser::new(black_box(HELLO_WORLD.as_bytes()), false).parse_all()) - }); -} - -static FOR_LOOP: &str = include_str!("bench_scripts/for_loop.js"); - -fn for_loop_parser(c: &mut Criterion) { - c.bench_function("For loop (Parser)", move |b| { - b.iter(|| Parser::new(black_box(FOR_LOOP.as_bytes()), false).parse_all()) - }); -} - -static LONG_REPETITION: &str = include_str!("bench_scripts/long_repetition.js"); - -fn long_file_parser(c: &mut Criterion) { - use std::{ - fs::{self, File}, - io::{BufWriter, Write}, - }; - // We include the lexing in the benchmarks, since they will get together soon, anyways. - const FILE_NAME: &str = "long_file_test.js"; - - { - let mut file = BufWriter::new( - File::create(FILE_NAME).unwrap_or_else(|_| panic!("could not create {}", FILE_NAME)), - ); - for _ in 0..400 { - file.write_all(LONG_REPETITION.as_bytes()) - .unwrap_or_else(|_| panic!("could not write {}", FILE_NAME)); - } - } - - let file = std::fs::File::open(FILE_NAME).expect("Could not open file"); - c.bench_function("Long file (Parser)", move |b| { - b.iter(|| Parser::new(black_box(&file), false).parse_all()) - }); - - fs::remove_file(FILE_NAME).unwrap_or_else(|_| panic!("could not remove {}", FILE_NAME)); -} - -static GOAL_SYMBOL_SWITCH: &str = include_str!("bench_scripts/goal_symbol_switch.js"); - -fn goal_symbol_switch(c: &mut Criterion) { - c.bench_function("Goal Symbols (Parser)", move |b| { - b.iter(|| Parser::new(black_box(GOAL_SYMBOL_SWITCH.as_bytes()), false).parse_all()) - }); -} - -static CLEAN_JS: &str = include_str!("bench_scripts/clean_js.js"); - -fn clean_js(c: &mut Criterion) { - c.bench_function("Clean js (Parser)", move |b| { - b.iter(|| Parser::new(black_box(CLEAN_JS.as_bytes()), false).parse_all()) - }); -} - -static MINI_JS: &str = include_str!("bench_scripts/mini_js.js"); - -fn mini_js(c: &mut Criterion) { - c.bench_function("Mini js (Parser)", move |b| { - b.iter(|| Parser::new(black_box(MINI_JS.as_bytes()), false).parse_all()) - }); -} - -criterion_group!( - parser, - expression_parser, - hello_world_parser, - for_loop_parser, - long_file_parser, - goal_symbol_switch, - clean_js, - mini_js, -); -criterion_main!(parser); diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index f1ee445226..861fef6350 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -299,7 +299,6 @@ impl Console { Ok(JsValue::undefined()) } - #[cfg(feature = "vm")] fn get_stack_trace(context: &mut Context) -> Vec { let mut stack_trace: Vec = vec![]; let mut prev_frame = context.vm.frame.as_ref(); @@ -312,12 +311,6 @@ impl Console { stack_trace } - #[cfg(not(feature = "vm"))] - fn get_stack_trace(_: &mut Context) -> Vec { - // TODO: Implement stack trace retrieval when "vm" feature is not available - vec![] - } - /// `console.trace(...data)` /// /// Prints a stack trace with "trace" logLevel, optionally labelled by data. diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 3381f96b60..7da13c0138 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -30,8 +30,6 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, NativeObject, ObjectData}, property::Attribute, property::PropertyDescriptor, - syntax::ast::node::declaration::Declaration, - syntax::ast::node::{FormalParameter, RcStatementList}, BoaProfiler, Context, JsResult, JsValue, }; use crate::{object::Object, symbol::WellKnownSymbols}; @@ -186,14 +184,6 @@ pub enum Function { constructor: bool, captures: Captures, }, - Ordinary { - constructor: bool, - this_mode: ThisMode, - body: RcStatementList, - params: Box<[FormalParameter]>, - environment: Environment, - }, - #[cfg(feature = "vm")] VmOrdinary { code: Gc, environment: Environment, @@ -207,72 +197,11 @@ impl fmt::Debug for Function { } impl Function { - // Adds the final rest parameters to the Environment as an array - #[cfg(not(feature = "vm"))] - pub(crate) fn add_rest_param( - param: &FormalParameter, - index: usize, - args_list: &[JsValue], - context: &mut Context, - local_env: &Environment, - ) { - use crate::builtins::Array; - // Create array of values - let array = Array::new_array(context); - Array::add_to_array_object(&array, args_list.get(index..).unwrap_or_default(), context) - .unwrap(); - - let binding_params = param.run(Some(array), context).unwrap_or_default(); - for binding_items in binding_params.iter() { - // Create binding - local_env - .create_mutable_binding(binding_items.0.as_ref(), false, true, context) - .expect("Failed to create binding"); - - // Set binding to value - local_env - .initialize_binding( - binding_items.0.as_ref(), - JsValue::new(binding_items.1.clone()), - context, - ) - .expect("Failed to intialize binding"); - } - } - - // Adds an argument to the environment - #[cfg(not(feature = "vm"))] - pub(crate) fn add_arguments_to_environment( - param: &FormalParameter, - value: JsValue, - local_env: &Environment, - context: &mut Context, - ) { - let binding_params = param.run(Some(value), context).unwrap_or_default(); - for binding_items in binding_params.iter() { - // Create binding - local_env - .create_mutable_binding(binding_items.0.as_ref(), false, true, context) - .expect("Failed to create binding"); - - // Set binding to value - local_env - .initialize_binding( - binding_items.0.as_ref(), - JsValue::new(binding_items.1.clone()), - context, - ) - .expect("Failed to intialize binding"); - } - } - /// Returns true if the function object is a constructor. pub fn is_constructor(&self) -> bool { match self { Self::Native { constructor, .. } => *constructor, Self::Closure { constructor, .. } => *constructor, - Self::Ordinary { constructor, .. } => *constructor, - #[cfg(feature = "vm")] Self::VmOrdinary { code, .. } => code.constructor, } } @@ -535,62 +464,12 @@ impl BuiltInFunctionObject { }, Some(name), ) => Ok(format!("function {}() {{\n [native Code]\n}}", &name).into()), - (Function::Ordinary { body, params, .. }, Some(name)) => { - let arguments: String = { - let mut argument_list: Vec> = Vec::new(); - for params_item in params.iter() { - let argument_item = match ¶ms_item.declaration() { - Declaration::Identifier { ident, .. } => Cow::Borrowed(ident.as_ref()), - Declaration::Pattern(pattern) => { - Cow::Owned(format!("{{{}}}", pattern.idents().join(","))) - } - }; - argument_list.push(argument_item); - } - argument_list.join(",") - }; - - let statement_list = &*body; - // This is a kluge. The implementaion in browser seems to suggest that - // the value here is printed exactly as defined in source. I'm not sure if - // that's possible here, but for now here's a dumb heuristic that prints functions - let is_multiline = { - let value = statement_list.to_string(); - value.lines().count() > 1 - }; - if is_multiline { - Ok( - // ?? For some reason statement_list string implementation - // sticks a \n at the end no matter what - format!( - "{}({}) {{\n{}}}", - &name, - arguments, - statement_list.to_string() - ) - .into(), - ) - } else { - Ok(format!( - "{}({}) {{{}}}", - &name, - arguments, - // The trim here is to remove a \n stuck at the end - // of the statement_list to_string method - statement_list.to_string().trim() - ) - .into()) - } - } - #[cfg(feature = "vm")] (Function::VmOrdinary { .. }, Some(name)) if name.is_empty() => { Ok("[Function (anonymous)]".into()) } - #[cfg(feature = "vm")] (Function::VmOrdinary { .. }, Some(name)) => { Ok(format!("[Function: {}]", &name).into()) } - #[cfg(feature = "vm")] (Function::VmOrdinary { .. }, None) => Ok("[Function (anonymous)]".into()), _ => Ok("TODO".into()), } diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 4df65edaa3..47b534f90b 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/boa/src/builtins/iterable/mod.rs @@ -195,12 +195,10 @@ impl IteratorRecord { } } - #[cfg(feature = "vm")] pub(crate) fn iterator_object(&self) -> &JsValue { &self.iterator_object } - #[cfg(feature = "vm")] pub(crate) fn next_function(&self) -> &JsValue { &self.next_function } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 1c68e236fc..0f3cbc42df 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -96,8 +96,6 @@ impl Json { // 9. Let unfiltered be completion.[[Value]]. // 10. Assert: unfiltered is either a String, Number, Boolean, Null, or an Object that is defined by either an ArrayLiteral or an ObjectLiteral. let unfiltered = context.eval(script_string.as_bytes())?; - #[cfg(feature = "vm")] - context.vm.pop_frame(); // 11. If IsCallable(reviver) is true, then if let Some(obj) = args.get_or_undefined(1).as_callable() { diff --git a/boa/src/context.rs b/boa/src/context.rs index ff63fd80d0..71e5b2ecc3 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -2,34 +2,23 @@ use crate::{ builtins::{ - self, - function::{Function, NativeFunctionSignature, ThisMode}, - intrinsics::IntrinsicObjects, - iterable::IteratorPrototypes, - typed_array::TypedArray, + self, function::NativeFunctionSignature, intrinsics::IntrinsicObjects, + iterable::IteratorPrototypes, typed_array::TypedArray, }, + bytecompiler::ByteCompiler, class::{Class, ClassBuilder}, - exec::Interpreter, - object::PROTOTYPE, object::{FunctionBuilder, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - syntax::{ - ast::{ - node::{statement_list::RcStatementList, FormalParameter, StatementList}, - Node, - }, - Parser, - }, - BoaProfiler, Executable, JsResult, JsString, JsValue, + syntax::{ast::node::StatementList, Parser}, + vm::{CallFrame, CodeBlock, FinallyReturn, Vm}, + BoaProfiler, JsResult, JsString, JsValue, }; +use gc::Gc; #[cfg(feature = "console")] use crate::builtins::console::Console; -#[cfg(feature = "vm")] -use crate::vm::{FinallyReturn, Vm}; - /// Store a builtin constructor (such as `Object`) and its corresponding prototype. #[derive(Debug, Clone)] pub struct StandardConstructor { @@ -326,14 +315,6 @@ impl StandardObjects { } } -/// Internal representation of the strict mode types. -#[derive(Debug, Copy, Clone)] -pub(crate) enum StrictType { - Off, - Global, - Function, -} - /// Javascript context. It is the primary way to interact with the runtime. /// /// `Context`s constructed in a thread share the same runtime, therefore it @@ -380,9 +361,6 @@ pub struct Context { /// realm holds both the global object and the environment pub(crate) realm: Realm, - /// The current executor. - executor: Interpreter, - /// console object state. #[cfg(feature = "console")] console: Console, @@ -399,28 +377,24 @@ pub struct Context { /// Cached intrinsic objects intrinsic_objects: IntrinsicObjects, - /// Whether or not strict mode is active. - strict: StrictType, + /// Whether or not global strict mode is active. + strict: bool, - #[cfg(feature = "vm")] pub(crate) vm: Vm, } impl Default for Context { fn default() -> Self { let realm = Realm::create(); - let executor = Interpreter::new(); let mut context = Self { realm, - executor, #[cfg(feature = "console")] console: Console::default(), iterator_prototypes: IteratorPrototypes::default(), typed_array_constructor: StandardConstructor::default(), standard_objects: Default::default(), intrinsic_objects: IntrinsicObjects::default(), - strict: StrictType::Off, - #[cfg(feature = "vm")] + strict: false, vm: Vm { frame: None, stack: Vec::with_capacity(1024), @@ -454,12 +428,6 @@ impl Context { pub fn new() -> Self { Default::default() } - - #[inline] - pub fn executor(&mut self) -> &mut Interpreter { - &mut self.executor - } - /// A helper function for getting an immutable reference to the `console` object. #[cfg(feature = "console")] pub(crate) fn console(&self) -> &Console { @@ -476,33 +444,15 @@ impl Context { /// Returns if strict mode is currently active. #[inline] pub fn strict(&self) -> bool { - matches!(self.strict, StrictType::Global | StrictType::Function) - } - - /// Returns the strict mode type. - #[inline] - pub(crate) fn strict_type(&self) -> StrictType { self.strict } - /// Set strict type. + /// Set the global strict mode of the context. #[inline] - pub(crate) fn set_strict(&mut self, strict: StrictType) { + pub fn set_strict_mode(&mut self, strict: bool) { self.strict = strict; } - /// Disable the strict mode. - #[inline] - pub fn set_strict_mode_off(&mut self) { - self.strict = StrictType::Off; - } - - /// Enable the global strict mode. - #[inline] - pub fn set_strict_mode_global(&mut self) { - self.strict = StrictType::Global; - } - /// Sets up the default global objects within Global #[inline] fn create_intrinsics(&mut self) { @@ -720,75 +670,6 @@ impl Context { Err(self.construct_uri_error(message)) } - /// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions - pub(crate) fn create_function( - &mut self, - name: N, - params: P, - mut body: StatementList, - constructor: bool, - this_mode: ThisMode, - ) -> JsResult - where - N: Into, - P: Into>, - { - let name = name.into(); - let function_prototype = self.standard_objects().function_object().prototype(); - - // Every new function has a prototype property pre-made - let prototype = self.construct_object(); - - // If a function is defined within a strict context, it is strict. - if self.strict() { - body.set_strict(true); - } - - let params = params.into(); - let params_len = params.len(); - let func = Function::Ordinary { - constructor, - this_mode, - body: RcStatementList::from(body), - params, - environment: self.get_current_environment().clone(), - }; - - let function = - JsObject::from_proto_and_data(function_prototype, ObjectData::function(func)); - - // Set constructor field to the newly created Value (function object) - let constructor = PropertyDescriptor::builder() - .value(function.clone()) - .writable(true) - .enumerable(false) - .configurable(true); - prototype.define_property_or_throw("constructor", constructor, self)?; - - let prototype = PropertyDescriptor::builder() - .value(prototype) - .writable(true) - .enumerable(false) - .configurable(false); - function.define_property_or_throw(PROTOTYPE, prototype, self)?; - - let length = PropertyDescriptor::builder() - .value(params_len) - .writable(false) - .enumerable(false) - .configurable(true); - function.define_property_or_throw("length", length, self)?; - - let name = PropertyDescriptor::builder() - .value(name) - .writable(false) - .enumerable(false) - .configurable(true); - function.define_property_or_throw("name", name, self)?; - - Ok(function.into()) - } - /// Register a global native function. /// /// This is more efficient that creating a closure function, since this does not allocate, @@ -881,29 +762,6 @@ impl Context { } } - #[inline] - pub(crate) fn set_value(&mut self, node: &Node, value: JsValue) -> JsResult { - match node { - Node::Identifier(ref name) => { - self.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, false, self)?), - 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, false, self)?) - } - _ => self.throw_type_error(format!("invalid assignment to {}", node)), - } - } - /// Register a global class of type `T`, where `T` implements `Class`. /// /// # Example @@ -983,7 +841,7 @@ impl Context { ); } - /// Evaluates the given code. + /// Evaluates the given code by compiling down to bytecode, then interpreting the bytecode into a value /// /// # Examples /// ``` @@ -995,9 +853,7 @@ impl Context { /// assert!(value.is_number()); /// assert_eq!(value.as_number().unwrap(), 4.0); /// ``` - #[cfg(not(feature = "vm"))] #[allow(clippy::unit_arg, clippy::drop_copy)] - #[inline] pub fn eval>(&mut self, src: T) -> JsResult { let main_timer = BoaProfiler::global().start_event("Main", "Main"); let src_bytes: &[u8] = src.as_ref(); @@ -1006,61 +862,34 @@ impl Context { .parse_all() .map_err(|e| e.to_string()); - let execution_result = match parsing_result { - Ok(statement_list) => { - if statement_list.strict() { - self.set_strict_mode_global(); - } - statement_list.run(self) - } - Err(e) => self.throw_syntax_error(e), + let statement_list = match parsing_result { + Ok(statement_list) => statement_list, + Err(e) => return self.throw_syntax_error(e), }; + let code_block = Context::compile(statement_list); + let result = self.execute(code_block); + // The main_timer needs to be dropped before the BoaProfiler is. drop(main_timer); BoaProfiler::global().drop(); - execution_result + result } - /// Evaluates the given code by compiling down to bytecode, then interpreting the bytecode into a value - /// - /// # 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); - /// ``` - #[cfg(feature = "vm")] - #[allow(clippy::unit_arg, clippy::drop_copy)] - pub fn eval>(&mut self, src: T) -> JsResult { - use gc::Gc; - - use crate::vm::CallFrame; - - let main_timer = BoaProfiler::global().start_event("Main", "Main"); - let src_bytes: &[u8] = src.as_ref(); - - let parsing_result = Parser::new(src_bytes, false) - .parse_all() - .map_err(|e| e.to_string()); - - let statement_list = match parsing_result { - Ok(statement_list) => statement_list, - Err(e) => return self.throw_syntax_error(e), - }; - - let mut compiler = crate::bytecompiler::ByteCompiler::new( - JsString::new("
"), - statement_list.strict(), - ); + // Compile the AST into a CodeBlock ready to execute by the VM + #[inline] + pub fn compile(statement_list: StatementList) -> CodeBlock { + let _ = BoaProfiler::global().start_event("Compilation", "Main"); + let mut compiler = ByteCompiler::new(JsString::new("
"), statement_list.strict()); compiler.compile_statement_list(&statement_list, true); - let code_block = compiler.finish(); + compiler.finish() + } + // Call the VM with the codeblock and return the result + #[inline] + pub fn execute(&mut self, code_block: CodeBlock) -> JsResult { + let _ = BoaProfiler::global().start_event("Execute", "Main"); let global_object = self.global_object().into(); self.vm.push_frame(CallFrame { @@ -1076,13 +905,8 @@ impl Context { param_count: 0, arg_count: 0, }); - let result = self.run(); - // The main_timer needs to be dropped before the BoaProfiler is. - drop(main_timer); - BoaProfiler::global().drop(); - - result + self.run() } /// Return the cached iterator prototypes. @@ -1110,7 +934,6 @@ impl Context { } /// Set the value of trace on the context - #[cfg(feature = "vm")] pub fn set_trace(&mut self, trace: bool) { self.vm.trace = trace; } diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 69391eab00..f75bac14d2 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -93,7 +93,6 @@ impl Context { .recursive_get_this_binding(self) } - #[cfg(feature = "vm")] pub(crate) fn get_global_this_binding(&mut self) -> JsResult { let global = self.realm.global_env.clone(); global.get_this_binding(self) diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs deleted file mode 100644 index eae8bef3b4..0000000000 --- a/boa/src/exec/mod.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Execution of the AST, this is where the interpreter actually runs - -#[cfg(test)] -mod tests; - -use crate::{Context, JsResult, JsValue}; - -pub trait Executable { - /// Runs this executable in the given context. - fn run(&self, context: &mut Context) -> JsResult; -} - -#[derive(Debug, Eq, PartialEq)] -pub(crate) enum InterpreterState { - Executing, - Return, - Break(Option>), - Continue(Option>), -} - -/// A Javascript intepreter -#[derive(Debug)] -pub struct Interpreter { - /// the current state of the interpreter. - state: InterpreterState, -} - -impl Default for Interpreter { - fn default() -> Self { - Self::new() - } -} - -impl Interpreter { - /// Creates a new interpreter. - pub fn new() -> Self { - Self { - state: InterpreterState::Executing, - } - } - - #[inline] - pub(crate) fn set_current_state(&mut self, new_state: InterpreterState) { - self.state = new_state - } - - #[inline] - pub(crate) fn get_current_state(&self) -> &InterpreterState { - &self.state - } -} diff --git a/boa/src/lib.rs b/boa/src/lib.rs index 4f2d0e1020..2604b63b84 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -43,10 +43,10 @@ This is an experimental Javascript lexer, parser and compiler written in Rust. C pub mod bigint; pub mod builtins; +pub mod bytecompiler; pub mod class; pub mod context; pub mod environment; -pub mod exec; pub mod gc; pub mod object; pub mod profiler; @@ -56,12 +56,11 @@ pub mod string; pub mod symbol; pub mod syntax; pub mod value; - -#[cfg(feature = "vm")] -pub mod bytecompiler; -#[cfg(feature = "vm")] pub mod vm; +#[cfg(test)] +mod tests; + /// A convenience module that re-exports the most commonly-used Boa APIs pub mod prelude { pub use crate::{object::JsObject, Context, JsBigInt, JsResult, JsString, JsValue}; @@ -69,7 +68,7 @@ pub mod prelude { use std::result::Result as StdResult; -pub(crate) use crate::{exec::Executable, profiler::BoaProfiler}; +pub(crate) use crate::profiler::BoaProfiler; // Export things to root level #[doc(inline)] @@ -99,18 +98,13 @@ pub fn parse>(src: T, strict_mode: bool) -> StdResult>(context: &mut Context, src: T) -> String { let src_bytes: &[u8] = src.as_ref(); - let result = context.eval(src_bytes).map_or_else( + context.eval(src_bytes).map_or_else( |e| format!("Uncaught {}", e.display()), |v| v.display().to_string(), - ); - - #[cfg(feature = "vm")] - context.vm.pop_frame(); - - result + ) } /// Execute the code using an existing Context. @@ -125,9 +119,6 @@ pub(crate) fn forward_val>(context: &mut Context, src: T) -> JsRe let src_bytes: &[u8] = src.as_ref(); let result = context.eval(src_bytes); - #[cfg(feature = "vm")] - context.vm.pop_frame(); - // The main_timer needs to be dropped before the BoaProfiler is. drop(main_timer); BoaProfiler::global().drop(); diff --git a/boa/src/object/internal_methods/function.rs b/boa/src/object/internal_methods/function.rs index e294e4ecaf..797868855f 100644 --- a/boa/src/object/internal_methods/function.rs +++ b/boa/src/object/internal_methods/function.rs @@ -6,21 +6,6 @@ use crate::{ Context, JsResult, JsValue, }; -#[cfg(not(feature = "vm"))] -use crate::{ - builtins::function::{ - arguments::Arguments, Captures, ClosureFunctionSignature, Function, NativeFunctionSignature, - }, - context::StandardObjects, - environment::{ - function_environment_record::{BindingStatus, FunctionEnvironmentRecord}, - lexical_environment::Environment, - }, - exec::{Executable, InterpreterState}, - object::{internal_methods::get_prototype_from_constructor, ObjectData}, - syntax::ast::node::RcStatementList, -}; - /// Definitions of the internal object methods for function objects. /// /// More information: @@ -54,10 +39,7 @@ fn function_call( args: &[JsValue], context: &mut Context, ) -> JsResult { - #[cfg(not(feature = "vm"))] - return call_construct(obj, this, args, context, false); - #[cfg(feature = "vm")] - return obj.call_internal(this, args, context); + obj.call_internal(this, args, context) } /// Construct an instance of this object with the specified arguments. @@ -74,261 +56,5 @@ fn function_construct( new_target: &JsValue, context: &mut Context, ) -> JsResult { - #[cfg(not(feature = "vm"))] - return call_construct(obj, new_target, args, context, true); - #[cfg(feature = "vm")] - return obj.construct_internal(args, new_target, context); -} - -/// Internal implementation of [`call`](#method.call) and [`construct`](#method.construct). -/// -/// # Panics -/// -/// Panics if the object is currently mutably borrowed. -/// -/// -/// -/// -/// -#[track_caller] -#[cfg(not(feature = "vm"))] -pub(super) fn call_construct( - obj: &JsObject, - this_target: &JsValue, - args: &[JsValue], - context: &mut Context, - construct: bool, -) -> JsResult { - /// The body of a JavaScript function. - /// - /// This is needed for the call method since we cannot mutate the function itself since we - /// already borrow it so we get the function body clone it then drop the borrow and run the body - enum FunctionBody { - BuiltInFunction(NativeFunctionSignature), - BuiltInConstructor(NativeFunctionSignature), - Closure { - function: Box, - captures: Captures, - }, - Ordinary(RcStatementList), - } - - let this_function_object = obj.clone(); - let mut has_parameter_expressions = false; - - let body = if let Some(function) = obj.borrow().as_function() { - if construct && !function.is_constructor() { - let name = obj.get("name", context)?.display().to_string(); - return context.throw_type_error(format!("{} is not a constructor", name)); - } else { - match function { - Function::Native { - function, - constructor, - } => { - if *constructor || construct { - FunctionBody::BuiltInConstructor(*function) - } else { - FunctionBody::BuiltInFunction(*function) - } - } - Function::Closure { - function, captures, .. - } => FunctionBody::Closure { - function: function.clone(), - captures: captures.clone(), - }, - Function::Ordinary { - constructor: _, - this_mode, - body, - params, - environment, - } => { - let this = if construct { - // If the prototype of the constructor is not an object, then use the default object - // prototype as prototype for the new object - // see - // see - let proto = get_prototype_from_constructor( - this_target, - StandardObjects::object_object, - context, - )?; - JsObject::from_proto_and_data(Some(proto), ObjectData::ordinary()).into() - } else { - this_target.clone() - }; - - // Create a new Function environment whose parent is set to the scope of the function declaration (obj.environment) - // - let local_env = FunctionEnvironmentRecord::new( - this_function_object.clone(), - if construct || !this_mode.is_lexical() { - Some(this.clone()) - } else { - None - }, - Some(environment.clone()), - // Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records - if this_mode.is_lexical() { - BindingStatus::Lexical - } else { - BindingStatus::Uninitialized - }, - JsValue::undefined(), - context, - )?; - - let mut arguments_in_parameter_names = false; - let mut is_simple_parameter_list = true; - - for param in params.iter() { - has_parameter_expressions = - has_parameter_expressions || param.init().is_some(); - - for param_name in param.names() { - arguments_in_parameter_names = - arguments_in_parameter_names || param_name == "arguments"; - } - - is_simple_parameter_list = is_simple_parameter_list - && !param.is_rest_param() - && param.is_identifier() - && param.init().is_none() - } - - // Turn local_env into Environment so it can be cloned - let local_env: Environment = local_env.into(); - - // An arguments object is added when all of the following conditions are met - // - If not in an arrow function (10.2.11.16) - // - If the parameter list does not contain `arguments` (10.2.11.17) - // - If there are default parameters or if lexical names and function names do not contain `arguments` (10.2.11.18) - // - // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation - if !this_mode.is_lexical() - && !arguments_in_parameter_names - && (has_parameter_expressions - || (!body.lexically_declared_names().contains("arguments") - && !body.function_declared_names().contains("arguments"))) - { - // Add arguments object - let arguments_obj = - if context.strict() || body.strict() || !is_simple_parameter_list { - Arguments::create_unmapped_arguments_object(args, context) - } else { - Arguments::create_mapped_arguments_object( - obj, params, args, &local_env, context, - ) - }; - local_env.create_mutable_binding("arguments", false, true, context)?; - local_env.initialize_binding("arguments", arguments_obj.into(), context)?; - } - - // Push the environment first so that it will be used by default parameters - context.push_environment(local_env.clone()); - - // Add argument bindings to the function environment - for (i, param) in params.iter().enumerate() { - // Rest Parameters - if param.is_rest_param() { - Function::add_rest_param(param, i, args, context, &local_env); - break; - } - - let value = match args.get(i).cloned() { - None | Some(JsValue::Undefined) => param - .init() - .map(|init| init.run(context).ok()) - .flatten() - .unwrap_or_default(), - Some(value) => value, - }; - - Function::add_arguments_to_environment(param, value, &local_env, context); - } - - if has_parameter_expressions { - // Create a second environment when default parameter expressions are used - // This prevents variables declared in the function body from being - // used in default parameter initializers. - // https://tc39.es/ecma262/#sec-functiondeclarationinstantiation - let second_env = FunctionEnvironmentRecord::new( - this_function_object, - if construct || !this_mode.is_lexical() { - Some(this) - } else { - None - }, - Some(local_env), - // Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records - if this_mode.is_lexical() { - BindingStatus::Lexical - } else { - BindingStatus::Uninitialized - }, - JsValue::undefined(), - context, - )?; - context.push_environment(second_env); - } - - FunctionBody::Ordinary(body.clone()) - } - #[cfg(feature = "vm")] - Function::VmOrdinary { .. } => { - todo!("vm call") - } - } - } - } else { - return context.throw_type_error("not a function"); - }; - - match body { - FunctionBody::BuiltInConstructor(function) if construct => { - function(this_target, args, context) - } - FunctionBody::BuiltInConstructor(function) => { - function(&JsValue::undefined(), args, context) - } - FunctionBody::BuiltInFunction(function) => function(this_target, args, context), - FunctionBody::Closure { function, captures } => { - (function)(this_target, args, captures, context) - } - FunctionBody::Ordinary(body) => { - let result = body.run(context); - let this = context.get_this_binding(); - - if has_parameter_expressions { - context.pop_environment(); - } - context.pop_environment(); - - if construct { - // https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget - // 12. If result.[[Type]] is return, then - if context.executor().get_current_state() == &InterpreterState::Return { - // a. If Type(result.[[Value]]) is Object, return NormalCompletion(result.[[Value]]). - if let Ok(v) = &result { - if v.is_object() { - return result; - } - } - } - - // 13. Else, ReturnIfAbrupt(result). - result?; - - // 14. Return ? constructorEnv.GetThisBinding(). - this - } else if context.executor().get_current_state() == &InterpreterState::Return { - result - } else { - result?; - Ok(JsValue::undefined()) - } - } - } + obj.construct_internal(args, new_target, context) } diff --git a/boa/src/syntax/ast/node/array/mod.rs b/boa/src/syntax/ast/node/array/mod.rs index a387ef011e..34d3d88350 100644 --- a/boa/src/syntax/ast/node/array/mod.rs +++ b/boa/src/syntax/ast/node/array/mod.rs @@ -1,12 +1,7 @@ //! Array declaration node. use super::{join_nodes, Node}; -use crate::{ - builtins::Array, - exec::Executable, - gc::{Finalize, Trace}, - BoaProfiler, Context, JsResult, JsValue, -}; +use crate::gc::{Finalize, Trace}; use std::fmt; #[cfg(feature = "deser")] @@ -38,37 +33,6 @@ pub struct ArrayDecl { arr: Box<[Node]>, } -impl Executable for ArrayDecl { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec"); - let array = Array::new_array(context); - let mut elements = Vec::new(); - for elem in self.as_ref() { - if let Node::Spread(ref x) = elem { - let val = x.run(context)?; - let iterator_record = val.get_iterator(context, None, None)?; - // TODO after proper internal Array representation as per https://github.com/boa-dev/boa/pull/811#discussion_r502460858 - // next_index variable should be utilized here as per https://tc39.es/ecma262/#sec-runtime-semantics-arrayaccumulation - // let mut next_index = 0; - loop { - let next = iterator_record.next(context)?; - if next.done { - break; - } - let next_value = next.value; - //next_index += 1; - elements.push(next_value); - } - } else { - elements.push(elem.run(context)?); - } - } - - Array::add_to_array_object(&array, &elements, context)?; - Ok(array) - } -} - impl AsRef<[Node]> for ArrayDecl { fn as_ref(&self) -> &[Node] { &self.arr diff --git a/boa/src/syntax/ast/node/await_expr/mod.rs b/boa/src/syntax/ast/node/await_expr/mod.rs index ecdeef9768..e2755483e6 100644 --- a/boa/src/syntax/ast/node/await_expr/mod.rs +++ b/boa/src/syntax/ast/node/await_expr/mod.rs @@ -1,7 +1,6 @@ //! Await expression node. use super::Node; -use crate::{exec::Executable, BoaProfiler, Context, JsResult, JsValue}; use gc::{Finalize, Trace}; use std::fmt; @@ -26,14 +25,6 @@ pub struct AwaitExpr { expr: Box, } -impl Executable for AwaitExpr { - fn run(&self, _: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("AwaitExpression", "exec"); - // TODO: Implement AwaitExpr - Ok(JsValue::undefined()) - } -} - impl From for AwaitExpr where T: Into>, diff --git a/boa/src/syntax/ast/node/block/mod.rs b/boa/src/syntax/ast/node/block/mod.rs index 71ce68ed2a..b5be4073c2 100644 --- a/boa/src/syntax/ast/node/block/mod.rs +++ b/boa/src/syntax/ast/node/block/mod.rs @@ -1,13 +1,7 @@ //! Block AST node. use super::{Node, StatementList}; -use crate::{ - environment::declarative_environment_record::DeclarativeEnvironmentRecord, - exec::Executable, - exec::InterpreterState, - gc::{Finalize, Trace}, - BoaProfiler, Context, JsResult, JsValue, -}; +use crate::gc::{Finalize, Trace}; use std::{collections::HashSet, fmt}; #[cfg(feature = "deser")] @@ -61,53 +55,6 @@ impl Block { } } -impl Executable for Block { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("Block", "exec"); - { - let env = context.get_current_environment(); - context.push_environment(DeclarativeEnvironmentRecord::new(Some(env))); - } - - // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation - // The return value is uninitialized, which means it defaults to Value::Undefined - let mut obj = JsValue::default(); - for statement in self.items() { - obj = statement.run(context).map_err(|e| { - // No matter how control leaves the Block the LexicalEnvironment is always - // restored to its former state. - context.pop_environment(); - e - })?; - - match context.executor().get_current_state() { - InterpreterState::Return => { - // Early return. - break; - } - InterpreterState::Break(_label) => { - // TODO, break to a label. - - // Early break. - break; - } - InterpreterState::Continue(_label) => { - // TODO, continue to a label - break; - } - InterpreterState::Executing => { - // Continue execution - } - } - } - - // pop the block env - let _ = context.pop_environment(); - - Ok(obj) - } -} - impl From for Block where T: Into, diff --git a/boa/src/syntax/ast/node/break_node/mod.rs b/boa/src/syntax/ast/node/break_node/mod.rs index b5f7fb298e..ade6c36be1 100644 --- a/boa/src/syntax/ast/node/break_node/mod.rs +++ b/boa/src/syntax/ast/node/break_node/mod.rs @@ -1,10 +1,5 @@ use super::Node; -use crate::{ - exec::Executable, - exec::InterpreterState, - gc::{Finalize, Trace}, - Context, JsResult, JsValue, -}; +use crate::gc::{Finalize, Trace}; use std::fmt; #[cfg(feature = "deser")] @@ -51,16 +46,6 @@ impl Break { } } -impl Executable for Break { - fn run(&self, context: &mut Context) -> JsResult { - context - .executor() - .set_current_state(InterpreterState::Break(self.label().map(Box::from))); - - Ok(JsValue::undefined()) - } -} - impl fmt::Display for Break { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( diff --git a/boa/src/syntax/ast/node/break_node/tests.rs b/boa/src/syntax/ast/node/break_node/tests.rs index 20487ee399..80e7ddfd79 100644 --- a/boa/src/syntax/ast/node/break_node/tests.rs +++ b/boa/src/syntax/ast/node/break_node/tests.rs @@ -1,23 +1,3 @@ -use crate::{ - exec::{Executable, InterpreterState}, - syntax::ast::node::Break, - Context, -}; - -#[test] -fn check_post_state() { - let mut context = Context::new(); - - let brk: Break = Break::new("label"); - - brk.run(&mut context).unwrap(); - - assert_eq!( - context.executor().get_current_state(), - &InterpreterState::Break(Some("label".into())) - ); -} - #[test] fn fmt() { // Blocks do not store their label, so we cannot test with diff --git a/boa/src/syntax/ast/node/call/mod.rs b/boa/src/syntax/ast/node/call/mod.rs index 30314a9332..3b40ea1d3a 100644 --- a/boa/src/syntax/ast/node/call/mod.rs +++ b/boa/src/syntax/ast/node/call/mod.rs @@ -1,9 +1,6 @@ use crate::{ - exec::Executable, - exec::InterpreterState, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, Node}, - BoaProfiler, Context, JsResult, JsValue, }; use std::fmt; @@ -58,68 +55,6 @@ impl Call { } } -impl Executable for Call { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("Call", "exec"); - let (this, func) = match self.expr() { - Node::GetConstField(ref get_const_field) => { - let mut obj = get_const_field.obj().run(context)?; - if !obj.is_object() { - obj = JsValue::from(obj.to_object(context)?); - } - ( - obj.clone(), - obj.get_field(get_const_field.field(), context)?, - ) - } - Node::GetField(ref get_field) => { - let mut obj = get_field.obj().run(context)?; - if !obj.is_object() { - obj = JsValue::from(obj.to_object(context)?); - } - let field = get_field.field().run(context)?; - ( - obj.clone(), - obj.get_field(field.to_property_key(context)?, context)?, - ) - } - _ => ( - // 'this' binding should come from the function's self-contained environment - context.global_object().into(), - self.expr().run(context)?, - ), - }; - let mut v_args = Vec::with_capacity(self.args().len()); - for arg in self.args() { - if let Node::Spread(ref x) = arg { - let val = x.run(context)?; - let iterator_record = val.get_iterator(context, None, None)?; - loop { - let next = iterator_record.next(context)?; - if next.done { - break; - } - let next_value = next.value; - v_args.push(next_value); - } - break; // after spread we don't accept any new arguments - } else { - v_args.push(arg.run(context)?); - } - } - - // execute the function call itself - let fnct_result = context.call(&func, &this, &v_args); - - // unset the early return flag - context - .executor() - .set_current_state(InterpreterState::Executing); - - fnct_result - } -} - impl fmt::Display for Call { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}(", self.expr)?; diff --git a/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs b/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs index c37ecbec26..14ad10372e 100644 --- a/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs +++ b/boa/src/syntax/ast/node/conditional/conditional_op/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -59,16 +57,6 @@ impl ConditionalOp { } } -impl Executable for ConditionalOp { - fn run(&self, context: &mut Context) -> JsResult { - Ok(if self.cond().run(context)?.to_boolean() { - self.if_true().run(context)? - } else { - self.if_false().run(context)? - }) - } -} - impl fmt::Display for ConditionalOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( diff --git a/boa/src/syntax/ast/node/conditional/if_node/mod.rs b/boa/src/syntax/ast/node/conditional/if_node/mod.rs index e04dd8f3e6..955e2ce1b9 100644 --- a/boa/src/syntax/ast/node/conditional/if_node/mod.rs +++ b/boa/src/syntax/ast/node/conditional/if_node/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -78,18 +76,6 @@ impl If { } } -impl Executable for If { - fn run(&self, context: &mut Context) -> JsResult { - Ok(if self.cond().run(context)?.to_boolean() { - self.body().run(context)? - } else if let Some(else_e) = self.else_node() { - else_e.run(context)? - } else { - JsValue::undefined() - }) - } -} - impl fmt::Display for If { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs index 11fcccca2c..775558cb2c 100644 --- a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs @@ -1,9 +1,6 @@ use crate::{ - builtins::function::ThisMode, - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - Context, JsResult, JsValue, }; use std::fmt; @@ -71,18 +68,6 @@ impl ArrowFunctionDecl { } } -impl Executable for ArrowFunctionDecl { - fn run(&self, context: &mut Context) -> JsResult { - context.create_function( - "", - self.params().to_vec(), - self.body().clone(), - false, - ThisMode::Lexical, - ) - } -} - impl fmt::Display for ArrowFunctionDecl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs index 4d528d0bfa..e23b3c8e98 100644 --- a/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs @@ -1,10 +1,6 @@ //! Async Function Declaration. -use crate::{ - exec::Executable, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - BoaProfiler, Context, JsResult, JsValue, -}; +use crate::syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}; use gc::{Finalize, Trace}; use std::fmt; @@ -75,14 +71,6 @@ impl AsyncFunctionDecl { } } -impl Executable for AsyncFunctionDecl { - fn run(&self, _: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("AsyncFunctionDecl", "exec"); - // TODO: Implement AsyncFunctionDecl - Ok(JsValue::undefined()) - } -} - impl From for Node { fn from(decl: AsyncFunctionDecl) -> Self { Self::AsyncFunctionDecl(decl) diff --git a/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs b/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs index cfe6831202..9ca9518516 100644 --- a/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs @@ -1,10 +1,6 @@ //! Async Function Expression. -use crate::{ - exec::Executable, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - Context, JsResult, JsValue, -}; +use crate::syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}; use gc::{Finalize, Trace}; use std::fmt; @@ -80,13 +76,6 @@ impl AsyncFunctionExpr { } } -impl Executable for AsyncFunctionExpr { - fn run(&self, _: &mut Context) -> JsResult { - // TODO: Implement AsyncFunctionExpr - Ok(JsValue::undefined()) - } -} - impl fmt::Display for AsyncFunctionExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/declaration/async_generator_decl/mod.rs b/boa/src/syntax/ast/node/declaration/async_generator_decl/mod.rs index 74a51610da..e49456a321 100644 --- a/boa/src/syntax/ast/node/declaration/async_generator_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/async_generator_decl/mod.rs @@ -1,10 +1,6 @@ //! Async Generator Declaration -use crate::{ - exec::Executable, - syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - BoaProfiler, Context, JsResult, JsValue, -}; +use crate::syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}; use gc::{Finalize, Trace}; use std::fmt; @@ -73,14 +69,6 @@ impl AsyncGeneratorDecl { } } -impl Executable for AsyncGeneratorDecl { - fn run(&self, _: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("AsyncGeneratorDecl", "exec"); - //TODO: Implement AsyncGeneratorDecl - Ok(JsValue::undefined()) - } -} - impl From for Node { fn from(decl: AsyncGeneratorDecl) -> Self { Self::AsyncGeneratorDecl(decl) diff --git a/boa/src/syntax/ast/node/declaration/async_generator_expr/mod.rs b/boa/src/syntax/ast/node/declaration/async_generator_expr/mod.rs index c4ab57e365..e7825d899c 100644 --- a/boa/src/syntax/ast/node/declaration/async_generator_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/async_generator_expr/mod.rs @@ -1,10 +1,8 @@ //! Async Generator Expression use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - Context, JsResult, JsValue, }; use std::fmt; @@ -85,13 +83,6 @@ impl AsyncGeneratorExpr { } } -impl Executable for AsyncGeneratorExpr { - fn run(&self, _context: &mut Context) -> JsResult { - //TODO: Implement AsyncGeneratorFunction - Ok(JsValue::undefined()) - } -} - impl fmt::Display for AsyncGeneratorExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs index f5198f55d7..952ae8cb48 100644 --- a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs @@ -1,10 +1,6 @@ use crate::{ - builtins::function::ThisMode, - environment::lexical_environment::VariableScope, - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - BoaProfiler, Context, JsResult, JsValue, }; use std::fmt; @@ -85,28 +81,6 @@ impl FunctionDecl { } } -impl Executable for FunctionDecl { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec"); - let val = context.create_function( - self.name(), - self.parameters().to_vec(), - self.body().clone(), - true, - ThisMode::Global, - )?; - - if context.has_binding(self.name())? { - context.set_mutable_binding(self.name(), val, context.strict())?; - } else { - context.create_mutable_binding(self.name(), false, VariableScope::Function)?; - - context.initialize_binding(self.name(), val)?; - } - Ok(JsValue::undefined()) - } -} - impl From for Node { fn from(decl: FunctionDecl) -> Self { Self::FunctionDecl(decl) diff --git a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs index e93a06f166..eaeb2056e6 100644 --- a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs @@ -1,9 +1,6 @@ use crate::{ - builtins::function::ThisMode, - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - Context, JsResult, JsValue, }; use std::fmt; @@ -97,20 +94,6 @@ impl FunctionExpr { } } -impl Executable for FunctionExpr { - fn run(&self, context: &mut Context) -> JsResult { - let val = context.create_function( - self.name().unwrap_or(""), - self.parameters().to_vec(), - self.body().clone(), - true, - ThisMode::Global, - )?; - - Ok(val) - } -} - impl fmt::Display for FunctionExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/declaration/generator_decl/mod.rs b/boa/src/syntax/ast/node/declaration/generator_decl/mod.rs index 60116ec350..ef4fe8bc2b 100644 --- a/boa/src/syntax/ast/node/declaration/generator_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/generator_decl/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - BoaProfiler, Context, JsResult, JsValue, }; use std::fmt; @@ -74,15 +72,6 @@ impl GeneratorDecl { } } -impl Executable for GeneratorDecl { - fn run(&self, _context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("GeneratorDecl", "exec"); - // TODO: Implement GeneratorFunction - // https://tc39.es/ecma262/#sec-generatorfunction-objects - Ok(JsValue::undefined()) - } -} - impl From for Node { fn from(decl: GeneratorDecl) -> Self { Self::GeneratorDecl(decl) diff --git a/boa/src/syntax/ast/node/declaration/generator_expr/mod.rs b/boa/src/syntax/ast/node/declaration/generator_expr/mod.rs index 70d7aa98ab..1f555801d2 100644 --- a/boa/src/syntax/ast/node/declaration/generator_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/generator_expr/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, - Context, JsResult, JsValue, }; use std::fmt; @@ -88,14 +86,6 @@ impl GeneratorExpr { } } -impl Executable for GeneratorExpr { - fn run(&self, _context: &mut Context) -> JsResult { - // TODO: Implement GeneratorFunction - // https://tc39.es/ecma262/#sec-generatorfunction-objects - Ok(JsValue::undefined()) - } -} - impl fmt::Display for GeneratorExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/declaration/mod.rs b/boa/src/syntax/ast/node/declaration/mod.rs index 5a9bebe7fd..73350fd4ef 100644 --- a/boa/src/syntax/ast/node/declaration/mod.rs +++ b/boa/src/syntax/ast/node/declaration/mod.rs @@ -1,11 +1,7 @@ //! Declaration nodes use crate::{ - builtins::Array, - environment::lexical_environment::VariableScope, - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{join_nodes, Identifier, Node}, - Context, JsResult, JsValue, }; use std::fmt; @@ -95,101 +91,6 @@ pub enum DeclarationList { Var(Box<[Declaration]>), } -impl Executable for DeclarationList { - fn run(&self, context: &mut Context) -> JsResult { - for decl in self.as_ref() { - use DeclarationList::*; - let val = match decl.init() { - None if self.is_const() => { - return context.throw_syntax_error("missing = in const declaration") - } - Some(init) => init.run(context)?, - None => JsValue::undefined(), - }; - - match &decl { - Declaration::Identifier { ident, init } => { - if self.is_var() && context.has_binding(ident.as_ref())? { - if init.is_some() { - context.set_mutable_binding(ident.as_ref(), val, context.strict())?; - } - continue; - } - - match &self { - Const(_) => context.create_immutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?, - Let(_) => context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?, - Var(_) => context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Function, - )?, - } - - context.initialize_binding(ident.as_ref(), val)?; - } - Declaration::Pattern(p) => { - for (ident, value) in p.run(None, context)? { - if self.is_var() && context.has_binding(ident.as_ref())? { - if !value.is_undefined() { - context.set_mutable_binding( - ident.as_ref(), - value, - context.strict(), - )?; - } - continue; - } - - match &self { - Const(_) => context.create_immutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?, - Let(_) => context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?, - Var(_) => context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Function, - )?, - } - - context.initialize_binding(ident.as_ref(), value)?; - } - } - } - } - - Ok(JsValue::undefined()) - } -} - -impl DeclarationList { - #[allow(dead_code)] - pub(in crate::syntax) fn is_let(&self) -> bool { - matches!(self, Self::Let(_)) - } - pub(in crate::syntax) fn is_const(&self) -> bool { - matches!(self, Self::Const(_)) - } - pub(in crate::syntax) fn is_var(&self) -> bool { - matches!(self, Self::Var(_)) - } -} - impl AsRef<[Declaration]> for DeclarationList { fn as_ref(&self) -> &[Declaration] { use DeclarationList::*; @@ -355,22 +256,6 @@ impl fmt::Display for DeclarationPattern { } impl DeclarationPattern { - /// Initialize the values of an object/array binding pattern. - /// - /// This function only calls the specific initialization function for either the object or the array binding pattern. - /// For specific documentation and references to the ECMAScript spec, look at the called initialization functions. - #[inline] - pub(in crate::syntax) fn run( - &self, - init: Option, - context: &mut Context, - ) -> JsResult, JsValue)>> { - match &self { - DeclarationPattern::Object(pattern) => pattern.run(init, context), - DeclarationPattern::Array(pattern) => pattern.run(init, context), - } - } - /// Gets the list of identifiers declared by the binding pattern. /// /// A single binding pattern may declare 0 to n identifiers. @@ -443,128 +328,10 @@ impl DeclarationPatternObject { /// Gets the bindings for the object binding pattern. #[inline] - #[cfg(feature = "vm")] pub(crate) fn bindings(&self) -> &Vec { &self.bindings } - /// Initialize the values of an object binding pattern. - /// - /// More information: - /// - [ECMAScript reference: 8.5.2 Runtime Semantics: BindingInitialization][spec1] - /// - [ECMAScript reference:14.3.3.3 Runtime Semantics: KeyedBindingInitialization][spec2] - /// - [ECMAScript reference:14.3.3.2 Runtime Semantics: RestBindingInitialization][spec3] - /// - /// [spec1]: https://tc39.es/ecma262/#sec-runtime-semantics-bindinginitialization - /// [spec2]: https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization - /// [spec3]: https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-restbindinginitialization - pub(in crate::syntax) fn run( - &self, - init: Option, - context: &mut Context, - ) -> JsResult, JsValue)>> { - let value = if let Some(value) = init { - value - } else if let Some(node) = &self.init { - node.run(context)? - } else { - JsValue::undefined() - }; - - if value.is_null() { - return context.throw_type_error("Cannot destructure 'null' value"); - } - if value.is_undefined() { - return context.throw_type_error("Cannot destructure 'undefined' value"); - } - - // 1. Perform ? RequireObjectCoercible(value). - let value = value.require_object_coercible(context)?; - let mut results = Vec::new(); - - // 2. Return the result of performing BindingInitialization for ObjectBindingPattern using value and environment as arguments. - for binding in &self.bindings { - use BindingPatternTypeObject::*; - - match binding { - // ObjectBindingPattern : { } - Empty => { - // 1. Return NormalCompletion(empty). - } - // SingleNameBinding : BindingIdentifier Initializer[opt] - SingleName { - ident, - property_name, - default_init, - } => { - // 1. Let bindingId be StringValue of BindingIdentifier. - // 2. Let lhs be ? ResolveBinding(bindingId, environment). - - // 3. Let v be ? GetV(value, propertyName). - let mut v = value.get_field(property_name.as_ref(), context)?; - - // 4. If Initializer is present and v is undefined, then - if let Some(init) = default_init { - if v.is_undefined() { - // TODO: a. not implemented yet: - // a. If IsAnonymousFunctionDefinition(Initializer) is true, then - // i. Set v to the result of performing NamedEvaluation for Initializer with argument bindingId. - - // b. Else, - // i. Let defaultValue be the result of evaluating Initializer. - // ii. Set v to ? GetValue(defaultValue). - v = init.run(context)?; - } - } - - // 5. If environment is undefined, return ? PutValue(lhs, v). - // 6. Return InitializeReferencedBinding(lhs, v). - results.push((ident.clone(), v)); - } - // BindingRestProperty : ... BindingIdentifier - RestProperty { - ident, - excluded_keys, - } => { - // 1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment). - - // 2. Let restObj be ! OrdinaryObjectCreate(%Object.prototype%). - let rest_obj = context.construct_object(); - - // 3. Perform ? CopyDataProperties(restObj, value, excludedNames). - rest_obj.copy_data_properties(value, excluded_keys.clone(), context)?; - - // 4. If environment is undefined, return PutValue(lhs, restObj). - // 5. Return InitializeReferencedBinding(lhs, restObj). - results.push((ident.clone(), rest_obj.into())); - } - // BindingElement : BindingPattern Initializer[opt] - BindingPattern { - ident, - pattern, - default_init, - } => { - // 1. Let v be ? GetV(value, propertyName). - let mut v = value.get_field(ident.as_ref(), context)?; - - // 2. If Initializer is present and v is undefined, then - if let Some(init) = default_init { - if v.is_undefined() { - // a. Let defaultValue be the result of evaluating Initializer. - // b. Set v to ? GetValue(defaultValue). - v = init.run(context)?; - } - } - - // 3. Return the result of performing BindingInitialization for BindingPattern passing v and environment as arguments. - results.append(&mut pattern.run(Some(v), context)?); - } - } - } - - Ok(results) - } - /// Gets the list of identifiers declared by the object binding pattern. #[inline] pub(crate) fn idents(&self) -> Vec<&str> { @@ -658,207 +425,10 @@ impl DeclarationPatternArray { /// Gets the bindings for the array binding pattern. #[inline] - #[cfg(feature = "vm")] pub(crate) fn bindings(&self) -> &Vec { &self.bindings } - /// Initialize the values of an array binding pattern. - /// - /// More information: - /// - [ECMAScript reference: 8.5.2 Runtime Semantics: BindingInitialization][spec1] - /// - [ECMAScript reference: 8.5.3 Runtime Semantics: IteratorBindingInitialization][spec2] - /// - /// [spec1]: https://tc39.es/ecma262/#sec-runtime-semantics-bindinginitialization - /// [spec2]: https://tc39.es/ecma262/#sec-runtime-semantics-iteratorbindinginitialization - pub(in crate::syntax) fn run( - &self, - init: Option, - context: &mut Context, - ) -> JsResult, JsValue)>> { - let value = if let Some(value) = init { - value - } else if let Some(node) = &self.init { - node.run(context)? - } else { - JsValue::undefined() - }; - - if value.is_null() { - return context.throw_type_error("Cannot destructure 'null' value"); - } - if value.is_undefined() { - return context.throw_type_error("Cannot destructure 'undefined' value"); - } - - // 1. Let iteratorRecord be ? GetIterator(value). - let iterator = value.get_iterator(context, None, None)?; - let mut result = Vec::new(); - - // 2. Let result be IteratorBindingInitialization of ArrayBindingPattern with arguments iteratorRecord and environment. - for binding in &self.bindings { - use BindingPatternTypeArray::*; - - match binding { - // ArrayBindingPattern : [ ] - Empty => { - // 1. Return NormalCompletion(empty). - } - // ArrayBindingPattern : [ Elision ] - // Note: This captures all elisions due to our representation of a the binding pattern. - Elision => { - // 1. If iteratorRecord.[[Done]] is false, then - // a. Let next be IteratorStep(iteratorRecord). - // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. - // c. ReturnIfAbrupt(next). - // d. If next is false, set iteratorRecord.[[Done]] to true. - let _ = iterator.next(context)?; - - // 2. Return NormalCompletion(empty). - } - // SingleNameBinding : BindingIdentifier Initializer[opt] - SingleName { - ident, - default_init, - } => { - // 1. Let bindingId be StringValue of BindingIdentifier. - // 2. Let lhs be ? ResolveBinding(bindingId, environment). - - let next = iterator.next(context)?; - - // 3. If iteratorRecord.[[Done]] is false, then - // 4. If iteratorRecord.[[Done]] is true, let v be undefined. - let mut v = if !next.done { - // a. Let next be IteratorStep(iteratorRecord). - // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. - // c. ReturnIfAbrupt(next). - // d. If next is false, set iteratorRecord.[[Done]] to true. - // e. Else, - // i. Let v be IteratorValue(next). - // ii. If v is an abrupt completion, set iteratorRecord.[[Done]] to true. - // iii. ReturnIfAbrupt(v). - next.value - } else { - JsValue::undefined() - }; - - // 5. If Initializer is present and v is undefined, then - if let Some(init) = default_init { - if v.is_undefined() { - // TODO: a. not implemented yet: - // a. If IsAnonymousFunctionDefinition(Initializer) is true, then - // i. Set v to the result of performing NamedEvaluation for Initializer with argument bindingId. - - // b. Else, - // i. Let defaultValue be the result of evaluating Initializer. - // ii. Set v to ? GetValue(defaultValue). - v = init.run(context)? - } - } - - // 6. If environment is undefined, return ? PutValue(lhs, v). - // 7. Return InitializeReferencedBinding(lhs, v). - result.push((ident.clone(), v)); - } - // BindingElement : BindingPattern Initializer[opt] - BindingPattern { pattern } => { - let next = iterator.next(context)?; - - // 1. If iteratorRecord.[[Done]] is false, then - // 2. If iteratorRecord.[[Done]] is true, let v be undefined. - let v = if !next.done { - // a. Let next be IteratorStep(iteratorRecord). - // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. - // c. ReturnIfAbrupt(next). - // d. If next is false, set iteratorRecord.[[Done]] to true. - // e. Else, - // i. Let v be IteratorValue(next). - // ii. If v is an abrupt completion, set iteratorRecord.[[Done]] to true. - // iii. ReturnIfAbrupt(v). - Some(next.value) - } else { - None - }; - - // 3. If Initializer is present and v is undefined, then - // a. Let defaultValue be the result of evaluating Initializer. - // b. Set v to ? GetValue(defaultValue). - - // 4. Return the result of performing BindingInitialization of BindingPattern with v and environment as the arguments. - result.append(&mut pattern.run(v, context)?); - } - // BindingRestElement : ... BindingIdentifier - SingleNameRest { ident } => { - // 1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment). - // 2. Let A be ! ArrayCreate(0). - // 3. Let n be 0. - let a = Array::array_create(0, None, context) - .expect("Array creation with 0 length should never fail"); - - // 4. Repeat, - loop { - let next = iterator.next(context)?; - // a. If iteratorRecord.[[Done]] is false, then - // i. Let next be IteratorStep(iteratorRecord). - // ii. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. - // iii. ReturnIfAbrupt(next). - // iv. If next is false, set iteratorRecord.[[Done]] to true. - - // b. If iteratorRecord.[[Done]] is true, then - if next.done { - // i. If environment is undefined, return ? PutValue(lhs, A). - // ii. Return InitializeReferencedBinding(lhs, A). - break result.push((ident.clone(), a.clone().into())); - } - - // c. Let nextValue be IteratorValue(next). - // d. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true. - // e. ReturnIfAbrupt(nextValue). - - // f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), nextValue). - // g. Set n to n + 1. - Array::add_to_array_object(&a.clone().into(), &[next.value], context)?; - } - } - // BindingRestElement : ... BindingPattern - BindingPatternRest { pattern } => { - // 1. Let A be ! ArrayCreate(0). - // 2. Let n be 0. - let a = Array::array_create(0, None, context) - .expect("Array creation with 0 length should never fail"); - - // 3. Repeat, - loop { - // a. If iteratorRecord.[[Done]] is false, then - // i. Let next be IteratorStep(iteratorRecord). - // ii. If next is an abrupt completion, set iteratorRecord.[[Done]] to true. - // iii. ReturnIfAbrupt(next). - // iv. If next is false, set iteratorRecord.[[Done]] to true. - let next = iterator.next(context)?; - - // b. If iteratorRecord.[[Done]] is true, then - if next.done { - // i. Return the result of performing BindingInitialization of BindingPattern with A and environment as the arguments. - break result - .append(&mut pattern.run(Some(a.clone().into()), context)?); - } - - // c. Let nextValue be IteratorValue(next). - // d. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true. - // e. ReturnIfAbrupt(nextValue). - // f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), nextValue). - // g. Set n to n + 1. - Array::add_to_array_object(&a.clone().into(), &[next.value], context)?; - } - } - } - } - - // 3. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, result). - // 4. Return result. - Ok(result) - } - /// Gets the list of identifiers declared by the array binding pattern. #[inline] pub(crate) fn idents(&self) -> Vec<&str> { diff --git a/boa/src/syntax/ast/node/field/get_const_field/mod.rs b/boa/src/syntax/ast/node/field/get_const_field/mod.rs index 7ae400cf1f..54e44a0252 100644 --- a/boa/src/syntax/ast/node/field/get_const_field/mod.rs +++ b/boa/src/syntax/ast/node/field/get_const_field/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -61,17 +59,6 @@ impl GetConstField { } } -impl Executable for GetConstField { - fn run(&self, context: &mut Context) -> JsResult { - let mut obj = self.obj().run(context)?; - if !obj.is_object() { - obj = JsValue::Object(obj.to_object(context)?); - } - - obj.get_field(self.field(), context) - } -} - impl fmt::Display for GetConstField { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}.{}", self.obj(), self.field()) diff --git a/boa/src/syntax/ast/node/field/get_field/mod.rs b/boa/src/syntax/ast/node/field/get_field/mod.rs index 6efa7367c2..8ce069378c 100644 --- a/boa/src/syntax/ast/node/field/get_field/mod.rs +++ b/boa/src/syntax/ast/node/field/get_field/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -60,18 +58,6 @@ impl GetField { } } -impl Executable for GetField { - fn run(&self, context: &mut Context) -> JsResult { - let mut obj = self.obj().run(context)?; - if !obj.is_object() { - obj = JsValue::Object(obj.to_object(context)?); - } - let field = self.field().run(context)?; - - obj.get_field(field.to_property_key(context)?, context) - } -} - impl fmt::Display for GetField { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}[{}]", self.obj(), self.field()) diff --git a/boa/src/syntax/ast/node/identifier/mod.rs b/boa/src/syntax/ast/node/identifier/mod.rs index a1a301a809..37cd58a26e 100644 --- a/boa/src/syntax/ast/node/identifier/mod.rs +++ b/boa/src/syntax/ast/node/identifier/mod.rs @@ -1,10 +1,8 @@ //! Local identifier node. use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - BoaProfiler, Context, JsResult, JsValue, }; use std::fmt; @@ -33,13 +31,6 @@ pub struct Identifier { ident: Box, } -impl Executable for Identifier { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("Identifier", "exec"); - context.get_binding_value(self.as_ref()) - } -} - impl fmt::Display for Identifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.ident, f) diff --git a/boa/src/syntax/ast/node/iteration/continue_node/mod.rs b/boa/src/syntax/ast/node/iteration/continue_node/mod.rs index cda656a45f..013f635815 100644 --- a/boa/src/syntax/ast/node/iteration/continue_node/mod.rs +++ b/boa/src/syntax/ast/node/iteration/continue_node/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -45,16 +43,6 @@ impl Continue { } } -impl Executable for Continue { - fn run(&self, context: &mut Context) -> JsResult { - context - .executor() - .set_current_state(InterpreterState::Continue(self.label().map(Box::from))); - - Ok(JsValue::undefined()) - } -} - impl fmt::Display for Continue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "continue")?; diff --git a/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs b/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs index 9310909c6c..d324a04f38 100644 --- a/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -73,34 +71,6 @@ impl DoWhileLoop { } } -impl Executable for DoWhileLoop { - fn run(&self, context: &mut Context) -> JsResult { - let mut result; - loop { - result = self.body().run(context)?; - match context.executor().get_current_state() { - InterpreterState::Break(label) => { - handle_state_with_labels!(self, label, context, break); - break; - } - InterpreterState::Continue(label) => { - handle_state_with_labels!(self, label, context, continue); - } - InterpreterState::Return => { - return Ok(result); - } - InterpreterState::Executing => { - // Continue execution. - } - } - if !self.cond().run(context)?.to_boolean() { - break; - } - } - Ok(result) - } -} - impl fmt::Display for DoWhileLoop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs index 022c464e65..f8beefbb62 100644 --- a/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs @@ -1,13 +1,6 @@ use crate::{ - builtins::{iterable::IteratorRecord, ForInIterator}, - environment::{ - declarative_environment_record::DeclarativeEnvironmentRecord, - lexical_environment::VariableScope, - }, - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::{iteration::IterableLoopInitializer, Declaration, Node}, - BoaProfiler, Context, JsResult, JsValue, + syntax::ast::node::{iteration::IterableLoopInitializer, Node}, }; use std::fmt; @@ -77,150 +70,3 @@ impl From for Node { Self::ForInLoop(for_in) } } - -impl Executable for ForInLoop { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("ForIn", "exec"); - let object = self.expr().run(context)?; - let mut result = JsValue::undefined(); - - if object.is_null_or_undefined() { - return Ok(result); - } - let object = object.to_object(context)?; - let for_in_iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), context); - let next_function = for_in_iterator - .get_property("next") - .as_ref() - .map(|p| p.expect_value()) - .cloned() - .ok_or_else(|| context.construct_type_error("Could not find property `next`"))?; - let iterator = IteratorRecord::new(for_in_iterator, next_function); - - loop { - { - let env = context.get_current_environment(); - context.push_environment(DeclarativeEnvironmentRecord::new(Some(env))); - } - let iterator_result = iterator.next(context)?; - if iterator_result.done { - context.pop_environment(); - break; - } - let next_result = iterator_result.value; - - match self.init() { - IterableLoopInitializer::Identifier(ref name) => { - if context.has_binding(name.as_ref())? { - // Binding already exists - context.set_mutable_binding( - name.as_ref(), - next_result.clone(), - context.strict(), - )?; - } else { - context.create_mutable_binding( - name.as_ref(), - true, - VariableScope::Function, - )?; - context.initialize_binding(name.as_ref(), next_result)?; - } - } - IterableLoopInitializer::Var(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { - if context.has_binding(ident.as_ref())? { - context.set_mutable_binding( - ident.as_ref(), - next_result, - context.strict(), - )?; - } else { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Function, - )?; - context.initialize_binding(ident.as_ref(), next_result)?; - } - } - Declaration::Pattern(p) => { - for (ident, value) in p.run(Some(next_result), context)? { - if context.has_binding(ident.as_ref())? { - context.set_mutable_binding( - ident.as_ref(), - value, - context.strict(), - )?; - } else { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Function, - )?; - context.initialize_binding(ident.as_ref(), value)?; - } - } - } - }, - IterableLoopInitializer::Let(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), next_result)?; - } - Declaration::Pattern(p) => { - for (ident, value) in p.run(Some(next_result), context)? { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), value)?; - } - } - }, - IterableLoopInitializer::Const(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { - context.create_immutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), next_result)?; - } - Declaration::Pattern(p) => { - for (ident, value) in p.run(Some(next_result), context)? { - context.create_immutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), value)?; - } - } - }, - } - - result = self.body().run(context)?; - match context.executor().get_current_state() { - InterpreterState::Break(label) => { - handle_state_with_labels!(self, label, context, break); - break; - } - InterpreterState::Continue(label) => { - handle_state_with_labels!(self, label, context, continue); - } - InterpreterState::Return => return Ok(result), - InterpreterState::Executing => { - // Continue execution. - } - } - let _ = context.pop_environment(); - } - Ok(result) - } -} diff --git a/boa/src/syntax/ast/node/iteration/for_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_loop/mod.rs index 47b90acaba..c608e1ac2d 100644 --- a/boa/src/syntax/ast/node/iteration/for_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_loop/mod.rs @@ -1,9 +1,6 @@ use crate::{ - environment::declarative_environment_record::DeclarativeEnvironmentRecord, - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, syntax::ast::node::Node, - BoaProfiler, Context, JsResult, JsValue, }; use std::fmt; @@ -97,56 +94,6 @@ impl ForLoop { } } -impl Executable for ForLoop { - fn run(&self, context: &mut Context) -> JsResult { - // Create the block environment. - let _timer = BoaProfiler::global().start_event("ForLoop", "exec"); - { - let env = context.get_current_environment(); - context.push_environment(DeclarativeEnvironmentRecord::new(Some(env))); - } - - if let Some(init) = self.init() { - init.run(context)?; - } - - while self - .condition() - .map(|cond| cond.run(context).map(|v| v.to_boolean())) - .transpose()? - .unwrap_or(true) - { - let result = self.body().run(context)?; - - match context.executor().get_current_state() { - InterpreterState::Break(label) => { - handle_state_with_labels!(self, label, context, break); - break; - } - InterpreterState::Continue(label) => { - handle_state_with_labels!(self, label, context, continue); - } - - InterpreterState::Return => { - return Ok(result); - } - InterpreterState::Executing => { - // Continue execution. - } - } - - if let Some(final_expr) = self.final_expr() { - final_expr.run(context)?; - } - } - - // pop the block env - let _ = context.pop_environment(); - - Ok(JsValue::undefined()) - } -} - impl fmt::Display for ForLoop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs b/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs index efa38a4708..6b4a954102 100644 --- a/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs @@ -1,12 +1,6 @@ use crate::{ - environment::{ - declarative_environment_record::DeclarativeEnvironmentRecord, - lexical_environment::VariableScope, - }, - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, - syntax::ast::node::{iteration::IterableLoopInitializer, Declaration, Node}, - BoaProfiler, Context, JsResult, JsValue, + syntax::ast::node::{iteration::IterableLoopInitializer, Node}, }; use std::fmt; @@ -76,138 +70,3 @@ impl From for Node { Self::ForOfLoop(for_of) } } - -impl Executable for ForOfLoop { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("ForOf", "exec"); - let iterable = self.iterable().run(context)?; - let iterator = iterable.get_iterator(context, None, None)?; - let mut result = JsValue::undefined(); - - loop { - { - let env = context.get_current_environment(); - context.push_environment(DeclarativeEnvironmentRecord::new(Some(env))); - } - let iterator_result = iterator.next(context)?; - if iterator_result.done { - context.pop_environment(); - break; - } - let next_result = iterator_result.value; - - match self.init() { - IterableLoopInitializer::Identifier(ref name) => { - if context.has_binding(name.as_ref())? { - // Binding already exists - context.set_mutable_binding( - name.as_ref(), - next_result.clone(), - context.strict(), - )?; - } else { - context.create_mutable_binding( - name.as_ref(), - true, - VariableScope::Function, - )?; - context.initialize_binding(name.as_ref(), next_result)?; - } - } - IterableLoopInitializer::Var(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { - if context.has_binding(ident.as_ref())? { - context.set_mutable_binding( - ident.as_ref(), - next_result, - context.strict(), - )?; - } else { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Function, - )?; - context.initialize_binding(ident.as_ref(), next_result)?; - } - } - Declaration::Pattern(p) => { - for (ident, value) in p.run(Some(next_result), context)? { - if context.has_binding(ident.as_ref())? { - context.set_mutable_binding( - ident.as_ref(), - value, - context.strict(), - )?; - } else { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Function, - )?; - context.initialize_binding(ident.as_ref(), value)?; - } - } - } - }, - IterableLoopInitializer::Let(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), next_result)?; - } - Declaration::Pattern(p) => { - for (ident, value) in p.run(Some(next_result), context)? { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), value)?; - } - } - }, - IterableLoopInitializer::Const(declaration) => match declaration { - Declaration::Identifier { ident, .. } => { - context.create_immutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), next_result)?; - } - Declaration::Pattern(p) => { - for (ident, value) in p.run(Some(next_result), context)? { - context.create_immutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), value)?; - } - } - }, - } - - result = self.body().run(context)?; - match context.executor().get_current_state() { - InterpreterState::Break(label) => { - handle_state_with_labels!(self, label, context, break); - break; - } - InterpreterState::Continue(label) => { - handle_state_with_labels!(self, label, context, continue); - } - InterpreterState::Return => return Ok(result), - InterpreterState::Executing => { - // Continue execution. - } - } - let _ = context.pop_environment(); - } - Ok(result) - } -} diff --git a/boa/src/syntax/ast/node/iteration/mod.rs b/boa/src/syntax/ast/node/iteration/mod.rs index 5402fd132d..7d5dc77644 100644 --- a/boa/src/syntax/ast/node/iteration/mod.rs +++ b/boa/src/syntax/ast/node/iteration/mod.rs @@ -16,27 +16,6 @@ use serde::{Deserialize, Serialize}; #[cfg(test)] mod tests; -// Checking labels for break and continue is the same operation for `ForLoop`, `While` and `DoWhile` -macro_rules! handle_state_with_labels { - ($self:ident, $label:ident, $interpreter:ident, $state:tt) => {{ - if let Some(brk_label) = $label { - if let Some(stmt_label) = $self.label() { - // Break from where we are, keeping "continue" set as the state - if stmt_label != brk_label.as_ref() { - break; - } - } else { - // if a label is set but the current block has no label, break - break; - } - } - - $interpreter - .executor() - .set_current_state(InterpreterState::Executing); - }}; -} - #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum IterableLoopInitializer { diff --git a/boa/src/syntax/ast/node/iteration/while_loop/mod.rs b/boa/src/syntax/ast/node/iteration/while_loop/mod.rs index 0438c2f634..5a47fcaa75 100644 --- a/boa/src/syntax/ast/node/iteration/while_loop/mod.rs +++ b/boa/src/syntax/ast/node/iteration/while_loop/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -71,31 +69,6 @@ impl WhileLoop { } } -impl Executable for WhileLoop { - fn run(&self, context: &mut Context) -> JsResult { - let mut result = JsValue::undefined(); - while self.cond().run(context)?.to_boolean() { - result = self.body().run(context)?; - match context.executor().get_current_state() { - InterpreterState::Break(label) => { - handle_state_with_labels!(self, label, context, break); - break; - } - InterpreterState::Continue(label) => { - handle_state_with_labels!(self, label, context, continue) - } - InterpreterState::Return => { - return Ok(result); - } - InterpreterState::Executing => { - // Continue execution. - } - } - } - Ok(result) - } -} - impl fmt::Display for WhileLoop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/mod.rs b/boa/src/syntax/ast/node/mod.rs index 1e733f1f93..dfe1e89058 100644 --- a/boa/src/syntax/ast/node/mod.rs +++ b/boa/src/syntax/ast/node/mod.rs @@ -51,11 +51,7 @@ pub use self::{ try_node::{Catch, Finally, Try}, }; use super::Const; -use crate::{ - exec::Executable, - gc::{empty_trace, Finalize, Trace}, - BoaProfiler, Context, JsResult, JsValue, -}; +use crate::gc::{empty_trace, Finalize, Trace}; use std::{ cmp::Ordering, fmt::{self, Display}, @@ -330,72 +326,6 @@ impl Node { } } -impl Executable for Node { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("Executable", "exec"); - match *self { - Node::AsyncFunctionDecl(ref decl) => decl.run(context), - Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(context), - Node::AsyncGeneratorExpr(ref expr) => expr.run(context), - Node::AsyncGeneratorDecl(ref decl) => decl.run(context), - Node::AwaitExpr(ref expr) => expr.run(context), - Node::Call(ref call) => call.run(context), - Node::Const(Const::Null) => Ok(JsValue::null()), - Node::Const(Const::Num(num)) => Ok(JsValue::new(num)), - Node::Const(Const::Int(num)) => Ok(JsValue::new(num)), - Node::Const(Const::BigInt(ref num)) => Ok(JsValue::new(num.clone())), - Node::Const(Const::Undefined) => Ok(JsValue::undefined()), - // we can't move String from Const into value, because const is a garbage collected value - // Which means Drop() get's called on Const, but str will be gone at that point. - // Do Const values need to be garbage collected? We no longer need them once we've generated Values - Node::Const(Const::String(ref value)) => Ok(JsValue::new(value.to_string())), - Node::Const(Const::Bool(value)) => Ok(JsValue::new(value)), - Node::Block(ref block) => block.run(context), - Node::Identifier(ref identifier) => identifier.run(context), - Node::GetConstField(ref get_const_field_node) => get_const_field_node.run(context), - Node::GetField(ref get_field) => get_field.run(context), - Node::WhileLoop(ref while_loop) => while_loop.run(context), - Node::DoWhileLoop(ref do_while) => do_while.run(context), - Node::ForLoop(ref for_loop) => for_loop.run(context), - Node::ForOfLoop(ref for_of_loop) => for_of_loop.run(context), - Node::ForInLoop(ref for_in_loop) => for_in_loop.run(context), - Node::If(ref if_smt) => if_smt.run(context), - Node::ConditionalOp(ref op) => op.run(context), - Node::Switch(ref switch) => switch.run(context), - Node::Object(ref obj) => obj.run(context), - Node::ArrayDecl(ref arr) => arr.run(context), - // - Node::FunctionDecl(ref decl) => decl.run(context), - // - Node::FunctionExpr(ref function_expr) => function_expr.run(context), - Node::ArrowFunctionDecl(ref decl) => decl.run(context), - Node::BinOp(ref op) => op.run(context), - Node::UnaryOp(ref op) => op.run(context), - Node::New(ref call) => call.run(context), - Node::Return(ref ret) => ret.run(context), - Node::TaggedTemplate(ref template) => template.run(context), - Node::TemplateLit(ref template) => template.run(context), - Node::Throw(ref throw) => throw.run(context), - Node::Assign(ref op) => op.run(context), - Node::VarDeclList(ref decl) => decl.run(context), - Node::LetDeclList(ref decl) => decl.run(context), - Node::ConstDeclList(ref decl) => decl.run(context), - Node::Spread(ref spread) => spread.run(context), - Node::This => { - // Will either return `this` binding or undefined - context.get_this_binding() - } - Node::Try(ref try_node) => try_node.run(context), - Node::Break(ref break_node) => break_node.run(context), - Node::Continue(ref continue_node) => continue_node.run(context), - Node::Empty => Ok(JsValue::undefined()), - Node::Yield(ref y) => y.run(context), - Node::GeneratorDecl(ref decl) => decl.run(context), - Node::GeneratorExpr(ref expr) => expr.run(context), - } - } -} - /// Utility to join multiple Nodes into a single string. fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[N]) -> fmt::Result where @@ -473,25 +403,6 @@ impl FormalParameter { self.is_rest_param } - pub fn run( - &self, - init: Option, - context: &mut Context, - ) -> JsResult, JsValue)>> { - match &self.declaration { - Declaration::Identifier { ident, .. } => Ok(vec![( - ident.as_ref().to_string().into_boxed_str(), - init.unwrap(), - )]), - - Declaration::Pattern(pattern) => match &pattern { - DeclarationPattern::Object(object_pattern) => object_pattern.run(init, context), - - DeclarationPattern::Array(array_pattern) => array_pattern.run(init, context), - }, - } - } - pub fn is_identifier(&self) -> bool { matches!(&self.declaration, Declaration::Identifier { .. }) } diff --git a/boa/src/syntax/ast/node/new/mod.rs b/boa/src/syntax/ast/node/new/mod.rs index c5df53dec0..000f11eb31 100644 --- a/boa/src/syntax/ast/node/new/mod.rs +++ b/boa/src/syntax/ast/node/new/mod.rs @@ -1,9 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{Call, Node}, - value::JsValue, - BoaProfiler, Context, JsResult, }; use std::fmt; @@ -46,48 +43,11 @@ impl New { } /// Returns the inner call - #[cfg(feature = "vm")] pub(crate) fn call(&self) -> &Call { &self.call } } -impl Executable for New { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("New", "exec"); - - let func_object = self.expr().run(context)?; - let mut v_args = Vec::with_capacity(self.args().len()); - for arg in self.args() { - if let Node::Spread(ref x) = arg { - let val = x.run(context)?; - let iterator_record = val.get_iterator(context, None, None)?; - loop { - let next = iterator_record.next(context)?; - if next.done { - break; - } - let next_value = next.value; - v_args.push(next_value); - } - break; // after spread we don't accept any new arguments - } else { - v_args.push(arg.run(context)?); - } - } - - func_object - .as_constructor() - .ok_or_else(|| { - context.construct_type_error(format!( - "{} is not a constructor", - self.expr().to_string(), - )) - }) - .and_then(|cons| cons.construct(&v_args, &cons.clone().into(), context)) - } -} - impl From for New { fn from(call: Call) -> Self { Self { call } diff --git a/boa/src/syntax/ast/node/object/mod.rs b/boa/src/syntax/ast/node/object/mod.rs index 809cfc89c7..3024e4f0be 100644 --- a/boa/src/syntax/ast/node/object/mod.rs +++ b/boa/src/syntax/ast/node/object/mod.rs @@ -1,11 +1,8 @@ //! Object node. use crate::{ - exec::Executable, gc::{Finalize, Trace}, - property::PropertyDescriptor, - syntax::ast::node::{join_nodes, MethodDefinitionKind, Node, PropertyDefinition, PropertyName}, - BoaProfiler, Context, JsResult, JsValue, + syntax::ast::node::{join_nodes, MethodDefinitionKind, Node, PropertyDefinition}, }; use std::fmt; @@ -89,144 +86,6 @@ impl Object { } } -impl Executable for Object { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("object", "exec"); - let obj = context.construct_object(); - - // TODO: Implement the rest of the property types. - for property in self.properties().iter() { - match property { - PropertyDefinition::Property(name, value) => { - let name = match name { - PropertyName::Literal(name) => name.clone().into(), - PropertyName::Computed(node) => { - node.run(context)?.to_property_key(context)? - } - }; - obj.__define_own_property__( - name, - PropertyDescriptor::builder() - .value(value.run(context)?) - .writable(true) - .enumerable(true) - .configurable(true) - .build(), - context, - )?; - } - PropertyDefinition::MethodDefinition(kind, name, func) => { - let name = match name { - PropertyName::Literal(name) => name.clone().into(), - PropertyName::Computed(node) => { - node.run(context)?.to_property_key(context)? - } - }; - match kind { - MethodDefinitionKind::Ordinary => { - obj.__define_own_property__( - name, - PropertyDescriptor::builder() - .value(func.run(context)?) - .writable(true) - .enumerable(true) - .configurable(true) - .build(), - context, - )?; - } - MethodDefinitionKind::Get => { - let set = obj - .__get_own_property__(&name, context)? - .as_ref() - .and_then(|a| a.set()) - .cloned(); - obj.__define_own_property__( - name, - PropertyDescriptor::builder() - .maybe_get(func.run(context)?.as_object().cloned()) - .maybe_set(set) - .enumerable(true) - .configurable(true) - .build(), - context, - )?; - } - MethodDefinitionKind::Set => { - let get = obj - .__get_own_property__(&name, context)? - .as_ref() - .and_then(|a| a.get()) - .cloned(); - obj.__define_own_property__( - name, - PropertyDescriptor::builder() - .maybe_get(get) - .maybe_set(func.run(context)?.as_object().cloned()) - .enumerable(true) - .configurable(true) - .build(), - context, - )?; - } - &MethodDefinitionKind::Generator => { - // TODO: Implement generator method definition execution. - obj.__define_own_property__( - name, - PropertyDescriptor::builder() - .value(JsValue::undefined()) - .writable(true) - .enumerable(true) - .configurable(true) - .build(), - context, - )?; - } - &MethodDefinitionKind::AsyncGenerator => { - // TODO: Implement async generator method definition execution. - obj.__define_own_property__( - name, - PropertyDescriptor::builder() - .value(JsValue::undefined()) - .writable(true) - .enumerable(true) - .configurable(true) - .build(), - context, - )?; - } - &MethodDefinitionKind::Async => { - obj.__define_own_property__( - name, - PropertyDescriptor::builder() - .value(JsValue::undefined()) - .writable(true) - .enumerable(true) - .configurable(true) - .build(), - context, - )?; - } - } - } - // [spec]: https://tc39.es/ecma262/#sec-runtime-semantics-propertydefinitionevaluation - PropertyDefinition::SpreadObject(node) => { - let val = node.run(context)?; - - if val.is_null_or_undefined() { - continue; - } - - obj.copy_data_properties::(&val, vec![], context)?; - } - _ => {} // unimplemented!("{:?} type of property", i), - } - } - - Ok(obj.into()) - } -} - impl fmt::Display for Object { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/operator/assign/mod.rs b/boa/src/syntax/ast/node/operator/assign/mod.rs index 56a5ce3a1d..fe6a32e053 100644 --- a/boa/src/syntax/ast/node/operator/assign/mod.rs +++ b/boa/src/syntax/ast/node/operator/assign/mod.rs @@ -1,9 +1,6 @@ use crate::{ - environment::lexical_environment::VariableScope, - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - BoaProfiler, Context, JsResult, JsValue, }; use std::fmt; @@ -52,49 +49,6 @@ impl Assign { } } -impl Executable for Assign { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("Assign", "exec"); - let val = self.rhs().run(context)?; - match self.lhs() { - Node::Identifier(ref name) => { - if context.has_binding(name.as_ref())? { - // Binding already exists - context.set_mutable_binding(name.as_ref(), val.clone(), context.strict())?; - } else { - context.create_mutable_binding(name.as_ref(), true, VariableScope::Function)?; - context.initialize_binding(name.as_ref(), val.clone())?; - } - } - Node::GetConstField(ref get_const_field) => { - let value = get_const_field.obj().run(context)?; - let obj = value.to_object(context)?; - let succeeded = - obj.__set__(get_const_field.field().into(), val.clone(), value, context)?; - if !succeeded && context.strict() { - return context.throw_type_error( - "Assignment to read-only properties is not allowed in strict mode", - ); - } - } - Node::GetField(ref get_field) => { - let value = get_field.obj().run(context)?; - let obj = value.to_object(context)?; - let field = get_field.field().run(context)?; - let key = field.to_property_key(context)?; - let succeeded = obj.__set__(key, val.clone(), value, context)?; - if !succeeded && context.strict() { - return context.throw_type_error( - "Assignment to read-only properties is not allowed in strict mode", - ); - } - } - _ => (), - } - Ok(val) - } -} - impl fmt::Display for Assign { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} = {}", self.lhs, self.rhs) diff --git a/boa/src/syntax/ast/node/operator/bin_op/mod.rs b/boa/src/syntax/ast/node/operator/bin_op/mod.rs index 321cb7d605..f178e4c7e4 100644 --- a/boa/src/syntax/ast/node/operator/bin_op/mod.rs +++ b/boa/src/syntax/ast/node/operator/bin_op/mod.rs @@ -1,11 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, - syntax::ast::{ - node::Node, - op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp}, - }, - Context, JsResult, JsValue, + syntax::ast::{node::Node, op}, }; use std::fmt; @@ -55,148 +50,6 @@ impl BinOp { pub fn rhs(&self) -> &Node { &self.rhs } - - /// Runs the assignment operators. - fn run_assign(op: AssignOp, x: JsValue, y: &Node, context: &mut Context) -> JsResult { - match op { - AssignOp::Add => x.add(&y.run(context)?, context), - AssignOp::Sub => x.sub(&y.run(context)?, context), - AssignOp::Mul => x.mul(&y.run(context)?, context), - AssignOp::Exp => x.pow(&y.run(context)?, context), - AssignOp::Div => x.div(&y.run(context)?, context), - AssignOp::Mod => x.rem(&y.run(context)?, context), - AssignOp::And => x.bitand(&y.run(context)?, context), - AssignOp::Or => x.bitor(&y.run(context)?, context), - AssignOp::Xor => x.bitxor(&y.run(context)?, context), - AssignOp::Shl => x.shl(&y.run(context)?, context), - AssignOp::Shr => x.shr(&y.run(context)?, context), - AssignOp::Ushr => x.ushr(&y.run(context)?, context), - AssignOp::BoolAnd => { - if x.to_boolean() { - Ok(y.run(context)?) - } else { - Ok(x) - } - } - AssignOp::BoolOr => { - if x.to_boolean() { - Ok(x) - } else { - Ok(y.run(context)?) - } - } - AssignOp::Coalesce => { - if x.is_null_or_undefined() { - Ok(y.run(context)?) - } else { - Ok(x) - } - } - } - } -} - -impl Executable for BinOp { - fn run(&self, context: &mut Context) -> JsResult { - match self.op() { - op::BinOp::Num(op) => { - let x = self.lhs().run(context)?; - let y = self.rhs().run(context)?; - match op { - NumOp::Add => x.add(&y, context), - NumOp::Sub => x.sub(&y, context), - NumOp::Mul => x.mul(&y, context), - NumOp::Exp => x.pow(&y, context), - NumOp::Div => x.div(&y, context), - NumOp::Mod => x.rem(&y, context), - } - } - op::BinOp::Bit(op) => { - let x = self.lhs().run(context)?; - let y = self.rhs().run(context)?; - match op { - BitOp::And => x.bitand(&y, context), - BitOp::Or => x.bitor(&y, context), - BitOp::Xor => x.bitxor(&y, context), - BitOp::Shl => x.shl(&y, context), - BitOp::Shr => x.shr(&y, context), - BitOp::UShr => x.ushr(&y, context), - } - } - op::BinOp::Comp(op) => { - let x = self.lhs().run(context)?; - let y = self.rhs().run(context)?; - Ok(JsValue::new(match op { - CompOp::Equal => x.equals(&y, context)?, - CompOp::NotEqual => !x.equals(&y, context)?, - CompOp::StrictEqual => x.strict_equals(&y), - CompOp::StrictNotEqual => !x.strict_equals(&y), - CompOp::GreaterThan => x.gt(&y, context)?, - CompOp::GreaterThanOrEqual => x.ge(&y, context)?, - CompOp::LessThan => x.lt(&y, context)?, - CompOp::LessThanOrEqual => x.le(&y, context)?, - CompOp::In => { - if !y.is_object() { - return context.throw_type_error(format!( - "right-hand side of 'in' should be an object, got {}", - y.type_of() - )); - } - let key = x.to_property_key(context)?; - context.has_property(&y, &key)? - } - CompOp::InstanceOf => x.instance_of(&y, context)?, - })) - } - op::BinOp::Log(op) => Ok(match op { - LogOp::And => { - let left = self.lhs().run(context)?; - if !left.to_boolean() { - left - } else { - self.rhs().run(context)? - } - } - LogOp::Or => { - let left = self.lhs().run(context)?; - if left.to_boolean() { - left - } else { - self.rhs().run(context)? - } - } - LogOp::Coalesce => { - let left = self.lhs.run(context)?; - if left.is_null_or_undefined() { - self.rhs().run(context)? - } else { - left - } - } - }), - op::BinOp::Assign(op) => match self.lhs() { - Node::Identifier(ref name) => { - let v_a = context.get_binding_value(name.as_ref())?; - - let value = Self::run_assign(op, v_a, self.rhs(), context)?; - context.set_mutable_binding(name.as_ref(), value.clone(), context.strict())?; - Ok(value) - } - Node::GetConstField(ref get_const_field) => { - let v_r_a = get_const_field.obj().run(context)?; - let v_a = v_r_a.get_field(get_const_field.field(), context)?; - let value = Self::run_assign(op, v_a, self.rhs(), context)?; - v_r_a.set_field(get_const_field.field(), value.clone(), false, context)?; - Ok(value) - } - _ => Ok(JsValue::undefined()), - }, - op::BinOp::Comma => { - self.lhs().run(context)?; - Ok(self.rhs().run(context)?) - } - } - } } impl fmt::Display for BinOp { diff --git a/boa/src/syntax/ast/node/operator/unary_op/mod.rs b/boa/src/syntax/ast/node/operator/unary_op/mod.rs index d0622aeae6..69f04c5dd3 100644 --- a/boa/src/syntax/ast/node/operator/unary_op/mod.rs +++ b/boa/src/syntax/ast/node/operator/unary_op/mod.rs @@ -1,13 +1,9 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::{node::Node, op}, - Context, JsBigInt, JsResult, JsValue, }; use std::fmt; -use crate::builtins::Number; -use crate::value::Numeric; #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; @@ -49,84 +45,6 @@ impl UnaryOp { } } -impl Executable for UnaryOp { - fn run(&self, context: &mut Context) -> JsResult { - Ok(match self.op() { - op::UnaryOp::Minus => self.target().run(context)?.neg(context)?, - op::UnaryOp::Plus => JsValue::new(self.target().run(context)?.to_number(context)?), - op::UnaryOp::IncrementPost => { - let x = self.target().run(context)?; - let ret = x.clone(); - let result = match x.to_numeric(context)? { - Numeric::Number(n) => (n + 1.0).into(), - Numeric::BigInt(b) => (JsBigInt::add(&b, &JsBigInt::from(1))).into(), - }; - context.set_value(self.target(), result)?; - ret - } - op::UnaryOp::IncrementPre => { - let result = self.target().run(context)?.to_number(context)? + 1.0; - context.set_value(self.target(), result.into())? - } - op::UnaryOp::DecrementPost => { - let x = self.target().run(context)?; - let ret = x.clone(); - let result = x.to_number(context)? - 1.0; - context.set_value(self.target(), result.into())?; - ret - } - op::UnaryOp::DecrementPre => { - let result = self.target().run(context)?.to_number(context)? - 1.0; - context.set_value(self.target(), result.into())? - } - op::UnaryOp::Not => self.target().run(context)?.not(context)?.into(), - op::UnaryOp::Tilde => { - let expr = self.target().run(context)?; - let old_v = expr.to_numeric(context)?; - match old_v { - Numeric::Number(x) => JsValue::new(Number::not(x)), - Numeric::BigInt(x) => JsValue::new(JsBigInt::not(&x)), - } - } - op::UnaryOp::Void => { - self.target().run(context)?; - JsValue::undefined() - } - op::UnaryOp::Delete => match *self.target() { - Node::GetConstField(ref get_const_field) => { - let delete_status = get_const_field - .obj() - .run(context)? - .to_object(context)? - .__delete__(&get_const_field.field().into(), context)?; - - if !delete_status && context.strict() { - return context.throw_type_error("Cannot delete property"); - } else { - JsValue::new(delete_status) - } - } - Node::GetField(ref get_field) => { - let obj = get_field.obj().run(context)?; - let field = &get_field.field().run(context)?; - let delete_status = obj - .to_object(context)? - .__delete__(&field.to_property_key(context)?, context)?; - if !delete_status && context.strict() { - return context.throw_type_error("Cannot delete property"); - } else { - JsValue::new(delete_status) - } - } - // TODO: implement delete on references. - Node::Identifier(_) => JsValue::new(false), - _ => JsValue::new(true), - }, - op::UnaryOp::TypeOf => JsValue::new(self.target().run(context)?.type_of()), - }) - } -} - impl fmt::Display for UnaryOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}{}", self.op, self.target) diff --git a/boa/src/syntax/ast/node/return_smt/mod.rs b/boa/src/syntax/ast/node/return_smt/mod.rs index dc1dd95ad9..f886269953 100644 --- a/boa/src/syntax/ast/node/return_smt/mod.rs +++ b/boa/src/syntax/ast/node/return_smt/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -60,20 +58,6 @@ impl Return { } } -impl Executable for Return { - fn run(&self, context: &mut Context) -> JsResult { - let result = match self.expr() { - Some(v) => v.run(context), - None => Ok(JsValue::undefined()), - }; - // Set flag for return - context - .executor() - .set_current_state(InterpreterState::Return); - result - } -} - impl From for Node { fn from(return_smt: Return) -> Node { Node::Return(return_smt) diff --git a/boa/src/syntax/ast/node/spread/mod.rs b/boa/src/syntax/ast/node/spread/mod.rs index 2c3d26dac4..d8dd6952a1 100644 --- a/boa/src/syntax/ast/node/spread/mod.rs +++ b/boa/src/syntax/ast/node/spread/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -51,13 +49,6 @@ impl Spread { } } -impl Executable for Spread { - fn run(&self, context: &mut Context) -> JsResult { - // TODO: for now we can do nothing but return the value as-is - self.val().run(context) - } -} - impl fmt::Display for Spread { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "...{}", self.val()) diff --git a/boa/src/syntax/ast/node/statement_list/mod.rs b/boa/src/syntax/ast/node/statement_list/mod.rs index 5d9c138d73..9bc7b0f866 100644 --- a/boa/src/syntax/ast/node/statement_list/mod.rs +++ b/boa/src/syntax/ast/node/statement_list/mod.rs @@ -1,11 +1,8 @@ //! Statement list node. use crate::{ - context::StrictType, - exec::{Executable, InterpreterState}, gc::{empty_trace, Finalize, Trace}, syntax::ast::node::{Declaration, Node}, - BoaProfiler, Context, JsResult, JsValue, }; use std::{collections::HashSet, fmt, ops::Deref, rc::Rc}; @@ -129,61 +126,6 @@ impl StatementList { } } -impl Executable for StatementList { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("StatementList", "exec"); - - // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation - // The return value is uninitialized, which means it defaults to Value::Undefined - let mut obj = JsValue::default(); - context - .executor() - .set_current_state(InterpreterState::Executing); - - let strict_before = context.strict_type(); - - match context.strict_type() { - StrictType::Off if self.strict => context.set_strict(StrictType::Function), - StrictType::Function if !self.strict => context.set_strict_mode_off(), - _ => {} - } - - for (i, item) in self.items().iter().enumerate() { - let val = match item.run(context) { - Ok(val) => val, - Err(e) => { - context.set_strict(strict_before); - return Err(e); - } - }; - match context.executor().get_current_state() { - InterpreterState::Return => { - // Early return. - obj = val; - break; - } - InterpreterState::Break(_label) => { - // Early break. - break; - } - InterpreterState::Continue(_label) => { - break; - } - InterpreterState::Executing => { - // Continue execution - } - } - if i + 1 == self.items().len() { - obj = val; - } - } - - context.set_strict(strict_before); - - Ok(obj) - } -} - impl From for StatementList where T: Into>, diff --git a/boa/src/syntax/ast/node/switch/mod.rs b/boa/src/syntax/ast/node/switch/mod.rs index d7b9df61f0..cededb1efd 100644 --- a/boa/src/syntax/ast/node/switch/mod.rs +++ b/boa/src/syntax/ast/node/switch/mod.rs @@ -1,10 +1,8 @@ //! Switch node. //! use crate::{ - exec::{Executable, InterpreterState}, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -122,84 +120,6 @@ impl Switch { } } -impl Executable for Switch { - fn run(&self, context: &mut Context) -> JsResult { - let val = self.val().run(context)?; - let mut result = JsValue::null(); - let mut matched = false; - context - .executor() - .set_current_state(InterpreterState::Executing); - - // If a case block does not end with a break statement then subsequent cases will be run without - // checking their conditions until a break is encountered. - let mut fall_through: bool = false; - - for case in self.cases().iter() { - let cond = case.condition(); - let block = case.body(); - if fall_through || val.strict_equals(&cond.run(context)?) { - matched = true; - let result = block.run(context)?; - match context.executor().get_current_state() { - InterpreterState::Return => { - // Early return. - return Ok(result); - } - InterpreterState::Break(_label) => { - // TODO, break to a label. - // Break statement encountered so therefore end switch statement. - context - .executor() - .set_current_state(InterpreterState::Executing); - break; - } - InterpreterState::Continue(_label) => { - // TODO, continue to a label. - break; - } - InterpreterState::Executing => { - // Continuing execution / falling through to next case statement(s). - fall_through = true; - } - } - } - } - - if !matched { - if let Some(default) = self.default() { - context - .executor() - .set_current_state(InterpreterState::Executing); - for (i, item) in default.iter().enumerate() { - let val = item.run(context)?; - match context.executor().get_current_state() { - InterpreterState::Return => { - // Early return. - result = val; - break; - } - InterpreterState::Break(_label) => { - // TODO, break to a label. - - // Early break. - break; - } - _ => { - // Continue execution - } - } - if i == default.len() - 1 { - result = val; - } - } - } - } - - Ok(result) - } -} - impl fmt::Display for Switch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/template/mod.rs b/boa/src/syntax/ast/node/template/mod.rs index 4ab0bfbd63..6c336817ac 100644 --- a/boa/src/syntax/ast/node/template/mod.rs +++ b/boa/src/syntax/ast/node/template/mod.rs @@ -1,7 +1,6 @@ //! Template literal node. use super::Node; -use crate::{builtins::Array, exec::Executable, BoaProfiler, Context, JsResult, JsValue}; use gc::{Finalize, Trace}; #[cfg(feature = "deser")] @@ -30,33 +29,11 @@ impl TemplateLit { TemplateLit { elements } } - #[cfg(feature = "vm")] pub(crate) fn elements(&self) -> &Vec { &self.elements } } -impl Executable for TemplateLit { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("TemplateLiteral", "exec"); - let mut result = String::new(); - - for element in self.elements.iter() { - match element { - TemplateElement::String(s) => { - result.push_str(s); - } - TemplateElement::Expr(node) => { - let value = node.run(context)?; - let s = value.to_string(context)?; - result.push_str(&s); - } - } - } - Ok(result.into()) - } -} - impl fmt::Display for TemplateLit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "`")?; @@ -93,78 +70,23 @@ impl TaggedTemplate { } } - #[cfg(feature = "vm")] pub(crate) fn tag(&self) -> &Node { &self.tag } - #[cfg(feature = "vm")] pub(crate) fn raws(&self) -> &Vec> { &self.raws } - #[cfg(feature = "vm")] pub(crate) fn cookeds(&self) -> &Vec>> { &self.cookeds } - #[cfg(feature = "vm")] pub(crate) fn exprs(&self) -> &Vec { &self.exprs } } -impl Executable for TaggedTemplate { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("TaggedTemplate", "exec"); - - let template_object = Array::new_array(context); - let raw_array = Array::new_array(context); - - for (i, raw) in self.raws.iter().enumerate() { - raw_array.set_field(i, JsValue::new(raw.as_ref()), false, context)?; - } - - for (i, cooked) in self.cookeds.iter().enumerate() { - if let Some(cooked) = cooked { - template_object.set_field(i, JsValue::new(cooked.as_ref()), false, context)?; - } else { - template_object.set_field(i, JsValue::undefined(), false, context)?; - } - } - template_object.set_field("raw", raw_array, false, context)?; - - let (this, func) = match *self.tag { - Node::GetConstField(ref get_const_field) => { - let mut obj = get_const_field.obj().run(context)?; - if !obj.is_object() { - obj = JsValue::Object(obj.to_object(context)?); - } - ( - obj.clone(), - obj.get_field(get_const_field.field(), context)?, - ) - } - Node::GetField(ref get_field) => { - let obj = get_field.obj().run(context)?; - let field = get_field.field().run(context)?; - ( - obj.clone(), - obj.get_field(field.to_property_key(context)?, context)?, - ) - } - _ => (context.global_object().into(), self.tag.run(context)?), - }; - - let mut args = vec![template_object]; - for expr in self.exprs.iter() { - args.push(expr.run(context)?); - } - - context.call(&func, &this, &args) - } -} - impl fmt::Display for TaggedTemplate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}`", self.tag)?; diff --git a/boa/src/syntax/ast/node/throw/mod.rs b/boa/src/syntax/ast/node/throw/mod.rs index d28a99b3d3..39facb9082 100644 --- a/boa/src/syntax/ast/node/throw/mod.rs +++ b/boa/src/syntax/ast/node/throw/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -48,13 +46,6 @@ impl Throw { } } -impl Executable for Throw { - #[inline] - fn run(&self, context: &mut Context) -> JsResult { - Err(self.expr().run(context)?) - } -} - impl fmt::Display for Throw { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "throw {}", self.expr) diff --git a/boa/src/syntax/ast/node/try_node/mod.rs b/boa/src/syntax/ast/node/try_node/mod.rs index c460c10a63..ec304fa545 100644 --- a/boa/src/syntax/ast/node/try_node/mod.rs +++ b/boa/src/syntax/ast/node/try_node/mod.rs @@ -1,12 +1,6 @@ use crate::{ - environment::{ - declarative_environment_record::DeclarativeEnvironmentRecord, - lexical_environment::VariableScope, - }, - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{Block, Declaration, Node}, - BoaProfiler, Context, JsResult, JsValue, }; use std::fmt; @@ -94,63 +88,6 @@ impl Try { } } -impl Executable for Try { - fn run(&self, context: &mut Context) -> JsResult { - let _timer = BoaProfiler::global().start_event("Try", "exec"); - let res = self.block().run(context).map_or_else( - |err| { - if let Some(catch) = self.catch() { - let env = context.get_current_environment(); - context.push_environment(DeclarativeEnvironmentRecord::new(Some(env))); - - if let Some(param) = catch.parameter() { - match param { - Declaration::Identifier { ident, init } => { - debug_assert!(init.is_none()); - - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), err)?; - } - Declaration::Pattern(pattern) => { - debug_assert!(pattern.init().is_none()); - - for (ident, value) in pattern.run(Some(err), context)? { - context.create_mutable_binding( - ident.as_ref(), - false, - VariableScope::Block, - )?; - context.initialize_binding(ident.as_ref(), value)?; - } - } - } - } - - let res = catch.block().run(context); - - // pop the block env - let _ = context.pop_environment(); - - res - } else { - Err(err) - } - }, - Ok, - ); - - if let Some(finally) = self.finally() { - finally.run(context)?; - } - - res - } -} - impl fmt::Display for Try { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.display(f, 0) diff --git a/boa/src/syntax/ast/node/yield/mod.rs b/boa/src/syntax/ast/node/yield/mod.rs index ec485a6b66..1c134da82b 100644 --- a/boa/src/syntax/ast/node/yield/mod.rs +++ b/boa/src/syntax/ast/node/yield/mod.rs @@ -1,8 +1,6 @@ use crate::{ - exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::Node, - Context, JsResult, JsValue, }; use std::fmt; @@ -46,13 +44,6 @@ impl Yield { } } -impl Executable for Yield { - fn run(&self, _context: &mut Context) -> JsResult { - // TODO: Implement Generator execution - Ok(JsValue::undefined()) - } -} - impl From for Node { fn from(r#yield: Yield) -> Node { Node::Yield(r#yield) diff --git a/boa/src/exec/tests.rs b/boa/src/tests.rs similarity index 98% rename from boa/src/exec/tests.rs rename to boa/src/tests.rs index e8ebf0bef6..ef4acf8b0f 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/tests.rs @@ -772,13 +772,10 @@ mod in_operator { new a(); "#; - #[cfg(not(feature = "vm"))] - let error = "Uncaught \"TypeError\": \"a is not a constructor\""; - - #[cfg(feature = "vm")] - let error = "Uncaught \"TypeError\": \"not a constructor\""; - - check_output(&[TestAction::TestEq(scenario, error)]); + check_output(&[TestAction::TestEq( + scenario, + "Uncaught \"TypeError\": \"not a constructor\"", + )]); } #[test] @@ -1268,17 +1265,11 @@ fn not_a_function() { } "#; - #[cfg(not(feature = "vm"))] - let error = "\"TypeError: Value is not callable\""; - - #[cfg(feature = "vm")] - let error = "\"TypeError: not a callable function\""; - check_output(&[ TestAction::Execute(init), - TestAction::TestEq(scenario1, error), - TestAction::TestEq(scenario2, error), - TestAction::TestEq(scenario3, error), + TestAction::TestEq(scenario1, "\"TypeError: not a callable function\""), + TestAction::TestEq(scenario2, "\"TypeError: not a callable function\""), + TestAction::TestEq(scenario3, "\"TypeError: not a callable function\""), ]); } diff --git a/boa/src/vm/code_block.rs b/boa/src/vm/code_block.rs index 9cca9153c6..d8b5efa879 100644 --- a/boa/src/vm/code_block.rs +++ b/boa/src/vm/code_block.rs @@ -23,7 +23,15 @@ use gc::Gc; use std::{convert::TryInto, fmt::Write, mem::size_of}; /// This represents whether a value can be read from [`CodeBlock`] code. -pub unsafe trait Readable {} +/// +/// # Safety +/// +/// This trait is safe to implement as long as the type doesn't implement `Drop`. +/// At some point, if [negative impls][negative_impls] are stabilized, we might be able to remove +/// the unsafe bound. +/// +/// [negative_impls]: https://doc.rust-lang.org/beta/unstable-book/language-features/negative-impls.html +pub(crate) unsafe trait Readable {} unsafe impl Readable for u8 {} unsafe impl Readable for i8 {} @@ -40,7 +48,7 @@ unsafe impl Readable for f64 {} /// /// A CodeBlock is generated for each function compiled by the [ByteCompiler](crate::bytecompiler::ByteCompiler). /// It stores the bytecode and the other attributes of the function. -#[derive(Debug, Trace, Finalize)] +#[derive(Clone, Debug, Trace, Finalize)] pub struct CodeBlock { /// Name of this function pub(crate) name: JsString, @@ -99,7 +107,10 @@ impl CodeBlock { /// # Safety /// /// Does not check if read happens out-of-bounds. - pub unsafe fn read_unchecked(&self, offset: usize) -> T { + pub(crate) unsafe fn read_unchecked(&self, offset: usize) -> T + where + T: Readable, + { // This has to be an unaligned read because we can't guarantee that // the types are aligned. self.code.as_ptr().add(offset).cast::().read_unaligned() @@ -107,7 +118,10 @@ impl CodeBlock { /// Read type T from code. #[track_caller] - pub fn read(&self, offset: usize) -> T { + pub(crate) fn read(&self, offset: usize) -> T + where + T: Readable, + { assert!(offset + size_of::() - 1 < self.code.len()); // Safety: We checked that it is not an out-of-bounds read, @@ -478,7 +492,6 @@ impl JsObject { code: code.clone(), environment: environment.clone(), }, - Function::Ordinary { .. } => unreachable!(), } }; @@ -644,7 +657,6 @@ impl JsObject { code: code.clone(), environment: environment.clone(), }, - Function::Ordinary { .. } => unreachable!(), } }; diff --git a/boa/src/vm/mod.rs b/boa/src/vm/mod.rs index c7b52ff195..e6c1930f39 100644 --- a/boa/src/vm/mod.rs +++ b/boa/src/vm/mod.rs @@ -92,8 +92,6 @@ impl Vm { impl Context { fn execute_instruction(&mut self) -> JsResult { - let _timer = BoaProfiler::global().start_event("execute_instruction", "vm"); - macro_rules! bin_op { ($op:ident) => {{ let rhs = self.vm.pop(); @@ -103,10 +101,17 @@ impl Context { }}; } - let opcode = self.vm.frame().code.code[self.vm.frame().pc] - .try_into() - .unwrap(); - self.vm.frame_mut().pc += 1; + let opcode: Opcode = { + let _timer = BoaProfiler::global().start_event("Opcode retrieval", "vm"); + let opcode = self.vm.frame().code.code[self.vm.frame().pc] + .try_into() + .expect("could not convert code at PC to opcode"); + self.vm.frame_mut().pc += 1; + opcode + }; + + let _timer = + BoaProfiler::global().start_event(&format!("INST - {}", &opcode.as_str()), "vm"); match opcode { Opcode::Nop => {} @@ -721,7 +726,6 @@ impl Context { self.pop_environment(); } self.vm.frame_mut().pop_env_on_return = 0; - let _ = self.vm.pop_frame(); return Ok(true); } FinallyReturn::Err => { @@ -893,7 +897,6 @@ impl Context { self.pop_environment(); } self.vm.frame_mut().pop_env_on_return = 0; - let _ = self.vm.pop_frame(); return Ok(true); } } @@ -1177,6 +1180,7 @@ impl Context { Ok(should_exit) => { if should_exit { let result = self.vm.pop(); + self.vm.pop_frame(); return Ok(result); } } @@ -1230,6 +1234,7 @@ impl Context { println!("\n"); } + self.vm.pop_frame(); if self.vm.stack.is_empty() { return Ok(JsValue::undefined()); } diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index d735e7bbfb..9783e6b344 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -22,9 +22,6 @@ colored = "2.0.0" regex = "1.5.4" lazy_static = "1.4.0" -[features] -vm = ["Boa/vm"] - [target.x86_64-unknown-linux-gnu.dependencies] jemallocator = "0.3.2" diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index c02facd303..7fab28e70b 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -67,7 +67,6 @@ struct Opt { dump_ast: Option>, /// Dump the AST to stdout with the given format. - #[cfg(feature = "vm")] #[structopt(long = "trace", short = "t")] trace: bool, @@ -149,7 +148,6 @@ pub fn main() -> Result<(), std::io::Error> { let mut context = Context::new(); // Trace Output - #[cfg(feature = "vm")] context.set_trace(args.trace); for file in &args.files { diff --git a/boa_tester/Cargo.toml b/boa_tester/Cargo.toml index 161012fbe3..948132ac48 100644 --- a/boa_tester/Cargo.toml +++ b/boa_tester/Cargo.toml @@ -11,9 +11,6 @@ exclude = ["../.vscode/*", "../Dockerfile", "../Makefile", "../.editorConfig"] edition = "2021" rust-version = "1.56" -[features] -vm = ["Boa/vm"] - [dependencies] Boa = { path = "../boa" } structopt = "0.3.25" diff --git a/boa_tester/src/exec/js262.rs b/boa_tester/src/exec/js262.rs index 40a3a06845..cd61551de3 100644 --- a/boa_tester/src/exec/js262.rs +++ b/boa_tester/src/exec/js262.rs @@ -5,9 +5,6 @@ use boa::{ Context, JsResult, JsValue, }; -#[cfg(not(feature = "vm"))] -use boa::exec::Executable; - /// Initializes the object in the context. pub(super) fn init(context: &mut Context) -> JsObject { let global_obj = context.global_object(); @@ -90,11 +87,8 @@ fn eval_script(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsRe match boa::parse(source_text.as_str(), false) { // TODO: check strict Err(e) => context.throw_type_error(format!("Uncaught Syntax Error: {}", e)), - #[cfg(not(feature = "vm"))] - Ok(statement_list) => statement_list.run(context), // Calling eval here parses the code a second time. // TODO: We can fix this after we have have defined the public api for the vm executer. - #[cfg(feature = "vm")] Ok(_) => context.eval(source_text.as_str()), } } else { diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 64bd4cf04d..f4689fa2fe 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -152,9 +152,7 @@ impl Test { match self.set_up_env(harness, strict) { Ok(mut context) => { - if strict { - context.set_strict_mode_global(); - } + context.set_strict_mode(strict); let res = context.eval(&self.content.as_ref()); let passed = res.is_ok(); @@ -201,9 +199,7 @@ impl Test { } else { match self.set_up_env(harness, strict) { Ok(mut context) => { - if strict { - context.set_strict_mode_global(); - } + context.set_strict_mode(strict); match context.eval(&self.content.as_ref()) { Ok(res) => (false, format!("{}", res.display())), Err(e) => { diff --git a/boa_tester/src/results.rs b/boa_tester/src/results.rs index 9ed916d259..f906ae34a1 100644 --- a/boa_tester/src/results.rs +++ b/boa_tester/src/results.rs @@ -218,10 +218,7 @@ pub(crate) fn compare_results(base: &Path, new: &Path, markdown: bool) { ) } - #[cfg(feature = "vm")] println!("#### VM implementation"); - #[cfg(not(feature = "vm"))] - println!("#### Non-VM implementation"); println!("| Test result | main count | PR count | difference |"); println!("| :---------: | :----------: | :------: | :--------: |"); diff --git a/boa_wasm/src/lib.rs b/boa_wasm/src/lib.rs index 0a74c6eb8d..92635e18c3 100644 --- a/boa_wasm/src/lib.rs +++ b/boa_wasm/src/lib.rs @@ -1,22 +1,11 @@ -use boa::{exec::Executable, parse, Context}; +use boa::Context; use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn evaluate(src: &str) -> Result { // Setup executor - let mut context = Context::new(); - - let expr = match parse(src, false) { - Ok(res) => res, - Err(e) => { - return Err(format!( - "Uncaught {}", - context.construct_syntax_error(e.to_string()).display() - ) - .into()); - } - }; - expr.run(&mut context) + Context::new() + .eval(src) .map_err(|e| JsValue::from(format!("Uncaught {}", e.display()))) .map(|v| v.display().to_string()) }