From 3c86d3656645c0988bbb791a835780f702c49b7a Mon Sep 17 00:00:00 2001 From: Mike Voronov Date: Tue, 24 Aug 2021 16:14:15 +0300 Subject: [PATCH] Introduce CRDT data (#74) --- .github/workflows/publish_crates.yml | 4 +- .github/workflows/publish_interpreter.yml | 6 +- .github/workflows/publish_interpreter_dev.yml | 6 +- CHANGELOG.md | 11 + Cargo.lock | 42 +- Cargo.toml | 1 + air-interpreter/Cargo.toml | 4 +- air/Cargo.toml | 5 +- air/benches/call_benchmark.rs | 2 +- air/benches/chat_benchmark.rs | 22 +- air/benches/create_service_benchmark.rs | 6 +- air/src/air/outcome.rs | 91 - air/src/contexts/execution_trace_context.rs | 65 - .../execution_trace_context/executed_state.rs | 65 - air/src/contexts/mod.rs | 35 - air/src/execution/air/call/utils.rs | 166 -- air/src/execution/air/fold.rs | 155 - air/src/execution/air/fold/utils.rs | 217 -- air/src/execution/air/par.rs | 173 -- air/src/execution/utils/resolve.rs | 157 -- air/src/execution_step/air/ap.rs | 92 + .../air/ap/apply_to_arguments.rs | 105 + air/src/execution_step/air/ap/utils.rs | 89 + .../{execution => execution_step}/air/call.rs | 24 +- .../air/call/call_result_setter.rs | 171 ++ .../air/call/resolved_call.rs | 126 +- .../air/call/triplet.rs | 26 +- air/src/execution_step/air/call/utils.rs | 67 + .../air/compare_matchable/comparator.rs} | 58 +- .../air/compare_matchable/mod.rs | 4 +- air/src/execution_step/air/fold/fold_state.rs | 51 + air/src/execution_step/air/fold/mod.rs | 32 + air/src/execution_step/air/fold/utils.rs | 210 ++ .../air/fold/variable_handler.rs | 89 + air/src/execution_step/air/fold_scalar.rs | 62 + air/src/execution_step/air/fold_stream.rs | 68 + .../air/match_.rs | 6 +- .../air/mismatch.rs | 6 +- .../{execution => execution_step}/air/mod.rs | 83 +- air/src/execution_step/air/next.rs | 112 + .../{execution => execution_step}/air/null.rs | 4 +- air/src/execution_step/air/par.rs | 111 + .../air/par/completeness_updater.rs | 46 + .../{execution => execution_step}/air/seq.rs | 4 +- .../{execution => execution_step}/air/xor.rs | 17 +- .../boxed_value/iterable.rs | 39 +- .../boxed_value/iterable/json_path_result.rs | 12 +- .../boxed_value/iterable/resolved_call.rs | 29 +- .../iterable/vec_json_path_result.rs | 13 +- .../boxed_value/iterable/vec_resolved_call.rs | 19 +- .../boxed_value/jvaluable.rs | 13 +- .../cell_vec_resolved_call_result.rs | 31 +- .../boxed_value/jvaluable/empty.rs | 9 +- .../boxed_value/jvaluable/iterable_item.rs | 39 +- .../jvaluable/resolved_call_result.rs | 28 +- .../boxed_value/jvaluable/stream.rs | 110 + air/src/execution_step/boxed_value/mod.rs | 33 + .../boxed_value/scalar.rs} | 50 +- air/src/execution_step/boxed_value/stream.rs | 205 ++ .../execution_step/boxed_value/variable.rs | 49 + .../{execution => execution_step}/errors.rs | 101 +- air/src/execution_step/errors/catchable.rs | 24 + .../execution_context/context.rs} | 40 +- .../execution_context/error_descriptor.rs | 28 +- .../execution_context/instructions_tracker.rs | 72 + .../execution_step/execution_context/mod.rs | 25 + .../{execution => execution_step}/joinable.rs | 4 +- air/src/{execution => execution_step}/mod.rs | 19 +- .../trace_handler/data_keeper/errors.rs | 44 + .../trace_handler/data_keeper/keeper.rs | 72 + .../trace_handler/data_keeper/merge_ctx.rs | 83 + .../trace_handler/data_keeper/mod.rs | 31 + .../trace_handler/data_keeper/trace_slider.rs | 109 + .../execution_step/trace_handler/errors.rs | 35 + .../execution_step/trace_handler/handler.rs | 170 ++ .../trace_handler/merger/ap_merger.rs | 66 + .../trace_handler/merger/call_merger.rs | 120 + .../call_merger/call_result_constructor.rs | 46 + .../trace_handler/merger/call_merger/utils.rs | 108 + .../trace_handler/merger/errors.rs | 146 + .../trace_handler/merger/fold_merger.rs | 79 + .../trace_handler/merger/fold_merger/utils.rs | 88 + .../trace_handler/merger/mod.rs | 60 + .../trace_handler/merger/par_merger.rs | 62 + air/src/execution_step/trace_handler/mod.rs | 39 + .../trace_handler/state_automata/errors.rs | 63 + .../trace_handler/state_automata/fold_fsm.rs | 164 ++ .../state_automata/fold_fsm/lore_applier.rs | 72 + .../state_automata/fold_fsm/lore_ctor.rs | 146 + .../fold_fsm/lore_ctor_queue.rs | 103 + .../state_automata/fold_fsm/state_handler.rs | 68 + .../trace_handler/state_automata/fsm_queue.rs | 58 + .../trace_handler/state_automata/mod.rs | 45 + .../trace_handler/state_automata/par_fsm.rs | 105 + .../state_automata/par_fsm/par_builder.rs | 56 + .../state_automata/par_fsm/state_handler.rs | 121 + .../state_handler/new_states_calculation.rs | 59 + .../par_fsm/state_handler/utils.rs | 63 + .../state_automata/state_inserter.rs | 40 + .../trace_handler/state_automata/utils.rs | 92 + .../utils/mod.rs | 6 +- air/src/execution_step/utils/resolve.rs | 165 ++ air/src/lib.rs | 18 +- air/src/log_targets.rs | 28 +- air/src/preparation/data_merging.rs | 159 -- air/src/preparation/errors.rs | 124 - air/src/preparation/mod.rs | 34 - air/src/preparation/tests.rs | 128 - air/src/preparation_step/errors.rs | 50 + .../boxed_value => preparation_step}/mod.rs | 12 +- .../preparation.rs | 73 +- air/src/{air.rs => runner.rs} | 27 +- air/src/runner/outcome.rs | 118 + air/tests/test_module/instructions/ap.rs | 194 ++ air/tests/test_module/instructions/call.rs | 61 +- air/tests/test_module/instructions/fold.rs | 158 +- air/tests/test_module/instructions/match_.rs | 53 +- .../test_module/instructions/mismatch.rs | 41 +- air/tests/test_module/instructions/mod.rs | 1 + air/tests/test_module/instructions/par.rs | 21 +- air/tests/test_module/instructions/seq.rs | 26 +- air/tests/test_module/instructions/xor.rs | 64 +- .../test_module/integration/air_basic.rs | 42 +- .../test_module/integration/ap_with_fold.rs | 73 + .../integration/call_evidence_basic.rs | 427 +++ .../test_module/integration/dashboard.rs | 104 +- .../test_module/integration/data_merge.rs | 265 +- .../test_module/integration/empty_array.rs | 42 + .../test_module/integration/flattening.rs | 29 +- air/tests/test_module/integration/join.rs | 194 +- .../test_module/integration/join_behaviour.rs | 63 +- .../test_module/integration/json_path.rs | 7 +- .../test_module/integration/last_error.rs | 20 +- air/tests/test_module/integration/mod.rs | 5 + .../integration/network_explore.rs | 116 +- .../integration/scripts/fold_early_exit.clj | 74 + .../scripts/fold_par_early_exit.clj | 74 + .../integration/scripts/inner_folds_v1.clj | 53 +- .../integration/scripts/network_explore.clj | 10 +- .../integration/scripts/par_early_exit.clj | 31 + .../scripts/stream_fold_merging_v0.clj | 33 + .../scripts/stream_fold_merging_v1.clj | 33 + .../scripts/stream_fold_merging_v2.clj | 33 + .../integration/security_tetraplets.rs | 309 ++ .../auth_module/Cargo.lock | 56 +- .../log_storage/Cargo.lock | 56 +- air/tests/test_module/integration/streams.rs | 430 ++- .../integration/streams_early_exit.rs | 306 ++ avm/server/Cargo.toml | 2 +- avm/server/src/avm.rs | 12 +- avm/server/src/config.rs | 2 +- crates/air-parser/Cargo.toml | 10 +- crates/air-parser/src/parser/air.lalrpop | 113 +- crates/air-parser/src/parser/air.rs | 2502 +++++++++++------ crates/air-parser/src/parser/air_parser.rs | 35 +- crates/air-parser/src/parser/ast.rs | 76 +- crates/air-parser/src/parser/ast/impls.rs | 33 + crates/air-parser/src/parser/ast/traits.rs | 99 +- crates/air-parser/src/parser/errors.rs | 7 +- .../air-parser/src/parser/lexer/air_lexer.rs | 35 +- .../src/parser/lexer/call_variable_parser.rs | 19 +- crates/air-parser/src/parser/lexer/mod.rs | 2 +- crates/air-parser/src/parser/lexer/tests.rs | 4 +- crates/air-parser/src/parser/lexer/token.rs | 6 +- .../src/parser/lexer/token/traits.rs | 4 +- crates/air-parser/src/parser/tests.rs | 241 +- crates/air-parser/src/parser/validator.rs | 64 +- crates/interpreter-data/Cargo.toml | 20 + crates/interpreter-data/src/executed_state.rs | 125 + .../src/executed_state/impls.rs | 133 + .../src/executed_state/se_de.rs | 68 + .../interpreter-data/src/interpreter_data.rs | 77 + crates/interpreter-data/src/lib.rs | 28 + crates/interpreter-interface/src/lib.rs | 2 +- crates/polyplets/src/tetraplet.rs | 12 +- crates/polyplets/src/triplet.rs | 2 +- crates/test-utils/Cargo.toml | 2 +- crates/test-utils/src/call_services.rs | 153 + crates/test-utils/src/executed_state.rs | 93 +- crates/test-utils/src/lib.rs | 151 +- 180 files changed, 11182 insertions(+), 3897 deletions(-) delete mode 100644 air/src/air/outcome.rs delete mode 100644 air/src/contexts/execution_trace_context.rs delete mode 100644 air/src/contexts/execution_trace_context/executed_state.rs delete mode 100644 air/src/contexts/mod.rs delete mode 100644 air/src/execution/air/call/utils.rs delete mode 100644 air/src/execution/air/fold.rs delete mode 100644 air/src/execution/air/fold/utils.rs delete mode 100644 air/src/execution/air/par.rs delete mode 100644 air/src/execution/utils/resolve.rs create mode 100644 air/src/execution_step/air/ap.rs create mode 100644 air/src/execution_step/air/ap/apply_to_arguments.rs create mode 100644 air/src/execution_step/air/ap/utils.rs rename air/src/{execution => execution_step}/air/call.rs (80%) create mode 100644 air/src/execution_step/air/call/call_result_setter.rs rename air/src/{execution => execution_step}/air/call/resolved_call.rs (64%) rename air/src/{execution => execution_step}/air/call/triplet.rs (84%) create mode 100644 air/src/execution_step/air/call/utils.rs rename air/src/{execution/air/compare_matchable/compare_matchable.rs => execution_step/air/compare_matchable/comparator.rs} (74%) rename air/src/{execution => execution_step}/air/compare_matchable/mod.rs (88%) create mode 100644 air/src/execution_step/air/fold/fold_state.rs create mode 100644 air/src/execution_step/air/fold/mod.rs create mode 100644 air/src/execution_step/air/fold/utils.rs create mode 100644 air/src/execution_step/air/fold/variable_handler.rs create mode 100644 air/src/execution_step/air/fold_scalar.rs create mode 100644 air/src/execution_step/air/fold_stream.rs rename air/src/{execution => execution_step}/air/match_.rs (91%) rename air/src/{execution => execution_step}/air/mismatch.rs (91%) rename air/src/{execution => execution_step}/air/mod.rs (69%) create mode 100644 air/src/execution_step/air/next.rs rename air/src/{execution => execution_step}/air/null.rs (91%) create mode 100644 air/src/execution_step/air/par.rs create mode 100644 air/src/execution_step/air/par/completeness_updater.rs rename air/src/{execution => execution_step}/air/seq.rs (93%) rename air/src/{execution => execution_step}/air/xor.rs (78%) rename air/src/{execution => execution_step}/boxed_value/iterable.rs (68%) rename air/src/{execution => execution_step}/boxed_value/iterable/json_path_result.rs (86%) rename air/src/{execution => execution_step}/boxed_value/iterable/resolved_call.rs (75%) rename air/src/{execution => execution_step}/boxed_value/iterable/vec_json_path_result.rs (82%) rename air/src/{execution => execution_step}/boxed_value/iterable/vec_resolved_call.rs (81%) rename air/src/{execution => execution_step}/boxed_value/jvaluable.rs (82%) rename air/src/{execution => execution_step}/boxed_value/jvaluable/cell_vec_resolved_call_result.rs (67%) rename air/src/{execution => execution_step}/boxed_value/jvaluable/empty.rs (82%) rename air/src/{execution => execution_step}/boxed_value/jvaluable/iterable_item.rs (64%) rename air/src/{execution => execution_step}/boxed_value/jvaluable/resolved_call_result.rs (73%) create mode 100644 air/src/execution_step/boxed_value/jvaluable/stream.rs create mode 100644 air/src/execution_step/boxed_value/mod.rs rename air/src/{contexts/execution_context/avalue.rs => execution_step/boxed_value/scalar.rs} (50%) create mode 100644 air/src/execution_step/boxed_value/stream.rs create mode 100644 air/src/execution_step/boxed_value/variable.rs rename air/src/{execution => execution_step}/errors.rs (69%) create mode 100644 air/src/execution_step/errors/catchable.rs rename air/src/{contexts/execution_context.rs => execution_step/execution_context/context.rs} (75%) rename air/src/{contexts => execution_step}/execution_context/error_descriptor.rs (78%) create mode 100644 air/src/execution_step/execution_context/instructions_tracker.rs create mode 100644 air/src/execution_step/execution_context/mod.rs rename air/src/{execution => execution_step}/joinable.rs (92%) rename air/src/{execution => execution_step}/mod.rs (60%) create mode 100644 air/src/execution_step/trace_handler/data_keeper/errors.rs create mode 100644 air/src/execution_step/trace_handler/data_keeper/keeper.rs create mode 100644 air/src/execution_step/trace_handler/data_keeper/merge_ctx.rs create mode 100644 air/src/execution_step/trace_handler/data_keeper/mod.rs create mode 100644 air/src/execution_step/trace_handler/data_keeper/trace_slider.rs create mode 100644 air/src/execution_step/trace_handler/errors.rs create mode 100644 air/src/execution_step/trace_handler/handler.rs create mode 100644 air/src/execution_step/trace_handler/merger/ap_merger.rs create mode 100644 air/src/execution_step/trace_handler/merger/call_merger.rs create mode 100644 air/src/execution_step/trace_handler/merger/call_merger/call_result_constructor.rs create mode 100644 air/src/execution_step/trace_handler/merger/call_merger/utils.rs create mode 100644 air/src/execution_step/trace_handler/merger/errors.rs create mode 100644 air/src/execution_step/trace_handler/merger/fold_merger.rs create mode 100644 air/src/execution_step/trace_handler/merger/fold_merger/utils.rs create mode 100644 air/src/execution_step/trace_handler/merger/mod.rs create mode 100644 air/src/execution_step/trace_handler/merger/par_merger.rs create mode 100644 air/src/execution_step/trace_handler/mod.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/errors.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/fold_fsm.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_applier.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor_queue.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/fold_fsm/state_handler.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/fsm_queue.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/mod.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/par_fsm.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/par_fsm/par_builder.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/new_states_calculation.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/utils.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/state_inserter.rs create mode 100644 air/src/execution_step/trace_handler/state_automata/utils.rs rename air/src/{execution => execution_step}/utils/mod.rs (82%) create mode 100644 air/src/execution_step/utils/resolve.rs delete mode 100644 air/src/preparation/data_merging.rs delete mode 100644 air/src/preparation/errors.rs delete mode 100644 air/src/preparation/mod.rs delete mode 100644 air/src/preparation/tests.rs create mode 100644 air/src/preparation_step/errors.rs rename air/src/{execution/boxed_value => preparation_step}/mod.rs (78%) rename air/src/{preparation => preparation_step}/preparation.rs (52%) rename air/src/{air.rs => runner.rs} (59%) create mode 100644 air/src/runner/outcome.rs create mode 100644 air/tests/test_module/instructions/ap.rs create mode 100644 air/tests/test_module/integration/ap_with_fold.rs create mode 100644 air/tests/test_module/integration/call_evidence_basic.rs create mode 100644 air/tests/test_module/integration/empty_array.rs create mode 100644 air/tests/test_module/integration/scripts/fold_early_exit.clj create mode 100644 air/tests/test_module/integration/scripts/fold_par_early_exit.clj create mode 100644 air/tests/test_module/integration/scripts/par_early_exit.clj create mode 100644 air/tests/test_module/integration/scripts/stream_fold_merging_v0.clj create mode 100644 air/tests/test_module/integration/scripts/stream_fold_merging_v1.clj create mode 100644 air/tests/test_module/integration/scripts/stream_fold_merging_v2.clj create mode 100644 air/tests/test_module/integration/security_tetraplets.rs create mode 100644 air/tests/test_module/integration/streams_early_exit.rs create mode 100644 crates/air-parser/src/parser/ast/impls.rs create mode 100644 crates/interpreter-data/Cargo.toml create mode 100644 crates/interpreter-data/src/executed_state.rs create mode 100644 crates/interpreter-data/src/executed_state/impls.rs create mode 100644 crates/interpreter-data/src/executed_state/se_de.rs create mode 100644 crates/interpreter-data/src/interpreter_data.rs create mode 100644 crates/interpreter-data/src/lib.rs create mode 100644 crates/test-utils/src/call_services.rs diff --git a/.github/workflows/publish_crates.yml b/.github/workflows/publish_crates.yml index 6847e125..226b20f1 100644 --- a/.github/workflows/publish_crates.yml +++ b/.github/workflows/publish_crates.yml @@ -29,12 +29,12 @@ jobs: - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 profile: minimal override: true - uses: actions-rs/cargo@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 command: update args: --aggressive diff --git a/.github/workflows/publish_interpreter.yml b/.github/workflows/publish_interpreter.yml index 410bd7b9..97814d77 100644 --- a/.github/workflows/publish_interpreter.yml +++ b/.github/workflows/publish_interpreter.yml @@ -32,19 +32,19 @@ jobs: - name: Install Rust toolchain with wasm32-unknown-unknown uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 target: wasm32-unknown-unknown profile: minimal override: true - name: Install wasm32-wasi uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 target: wasm32-wasi profile: minimal - uses: actions-rs/cargo@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 command: update args: --aggressive diff --git a/.github/workflows/publish_interpreter_dev.yml b/.github/workflows/publish_interpreter_dev.yml index 346b0bbe..32a58bbd 100644 --- a/.github/workflows/publish_interpreter_dev.yml +++ b/.github/workflows/publish_interpreter_dev.yml @@ -41,19 +41,19 @@ jobs: - name: Install Rust toolchain with wasm32-unknown-unknown uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 target: wasm32-unknown-unknown profile: minimal override: true - name: Install wasm32-wasi uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 target: wasm32-wasi profile: minimal - uses: actions-rs/cargo@v1 with: - toolchain: nightly + toolchain: nightly-2021-05-16 command: update args: --aggressive diff --git a/CHANGELOG.md b/CHANGELOG.md index e238fcd6..e5865ab5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## Version 0.14.0 (2021-08-24) + +[PR 74](https://github.com/fluencelabs/aquavm/pull/74): +- introduced a new CRDT-like data format for streams: + - call results contains different values for streams and scalars + - introduced a new state for fold whose iterables are streams +- merging scheme was rewritten, and became lazy +- refactor the internal value mechanism +- introduced a new instruction `(ap` responsible for applying json path to scalars and save results as a new scalar or add it to a stream. In the second case it'll produce a new state in a data. +- introduced a new string literal `[]` represents empty array + ## Version 0.10.8 (2021-07-06) - improve the error message of the invalid executed state error ([PR 121](https://github.com/fluencelabs/aquavm/pull/121)) diff --git a/Cargo.lock b/Cargo.lock index 2d10a552..5d2e0d50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,8 +13,9 @@ dependencies = [ [[package]] name = "air" -version = "0.10.0" +version = "0.14.0" dependencies = [ + "air-interpreter-data", "air-interpreter-interface", "air-parser", "air-test-utils", @@ -38,7 +39,7 @@ dependencies = [ [[package]] name = "air-interpreter" -version = "0.10.0" +version = "0.14.0" dependencies = [ "air", "log", @@ -48,6 +49,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "air-interpreter-data" +version = "0.1.0" +dependencies = [ + "once_cell", + "semver 1.0.4", + "serde", + "serde_json", +] + [[package]] name = "air-interpreter-interface" version = "0.6.0" @@ -319,18 +330,18 @@ checksum = "475bd7aa7680b4ed8f6bb59745e882bcbaeb39326532bb79ffb1716480d9a274" [[package]] name = "codespan" -version = "0.9.5" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ebaf6bb6a863ad6aa3a18729e9710c53d75df03306714d9cc1f7357a00cd789" +checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" dependencies = [ "codespan-reporting", ] [[package]] name = "codespan-reporting" -version = "0.9.5" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0762455306b1ed42bc651ef6a2197aabda5e1d4a43c34d5eab5c1a3634e81d" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", "unicode-width", @@ -930,9 +941,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "js-sys" @@ -1015,9 +1026,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" [[package]] name = "lock_api" @@ -1227,9 +1238,9 @@ checksum = "8dc5838acba84ce4d802d672afd0814fae0ae7098021ae5b06d975e70d09f812" [[package]] name = "memchr" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memmap" @@ -1697,6 +1708,9 @@ name = "semver" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -1812,9 +1826,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.74" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index ad3a32a3..d2322ccf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crates/air-parser", "crates/polyplets", "crates/interpreter-interface", + "crates/interpreter-data", "crates/test-module", "crates/test-utils", "avm/server", diff --git a/air-interpreter/Cargo.toml b/air-interpreter/Cargo.toml index 4e7d2cbd..f187b6ea 100644 --- a/air-interpreter/Cargo.toml +++ b/air-interpreter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "air-interpreter" -version = "0.10.0" +version = "0.14.0" authors = ["Fluence Labs"] edition = "2018" publish = false @@ -24,7 +24,7 @@ wasm-bindgen = "=0.2.65" log = "0.4.11" serde = { version = "=1.0.118", features = [ "derive", "rc" ] } -serde_json = "1.0" +serde_json = "=1.0.61" [features] marine = ["air/marine"] diff --git a/air/Cargo.toml b/air/Cargo.toml index b934b4da..ce30700c 100644 --- a/air/Cargo.toml +++ b/air/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "air" -version = "0.10.0" +version = "0.14.0" authors = ["Fluence Labs"] edition = "2018" publish = false @@ -13,6 +13,7 @@ doctest = false [dependencies] air-parser = { path = "../crates/air-parser" } polyplets = { path = "../crates/polyplets" } +air-interpreter-data = { path = "../crates/interpreter-data" } air-interpreter-interface = { path = "../crates/interpreter-interface" } marine-rs-sdk = { version = "0.6.11", features = ["logger"] } @@ -38,7 +39,7 @@ once_cell = "1.4.1" env_logger = "0.7.1" maplit = "1.0.2" pretty_assertions = "0.6.1" -serde_json = "1.0.56" +serde_json = "=1.0.61" [[bench]] name = "call_benchmark" diff --git a/air/benches/call_benchmark.rs b/air/benches/call_benchmark.rs index 6c2d7c9f..a4a568e9 100644 --- a/air/benches/call_benchmark.rs +++ b/air/benches/call_benchmark.rs @@ -19,7 +19,7 @@ thread_local!(static SCRIPT: String = String::from( ); fn current_peer_id_call() -> Result { - VM.with(|vm| SCRIPT.with(|script| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", "[]"))) + VM.with(|vm| SCRIPT.with(|script| vm.borrow_mut().call_with_prev_data("", script.clone(), "", ""))) } fn criterion_benchmark(c: &mut Criterion) { diff --git a/air/benches/chat_benchmark.rs b/air/benches/chat_benchmark.rs index cac2f310..11b508f8 100644 --- a/air/benches/chat_benchmark.rs +++ b/air/benches/chat_benchmark.rs @@ -55,26 +55,26 @@ fn chat_sent_message_benchmark() -> Result { "#, ); - let res = CLIENT_1_VM - .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", "[]")) + let result = CLIENT_1_VM + .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", "")) .unwrap(); - let res = RELAY_1_VM - .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res.data)) + let result = RELAY_1_VM + .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", result.data)) .unwrap(); - let res = REMOTE_VM - .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res.data)) + let result = REMOTE_VM + .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", result.data)) .unwrap(); - let res_data = res.data.clone(); + let res_data = result.data.clone(); let res1 = RELAY_1_VM - .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res_data)) + .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", res_data)) .unwrap(); CLIENT_1_VM - .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res1.data)) + .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", res1.data)) .unwrap(); let res2 = RELAY_2_VM - .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res.data)) + .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", result.data)) .unwrap(); - CLIENT_2_VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", res2.data)) + CLIENT_2_VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", res2.data)) } fn criterion_benchmark(c: &mut Criterion) { diff --git a/air/benches/create_service_benchmark.rs b/air/benches/create_service_benchmark.rs index ae4b6fc8..1820643e 100644 --- a/air/benches/create_service_benchmark.rs +++ b/air/benches/create_service_benchmark.rs @@ -92,10 +92,10 @@ fn create_service_benchmark() -> Result { )"#, ); - let res = SET_VARIABLES_VM - .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "[]", "[]")) + let result = SET_VARIABLES_VM + .with(|vm| vm.borrow_mut().call_with_prev_data("", script.clone(), "", "")) .unwrap(); - VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script, "[]", res.data)) + VM.with(|vm| vm.borrow_mut().call_with_prev_data("", script, "", result.data)) } fn criterion_benchmark(c: &mut Criterion) { diff --git a/air/src/air/outcome.rs b/air/src/air/outcome.rs deleted file mode 100644 index f2bb1695..00000000 --- a/air/src/air/outcome.rs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use crate::execution::ExecutionError; -use crate::preparation::PreparationError; - -use crate::InterpreterOutcome; -use crate::INTERPRETER_SUCCESS; - -use serde::Serialize; - -use std::hash::Hash; -use std::rc::Rc; - -const EXECUTION_ERRORS_START_ID: i32 = 1000; - -/// Create InterpreterOutcome from supplied data and next_peer_pks, -/// set ret_code to INTERPRETER_SUCCESS. -pub(crate) fn from_path_and_peers(data: &T, next_peer_pks: Vec) -> InterpreterOutcome -where - T: ?Sized + Serialize, -{ - let data = serde_json::to_vec(data).expect("default serializer shouldn't fail"); - let next_peer_pks = dedup(next_peer_pks); - - InterpreterOutcome { - ret_code: INTERPRETER_SUCCESS, - error_message: String::new(), - data, - next_peer_pks, - } -} - -/// Create InterpreterOutcome from supplied data and error, -/// set ret_code based on the error. -pub(crate) fn from_preparation_error(data: impl Into>, err: PreparationError) -> InterpreterOutcome { - let ret_code = err.to_error_code() as i32; - let data = data.into(); - - InterpreterOutcome { - ret_code, - error_message: format!("{}", err), - data, - next_peer_pks: vec![], - } -} - -/// Create InterpreterOutcome from supplied data, next_peer_pks and error, -/// set ret_code based on the error. -pub(crate) fn from_execution_error( - data: &T, - next_peer_pks: Vec, - err: Rc, -) -> InterpreterOutcome -where - T: ?Sized + Serialize, -{ - let ret_code = err.to_error_code() as i32; - let ret_code = EXECUTION_ERRORS_START_ID + ret_code; - - let data = serde_json::to_vec(data).expect("default serializer shouldn't fail"); - let next_peer_pks = dedup(next_peer_pks); - - InterpreterOutcome { - ret_code, - error_message: format!("{}", err), - data, - next_peer_pks, - } -} - -/// Deduplicate values in a supplied vector. -fn dedup(mut vec: Vec) -> Vec { - use std::collections::HashSet; - - let set: HashSet<_> = vec.drain(..).collect(); - set.into_iter().collect() -} diff --git a/air/src/contexts/execution_trace_context.rs b/air/src/contexts/execution_trace_context.rs deleted file mode 100644 index 4fb4ea3e..00000000 --- a/air/src/contexts/execution_trace_context.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -mod executed_state; - -pub use executed_state::CallResult; -pub use executed_state::ExecutedState; -pub use executed_state::ParResult; - -use serde::Deserialize; -use serde::Serialize; -use std::fmt::Display; -use std::fmt::Formatter; - -pub type ExecutionTrace = std::collections::VecDeque; - -/// Encapsulates all necessary state regarding to the call paths. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub(crate) struct ExecutionTraceCtx { - /// Contains trace (serialized tree of states) after merging current and previous data, - /// interpreter used it to realize which instructions have been already executed. - pub(crate) current_trace: ExecutionTrace, - - /// Size of a current considered subtree inside current path. - pub(crate) current_subtree_size: usize, - - // TODO: consider change it to Vec for optimization - /// Stream for resulted path produced by the interpreter after execution. - pub(crate) new_trace: ExecutionTrace, -} - -impl ExecutionTraceCtx { - pub fn new(current_trace: ExecutionTrace) -> Self { - let current_subtree_size = current_trace.len(); - // a new execution trace will contain at least current_path.len() elements - let new_trace = ExecutionTrace::with_capacity(current_subtree_size); - - Self { - current_trace, - current_subtree_size, - new_trace, - } - } -} - -impl Display for ExecutionTraceCtx { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "current trace:\n{:?}", self.current_trace)?; - writeln!(f, "current subtree elements count:\n{:?}", self.current_subtree_size)?; - writeln!(f, "new trace:\n{:?}", self.new_trace) - } -} diff --git a/air/src/contexts/execution_trace_context/executed_state.rs b/air/src/contexts/execution_trace_context/executed_state.rs deleted file mode 100644 index 3a05bf4a..00000000 --- a/air/src/contexts/execution_trace_context/executed_state.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use crate::JValue; - -use serde::Deserialize; -use serde::Serialize; -use std::rc::Rc; - -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub struct ParResult(pub usize, pub usize); - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum CallResult { - /// Request was sent to a target node by node with such public key and it shouldn't be called again. - RequestSentBy(String), - - /// A corresponding call's been already executed with such value and result. - Executed(Rc), - - /// call_service ended with a service error. - CallServiceFailed(i32, Rc), -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum ExecutedState { - Par(ParResult), - Call(CallResult), -} - -impl ExecutedState { - pub fn par(left: usize, right: usize) -> Self { - Self::Par(ParResult(left, right)) - } -} - -impl std::fmt::Display for ExecutedState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use CallResult::*; - use ExecutedState::*; - - match self { - Par(ParResult(left, right)) => write!(f, "Par({}, {})", left, right), - Call(RequestSentBy(peer_id)) => write!(f, "RequestSentBy({})", peer_id), - Call(Executed(result)) => write!(f, "Executed({:?})", result), - Call(CallServiceFailed(ret_code, err_msg)) => write!(f, "CallServiceFailed({}, {})", ret_code, err_msg), - } - } -} diff --git a/air/src/contexts/mod.rs b/air/src/contexts/mod.rs deleted file mode 100644 index a398cff0..00000000 --- a/air/src/contexts/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -mod execution_context; -mod execution_trace_context; - -pub mod execution_trace { - pub use super::execution_trace_context::CallResult; - pub use super::execution_trace_context::ExecutedState; - pub use super::execution_trace_context::ExecutionTrace; - pub(crate) use super::execution_trace_context::ExecutionTraceCtx; - pub use super::execution_trace_context::ParResult; -} - -pub(crate) mod execution { - pub use super::execution_context::error_descriptor::LastError; - pub(crate) use super::execution_context::error_descriptor::LastErrorDescriptor; - pub(crate) use super::execution_context::error_descriptor::LastErrorWithTetraplets; - pub(crate) use super::execution_context::AValue; - pub(crate) use super::execution_context::ExecutionCtx; - pub(crate) use super::execution_context::ResolvedCallResult; -} diff --git a/air/src/execution/air/call/utils.rs b/air/src/execution/air/call/utils.rs deleted file mode 100644 index 4a712d7a..00000000 --- a/air/src/execution/air/call/utils.rs +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::Call; -use super::ExecutionCtx; -use super::ExecutionError; -use super::ExecutionResult; -use crate::contexts::execution::ResolvedCallResult; -use crate::contexts::execution_trace::*; -use crate::exec_err; -use crate::execution::Variable; -use crate::log_targets::EXECUTED_STATE_CHANGING; -use crate::JValue; - -use air_parser::ast::CallOutputValue; -use polyplets::ResolvedTriplet; - -use std::rc::Rc; - -/// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`. -pub(super) fn set_local_call_result<'i>( - result: Rc, - triplet: Rc, - output: &CallOutputValue<'i>, - exec_ctx: &mut ExecutionCtx<'i>, -) -> ExecutionResult<()> { - use crate::contexts::execution::AValue; - use std::cell::RefCell; - use std::collections::hash_map::Entry::{Occupied, Vacant}; - use ExecutionError::*; - - let executed_result = ResolvedCallResult { result, triplet }; - - match output { - CallOutputValue::Variable(Variable::Scalar(name)) => { - if let Some(fold_block_name) = exec_ctx.met_folds.back() { - let fold_state = match exec_ctx.data_cache.get_mut(*fold_block_name) { - Some(AValue::JValueFoldCursor(fold_state)) => fold_state, - _ => unreachable!("fold block data must be represented as fold cursor"), - }; - - fold_state.met_variables.insert(name, executed_result.clone()); - } - - match exec_ctx.data_cache.entry(name.to_string()) { - Vacant(entry) => { - entry.insert(AValue::JValueRef(executed_result)); - } - Occupied(mut entry) => { - // check that current execution flow is inside a fold block - if exec_ctx.met_folds.is_empty() { - // shadowing is allowed only inside fold blocks - return exec_err!(MultipleVariablesFound(entry.key().clone())); - } - - match entry.get() { - AValue::JValueRef(_) => {} - // shadowing is allowed only for scalar values - _ => return exec_err!(ShadowingError(entry.key().clone())), - }; - - entry.insert(AValue::JValueRef(executed_result)); - } - }; - } - CallOutputValue::Variable(Variable::Stream(name)) => { - match exec_ctx.data_cache.entry(name.to_string()) { - Occupied(mut entry) => match entry.get_mut() { - // if result is an array, insert result to the end of the array - AValue::JValueStreamRef(values) => values.borrow_mut().push(executed_result), - v => return exec_err!(IncompatibleAValueType(format!("{}", v), String::from("Array"))), - }, - Vacant(entry) => { - entry.insert(AValue::JValueStreamRef(RefCell::new(vec![executed_result]))); - } - }; - } - CallOutputValue::None => {} - } - - Ok(()) -} - -/// Writes an executed state of a particle being sent to remote node -pub(super) fn set_remote_call_result<'i>( - peer_pk: String, - exec_ctx: &mut ExecutionCtx<'i>, - trace_ctx: &mut ExecutionTraceCtx, -) { - exec_ctx.next_peer_pks.push(peer_pk); - exec_ctx.subtree_complete = false; - - let new_executed_state = ExecutedState::Call(CallResult::RequestSentBy(exec_ctx.current_peer_id.clone())); - log::trace!( - target: EXECUTED_STATE_CHANGING, - " adding new call executed state {:?}", - new_executed_state - ); - trace_ctx.new_trace.push_back(new_executed_state); -} - -/// This function looks at the existing call state, validates it, -/// and returns Ok(true) if the call should be executed further. -pub(super) fn handle_prev_state<'i>( - triplet: &Rc, - output: &CallOutputValue<'i>, - prev_state: ExecutedState, - exec_ctx: &mut ExecutionCtx<'i>, - trace_ctx: &mut ExecutionTraceCtx, - instruction: &Call<'i>, -) -> ExecutionResult { - use CallResult::*; - use ExecutedState::*; - - match &prev_state { - // this call was failed on one of the previous executions, - // here it's needed to bubble this special error up - Call(CallServiceFailed(ret_code, err_msg)) => { - let ret_code = *ret_code; - let err_msg = err_msg.clone(); - trace_ctx.new_trace.push_back(prev_state); - exec_ctx.subtree_complete = false; - exec_err!(ExecutionError::LocalServiceError(ret_code, err_msg)) - } - Call(RequestSentBy(..)) => { - let peer_pk = triplet.peer_pk.as_str(); - // check whether current node can execute this call - let is_current_peer = peer_pk == exec_ctx.current_peer_id; - if is_current_peer { - Ok(true) - } else { - exec_ctx.subtree_complete = false; - trace_ctx.new_trace.push_back(prev_state); - Ok(false) - } - } - // this instruction's been already executed - Call(Executed(result)) => { - set_local_call_result(result.clone(), triplet.clone(), output, exec_ctx)?; - trace_ctx.new_trace.push_back(prev_state); - Ok(false) - } - // state has inconsistent order - return a error, call shouldn't be executed - par_state @ Par(..) => exec_err!(ExecutionError::InvalidExecutedState { - instruction: instruction.to_string(), - expected_state: "call", - actual_state: par_state.clone(), - current_trace: trace_ctx.current_trace.clone(), - new_trace: trace_ctx.new_trace.clone(), - current_subtree_size: trace_ctx.current_subtree_size, - }), - } -} diff --git a/air/src/execution/air/fold.rs b/air/src/execution/air/fold.rs deleted file mode 100644 index db267b8c..00000000 --- a/air/src/execution/air/fold.rs +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -mod utils; - -use super::ExecutionCtx; -use super::ExecutionError; -use super::ExecutionResult; -use super::ExecutionTraceCtx; -use super::Instruction; -use crate::contexts::execution::AValue; -use crate::contexts::execution::ResolvedCallResult; -use crate::exec_err; -use crate::execution::boxed_value::*; -use crate::log_instruction; - -use air_parser::ast::Fold; -use air_parser::ast::Next; - -use std::collections::HashMap; -use std::rc::Rc; - -use utils::IterableValue; - -pub(crate) struct FoldState<'i> { - pub(crate) iterable: IterableValue, - pub(crate) instr_head: Rc>, - // map of met variables inside this (not any inner) fold block with their initial values - pub(crate) met_variables: HashMap<&'i str, ResolvedCallResult>, -} - -impl<'i> FoldState<'i> { - pub fn new(iterable: IterableValue, instr_head: Rc>) -> Self { - Self { - iterable, - instr_head, - met_variables: HashMap::new(), - } - } -} - -impl<'i> super::ExecutableInstruction<'i> for Fold<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { - use ExecutionError::MultipleFoldStates; - - log_instruction!(fold, exec_ctx, trace_ctx); - - let iterable = match utils::construct_iterable_value(&self.iterable, exec_ctx)? { - Some(iterable) => iterable, - None => return Ok(()), - }; - - let fold_state = FoldState::new(iterable, self.instruction.clone()); - - let previous_value = exec_ctx - .data_cache - .insert(self.iterator.to_string(), AValue::JValueFoldCursor(fold_state)); - - if previous_value.is_some() { - return exec_err!(MultipleFoldStates(self.iterator.to_string())); - } - exec_ctx.met_folds.push_back(self.iterator); - - self.instruction.execute(exec_ctx, trace_ctx)?; - - cleanup_variables(exec_ctx, &self.iterator); - - Ok(()) - } -} - -impl<'i> super::ExecutableInstruction<'i> for Next<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { - use ExecutionError::FoldStateNotFound; - use ExecutionError::IncompatibleAValueType; - - log_instruction!(next, exec_ctx, trace_ctx); - - let iterator_name = self.0; - let avalue = exec_ctx - .data_cache - .get_mut(iterator_name) - .ok_or_else(|| FoldStateNotFound(iterator_name.to_string()))?; - - let fold_state = match avalue { - AValue::JValueFoldCursor(state) => state, - v => { - // it's not possible to use unreachable here - // because at now next syntactically could be used without fold - return exec_err!(IncompatibleAValueType( - format!("{}", v), - String::from("JValueFoldCursor"), - )); - } - }; - - if !fold_state.iterable.next() { - // just do nothing to exit - return Ok(()); - } - - let next_instr = fold_state.instr_head.clone(); - next_instr.execute(exec_ctx, trace_ctx)?; - - // get the same fold state again because of borrow checker - match exec_ctx.data_cache.get_mut(iterator_name) { - // move iterator back to provide correct value for possible subtree after next - // (for example for cases such as right fold) - Some(AValue::JValueFoldCursor(fold_state)) => fold_state.iterable.prev(), - _ => unreachable!("iterator value shouldn't changed inside fold"), - }; - - Ok(()) - } -} - -fn cleanup_variables(exec_ctx: &mut ExecutionCtx<'_>, iterator: &str) { - let fold_state = match exec_ctx.data_cache.remove(iterator) { - Some(AValue::JValueFoldCursor(fold_state)) => fold_state, - _ => unreachable!("fold cursor is changed only inside fold block"), - }; - - for (variable_name, _) in fold_state.met_variables { - exec_ctx.data_cache.remove(variable_name); - } - exec_ctx.met_folds.pop_back(); - - // TODO: fix 3 or more inner folds behaviour - if let Some(fold_block_name) = exec_ctx.met_folds.back() { - let fold_state = match exec_ctx.data_cache.get(*fold_block_name) { - Some(AValue::JValueFoldCursor(fold_state)) => fold_state, - _ => unreachable!("fold block data must be represented as fold cursor"), - }; - - let mut upper_fold_values = HashMap::new(); - for (variable_name, variable) in fold_state.met_variables.iter() { - upper_fold_values.insert(variable_name.to_string(), AValue::JValueRef(variable.clone())); - } - - exec_ctx.data_cache.extend(upper_fold_values); - } -} diff --git a/air/src/execution/air/fold/utils.rs b/air/src/execution/air/fold/utils.rs deleted file mode 100644 index 22f09bf2..00000000 --- a/air/src/execution/air/fold/utils.rs +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::*; -use crate::exec_err; -use crate::execution::utils::get_variable_name; -use crate::JValue; -use crate::ResolvedTriplet; -use crate::SecurityTetraplet; - -use air_parser::ast; -use jsonpath_lib::select; -use jsonpath_lib::select_with_iter; - -use std::ops::Deref; -use std::rc::Rc; - -pub(super) type IterableValue = Box Iterable<'ctx, Item = IterableItem<'ctx>>>; - -/// Constructs iterable value for given instruction value, -/// return Some if iterable isn't empty and None otherwise. -pub(super) fn construct_iterable_value<'ctx>( - ast_iterable: &ast::IterableValue<'ctx>, - exec_ctx: &ExecutionCtx<'ctx>, -) -> ExecutionResult> { - match ast_iterable { - ast::IterableValue::Variable(variable) => { - let name = get_variable_name(variable); - handle_instruction_variable(exec_ctx, name) - } - ast::IterableValue::JsonPath { - variable, - path, - should_flatten, - } => { - let name = get_variable_name(variable); - handle_instruction_json_path(exec_ctx, name, path, *should_flatten) - } - } -} - -fn handle_instruction_variable<'ctx>( - exec_ctx: &ExecutionCtx<'ctx>, - variable_name: &str, -) -> ExecutionResult> { - let iterable: Option = match exec_ctx.data_cache.get(variable_name) { - Some(AValue::JValueRef(call_result)) => from_call_result(call_result.clone())?, - Some(AValue::JValueStreamRef(stream)) => { - let stream = stream.borrow(); - if stream.is_empty() { - return Ok(None); - } - - let call_results = stream.iter().cloned().collect::>(); - let foldable = IterableVecResolvedCall::init(call_results); - Some(Box::new(foldable)) - } - Some(AValue::JValueFoldCursor(fold_state)) => { - let iterable_value = fold_state.iterable.peek().unwrap(); - let jvalue = iterable_value.as_jvalue(); - let result = Rc::new(jvalue.into_owned()); - let triplet = as_triplet(&iterable_value); - - let call_result = ResolvedCallResult { result, triplet }; - from_call_result(call_result)? - } - _ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())), - }; - - Ok(iterable) -} - -/// Constructs iterable value from resolved call result. -fn from_call_result(call_result: ResolvedCallResult) -> ExecutionResult> { - use ExecutionError::IncompatibleJValueType; - - let len = match &call_result.result.deref() { - JValue::Array(array) => { - if array.is_empty() { - // skip fold if array is empty - return Ok(None); - } - array.len() - } - v => return exec_err!(IncompatibleJValueType((*v).clone(), "array")), - }; - - let foldable = IterableResolvedCall::init(call_result, len); - let foldable = Box::new(foldable); - - Ok(Some(foldable)) -} - -fn handle_instruction_json_path<'ctx>( - exec_ctx: &ExecutionCtx<'ctx>, - variable_name: &str, - json_path: &str, - should_flatten: bool, -) -> ExecutionResult> { - use ExecutionError::JValueStreamJsonPathError; - - match exec_ctx.data_cache.get(variable_name) { - Some(AValue::JValueRef(variable)) => { - let jvalues = apply_json_path(&variable.result, json_path)?; - from_jvalues(jvalues, variable.triplet.clone(), json_path, should_flatten) - } - Some(AValue::JValueStreamRef(stream)) => { - let stream = stream.borrow(); - if stream.is_empty() { - return Ok(None); - } - - let acc_iter = stream.iter().map(|v| v.result.deref()); - let (jvalues, tetraplet_indices) = select_with_iter(acc_iter, &json_path) - .map_err(|e| JValueStreamJsonPathError(stream.clone(), json_path.to_string(), e))?; - - let jvalues = construct_iterable_jvalues(jvalues, should_flatten)?; - let tetraplets = tetraplet_indices - .into_iter() - .map(|id| SecurityTetraplet { - triplet: stream[id].triplet.clone(), - json_path: json_path.to_string(), - }) - .collect::>(); - - let foldable = IterableVecJsonPathResult::init(jvalues, tetraplets); - Ok(Some(Box::new(foldable))) - } - Some(AValue::JValueFoldCursor(fold_state)) => { - let iterable_value = fold_state.iterable.peek().unwrap(); - let jvalues = iterable_value.apply_json_path(json_path)?; - let triplet = as_triplet(&iterable_value); - - from_jvalues(jvalues, triplet, json_path, should_flatten) - } - _ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())), - } -} - -fn apply_json_path<'jvalue, 'str>( - jvalue: &'jvalue JValue, - json_path: &'str str, -) -> ExecutionResult> { - use ExecutionError::JValueJsonPathError; - - select(jvalue, json_path).map_err(|e| Rc::new(JValueJsonPathError(jvalue.clone(), json_path.to_string(), e))) -} - -/// Applies json_path to provided jvalues and construct IterableValue from the result and given triplet. -fn from_jvalues( - jvalues: Vec<&JValue>, - triplet: Rc, - json_path: &str, - should_flatten: bool, -) -> ExecutionResult> { - let jvalues = construct_iterable_jvalues(jvalues, should_flatten)?; - - if jvalues.is_empty() { - return Ok(None); - } - - let tetraplet = SecurityTetraplet { - triplet, - json_path: json_path.to_string(), - }; - - let foldable = IterableJsonPathResult::init(jvalues, tetraplet); - Ok(Some(Box::new(foldable))) -} - -fn construct_iterable_jvalues(jvalues: Vec<&JValue>, should_flatten: bool) -> ExecutionResult> { - if !should_flatten { - let jvalues = jvalues.into_iter().cloned().collect(); - return Ok(jvalues); - } - - if jvalues.len() != 1 { - let jvalues = jvalues.into_iter().cloned().collect(); - let jvalue = JValue::Array(jvalues); - return exec_err!(ExecutionError::FlatteningError(jvalue)); - } - - match jvalues[0] { - JValue::Array(values) => Ok(values.clone()), - _ => { - let jvalues = jvalues.into_iter().cloned().collect(); - let jvalue = JValue::Array(jvalues); - exec_err!(ExecutionError::FlatteningError(jvalue)) - } - } -} - -fn as_triplet(iterable: &IterableItem<'_>) -> Rc { - use IterableItem::*; - - let tetraplet = match iterable { - RefRef((_, tetraplet)) => tetraplet, - RefValue((_, tetraplet)) => tetraplet, - RcValue((_, tetraplet)) => tetraplet, - }; - - // clone is cheap here, because triplet is under Rc - Rc::clone(&tetraplet.triplet) -} diff --git a/air/src/execution/air/par.rs b/air/src/execution/air/par.rs deleted file mode 100644 index aee57821..00000000 --- a/air/src/execution/air/par.rs +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::ExecutableInstruction; -use super::ExecutionCtx; -use super::ExecutionResult; -use super::ExecutionTraceCtx; -use super::Instruction; -use crate::contexts::execution_trace::ExecutedState; -use crate::contexts::execution_trace::ParResult; -use crate::log_instruction; -use crate::log_targets::EXECUTED_STATE_CHANGING; - -use air_parser::ast::Par; - -enum SubtreeType { - Left, - Right, -} - -impl std::fmt::Display for SubtreeType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Left => write!(f, "left"), - Self::Right => write!(f, "right"), - } - } -} - -impl<'i> ExecutableInstruction<'i> for Par<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { - use SubtreeType::*; - - log_instruction!(par, exec_ctx, trace_ctx); - - let (left_subtree_size, right_subtree_size) = extract_subtree_sizes(trace_ctx, &self)?; - - let par_pos = trace_ctx.new_trace.len(); - trace_ctx.new_trace.push_back(ExecutedState::par(0, 0)); - - // execute a left subtree of this par - execute_subtree(&self.0, left_subtree_size, exec_ctx, trace_ctx, par_pos, Left)?; - let left_subtree_complete = exec_ctx.subtree_complete; - - // execute a right subtree of this par - execute_subtree(&self.1, right_subtree_size, exec_ctx, trace_ctx, par_pos, Right)?; - let right_subtree_complete = exec_ctx.subtree_complete; - - // par is completed if at least one of its subtrees is completed - exec_ctx.subtree_complete = left_subtree_complete || right_subtree_complete; - - Ok(()) - } -} - -fn extract_subtree_sizes(trace_ctx: &mut ExecutionTraceCtx, instruction: &Par<'_>) -> ExecutionResult<(usize, usize)> { - use super::ExecutionError::InvalidExecutedState; - - if trace_ctx.current_subtree_size == 0 { - return Ok((0, 0)); - } - - trace_ctx.current_subtree_size -= 1; - - log::trace!( - target: EXECUTED_STATE_CHANGING, - " previous call executed state was found {:?}", - trace_ctx.current_trace[0] - ); - - // unwrap is safe here because of length's been checked - match trace_ctx.current_trace.pop_front().unwrap() { - ExecutedState::Par(ParResult(left, right)) => Ok((left, right)), - state => crate::exec_err!(InvalidExecutedState { - instruction: instruction.to_string(), - expected_state: "par", - actual_state: state, - current_trace: trace_ctx.current_trace.clone(), - new_trace: trace_ctx.new_trace.clone(), - current_subtree_size: trace_ctx.current_subtree_size, - }), - } -} - -/// Execute provided subtree and update Par state in trace_ctx.new_trace. -fn execute_subtree<'i>( - subtree: &Instruction<'i>, - subtree_size: usize, - exec_ctx: &mut ExecutionCtx<'i>, - trace_ctx: &mut ExecutionTraceCtx, - current_par_pos: usize, - subtree_type: SubtreeType, -) -> ExecutionResult<()> { - use super::ExecutionError::LocalServiceError; - - let before_subtree_size = trace_ctx.current_subtree_size; - trace_ctx.current_subtree_size = subtree_size; - let before_new_path_len = trace_ctx.new_trace.len(); - - exec_ctx.subtree_complete = determine_subtree_complete(&subtree); - - // execute a subtree - match subtree.execute(exec_ctx, trace_ctx) { - res @ Ok(_) => { - update_par_state(trace_ctx, subtree_type, current_par_pos, before_new_path_len); - trace_ctx.current_subtree_size = before_subtree_size - subtree_size; - res - } - // if there is a service error, update already added Par state - // and then bubble the error up - Err(err) if matches!(&*err, LocalServiceError(..)) => { - update_par_state(trace_ctx, subtree_type, current_par_pos, before_new_path_len); - trace_ctx.current_subtree_size = before_subtree_size - subtree_size; - Err(err) - } - err @ Err(_) => err, - } -} - -fn determine_subtree_complete(next_instruction: &Instruction<'_>) -> bool { - // this is needed to prevent situation when on such pattern - // (fold (Iterable i - // (par - // (call ..) - // (next i) - // ) - // ) - // par will be completed after the last next that wouldn't change subtree_complete - !matches!(next_instruction, Instruction::Next(_)) -} - -/// Set left or right fields of a Par identified by current_par_pos. -fn update_par_state( - trace_ctx: &mut ExecutionTraceCtx, - subtree_type: SubtreeType, - current_par_pos: usize, - before_new_path_len: usize, -) { - let new_subtree_size = trace_ctx.new_trace.len() - before_new_path_len; - - // unwrap is safe here, because this par is added at the beginning of this par instruction. - let par_state = trace_ctx.new_trace.get_mut(current_par_pos).unwrap(); - match par_state { - ExecutedState::Par(ParResult(left, right)) => { - if let SubtreeType::Left = subtree_type { - *left = new_subtree_size; - } else { - *right = new_subtree_size; - } - - log::trace!( - target: EXECUTED_STATE_CHANGING, - " set {} par subtree size to {}", - subtree_type, - new_subtree_size - ); - } - _ => unreachable!("current_pas_pos must point to a par state"), - } -} diff --git a/air/src/execution/utils/resolve.rs b/air/src/execution/utils/resolve.rs deleted file mode 100644 index 6cdb006d..00000000 --- a/air/src/execution/utils/resolve.rs +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use crate::contexts::execution::AValue; -use crate::contexts::execution::ExecutionCtx; -use crate::contexts::execution::LastErrorWithTetraplets; -use crate::execution::boxed_value::JValuable; -use crate::execution::ExecutionError; -use crate::execution::ExecutionResult; -use crate::JValue; -use crate::SecurityTetraplet; - -use air_parser::ast::CallInstrArgValue; -use air_parser::ast::LastErrorPath; -use serde_json::json; - -/// Resolve value to called function arguments. -pub(crate) fn resolve_to_args<'i>( - value: &CallInstrArgValue<'i>, - ctx: &ExecutionCtx<'i>, -) -> ExecutionResult<(JValue, Vec)> { - match value { - CallInstrArgValue::InitPeerId => prepare_consts(ctx.init_peer_id.clone(), ctx), - CallInstrArgValue::LastError(path) => prepare_last_error(path, ctx), - CallInstrArgValue::Literal(value) => prepare_consts(value.to_string(), ctx), - CallInstrArgValue::Boolean(value) => prepare_consts(*value, ctx), - CallInstrArgValue::Number(value) => prepare_consts(value, ctx), - CallInstrArgValue::Variable(variable) => prepare_variable(variable, ctx), - CallInstrArgValue::JsonPath { - variable, - path, - should_flatten, - } => prepare_json_path(variable, path, *should_flatten, ctx), - } -} - -#[allow(clippy::unnecessary_wraps)] -fn prepare_consts(arg: impl Into, ctx: &ExecutionCtx<'_>) -> ExecutionResult<(JValue, Vec)> { - let jvalue = arg.into(); - let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone()); - - Ok((jvalue, vec![tetraplet])) -} - -#[allow(clippy::unnecessary_wraps)] -fn prepare_last_error( - path: &LastErrorPath, - ctx: &ExecutionCtx<'_>, -) -> ExecutionResult<(JValue, Vec)> { - let LastErrorWithTetraplets { last_error, tetraplets } = ctx.last_error(); - let jvalue = match path { - LastErrorPath::Instruction => JValue::String(last_error.instruction), - LastErrorPath::Message => JValue::String(last_error.msg), - LastErrorPath::PeerId => JValue::String(last_error.peer_id), - LastErrorPath::None => json!(last_error), - }; - - Ok((jvalue, tetraplets)) -} - -fn prepare_variable<'i>( - variable: &Variable<'_>, - ctx: &ExecutionCtx<'i>, -) -> ExecutionResult<(JValue, Vec)> { - let resolved = resolve_variable(variable, ctx)?; - let tetraplets = resolved.as_tetraplets(); - let jvalue = resolved.into_jvalue(); - - Ok((jvalue, tetraplets)) -} - -fn resolve_variable<'ctx, 'i>( - variable: &Variable<'_>, - ctx: &'ctx ExecutionCtx<'i>, -) -> ExecutionResult> { - match variable { - Variable::Scalar(name) => resolve_to_jvaluable(name, ctx), - Variable::Stream(name) => { - // return an empty stream for not found stream - // here it ignores the join behaviour - if ctx.data_cache.get(*name).is_none() { - Ok(Box::new(())) - } else { - resolve_to_jvaluable(name, ctx) - } - } - } -} - -fn prepare_json_path<'i>( - variable: &Variable<'_>, - json_path: &str, - should_flatten: bool, - ctx: &ExecutionCtx<'i>, -) -> ExecutionResult<(JValue, Vec)> { - let name = get_variable_name(variable); - - let resolved = resolve_to_jvaluable(name, ctx)?; - let (jvalue, tetraplets) = resolved.apply_json_path_with_tetraplets(json_path)?; - - let jvalue = if should_flatten { - if jvalue.len() > 1 { - let jvalue = jvalue.into_iter().cloned().collect::>(); - return crate::exec_err!(ExecutionError::FlatteningError(JValue::Array(jvalue))); - } - jvalue[0].clone() - } else { - let jvalue = jvalue.into_iter().cloned().collect::>(); - JValue::Array(jvalue) - }; - - Ok((jvalue, tetraplets)) -} - -/// Constructs jvaluable result from `ExecutionCtx::data_cache` by name. -pub(crate) fn resolve_to_jvaluable<'name, 'i, 'ctx>( - name: &'name str, - ctx: &'ctx ExecutionCtx<'i>, -) -> ExecutionResult> { - use ExecutionError::VariableNotFound; - - let value = ctx - .data_cache - .get(name) - .ok_or_else(|| VariableNotFound(name.to_string()))?; - - match value { - AValue::JValueRef(value) => Ok(Box::new(value.clone())), - AValue::JValueStreamRef(stream) => Ok(Box::new(stream.borrow())), - AValue::JValueFoldCursor(fold_state) => { - let peeked_value = fold_state.iterable.peek().unwrap(); - Ok(Box::new(peeked_value)) - } - } -} - -use air_parser::ast::Variable; - -pub(crate) fn get_variable_name<'a>(variable: &'a Variable<'_>) -> &'a str { - match variable { - Variable::Scalar(name) => name, - Variable::Stream(name) => name, - } -} diff --git a/air/src/execution_step/air/ap.rs b/air/src/execution_step/air/ap.rs new file mode 100644 index 00000000..8e2e930c --- /dev/null +++ b/air/src/execution_step/air/ap.rs @@ -0,0 +1,92 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod apply_to_arguments; +mod utils; + +use super::call::call_result_setter::set_scalar_result; +use super::call::call_result_setter::set_stream_result; +use super::ExecutionCtx; +use super::ExecutionResult; +use super::TraceHandler; +use crate::execution_step::air::ResolvedCallResult; +use crate::execution_step::boxed_value::Variable; +use crate::execution_step::trace_handler::MergerApResult; +use crate::execution_step::utils::apply_json_path; +use crate::JValue; +use crate::SecurityTetraplet; +use apply_to_arguments::*; +use utils::*; + +use air_parser::ast::ApArgument; +use air_parser::ast::AstVariable; +use air_parser::ast::JsonPath; +use air_parser::ast::{Ap, LastErrorPath}; + +use std::cell::RefCell; +use std::rc::Rc; + +impl<'i> super::ExecutableInstruction<'i> for Ap<'i> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + let should_touch_trace = should_touch_trace(self); + + let merger_ap_result = if should_touch_trace { + let merger_ap_result = trace_ctx.meet_ap_start()?; + try_match_result_to_instr(&merger_ap_result, self)?; + merger_ap_result + } else { + MergerApResult::Empty + }; + + let result = apply_to_arg(&self.argument, exec_ctx, trace_ctx, should_touch_trace)?; + save_result(&self.result, &merger_ap_result, result, exec_ctx)?; + + if should_touch_trace { + // if generations are empty, then this ap instruction operates only with scalars and data + // shouldn't be updated + let final_ap_result = to_ap_result(&merger_ap_result, self, exec_ctx); + trace_ctx.meet_ap_end(final_ap_result); + } + + Ok(()) + } +} + +fn save_result<'ctx>( + ap_result_type: &AstVariable<'ctx>, + merger_ap_result: &MergerApResult, + result: ResolvedCallResult, + exec_ctx: &mut ExecutionCtx<'ctx>, +) -> ExecutionResult<()> { + match ap_result_type { + AstVariable::Scalar(name) => set_scalar_result(result, name, exec_ctx), + AstVariable::Stream(name) => { + let generation = ap_result_to_generation(merger_ap_result); + set_stream_result(result, generation, name.to_string(), exec_ctx).map(|_| ()) + } + } +} + +fn should_touch_trace(ap: &Ap<'_>) -> bool { + match (&ap.argument, &ap.result) { + (_, AstVariable::Stream(_)) => true, + (ApArgument::JsonPath(json_path), _) => match &json_path.variable { + AstVariable::Scalar(_) => false, + AstVariable::Stream(_) => true, + }, + _ => false, + } +} diff --git a/air/src/execution_step/air/ap/apply_to_arguments.rs b/air/src/execution_step/air/ap/apply_to_arguments.rs new file mode 100644 index 00000000..7674355a --- /dev/null +++ b/air/src/execution_step/air/ap/apply_to_arguments.rs @@ -0,0 +1,105 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +pub(super) fn apply_to_arg( + argument: &ApArgument<'_>, + exec_ctx: &ExecutionCtx<'_>, + trace_ctx: &TraceHandler, + should_touch_trace: bool, +) -> ExecutionResult { + let result = match argument { + ApArgument::ScalarVariable(scalar_name) => apply_scalar(scalar_name, exec_ctx, trace_ctx, should_touch_trace)?, + ApArgument::JsonPath(json_arg) => apply_json_argument(json_arg, exec_ctx, trace_ctx)?, + ApArgument::LastError(error_path) => apply_last_error(error_path, exec_ctx, trace_ctx)?, + ApArgument::Literal(value) => apply_const(value.to_string(), exec_ctx, trace_ctx), + ApArgument::Number(value) => apply_const(value, exec_ctx, trace_ctx), + ApArgument::Boolean(value) => apply_const(*value, exec_ctx, trace_ctx), + ApArgument::EmptyArray => apply_const(serde_json::json!([]), exec_ctx, trace_ctx), + }; + + Ok(result) +} + +fn apply_scalar( + scalar_name: &str, + exec_ctx: &ExecutionCtx<'_>, + trace_ctx: &TraceHandler, + should_touch_trace: bool, +) -> ExecutionResult { + use crate::execution_step::ExecutionError::VariableNotFound; + use crate::execution_step::Scalar; + + let scalar = exec_ctx + .scalars + .get(scalar_name) + .ok_or_else(|| VariableNotFound(scalar_name.to_string()))?; + + let mut result = match scalar { + Scalar::JValueRef(result) => result.clone(), + Scalar::JValueFoldCursor(iterator) => { + let result = iterator.iterable.peek().expect( + "peek always return elements inside fold,\ + this guaranteed by implementation of next and avoiding empty folds", + ); + result.into_resolved_result() + } + }; + + if should_touch_trace { + result.trace_pos = trace_ctx.trace_pos(); + } + + Ok(result) +} + +fn apply_const(value: impl Into, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ResolvedCallResult { + let value = Rc::new(value.into()); + let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone()); + let tetraplet = Rc::new(RefCell::new(tetraplet)); + + ResolvedCallResult::new(value, tetraplet, trace_ctx.trace_pos()) +} + +fn apply_last_error( + error_path: &LastErrorPath, + exec_ctx: &ExecutionCtx<'_>, + trace_ctx: &TraceHandler, +) -> ExecutionResult { + let (value, mut tetraplets) = crate::execution_step::utils::prepare_last_error(error_path, exec_ctx)?; + let value = Rc::new(value); + let tetraplet = tetraplets.remove(0); + + let result = ResolvedCallResult::new(value, tetraplet, trace_ctx.trace_pos()); + Ok(result) +} + +fn apply_json_argument( + json_arg: &JsonPath<'_>, + exec_ctx: &ExecutionCtx<'_>, + trace_ctx: &TraceHandler, +) -> ExecutionResult { + let variable = Variable::from_ast(&json_arg.variable); + let (jvalue, mut tetraplets) = apply_json_path(variable, json_arg.path, json_arg.should_flatten, exec_ctx)?; + + let tetraplet = tetraplets + .pop() + .unwrap_or_else(|| Rc::new(RefCell::new(SecurityTetraplet::default()))); + let result = ResolvedCallResult::new(Rc::new(jvalue), tetraplet, trace_ctx.trace_pos()); + + Ok(result) +} diff --git a/air/src/execution_step/air/ap/utils.rs b/air/src/execution_step/air/ap/utils.rs new file mode 100644 index 00000000..201922fa --- /dev/null +++ b/air/src/execution_step/air/ap/utils.rs @@ -0,0 +1,89 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionCtx; +use super::ExecutionResult; +use crate::execution_step::trace_handler::MergerApResult; +use crate::execution_step::Generation; + +use air_interpreter_data::ApResult; +use air_parser::ast::Ap; +use air_parser::ast::AstVariable; + +pub(super) fn ap_result_to_generation(ap_result: &MergerApResult) -> Generation { + match ap_result { + MergerApResult::Empty => Generation::Last, + MergerApResult::ApResult { res_generation, .. } => Generation::from_option(*res_generation), + } +} + +pub(super) fn try_match_result_to_instr(merger_ap_result: &MergerApResult, instr: &Ap<'_>) -> ExecutionResult<()> { + let res_generation = match merger_ap_result { + MergerApResult::ApResult { res_generation } => *res_generation, + MergerApResult::Empty => return Ok(()), + }; + + match_position_variable(&instr.result, res_generation, merger_ap_result) +} + +fn match_position_variable( + variable: &AstVariable<'_>, + generation: Option, + ap_result: &MergerApResult, +) -> ExecutionResult<()> { + use crate::execution_step::ExecutionError::ApResultNotCorrespondToInstr; + + match (variable, generation) { + (AstVariable::Stream(_), Some(_)) => Ok(()), + (AstVariable::Scalar(_), None) => Ok(()), + _ => return crate::exec_err!(ApResultNotCorrespondToInstr(ap_result.clone())), + } +} + +pub(super) fn to_ap_result(merger_ap_result: &MergerApResult, instr: &Ap<'_>, exec_ctx: &ExecutionCtx<'_>) -> ApResult { + if let MergerApResult::ApResult { res_generation } = merger_ap_result { + let res_generation = option_to_vec(*res_generation); + + return ApResult::new(res_generation); + } + + let res_generation = variable_to_generations(&instr.result, exec_ctx); + ApResult::new(res_generation) +} + +fn option_to_vec(value: Option) -> Vec { + match value { + Some(value) => vec![value], + None => vec![], + } +} + +fn variable_to_generations(variable: &AstVariable<'_>, exec_ctx: &ExecutionCtx<'_>) -> Vec { + match variable { + AstVariable::Scalar(_) => vec![], + AstVariable::Stream(name) => { + // unwrap here is safe because this function will be called only + // when this stream's been created + let stream = exec_ctx.streams.get(*name).unwrap(); + let generation = match stream.borrow().generations_count() { + 0 => 0, + n => n - 1, + }; + + vec![generation as u32] + } + } +} diff --git a/air/src/execution/air/call.rs b/air/src/execution_step/air/call.rs similarity index 80% rename from air/src/execution/air/call.rs rename to air/src/execution_step/air/call.rs index 4b79d568..6434fa16 100644 --- a/air/src/execution/air/call.rs +++ b/air/src/execution_step/air/call.rs @@ -14,6 +14,7 @@ * limitations under the License. */ +pub(crate) mod call_result_setter; mod resolved_call; mod triplet; mod utils; @@ -23,31 +24,30 @@ use resolved_call::ResolvedCall; use super::ExecutionCtx; use super::ExecutionError; use super::ExecutionResult; -use super::ExecutionTraceCtx; -use crate::contexts::execution::LastErrorDescriptor; -use crate::execution::joinable::Joinable; +use super::LastErrorDescriptor; +use super::TraceHandler; +use crate::execution_step::joinable::Joinable; +use crate::execution_step::RSecurityTetraplet; use crate::joinable_call; use crate::log_instruction; -use crate::SecurityTetraplet; use air_parser::ast::Call; use std::rc::Rc; impl<'i> super::ExecutableInstruction<'i> for Call<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { log_instruction!(call, exec_ctx, trace_ctx); + exec_ctx.tracker.met_call(); let resolved_call = joinable_call!(ResolvedCall::new(self, exec_ctx), exec_ctx).map_err(|e| { set_last_error(self, exec_ctx, e.clone(), None); e })?; - let triplet = resolved_call.as_triplet(); - joinable_call!(resolved_call.execute(exec_ctx, trace_ctx, &self), exec_ctx).map_err(|e| { - let tetraplet = SecurityTetraplet::from_triplet(triplet); + let tetraplet = resolved_call.as_tetraplet(); + joinable_call!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx).map_err(|e| { set_last_error(self, exec_ctx, e.clone(), Some(tetraplet)); - e }) } @@ -57,14 +57,14 @@ fn set_last_error<'i>( call: &Call<'i>, exec_ctx: &mut ExecutionCtx<'i>, e: Rc, - tetraplet: Option, + tetraplet: Option, ) { let current_peer_id = match &tetraplet { // use tetraplet if they set, because an error could be propagated from data // (from CallServiceFailed state) and exec_ctx.current_peer_id won't mean // a peer where the error was occurred - Some(tetraplet) => tetraplet.triplet.peer_pk.clone(), - None => exec_ctx.current_peer_id.clone(), + Some(tetraplet) => tetraplet.borrow().triplet.peer_pk.clone(), + None => exec_ctx.current_peer_id.to_string(), }; log::warn!("call failed with an error `{}`, peerId `{}`", e, current_peer_id); diff --git a/air/src/execution_step/air/call/call_result_setter.rs b/air/src/execution_step/air/call/call_result_setter.rs new file mode 100644 index 00000000..37480b66 --- /dev/null +++ b/air/src/execution_step/air/call/call_result_setter.rs @@ -0,0 +1,171 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use crate::exec_err; +use crate::execution_step::execution_context::*; +use crate::execution_step::trace_handler::TraceHandler; +use crate::execution_step::AstVariable; +use crate::execution_step::Generation; +use crate::execution_step::ResolvedCallResult; +use crate::execution_step::Scalar; +use crate::execution_step::Stream; + +use air_interpreter_data::CallResult; +use air_interpreter_data::Value; +use air_parser::ast::CallOutputValue; + +use std::cell::RefCell; +use std::collections::hash_map::Entry::{Occupied, Vacant}; + +/// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`. +/// Returns call result. +pub(crate) fn set_local_result<'i>( + executed_result: ResolvedCallResult, + output: &CallOutputValue<'i>, + exec_ctx: &mut ExecutionCtx<'i>, +) -> ExecutionResult { + let result_value = executed_result.result.clone(); + match output { + CallOutputValue::Variable(AstVariable::Scalar(name)) => { + set_scalar_result(executed_result, name, exec_ctx)?; + Ok(CallResult::executed_scalar(result_value)) + } + CallOutputValue::Variable(AstVariable::Stream(name)) => { + let generation = set_stream_result(executed_result, Generation::Last, name.to_string(), exec_ctx)?; + Ok(CallResult::executed_stream(result_value, generation)) + } + CallOutputValue::None => Ok(CallResult::executed_scalar(result_value)), + } +} + +pub(crate) fn set_result_from_value<'i>( + value: Value, + tetraplet: RSecurityTetraplet, + trace_pos: usize, + output: &CallOutputValue<'i>, + exec_ctx: &mut ExecutionCtx<'i>, +) -> ExecutionResult<()> { + match (output, value) { + (CallOutputValue::Variable(AstVariable::Scalar(name)), Value::Scalar(value)) => { + let result = ResolvedCallResult::new(value, tetraplet, trace_pos); + set_scalar_result(result, name, exec_ctx)?; + } + (CallOutputValue::Variable(AstVariable::Stream(name)), Value::Stream { value, generation }) => { + let result = ResolvedCallResult::new(value, tetraplet, trace_pos); + let generation = Generation::Nth(generation); + let _ = set_stream_result(result, generation, name.to_string(), exec_ctx)?; + } + // it isn't needed to check there that output and value matches because + // it's been already in trace handler + _ => {} + }; + + Ok(()) +} + +macro_rules! shadowing_allowed( + ($exec_ctx:ident, $entry:ident) => { { + // check that current execution_step flow is inside a fold block + if $exec_ctx.met_folds.is_empty() { + // shadowing is allowed only inside fold blocks + return exec_err!(ExecutionError::MultipleVariablesFound($entry.key().clone())); + } + + match $entry.get() { + Scalar::JValueRef(_) => {} + // shadowing is allowed only for JValue not iterable + _ => return exec_err!(ExecutionError::IterableShadowing($entry.key().clone())), + }; + + ExecutionResult::Ok(()) + }} +); + +// TODO: decouple this function to a separate module +pub(crate) fn set_scalar_result<'i>( + executed_result: ResolvedCallResult, + scalar_name: &'i str, + exec_ctx: &mut ExecutionCtx<'i>, +) -> ExecutionResult<()> { + meet_scalar(scalar_name, executed_result.clone(), exec_ctx)?; + + match exec_ctx.scalars.entry(scalar_name.to_string()) { + Vacant(entry) => { + entry.insert(Scalar::JValueRef(executed_result)); + } + Occupied(mut entry) => { + // the macro instead of a function because of borrowing + shadowing_allowed!(exec_ctx, entry)?; + entry.insert(Scalar::JValueRef(executed_result)); + } + }; + + Ok(()) +} + +/// Inserts meet variable name into met calls in fold to allow shadowing. +fn meet_scalar<'i>( + scalar_name: &'i str, + executed_result: ResolvedCallResult, + exec_ctx: &mut ExecutionCtx<'i>, +) -> ExecutionResult<()> { + if let Some(fold_block_name) = exec_ctx.met_folds.back() { + let fold_state = match exec_ctx.scalars.get_mut(*fold_block_name) { + Some(Scalar::JValueFoldCursor(fold_state)) => fold_state, + _ => unreachable!("fold block data must be represented as fold cursor"), + }; + + fold_state.met_variables.insert(scalar_name, executed_result); + } + + Ok(()) +} + +// TODO: decouple this function to a separate module +pub(crate) fn set_stream_result( + executed_result: ResolvedCallResult, + generation: Generation, + stream_name: String, + exec_ctx: &mut ExecutionCtx<'_>, +) -> ExecutionResult { + let generation = match exec_ctx.streams.entry(stream_name) { + Occupied(mut entry) => { + // if result is an array, insert result to the end of the array + entry.get_mut().borrow_mut().add_value(executed_result, generation)? + } + Vacant(entry) => { + let stream = Stream::from_value(executed_result); + entry.insert(RefCell::new(stream)); + 0 + } + }; + + Ok(generation) +} + +/// Writes an executed state of a particle being sent to remote node. +pub(crate) fn set_remote_call_result<'i>( + peer_pk: String, + exec_ctx: &mut ExecutionCtx<'i>, + trace_ctx: &mut TraceHandler, +) { + exec_ctx.next_peer_pks.push(peer_pk); + exec_ctx.subtree_complete = false; + + let new_call_result = CallResult::RequestSentBy(exec_ctx.current_peer_id.clone()); + trace_ctx.meet_call_end(new_call_result); +} diff --git a/air/src/execution/air/call/resolved_call.rs b/air/src/execution_step/air/call/resolved_call.rs similarity index 64% rename from air/src/execution/air/call/resolved_call.rs rename to air/src/execution_step/air/call/resolved_call.rs index bc4406d7..f98271da 100644 --- a/air/src/execution/air/call/resolved_call.rs +++ b/air/src/execution_step/air/call/resolved_call.rs @@ -16,28 +16,28 @@ #![allow(unused_unsafe)] // for wasm_bindgen target where calling FFI is safe +use super::call_result_setter::*; use super::triplet::Triplet; use super::utils::*; -use super::Call; -use super::ExecutionCtx; -use super::ExecutionError; -use super::ExecutionResult; -use crate::build_targets::CallServiceResult; -use crate::build_targets::CALL_SERVICE_SUCCESS; -use crate::contexts::execution_trace::*; -use crate::log_targets::EXECUTED_STATE_CHANGING; +use super::*; +use crate::execution_step::air::ResolvedCallResult; +use crate::execution_step::trace_handler::MergerCallResult; +use crate::execution_step::trace_handler::TraceHandler; +use crate::execution_step::RSecurityTetraplet; +use crate::execution_step::SecurityTetraplets; use crate::JValue; -use crate::ResolvedTriplet; use crate::SecurityTetraplet; +use air_interpreter_data::CallResult; use air_parser::ast::{CallInstrArgValue, CallOutputValue}; +use std::cell::RefCell; use std::rc::Rc; /// Represents Call instruction with resolved internal parts. #[derive(Debug, Clone, PartialEq)] pub(super) struct ResolvedCall<'i> { - triplet: Rc, + tetraplet: RSecurityTetraplet, function_arg_paths: Rc>>, output: CallOutputValue<'i>, } @@ -45,7 +45,7 @@ pub(super) struct ResolvedCall<'i> { #[derive(Debug, Clone, PartialEq)] struct ResolvedArguments { call_arguments: String, - tetraplets: Vec>, + tetraplets: Vec, } impl<'i> ResolvedCall<'i> { @@ -53,35 +53,27 @@ impl<'i> ResolvedCall<'i> { pub(super) fn new(raw_call: &Call<'i>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult { let triplet = Triplet::try_from(&raw_call.peer_part, &raw_call.function_part)?; let triplet = triplet.resolve(exec_ctx)?; - let triplet = Rc::new(triplet); + let tetraplet = SecurityTetraplet::from_triplet(triplet); + let tetraplet = Rc::new(RefCell::new(tetraplet)); Ok(Self { - triplet, + tetraplet, function_arg_paths: raw_call.args.clone(), output: raw_call.output.clone(), }) } - /// Executes resolved instruction, updates contexts based on a execution result. - pub(super) fn execute( - self, - exec_ctx: &mut ExecutionCtx<'i>, - trace_ctx: &mut ExecutionTraceCtx, - instruction: &Call<'i>, - ) -> ExecutionResult<()> { - use CallResult::Executed; - use ExecutedState::Call; - use ExecutionError::CallServiceResultDeError as DeError; - - let should_execute = self.prepare_executed_state(exec_ctx, trace_ctx, instruction)?; + /// Executes resolved instruction, updates contexts based on a execution_step result. + pub(super) fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + let should_execute = self.prepare_current_executed_state(exec_ctx, trace_ctx)?; if !should_execute { return Ok(()); } // call can be executed only on peers with such peer_id - if self.triplet.peer_pk != exec_ctx.current_peer_id { - set_remote_call_result(self.triplet.peer_pk.clone(), exec_ctx, trace_ctx); - + let triplet = &self.tetraplet.borrow().triplet; + if triplet.peer_pk.as_str() != exec_ctx.current_peer_id.as_str() { + set_remote_call_result(triplet.peer_pk.clone(), exec_ctx, trace_ctx); return Ok(()); } @@ -94,12 +86,24 @@ impl<'i> ResolvedCall<'i> { let service_result = unsafe { crate::build_targets::call_service( - &self.triplet.service_id, - &self.triplet.function_name, + &triplet.service_id, + &triplet.function_name, &call_arguments, &serialized_tetraplets, ) }; + exec_ctx.tracker.met_executed_call(); + + self.update_state_with_service_result(service_result, exec_ctx, trace_ctx) + } + + fn update_state_with_service_result( + &self, + service_result: CallServiceResult, + exec_ctx: &mut ExecutionCtx<'i>, + trace_ctx: &mut TraceHandler, + ) -> ExecutionResult<()> { + use ExecutionError::CallServiceResultDeError as DeError; // check that service call succeeded let service_result = handle_service_error(service_result, trace_ctx)?; @@ -107,63 +111,43 @@ impl<'i> ResolvedCall<'i> { let result: JValue = serde_json::from_str(&service_result.result).map_err(|e| DeError(service_result, e))?; let result = Rc::new(result); - set_local_call_result(result.clone(), self.triplet.clone(), &self.output, exec_ctx)?; - let new_executed_state = Call(Executed(result)); + let trace_pos = trace_ctx.trace_pos(); - log::trace!( - target: EXECUTED_STATE_CHANGING, - " adding new call executed state {:?}", - new_executed_state - ); - - trace_ctx.new_trace.push_back(new_executed_state); + let executed_result = ResolvedCallResult::new(result, self.tetraplet.clone(), trace_pos); + let new_call_result = set_local_result(executed_result, &self.output, exec_ctx)?; + trace_ctx.meet_call_end(new_call_result); Ok(()) } - pub(super) fn as_triplet(&self) -> Rc { - self.triplet.clone() + pub(super) fn as_tetraplet(&self) -> RSecurityTetraplet { + self.tetraplet.clone() } /// Determine whether this call should be really called and adjust prev executed trace accordingly. - fn prepare_executed_state( + fn prepare_current_executed_state( &self, exec_ctx: &mut ExecutionCtx<'i>, - trace_ctx: &mut ExecutionTraceCtx, - instruction: &Call<'i>, + trace_ctx: &mut TraceHandler, ) -> ExecutionResult { - if trace_ctx.current_subtree_size == 0 { - log::trace!( - target: EXECUTED_STATE_CHANGING, - " previous executed trace state wasn't found" - ); - return Ok(true); - } - - trace_ctx.current_subtree_size -= 1; - // unwrap is safe here, because current_subtree_size depends on current_path len, - // and it's been checked previously - let prev_state = trace_ctx.current_trace.pop_front().unwrap(); - - log::trace!( - target: EXECUTED_STATE_CHANGING, - " previous executed trace found {:?}", - prev_state - ); + let (call_result, trace_pos) = match trace_ctx.meet_call_start(&self.output)? { + MergerCallResult::CallResult { value, trace_pos } => (value, trace_pos), + MergerCallResult::Empty => return Ok(true), + }; handle_prev_state( - &self.triplet, + &self.tetraplet, &self.output, - prev_state, + call_result, + trace_pos, exec_ctx, trace_ctx, - instruction, ) } /// Prepare arguments of this call instruction by resolving and preparing their security tetraplets. fn resolve_args(&self, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult { - use crate::execution::utils::resolve_to_args; + use crate::execution_step::utils::resolve_to_args; let function_args = self.function_arg_paths.iter(); let mut call_arguments = Vec::new(); @@ -186,12 +170,14 @@ impl<'i> ResolvedCall<'i> { } } +use crate::build_targets::CallServiceResult; + fn handle_service_error( service_result: CallServiceResult, - trace_ctx: &mut ExecutionTraceCtx, + trace_ctx: &mut TraceHandler, ) -> ExecutionResult { + use crate::build_targets::CALL_SERVICE_SUCCESS; use CallResult::CallServiceFailed; - use ExecutedState::Call; if service_result.ret_code == CALL_SERVICE_SUCCESS { return Ok(service_result); @@ -201,9 +187,7 @@ fn handle_service_error( let error = ExecutionError::LocalServiceError(service_result.ret_code, error_message.clone()); let error = Rc::new(error); - trace_ctx - .new_trace - .push_back(Call(CallServiceFailed(service_result.ret_code, error_message))); + trace_ctx.meet_call_end(CallServiceFailed(service_result.ret_code, error_message)); Err(error) } diff --git a/air/src/execution/air/call/triplet.rs b/air/src/execution_step/air/call/triplet.rs similarity index 84% rename from air/src/execution/air/call/triplet.rs rename to air/src/execution_step/air/call/triplet.rs index c43b37da..7afd91de 100644 --- a/air/src/execution/air/call/triplet.rs +++ b/air/src/execution_step/air/call/triplet.rs @@ -45,9 +45,7 @@ impl<'a, 'i> Triplet<'a, 'i> { Ok((peer_pk, peer_service_id, func_name)) } (PeerPk(peer_pk), ServiceIdWithFuncName(service_id, func_name)) => Ok((peer_pk, service_id, func_name)), - (PeerPk(_), FuncName(_)) => exec_err!(ExecutionError::InstructionError(String::from( - "call should have service id specified by peer part or function part", - ))), + (PeerPk(_), FuncName(_)) => exec_err!(ExecutionError::IncorrectCallTriplet), }?; Ok(Self { @@ -79,31 +77,23 @@ impl<'a, 'i> Triplet<'a, 'i> { /// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc. // TODO: return Rc to avoid excess cloning fn resolve_to_string<'i>(value: &CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult { - use crate::execution::utils::get_variable_name; - use crate::execution::utils::resolve_to_jvaluable; + use crate::execution_step::utils::resolve_ast_variable; let resolved = match value { CallInstrValue::InitPeerId => ctx.init_peer_id.clone(), CallInstrValue::Literal(value) => value.to_string(), CallInstrValue::Variable(variable) => { - let name = get_variable_name(variable); - let resolved = resolve_to_jvaluable(name, ctx)?; + let resolved = resolve_ast_variable(variable, ctx)?; let jvalue = resolved.into_jvalue(); jvalue_to_string(jvalue)? } - CallInstrValue::JsonPath { - variable, - path, - should_flatten, - } => { + CallInstrValue::JsonPath(json_path) => { // this is checked on the parsing stage - debug_assert!(*should_flatten); + debug_assert!(json_path.should_flatten); - let name = get_variable_name(variable); - - let resolved = resolve_to_jvaluable(name, ctx)?; - let resolved = resolved.apply_json_path(path)?; - vec_to_string(resolved, path)? + let resolved = resolve_ast_variable(&json_path.variable, ctx)?; + let resolved = resolved.apply_json_path(json_path.path)?; + vec_to_string(resolved, json_path.path)? } }; diff --git a/air/src/execution_step/air/call/utils.rs b/air/src/execution_step/air/call/utils.rs new file mode 100644 index 00000000..56ab05f5 --- /dev/null +++ b/air/src/execution_step/air/call/utils.rs @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use crate::exec_err; +use crate::execution_step::trace_handler::TraceHandler; +use crate::execution_step::RSecurityTetraplet; + +use crate::execution_step::air::call::call_result_setter::set_result_from_value; +use air_interpreter_data::CallResult; +use air_parser::ast::CallOutputValue; + +/// This function looks at the existing call state, validates it, +/// and returns Ok(true) if the call should be executed further. +pub(super) fn handle_prev_state<'i>( + tetraplet: &RSecurityTetraplet, + output: &CallOutputValue<'i>, + prev_result: CallResult, + trace_pos: usize, + exec_ctx: &mut ExecutionCtx<'i>, + trace_ctx: &mut TraceHandler, +) -> ExecutionResult { + use CallResult::*; + + let result = match &prev_result { + // this call was failed on one of the previous executions, + // here it's needed to bubble this special error up + CallServiceFailed(ret_code, err_msg) => { + exec_ctx.subtree_complete = false; + exec_err!(ExecutionError::LocalServiceError(*ret_code, err_msg.clone())) + } + RequestSentBy(..) => { + // check whether current node can execute this call + let is_current_peer = tetraplet.borrow().triplet.peer_pk.as_str() == exec_ctx.current_peer_id.as_str(); + if is_current_peer { + // if this peer could execute this call early return and + return Ok(true); + } + + exec_ctx.subtree_complete = false; + Ok(false) + } + // this instruction's been already executed + Executed(value) => { + set_result_from_value(value.clone(), tetraplet.clone(), trace_pos, output, exec_ctx)?; + + exec_ctx.subtree_complete = true; + Ok(false) + } + }; + + trace_ctx.meet_call_end(prev_result); + result +} diff --git a/air/src/execution/air/compare_matchable/compare_matchable.rs b/air/src/execution_step/air/compare_matchable/comparator.rs similarity index 74% rename from air/src/execution/air/compare_matchable/compare_matchable.rs rename to air/src/execution_step/air/compare_matchable/comparator.rs index 8e052e3a..a2573b99 100644 --- a/air/src/execution/air/compare_matchable/compare_matchable.rs +++ b/air/src/execution_step/air/compare_matchable/comparator.rs @@ -14,10 +14,9 @@ * limitations under the License. */ -use crate::contexts::execution::ExecutionCtx; -use crate::execution::air::ExecutionResult; -use crate::execution::utils::get_variable_name; -use crate::execution::utils::resolve_to_jvaluable; +use crate::execution_step::air::ExecutionResult; +use crate::execution_step::execution_context::ExecutionCtx; +use crate::execution_step::utils::resolve_ast_variable; use crate::JValue; use air_parser::ast; @@ -54,40 +53,25 @@ pub(crate) fn are_matchable_eq<'ctx>( (matchable, Number(value)) => compare_matchable(matchable, exec_ctx, make_number_comparator(value)), (Variable(left_variable), Variable(right_variable)) => { - let left_name = get_variable_name(left_variable); - let left_jvaluable = resolve_to_jvaluable(left_name, exec_ctx)?; + let left_jvaluable = resolve_ast_variable(left_variable, exec_ctx)?; let left_value = left_jvaluable.as_jvalue(); - let right_name = get_variable_name(right_variable); - let right_jvaluable = resolve_to_jvaluable(right_name, exec_ctx)?; + let right_jvaluable = resolve_ast_variable(right_variable, exec_ctx)?; let right_value = right_jvaluable.as_jvalue(); Ok(left_value == right_value) } - ( - JsonPath { - variable: lv, - path: lp, - should_flatten: lsf, - }, - JsonPath { - variable: rv, - path: rp, - should_flatten: rsf, - }, - ) => { + (JsonPath(lhs), JsonPath(rhs)) => { // TODO: improve comparison - if lsf != rsf { + if lhs.should_flatten != rhs.should_flatten { return Ok(false); } - let left_name = get_variable_name(lv); - let left_jvaluable = resolve_to_jvaluable(left_name, exec_ctx)?; - let left_value = left_jvaluable.apply_json_path(lp)?; + let left_jvaluable = resolve_ast_variable(&lhs.variable, exec_ctx)?; + let left_value = left_jvaluable.apply_json_path(lhs.path)?; - let right_name = get_variable_name(rv); - let right_jvaluable = resolve_to_jvaluable(right_name, exec_ctx)?; - let right_value = right_jvaluable.apply_json_path(rp)?; + let right_jvaluable = resolve_ast_variable(&rhs.variable, exec_ctx)?; + let right_value = right_jvaluable.apply_json_path(rhs.path)?; Ok(left_value == right_value) } @@ -123,22 +107,20 @@ fn compare_matchable<'ctx>( let jvalue = (*bool).into(); Ok(comparator(Cow::Owned(jvalue))) } + EmptyArray => { + let jvalue = JValue::Array(vec![]); + Ok(comparator(Cow::Owned(jvalue))) + } Variable(variable) => { - let name = get_variable_name(variable); - let jvaluable = resolve_to_jvaluable(name, exec_ctx)?; + let jvaluable = resolve_ast_variable(variable, exec_ctx)?; let jvalue = jvaluable.as_jvalue(); Ok(comparator(jvalue)) } - JsonPath { - variable, - path, - should_flatten, - } => { - let var_name = get_variable_name(variable); - let jvaluable = resolve_to_jvaluable(var_name, exec_ctx)?; - let jvalues = jvaluable.apply_json_path(path)?; + JsonPath(json_path) => { + let jvaluable = resolve_ast_variable(&json_path.variable, exec_ctx)?; + let jvalues = jvaluable.apply_json_path(json_path.path)?; - let jvalue = if *should_flatten { + let jvalue = if json_path.should_flatten { if jvalues.len() != 1 { return Ok(false); } diff --git a/air/src/execution/air/compare_matchable/mod.rs b/air/src/execution_step/air/compare_matchable/mod.rs similarity index 88% rename from air/src/execution/air/compare_matchable/mod.rs rename to air/src/execution_step/air/compare_matchable/mod.rs index 9f529851..e3d4dbc1 100644 --- a/air/src/execution/air/compare_matchable/mod.rs +++ b/air/src/execution_step/air/compare_matchable/mod.rs @@ -14,6 +14,6 @@ * limitations under the License. */ -mod compare_matchable; +mod comparator; -pub(super) use compare_matchable::are_matchable_eq; +pub(super) use comparator::are_matchable_eq; diff --git a/air/src/execution_step/air/fold/fold_state.rs b/air/src/execution_step/air/fold/fold_state.rs new file mode 100644 index 00000000..a0d9ff37 --- /dev/null +++ b/air/src/execution_step/air/fold/fold_state.rs @@ -0,0 +1,51 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::Instruction; +use super::IterableValue; +use super::ResolvedCallResult; + +use std::collections::HashMap; +use std::rc::Rc; + +pub(crate) struct FoldState<'i> { + pub(crate) iterable: IterableValue, + pub(crate) iterable_type: IterableType, + pub(crate) instr_head: Rc>, + // map of met variables inside this (not any inner) fold block with their initial values + pub(crate) met_variables: HashMap<&'i str, ResolvedCallResult>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum IterableType { + Scalar, + Stream(usize), +} + +impl<'i> FoldState<'i> { + pub(crate) fn from_iterable( + iterable: IterableValue, + iterable_type: IterableType, + instr_head: Rc>, + ) -> Self { + Self { + iterable, + iterable_type, + instr_head, + met_variables: HashMap::new(), + } + } +} diff --git a/air/src/execution_step/air/fold/mod.rs b/air/src/execution_step/air/fold/mod.rs new file mode 100644 index 00000000..7fb4f242 --- /dev/null +++ b/air/src/execution_step/air/fold/mod.rs @@ -0,0 +1,32 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod fold_state; +mod utils; +mod variable_handler; + +pub(crate) use fold_state::FoldState; +pub(crate) use fold_state::IterableType; +pub(super) use utils::*; +pub(super) use variable_handler::VariableHandler; + +use super::ExecutionCtx; +use super::ExecutionError; +use super::ExecutionResult; +use super::Instruction; +use super::ResolvedCallResult; +use super::Scalar; +use crate::execution_step::boxed_value::*; diff --git a/air/src/execution_step/air/fold/utils.rs b/air/src/execution_step/air/fold/utils.rs new file mode 100644 index 00000000..b2408e82 --- /dev/null +++ b/air/src/execution_step/air/fold/utils.rs @@ -0,0 +1,210 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use crate::exec_err; +use crate::execution_step::RSecurityTetraplet; +use crate::JValue; + +use air_parser::ast; +use jsonpath_lib::select; + +use std::ops::Deref; +use std::rc::Rc; + +// TODO: refactor this file after switching to boxed value + +pub(crate) type IterableValue = Box Iterable<'ctx, Item = IterableItem<'ctx>>>; + +pub(crate) enum FoldIterableScalar { + Empty, + Scalar(IterableValue), +} + +pub(crate) enum FoldIterableStream { + Empty, + Stream(Vec), +} + +/// Constructs iterable value for given scalar iterable. +pub(crate) fn construct_scalar_iterable_value<'ctx>( + ast_iterable: &ast::IterableScalarValue<'ctx>, + exec_ctx: &ExecutionCtx<'ctx>, +) -> ExecutionResult { + match ast_iterable { + ast::IterableScalarValue::ScalarVariable(scalar_name) => create_scalar_iterable(exec_ctx, scalar_name), + ast::IterableScalarValue::JsonPath { + scalar_name, + path, + should_flatten, + } => create_scalar_json_path_iterable(exec_ctx, scalar_name, path, *should_flatten), + } +} + +/// Constructs iterable value for given stream iterable. +pub(crate) fn construct_stream_iterable_value<'ctx>( + stream_name: &'ctx str, + exec_ctx: &ExecutionCtx<'ctx>, +) -> ExecutionResult { + match exec_ctx.streams.get(stream_name) { + Some(stream) => { + let stream = stream.borrow(); + if stream.is_empty() { + return Ok(FoldIterableStream::Empty); + } + + let mut iterables = Vec::with_capacity(stream.generations_count()); + + for iterable in stream.slice_iter(Generation::Last).unwrap() { + if iterable.is_empty() { + continue; + } + + let call_results = iterable.to_vec(); + let foldable = IterableVecResolvedCall::init(call_results); + let foldable: IterableValue = Box::new(foldable); + iterables.push(foldable); + } + + Ok(FoldIterableStream::Stream(iterables)) + } + // it's possible to met streams without variables at the moment in fold, + // they should be treated as empty. + None => Ok(FoldIterableStream::Empty), + } +} + +fn create_scalar_iterable<'ctx>( + exec_ctx: &ExecutionCtx<'ctx>, + variable_name: &str, +) -> ExecutionResult { + match exec_ctx.scalars.get(variable_name) { + Some(Scalar::JValueRef(call_result)) => from_call_result(call_result.clone()), + Some(Scalar::JValueFoldCursor(fold_state)) => { + let iterable_value = fold_state.iterable.peek().unwrap(); + let call_result = iterable_value.into_resolved_result(); + from_call_result(call_result) + } + _ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())), + } +} + +/// Constructs iterable value from resolved call result. +fn from_call_result(call_result: ResolvedCallResult) -> ExecutionResult { + use ExecutionError::IncompatibleJValueType; + + let len = match &call_result.result.deref() { + JValue::Array(array) => { + if array.is_empty() { + // skip fold if array is empty + return Ok(FoldIterableScalar::Empty); + } + array.len() + } + v => return exec_err!(IncompatibleJValueType((*v).clone(), "array")), + }; + + let foldable = IterableResolvedCall::init(call_result, len); + let foldable = Box::new(foldable); + let iterable = FoldIterableScalar::Scalar(foldable); + + Ok(iterable) +} + +fn create_scalar_json_path_iterable<'ctx>( + exec_ctx: &ExecutionCtx<'ctx>, + scalar_name: &str, + json_path: &str, + should_flatten: bool, +) -> ExecutionResult { + match exec_ctx.scalars.get(scalar_name) { + Some(Scalar::JValueRef(variable)) => { + let jvalues = apply_json_path(&variable.result, json_path)?; + from_jvalues(jvalues, variable.tetraplet.clone(), json_path, should_flatten) + } + Some(Scalar::JValueFoldCursor(fold_state)) => { + let iterable_value = fold_state.iterable.peek().unwrap(); + let jvalues = iterable_value.apply_json_path(json_path)?; + let tetraplet = as_tetraplet(&iterable_value); + + from_jvalues(jvalues, tetraplet, json_path, should_flatten) + } + _ => return exec_err!(ExecutionError::VariableNotFound(scalar_name.to_string())), + } +} + +fn apply_json_path<'jvalue, 'str>( + jvalue: &'jvalue JValue, + json_path: &'str str, +) -> ExecutionResult> { + use ExecutionError::JValueJsonPathError; + + select(jvalue, json_path).map_err(|e| Rc::new(JValueJsonPathError(jvalue.clone(), json_path.to_string(), e))) +} + +/// Applies json_path to provided jvalues and construct IterableValue from the result and given triplet. +fn from_jvalues( + jvalues: Vec<&JValue>, + tetraplet: RSecurityTetraplet, + json_path: &str, + should_flatten: bool, +) -> ExecutionResult { + let jvalues = construct_iterable_jvalues(jvalues, should_flatten)?; + + if jvalues.is_empty() { + return Ok(FoldIterableScalar::Empty); + } + + tetraplet.borrow_mut().add_json_path(json_path); + + let foldable = IterableJsonPathResult::init(jvalues, tetraplet); + let iterable = FoldIterableScalar::Scalar(Box::new(foldable)); + Ok(iterable) +} + +fn construct_iterable_jvalues(jvalues: Vec<&JValue>, should_flatten: bool) -> ExecutionResult> { + if !should_flatten { + let jvalues = jvalues.into_iter().cloned().collect(); + return Ok(jvalues); + } + + if jvalues.len() != 1 { + let jvalues = jvalues.into_iter().cloned().collect(); + let jvalue = JValue::Array(jvalues); + return exec_err!(ExecutionError::FlatteningError(jvalue)); + } + + match jvalues[0] { + JValue::Array(values) => Ok(values.clone()), + _ => { + let jvalues = jvalues.into_iter().cloned().collect(); + let jvalue = JValue::Array(jvalues); + exec_err!(ExecutionError::FlatteningError(jvalue)) + } + } +} + +fn as_tetraplet(iterable: &IterableItem<'_>) -> RSecurityTetraplet { + use IterableItem::*; + + let tetraplet = match iterable { + RefRef((_, tetraplet, _)) => tetraplet, + RefValue((_, tetraplet, _)) => tetraplet, + RcValue((_, tetraplet, _)) => tetraplet, + }; + + (*tetraplet).clone() +} diff --git a/air/src/execution_step/air/fold/variable_handler.rs b/air/src/execution_step/air/fold/variable_handler.rs new file mode 100644 index 00000000..db9eed33 --- /dev/null +++ b/air/src/execution_step/air/fold/variable_handler.rs @@ -0,0 +1,89 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionCtx; +use super::ExecutionResult; +use super::FoldState; +use super::Scalar; + +use std::collections::HashMap; + +pub(crate) struct VariableHandler<'i> { + iterator: &'i str, +} + +impl<'i> VariableHandler<'i> { + pub(crate) fn init<'ctx: 'i>( + exec_ctx: &mut ExecutionCtx<'ctx>, + iterator: &'ctx str, + fold_state: FoldState<'ctx>, + ) -> ExecutionResult { + Self::try_insert_fold_state(exec_ctx, iterator, fold_state)?; + Self::meet_iterator(exec_ctx, iterator); + + let handler = Self { iterator }; + Ok(handler) + } + + pub(crate) fn cleanup(self, exec_ctx: &mut ExecutionCtx<'_>) { + let fold_state = match exec_ctx.scalars.remove(self.iterator) { + Some(Scalar::JValueFoldCursor(fold_state)) => fold_state, + _ => unreachable!("fold cursor is changed only inside fold block"), + }; + + for (variable_name, _) in fold_state.met_variables { + exec_ctx.scalars.remove(variable_name); + } + exec_ctx.met_folds.pop_back(); + + // TODO: fix 3 or more inner folds behaviour + if let Some(fold_block_name) = exec_ctx.met_folds.back() { + let fold_state = match exec_ctx.scalars.get(*fold_block_name) { + Some(Scalar::JValueFoldCursor(fold_state)) => fold_state, + _ => unreachable!("fold block data must be represented as fold cursor"), + }; + + let mut upper_fold_values = HashMap::new(); + for (variable_name, variable) in fold_state.met_variables.iter() { + upper_fold_values.insert(variable_name.to_string(), Scalar::JValueRef(variable.clone())); + } + + exec_ctx.scalars.extend(upper_fold_values); + } + } + + fn try_insert_fold_state<'ctx>( + exec_ctx: &mut ExecutionCtx<'ctx>, + iterator: &'ctx str, + fold_state: FoldState<'ctx>, + ) -> ExecutionResult<()> { + use super::ExecutionError::MultipleFoldStates; + + let previous_value = exec_ctx + .scalars + .insert(iterator.to_string(), Scalar::JValueFoldCursor(fold_state)); + + if previous_value.is_some() { + return crate::exec_err!(MultipleFoldStates(iterator.to_string())); + } + + Ok(()) + } + + fn meet_iterator<'ctx>(exec_ctx: &mut ExecutionCtx<'ctx>, iterator: &'ctx str) { + exec_ctx.met_folds.push_back(iterator); + } +} diff --git a/air/src/execution_step/air/fold_scalar.rs b/air/src/execution_step/air/fold_scalar.rs new file mode 100644 index 00000000..00872afd --- /dev/null +++ b/air/src/execution_step/air/fold_scalar.rs @@ -0,0 +1,62 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::fold::*; +use super::ExecutableInstruction; +use super::ExecutionCtx; +use super::ExecutionResult; +use super::TraceHandler; +use crate::log_instruction; + +use air_parser::ast::FoldScalar; +use air_parser::ast::Instruction; +use std::rc::Rc; + +impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + log_instruction!(fold, exec_ctx, trace_ctx); + + match construct_scalar_iterable_value(&self.iterable, exec_ctx)? { + FoldIterableScalar::Empty => Ok(()), + FoldIterableScalar::Scalar(iterable) => fold( + iterable, + IterableType::Scalar, + self.iterator, + self.instruction.clone(), + exec_ctx, + trace_ctx, + ), + } + } +} + +pub(super) fn fold<'i>( + iterable: IterableValue, + iterable_type: IterableType, + iterator: &'i str, + instruction: Rc>, + exec_ctx: &mut ExecutionCtx<'i>, + trace_ctx: &mut TraceHandler, +) -> ExecutionResult<()> { + let fold_state = FoldState::from_iterable(iterable, iterable_type, instruction.clone()); + let variable_handler = VariableHandler::init(exec_ctx, iterator, fold_state)?; + + instruction.execute(exec_ctx, trace_ctx)?; + + variable_handler.cleanup(exec_ctx); + + Ok(()) +} diff --git a/air/src/execution_step/air/fold_stream.rs b/air/src/execution_step/air/fold_stream.rs new file mode 100644 index 00000000..0a4f1af3 --- /dev/null +++ b/air/src/execution_step/air/fold_stream.rs @@ -0,0 +1,68 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::fold::*; +use super::fold_scalar::fold; +use super::ExecutableInstruction; +use super::ExecutionCtx; +use super::ExecutionResult; +use super::TraceHandler; +use crate::log_instruction; + +use air_parser::ast::FoldStream; + +impl<'i> ExecutableInstruction<'i> for FoldStream<'i> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + log_instruction!(fold, exec_ctx, trace_ctx); + + let iterables = match construct_stream_iterable_value(self.stream_name, exec_ctx)? { + FoldIterableStream::Empty => return Ok(()), + FoldIterableStream::Stream(iterables) => iterables, + }; + + let fold_id = exec_ctx.tracker.fold.seen_stream_count; + trace_ctx.meet_fold_start(fold_id)?; + + for iterable in iterables { + let value = match iterable.peek() { + Some(value) => value, + // it's ok, because some generation level of a stream on some point inside execution + // flow could contain zero values + None => continue, + }; + + let value_pos = value.pos(); + trace_ctx.meet_iteration_start(fold_id, value_pos)?; + fold( + iterable, + IterableType::Stream(fold_id), + self.iterator, + self.instruction.clone(), + exec_ctx, + trace_ctx, + )?; + trace_ctx.meet_generation_end(fold_id)?; + + if !exec_ctx.subtree_complete { + break; + } + } + + trace_ctx.meet_fold_end(fold_id)?; + + Ok(()) + } +} diff --git a/air/src/execution/air/match_.rs b/air/src/execution_step/air/match_.rs similarity index 91% rename from air/src/execution/air/match_.rs rename to air/src/execution_step/air/match_.rs index 4c402dcd..2009dfc7 100644 --- a/air/src/execution/air/match_.rs +++ b/air/src/execution_step/air/match_.rs @@ -18,15 +18,15 @@ use super::compare_matchable::are_matchable_eq; use super::ExecutionCtx; use super::ExecutionError; use super::ExecutionResult; -use super::ExecutionTraceCtx; -use crate::execution::joinable::Joinable; +use super::TraceHandler; +use crate::execution_step::joinable::Joinable; use crate::joinable; use crate::log_instruction; use air_parser::ast::Match; impl<'i> super::ExecutableInstruction<'i> for Match<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { log_instruction!(match_, exec_ctx, trace_ctx); let are_values_equal = joinable!( diff --git a/air/src/execution/air/mismatch.rs b/air/src/execution_step/air/mismatch.rs similarity index 91% rename from air/src/execution/air/mismatch.rs rename to air/src/execution_step/air/mismatch.rs index aaad0073..85c00c02 100644 --- a/air/src/execution/air/mismatch.rs +++ b/air/src/execution_step/air/mismatch.rs @@ -18,15 +18,15 @@ use super::compare_matchable::are_matchable_eq; use super::ExecutionCtx; use super::ExecutionError; use super::ExecutionResult; -use super::ExecutionTraceCtx; -use crate::execution::joinable::Joinable; +use super::TraceHandler; +use crate::execution_step::joinable::Joinable; use crate::joinable; use crate::log_instruction; use air_parser::ast::MisMatch; impl<'i> super::ExecutableInstruction<'i> for MisMatch<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { log_instruction!(match_, exec_ctx, trace_ctx); let are_values_equal = joinable!( diff --git a/air/src/execution/air/mod.rs b/air/src/execution_step/air/mod.rs similarity index 69% rename from air/src/execution/air/mod.rs rename to air/src/execution_step/air/mod.rs index f36b017e..020b6327 100644 --- a/air/src/execution/air/mod.rs +++ b/air/src/execution_step/air/mod.rs @@ -14,11 +14,15 @@ * limitations under the License. */ +mod ap; mod call; mod compare_matchable; mod fold; +mod fold_scalar; +mod fold_stream; mod match_; mod mismatch; +mod next; mod null; mod par; mod seq; @@ -26,11 +30,14 @@ mod xor; pub(crate) use fold::FoldState; -pub(self) use super::ExecutionError; -pub(self) use super::ExecutionResult; -pub(self) use crate::contexts::execution::ExecutionCtx; -pub(self) use crate::contexts::execution::LastErrorDescriptor; -pub(self) use crate::contexts::execution_trace::ExecutionTraceCtx; +use super::boxed_value::ResolvedCallResult; +use super::boxed_value::Scalar; +use super::execution_context::*; +use super::Catchable; +use super::ExecutionCtx; +use super::ExecutionError; +use super::ExecutionResult; +use crate::execution_step::TraceHandler; use air_parser::ast::Instruction; @@ -45,7 +52,7 @@ macro_rules! execute { let instruction = format!("{}", $self); let last_error = - LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.clone(), None); + LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.to_string(), None); $exec_ctx.last_error = Some(last_error); Err(e) } @@ -54,6 +61,31 @@ macro_rules! execute { }; } +/// Executes fold instruction, updates last error if needed, and call error_exit of TraceHandler. +macro_rules! execute_fold { + ($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {{ + $exec_ctx.tracker.met_fold_stream(); + let fold_id = $exec_ctx.tracker.fold.seen_stream_count; + + match $instr.execute($exec_ctx, $trace_ctx) { + Err(e) => { + $trace_ctx.fold_end_with_error(fold_id); + + if !$exec_ctx.last_error_could_be_set { + return Err(e); + } + + let instruction = format!("{}", $self); + let last_error = + LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.to_string(), None); + $exec_ctx.last_error = Some(last_error); + Err(e) + } + v => v, + } + }}; +} + /// Executes match/mismatch instructions and updates last error if error type wasn't /// MatchWithoutXorError or MismatchWithoutXorError. macro_rules! execute_match_mismatch { @@ -71,7 +103,7 @@ macro_rules! execute_match_mismatch { let instruction = format!("{}", $self); let last_error = - LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.clone(), None); + LastErrorDescriptor::new(e.clone(), instruction, $exec_ctx.current_peer_id.to_string(), None); $exec_ctx.last_error = Some(last_error); Err(e) } @@ -81,17 +113,19 @@ macro_rules! execute_match_mismatch { } pub(crate) trait ExecutableInstruction<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()>; + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()>; } impl<'i> ExecutableInstruction<'i> for Instruction<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { match self { // call isn't wrapped by the execute macro because // it internally sets last_error with resolved triplet Instruction::Call(call) => call.execute(exec_ctx, trace_ctx), - Instruction::Fold(fold) => execute!(self, fold, exec_ctx, trace_ctx), + Instruction::Ap(ap) => execute!(self, ap, exec_ctx, trace_ctx), + Instruction::FoldScalar(fold) => execute!(self, fold, exec_ctx, trace_ctx), + Instruction::FoldStream(fold) => execute_fold!(self, fold, exec_ctx, trace_ctx), Instruction::Next(next) => execute!(self, next, exec_ctx, trace_ctx), Instruction::Null(null) => execute!(self, null, exec_ctx, trace_ctx), Instruction::Par(par) => execute!(self, par, exec_ctx, trace_ctx), @@ -112,15 +146,23 @@ macro_rules! log_instruction { ($instr_name:expr, $exec_ctx:expr, $trace_ctx:expr) => { log::debug!(target: crate::log_targets::INSTRUCTION, "> {}", stringify!($instr_name)); - let mut data_cache_log = String::from(" data cache:"); - if $exec_ctx.data_cache.is_empty() { - data_cache_log.push_str(" empty"); + let mut variables = String::from(" scalars:"); + if $exec_ctx.scalars.is_empty() { + variables.push_str(" empty"); } - for (key, value) in $exec_ctx.data_cache.iter() { - data_cache_log.push_str(&format!("\n {} => {}", key, value)); + for (key, value) in $exec_ctx.scalars.iter() { + variables.push_str(&format!("\n {} => {}", key, value)); } - log::trace!(target: crate::log_targets::DATA_CACHE, "{}", data_cache_log); + variables.push_str(" streams:"); + if $exec_ctx.streams.is_empty() { + variables.push_str(" empty"); + } + for (key, value) in $exec_ctx.streams.iter() { + variables.push_str(&format!("\n {} => {}", key, value.borrow())); + } + + log::trace!(target: crate::log_targets::DATA_CACHE, "{}", variables); log::trace!( target: crate::log_targets::NEXT_PEER_PKS, " next peers pk: {:?}", @@ -132,20 +174,15 @@ macro_rules! log_instruction { $exec_ctx.subtree_complete ); - log::debug!( - target: crate::log_targets::EXECUTED_TRACE, - " current call executed trace: {:?}", - $trace_ctx.current_trace - ); log::trace!( target: crate::log_targets::SUBTREE_ELEMENTS, " subtree elements count: {:?}", - $trace_ctx.current_subtree_size + $trace_ctx.subtree_sizes() ); log::debug!( target: crate::log_targets::NEW_EXECUTED_TRACE, " new call executed trace: {:?}", - $trace_ctx.new_trace + $trace_ctx.as_result_trace() ); }; } diff --git a/air/src/execution_step/air/next.rs b/air/src/execution_step/air/next.rs new file mode 100644 index 00000000..09c2c022 --- /dev/null +++ b/air/src/execution_step/air/next.rs @@ -0,0 +1,112 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::fold::IterableType; +use super::ExecutionCtx; +use super::ExecutionError; +use super::ExecutionResult; +use super::FoldState; +use super::Scalar; +use super::TraceHandler; +use crate::exec_err; +use crate::log_instruction; + +use air_parser::ast::Next; + +impl<'i> super::ExecutableInstruction<'i> for Next<'i> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + log_instruction!(next, exec_ctx, trace_ctx); + + let iterator_name = self.0; + let fold_state = try_get_fold_state(exec_ctx, iterator_name)?; + maybe_meet_iteration_end(fold_state, trace_ctx)?; + + if !fold_state.iterable.next() { + maybe_meet_back_iterator(fold_state, trace_ctx)?; + + // just do nothing to exit + return Ok(()); + } + + let next_instr = fold_state.instr_head.clone(); + maybe_meet_iteration_start(fold_state, trace_ctx)?; + + next_instr.execute(exec_ctx, trace_ctx)?; + + // get the same fold state again because of borrow checker + match exec_ctx.scalars.get_mut(iterator_name) { + // move iterator back to provide correct value for possible subtree after next + // (for example for cases such as right fold) + Some(Scalar::JValueFoldCursor(fold_state)) => fold_state.iterable.prev(), + _ => unreachable!("iterator value shouldn't changed inside fold"), + }; + + // get this fold state the second time to bypass borrow checker + let fold_state = try_get_fold_state(exec_ctx, iterator_name)?; + maybe_meet_back_iterator(fold_state, trace_ctx)?; + + Ok(()) + } +} + +fn try_get_fold_state<'i, 'ctx>( + exec_ctx: &'ctx mut ExecutionCtx<'i>, + iterator_name: &str, +) -> ExecutionResult<&'ctx mut FoldState<'i>> { + use ExecutionError::FoldStateNotFound; + use ExecutionError::IncompatibleAValueType; + + let avalue = exec_ctx + .scalars + .get_mut(iterator_name) + .ok_or_else(|| FoldStateNotFound(iterator_name.to_string()))?; + + match avalue { + Scalar::JValueFoldCursor(state) => Ok(state), + v => { + // it's not possible to use unreachable here + // because at now next syntactically could be used without fold + exec_err!(IncompatibleAValueType( + format!("{}", v), + String::from("JValueFoldCursor"), + )) + } + } +} + +fn maybe_meet_iteration_start(fold_state: &FoldState<'_>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + if let IterableType::Stream(fold_id) = &fold_state.iterable_type { + trace_ctx.meet_iteration_start(*fold_id, fold_state.iterable.peek().unwrap().pos())?; + } + + Ok(()) +} + +fn maybe_meet_iteration_end(fold_state: &FoldState<'_>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + if let IterableType::Stream(fold_id) = &fold_state.iterable_type { + trace_ctx.meet_iteration_end(*fold_id)?; + } + + Ok(()) +} + +fn maybe_meet_back_iterator(fold_state: &FoldState<'_>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + if let IterableType::Stream(fold_id) = &fold_state.iterable_type { + trace_ctx.meet_back_iterator(*fold_id)?; + } + + Ok(()) +} diff --git a/air/src/execution/air/null.rs b/air/src/execution_step/air/null.rs similarity index 91% rename from air/src/execution/air/null.rs rename to air/src/execution_step/air/null.rs index 0d030677..3a1f9646 100644 --- a/air/src/execution/air/null.rs +++ b/air/src/execution_step/air/null.rs @@ -16,13 +16,13 @@ use super::ExecutionCtx; use super::ExecutionResult; -use super::ExecutionTraceCtx; +use super::TraceHandler; use crate::log_instruction; use air_parser::ast::Null; impl<'i> super::ExecutableInstruction<'i> for Null { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { log_instruction!(null, exec_ctx, trace_ctx); Ok(()) diff --git a/air/src/execution_step/air/par.rs b/air/src/execution_step/air/par.rs new file mode 100644 index 00000000..9072030f --- /dev/null +++ b/air/src/execution_step/air/par.rs @@ -0,0 +1,111 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod completeness_updater; + +use super::Catchable; +use super::ExecutableInstruction; +use super::ExecutionCtx; +use super::ExecutionError; +use super::ExecutionResult; +use super::Instruction; +use super::TraceHandler; +use crate::execution_step::trace_handler::SubtreeType; +use crate::log_instruction; +use completeness_updater::ParCompletenessUpdater; + +use air_parser::ast::Par; +use std::rc::Rc; + +#[rustfmt::skip] +impl<'i> ExecutableInstruction<'i> for Par<'i> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { + log_instruction!(par, exec_ctx, trace_ctx); + + let mut completeness_updater = ParCompletenessUpdater::new(); + trace_ctx.meet_par_start()?; + + // execute a left subtree of par + let left_result = execute_subtree(&self.0, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Left)?; + + // execute a right subtree of par + let right_result = execute_subtree(&self.1, exec_ctx, trace_ctx, &mut completeness_updater, SubtreeType::Right)?; + + completeness_updater.set_completeness(exec_ctx); + prepare_par_result(left_result, right_result, exec_ctx) + } +} + +/// Execute provided subtree and update Par state in trace_ctx.new_trace. +fn execute_subtree<'i>( + subtree: &Instruction<'i>, + exec_ctx: &mut ExecutionCtx<'i>, + trace_ctx: &mut TraceHandler, + completeness_updater: &mut ParCompletenessUpdater, + subtree_type: SubtreeType, +) -> ExecutionResult { + exec_ctx.subtree_complete = determine_subtree_complete(subtree); + + // execute a subtree + let result = match subtree.execute(exec_ctx, trace_ctx) { + Ok(_) => { + trace_ctx.meet_par_subtree_end(subtree_type)?; + SubtreeResult::Succeeded + } + Err(e) if !e.is_catchable() => { + return Err(e); + } + Err(e) => { + trace_ctx.meet_par_subtree_end(subtree_type)?; + SubtreeResult::Failed(e) + } + }; + + completeness_updater.update_completeness(exec_ctx, subtree_type); + Ok(result) +} + +enum SubtreeResult { + Succeeded, + Failed(Rc), +} + +fn prepare_par_result( + left_result: SubtreeResult, + right_result: SubtreeResult, + exec_ctx: &mut ExecutionCtx<'_>, +) -> ExecutionResult<()> { + match (left_result, right_result) { + (SubtreeResult::Succeeded, _) | (_, SubtreeResult::Succeeded) => { + // clear the last error in case of par succeeded + exec_ctx.last_error = None; + Ok(()) + } + (SubtreeResult::Failed(_), SubtreeResult::Failed(err)) => Err(err), + } +} + +fn determine_subtree_complete(next_instruction: &Instruction<'_>) -> bool { + // this is needed to prevent situation when on such pattern + // (fold (Iterable i + // (par + // (call ..) + // (next i) + // ) + // ) + // par will be completed after the last next that wouldn't change subtree_complete + !matches!(next_instruction, Instruction::Next(_)) +} diff --git a/air/src/execution_step/air/par/completeness_updater.rs b/air/src/execution_step/air/par/completeness_updater.rs new file mode 100644 index 00000000..bd650d82 --- /dev/null +++ b/air/src/execution_step/air/par/completeness_updater.rs @@ -0,0 +1,46 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionCtx; +use super::SubtreeType; + +#[derive(Debug, Default, Clone)] +pub(super) struct ParCompletenessUpdater { + left_subtree_complete: bool, + right_subtree_complete: bool, +} + +impl ParCompletenessUpdater { + pub(super) fn new() -> Self { + Self { + left_subtree_complete: false, + right_subtree_complete: false, + } + } + + pub(super) fn update_completeness(&mut self, exec_ctx: &ExecutionCtx<'_>, subtree_type: SubtreeType) { + match subtree_type { + SubtreeType::Left => self.left_subtree_complete = exec_ctx.subtree_complete, + SubtreeType::Right => self.right_subtree_complete = exec_ctx.subtree_complete, + } + } + + pub(super) fn set_completeness(self, exec_ctx: &mut ExecutionCtx<'_>) { + // par is completed if at least one of its subtrees is completed + let subtree_complete = self.left_subtree_complete || self.right_subtree_complete; + exec_ctx.subtree_complete = subtree_complete; + } +} diff --git a/air/src/execution/air/seq.rs b/air/src/execution_step/air/seq.rs similarity index 93% rename from air/src/execution/air/seq.rs rename to air/src/execution_step/air/seq.rs index b75ea082..93d1ec09 100644 --- a/air/src/execution/air/seq.rs +++ b/air/src/execution_step/air/seq.rs @@ -16,13 +16,13 @@ use super::ExecutionCtx; use super::ExecutionResult; -use super::ExecutionTraceCtx; +use super::TraceHandler; use crate::log_instruction; use air_parser::ast::Seq; impl<'i> super::ExecutableInstruction<'i> for Seq<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { log_instruction!(seq, exec_ctx, trace_ctx); exec_ctx.subtree_complete = true; diff --git a/air/src/execution/air/xor.rs b/air/src/execution_step/air/xor.rs similarity index 78% rename from air/src/execution/air/xor.rs rename to air/src/execution_step/air/xor.rs index 60df3af3..e370aab1 100644 --- a/air/src/execution/air/xor.rs +++ b/air/src/execution_step/air/xor.rs @@ -14,21 +14,22 @@ * limitations under the License. */ +use super::Catchable; use super::ExecutionCtx; use super::ExecutionError; use super::ExecutionResult; -use super::ExecutionTraceCtx; +use super::TraceHandler; use crate::log_instruction; use air_parser::ast::Xor; impl<'i> super::ExecutableInstruction<'i> for Xor<'i> { - fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> { + fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> { log_instruction!(xor, exec_ctx, trace_ctx); exec_ctx.subtree_complete = true; match self.0.execute(exec_ctx, trace_ctx) { - Err(e) if is_catchable_by_xor(&e) => { + Err(e) if e.is_catchable() => { exec_ctx.subtree_complete = true; exec_ctx.last_error_could_be_set = true; print_xor_log(&e); @@ -40,16 +41,10 @@ impl<'i> super::ExecutableInstruction<'i> for Xor<'i> { } } -/// Returns true, if this execution error type should be caught by xor. -fn is_catchable_by_xor(exec_error: &ExecutionError) -> bool { - // this type of errors related to invalid data and should treat as hard errors. - !matches!(exec_error, ExecutionError::InvalidExecutedState { .. }) -} - fn print_xor_log(e: &ExecutionError) { match e { - // These errors actually aren't real errors, but a way to bubble execution up from match - // to a corresponding xor. They'll become errors iff there is no such xor and execution is + // These errors actually aren't real errors, but a way to bubble execution_step up from match + // to a corresponding xor. They'll become errors iff there is no such xor and execution_step is // bubble up until the very beginning of current subtree. So the error message shouldn't // be print out in order not to confuse users. ExecutionError::MatchWithoutXorError | ExecutionError::MismatchWithoutXorError => {} diff --git a/air/src/execution/boxed_value/iterable.rs b/air/src/execution_step/boxed_value/iterable.rs similarity index 68% rename from air/src/execution/boxed_value/iterable.rs rename to air/src/execution_step/boxed_value/iterable.rs index c9403217..f93444b3 100644 --- a/air/src/execution/boxed_value/iterable.rs +++ b/air/src/execution_step/boxed_value/iterable.rs @@ -21,11 +21,11 @@ mod vec_resolved_call; pub(crate) use json_path_result::IterableJsonPathResult; pub(crate) use resolved_call::IterableResolvedCall; -pub(crate) use vec_json_path_result::IterableVecJsonPathResult; pub(crate) use vec_resolved_call::IterableVecResolvedCall; +use super::ResolvedCallResult; +use crate::execution_step::RSecurityTetraplet; use crate::JValue; -use crate::SecurityTetraplet; use std::rc::Rc; @@ -45,6 +45,9 @@ pub(crate) trait Iterable<'ctx> { /// Return current iterable value if Iterable value is not empty and None otherwise. fn peek(&'ctx self) -> Option; + + /// Returns length of the current iterator. + fn len(&self) -> usize; } /// Combines all possible iterable item types. @@ -53,9 +56,35 @@ pub(crate) trait Iterable<'ctx> { /// through, i.e., it is the `iterable` in the `(fold collection iterable instruction)` statement. #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) enum IterableItem<'ctx> { - RefRef((&'ctx JValue, &'ctx SecurityTetraplet)), - RefValue((&'ctx JValue, SecurityTetraplet)), - RcValue((Rc, SecurityTetraplet)), + RefRef((&'ctx JValue, &'ctx RSecurityTetraplet, usize)), + RefValue((&'ctx JValue, RSecurityTetraplet, usize)), + RcValue((Rc, RSecurityTetraplet, usize)), +} + +impl IterableItem<'_> { + pub(crate) fn pos(&self) -> usize { + use IterableItem::*; + + let pos = match self { + RefRef((.., pos)) => pos, + RefValue((.., pos)) => pos, + RcValue((.., pos)) => pos, + }; + + *pos + } + + pub(crate) fn into_resolved_result(self) -> ResolvedCallResult { + use IterableItem::*; + + let (value, tetraplet, pos) = match self { + RefRef((value, tetraplet, pos)) => (Rc::new(value.clone()), tetraplet.clone(), pos), + RefValue((value, tetraplet, pos)) => (Rc::new(value.clone()), tetraplet, pos), + RcValue(ingredients) => ingredients, + }; + + ResolvedCallResult::new(value, tetraplet, pos) + } } #[macro_export] diff --git a/air/src/execution/boxed_value/iterable/json_path_result.rs b/air/src/execution_step/boxed_value/iterable/json_path_result.rs similarity index 86% rename from air/src/execution/boxed_value/iterable/json_path_result.rs rename to air/src/execution_step/boxed_value/iterable/json_path_result.rs index fb18db4b..f1982ad5 100644 --- a/air/src/execution/boxed_value/iterable/json_path_result.rs +++ b/air/src/execution_step/boxed_value/iterable/json_path_result.rs @@ -16,22 +16,22 @@ use super::Iterable; use super::IterableItem; +use crate::execution_step::RSecurityTetraplet; use crate::foldable_next; use crate::foldable_prev; use crate::JValue; -use crate::SecurityTetraplet; /// Used for iterating over a result of applied to a JValue json path. #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct IterableJsonPathResult { pub(crate) jvalues: Vec, // consider adding index for each tetraplet - pub(crate) tetraplet: SecurityTetraplet, + pub(crate) tetraplet: RSecurityTetraplet, pub(crate) cursor: usize, } impl IterableJsonPathResult { - pub(crate) fn init(jvalues: Vec, tetraplet: SecurityTetraplet) -> Self { + pub(crate) fn init(jvalues: Vec, tetraplet: RSecurityTetraplet) -> Self { Self { jvalues, tetraplet, @@ -57,8 +57,12 @@ impl<'ctx> Iterable<'ctx> for IterableJsonPathResult { } let jvalue = &self.jvalues[self.cursor]; - let result = IterableItem::RefRef((jvalue, &self.tetraplet)); + let result = IterableItem::RefRef((jvalue, &self.tetraplet, 0)); Some(result) } + + fn len(&self) -> usize { + self.jvalues.len() + } } diff --git a/air/src/execution/boxed_value/iterable/resolved_call.rs b/air/src/execution_step/boxed_value/iterable/resolved_call.rs similarity index 75% rename from air/src/execution/boxed_value/iterable/resolved_call.rs rename to air/src/execution_step/boxed_value/iterable/resolved_call.rs index 80863495..aca17abb 100644 --- a/air/src/execution/boxed_value/iterable/resolved_call.rs +++ b/air/src/execution_step/boxed_value/iterable/resolved_call.rs @@ -16,11 +16,12 @@ use super::Iterable; use super::IterableItem; -use crate::contexts::execution::ResolvedCallResult; +use super::ResolvedCallResult; use crate::foldable_next; use crate::foldable_prev; use crate::JValue; -use crate::SecurityTetraplet; + +use std::ops::Deref; /// Used for iterating over JValue of array type. #[derive(Clone, Debug, Eq, PartialEq)] @@ -52,25 +53,29 @@ impl<'ctx> Iterable<'ctx> for IterableResolvedCall { } fn peek(&'ctx self) -> Option { - use std::ops::Deref; - if self.len == 0 { return None; } - let triplet = self.call_result.triplet.clone(); - let tetraplet = SecurityTetraplet { - triplet, - // TODO: consider set json_path to the current cursor here - json_path: String::new(), - }; + let ResolvedCallResult { + result, + tetraplet, + trace_pos, + } = &self.call_result; - let jvalue = match &self.call_result.result.deref() { + let jvalue = match &result.deref() { JValue::Array(array) => &array[self.cursor], _ => unimplemented!("this jvalue is set only by fold instruction, so it must have an array type"), }; - let result = IterableItem::RefValue((jvalue, tetraplet)); + let result = IterableItem::RefValue((jvalue, tetraplet.clone(), *trace_pos)); Some(result) } + + fn len(&self) -> usize { + match self.call_result.result.deref() { + JValue::Array(array) => array.len(), + _ => unimplemented!("this jvalue is set only by fold instruction, so it must have an array type"), + } + } } diff --git a/air/src/execution/boxed_value/iterable/vec_json_path_result.rs b/air/src/execution_step/boxed_value/iterable/vec_json_path_result.rs similarity index 82% rename from air/src/execution/boxed_value/iterable/vec_json_path_result.rs rename to air/src/execution_step/boxed_value/iterable/vec_json_path_result.rs index 75856b29..ef1a0d23 100644 --- a/air/src/execution/boxed_value/iterable/vec_json_path_result.rs +++ b/air/src/execution_step/boxed_value/iterable/vec_json_path_result.rs @@ -16,21 +16,22 @@ use super::Iterable; use super::IterableItem; +use crate::execution_step::SecurityTetraplets; use crate::foldable_next; use crate::foldable_prev; use crate::JValue; -use crate::SecurityTetraplet; /// Used for iterating over a result of applied to an stream json path. #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct IterableVecJsonPathResult { pub(crate) jvalues: Vec, - pub(crate) tetraplets: Vec, + pub(crate) tetraplets: SecurityTetraplets, pub(crate) cursor: usize, } impl IterableVecJsonPathResult { - pub(crate) fn init(jvalues: Vec, tetraplets: Vec) -> Self { + #[allow(dead_code)] + pub(crate) fn init(jvalues: Vec, tetraplets: SecurityTetraplets) -> Self { // TODO: add assert on length Self { jvalues, @@ -58,8 +59,12 @@ impl<'ctx> Iterable<'ctx> for IterableVecJsonPathResult { let jvalue = &self.jvalues[self.cursor]; let tetraplet = &self.tetraplets[self.cursor]; - let result = IterableItem::RefRef((jvalue, tetraplet)); + let result = IterableItem::RefRef((jvalue, tetraplet, 0)); Some(result) } + + fn len(&self) -> usize { + self.jvalues.len() + } } diff --git a/air/src/execution/boxed_value/iterable/vec_resolved_call.rs b/air/src/execution_step/boxed_value/iterable/vec_resolved_call.rs similarity index 81% rename from air/src/execution/boxed_value/iterable/vec_resolved_call.rs rename to air/src/execution_step/boxed_value/iterable/vec_resolved_call.rs index 64a65e6d..5c3a47de 100644 --- a/air/src/execution/boxed_value/iterable/vec_resolved_call.rs +++ b/air/src/execution_step/boxed_value/iterable/vec_resolved_call.rs @@ -16,10 +16,9 @@ use super::Iterable; use super::IterableItem; -use crate::contexts::execution::ResolvedCallResult; +use super::ResolvedCallResult; use crate::foldable_next; use crate::foldable_prev; -use crate::SecurityTetraplet; /// Used for iterating over stream with JValues. #[derive(Clone, Debug, Eq, PartialEq)] @@ -53,13 +52,17 @@ impl<'ctx> Iterable<'ctx> for IterableVecResolvedCall { return None; } - let ResolvedCallResult { result, triplet } = self.call_results[self.cursor].clone(); - let tetraplet = SecurityTetraplet { - triplet, - json_path: String::new(), - }; + let ResolvedCallResult { + result, + tetraplet, + trace_pos, + } = &self.call_results[self.cursor]; - let result = IterableItem::RcValue((result, tetraplet)); + let result = IterableItem::RcValue((result.clone(), tetraplet.clone(), *trace_pos)); Some(result) } + + fn len(&self) -> usize { + self.call_results.len() + } } diff --git a/air/src/execution/boxed_value/jvaluable.rs b/air/src/execution_step/boxed_value/jvaluable.rs similarity index 82% rename from air/src/execution/boxed_value/jvaluable.rs rename to air/src/execution_step/boxed_value/jvaluable.rs index 45086451..c19141cb 100644 --- a/air/src/execution/boxed_value/jvaluable.rs +++ b/air/src/execution_step/boxed_value/jvaluable.rs @@ -18,12 +18,16 @@ mod cell_vec_resolved_call_result; mod empty; mod iterable_item; mod resolved_call_result; +mod stream; use super::iterable::IterableItem; use super::ExecutionError; use super::ExecutionResult; +use super::ResolvedCallResult; +use crate::execution_step::SecurityTetraplets; use crate::JValue; -use crate::SecurityTetraplet; + +pub(crate) use stream::StreamJvaluableIngredients; use std::borrow::Cow; @@ -33,10 +37,7 @@ pub(crate) trait JValuable { fn apply_json_path(&self, json_path: &str) -> ExecutionResult>; /// Applies json path to the internal value, produces JValue with tetraplet. - fn apply_json_path_with_tetraplets( - &self, - json_path: &str, - ) -> ExecutionResult<(Vec<&JValue>, Vec)>; + fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)>; /// Return internal value as borrowed if it's possible, owned otherwise. fn as_jvalue(&self) -> Cow<'_, JValue>; @@ -45,5 +46,5 @@ pub(crate) trait JValuable { fn into_jvalue(self: Box) -> JValue; /// Return tetraplets associating with internal value. - fn as_tetraplets(&self) -> Vec; + fn as_tetraplets(&self) -> SecurityTetraplets; } diff --git a/air/src/execution/boxed_value/jvaluable/cell_vec_resolved_call_result.rs b/air/src/execution_step/boxed_value/jvaluable/cell_vec_resolved_call_result.rs similarity index 67% rename from air/src/execution/boxed_value/jvaluable/cell_vec_resolved_call_result.rs rename to air/src/execution_step/boxed_value/jvaluable/cell_vec_resolved_call_result.rs index e52d26f7..20ccb98e 100644 --- a/air/src/execution/boxed_value/jvaluable/cell_vec_resolved_call_result.rs +++ b/air/src/execution_step/boxed_value/jvaluable/cell_vec_resolved_call_result.rs @@ -14,12 +14,12 @@ * limitations under the License. */ -use super::ExecutionError::JValueStreamJsonPathError; +use super::ExecutionError::GenerationStreamJsonPathError; use super::ExecutionResult; use super::JValuable; -use crate::contexts::execution::ResolvedCallResult; +use super::ResolvedCallResult; +use crate::execution_step::SecurityTetraplets; use crate::JValue; -use crate::SecurityTetraplet; use jsonpath_lib::select_with_iter; @@ -30,27 +30,25 @@ impl JValuable for std::cell::Ref<'_, Vec> { fn apply_json_path(&self, json_path: &str) -> ExecutionResult> { let acc_iter = self.iter().map(|r| r.result.deref()); let (selected_values, _) = select_with_iter(acc_iter, json_path).map_err(|e| { - JValueStreamJsonPathError(self.iter().cloned().collect::>(), json_path.to_string(), e) + GenerationStreamJsonPathError(self.iter().cloned().collect::>(), json_path.to_string(), e) })?; Ok(selected_values) } - fn apply_json_path_with_tetraplets( - &self, - json_path: &str, - ) -> ExecutionResult<(Vec<&JValue>, Vec)> { + fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> { let acc_iter = self.iter().map(|r| r.result.deref()); let (selected_values, tetraplet_indices) = select_with_iter(acc_iter, json_path).map_err(|e| { - JValueStreamJsonPathError(self.iter().cloned().collect::>(), json_path.to_string(), e) + GenerationStreamJsonPathError(self.iter().cloned().collect::>(), json_path.to_string(), e) })?; let tetraplets = tetraplet_indices .into_iter() - .map(|id| SecurityTetraplet { - triplet: self[id].triplet.clone(), - json_path: json_path.to_string(), + .map(|id| { + let tetraplet = self[id].tetraplet.clone(); + tetraplet.borrow_mut().add_json_path(json_path); + tetraplet }) .collect::>(); @@ -67,12 +65,7 @@ impl JValuable for std::cell::Ref<'_, Vec> { JValue::Array(jvalue_array) } - fn as_tetraplets(&self) -> Vec { - self.iter() - .map(|r| SecurityTetraplet { - triplet: r.triplet.clone(), - json_path: String::new(), - }) - .collect::>() + fn as_tetraplets(&self) -> SecurityTetraplets { + self.iter().map(|r| r.tetraplet.clone()).collect::>() } } diff --git a/air/src/execution/boxed_value/jvaluable/empty.rs b/air/src/execution_step/boxed_value/jvaluable/empty.rs similarity index 82% rename from air/src/execution/boxed_value/jvaluable/empty.rs rename to air/src/execution_step/boxed_value/jvaluable/empty.rs index 6150ba81..fd4e5f16 100644 --- a/air/src/execution/boxed_value/jvaluable/empty.rs +++ b/air/src/execution_step/boxed_value/jvaluable/empty.rs @@ -16,8 +16,8 @@ use super::ExecutionResult; use super::JValuable; +use crate::execution_step::SecurityTetraplets; use crate::JValue; -use crate::SecurityTetraplet; use std::borrow::Cow; @@ -26,10 +26,7 @@ impl JValuable for () { Ok(vec![]) } - fn apply_json_path_with_tetraplets( - &self, - _json_path: &str, - ) -> ExecutionResult<(Vec<&JValue>, Vec)> { + fn apply_json_path_with_tetraplets(&self, _json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> { Ok((vec![], vec![])) } @@ -41,7 +38,7 @@ impl JValuable for () { JValue::Array(vec![]) } - fn as_tetraplets(&self) -> Vec { + fn as_tetraplets(&self) -> SecurityTetraplets { vec![] } } diff --git a/air/src/execution/boxed_value/jvaluable/iterable_item.rs b/air/src/execution_step/boxed_value/jvaluable/iterable_item.rs similarity index 64% rename from air/src/execution/boxed_value/jvaluable/iterable_item.rs rename to air/src/execution_step/boxed_value/jvaluable/iterable_item.rs index 09e55418..0af070c7 100644 --- a/air/src/execution/boxed_value/jvaluable/iterable_item.rs +++ b/air/src/execution_step/boxed_value/jvaluable/iterable_item.rs @@ -18,8 +18,8 @@ use super::ExecutionError::JValueJsonPathError as JsonPathError; use super::ExecutionResult; use super::IterableItem; use super::JValuable; +use crate::execution_step::SecurityTetraplets; use crate::JValue; -use crate::SecurityTetraplet; use jsonpath_lib::select; @@ -31,9 +31,9 @@ impl<'ctx> JValuable for IterableItem<'ctx> { use super::IterableItem::*; let jvalue = match self { - RefRef((jvalue, _)) => *jvalue, - RefValue((jvalue, _)) => jvalue, - RcValue((jvalue, _)) => jvalue.deref(), + RefRef((jvalue, ..)) => *jvalue, + RefValue((jvalue, ..)) => jvalue, + RcValue((jvalue, ..)) => jvalue.deref(), }; let selected_jvalues = @@ -41,16 +41,13 @@ impl<'ctx> JValuable for IterableItem<'ctx> { Ok(selected_jvalues) } - fn apply_json_path_with_tetraplets( - &self, - json_path: &str, - ) -> ExecutionResult<(Vec<&JValue>, Vec)> { + fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> { use super::IterableItem::*; let (jvalue, tetraplet) = match self { - RefRef((jvalue, tetraplet)) => (*jvalue, *tetraplet), - RefValue((jvalue, tetraplet)) => (*jvalue, tetraplet), - RcValue((jvalue, tetraplet)) => (jvalue.deref(), tetraplet), + RefRef((jvalue, tetraplet, _)) => (*jvalue, *tetraplet), + RefValue((jvalue, tetraplet, _)) => (*jvalue, tetraplet), + RcValue((jvalue, tetraplet, _)) => (jvalue.deref(), tetraplet), }; let selected_jvalues = @@ -62,9 +59,9 @@ impl<'ctx> JValuable for IterableItem<'ctx> { use super::IterableItem::*; match self { - RefRef((jvalue, _)) => Cow::Borrowed(jvalue), - RefValue((jvalue, _)) => Cow::Borrowed(jvalue), - RcValue((jvalue, _)) => Cow::Borrowed(jvalue.deref()), + RefRef((jvalue, ..)) => Cow::Borrowed(jvalue), + RefValue((jvalue, ..)) => Cow::Borrowed(jvalue), + RcValue((jvalue, ..)) => Cow::Borrowed(jvalue.deref()), } } @@ -72,23 +69,23 @@ impl<'ctx> JValuable for IterableItem<'ctx> { use super::IterableItem::*; match *self { - RefRef((jvalue, _)) => jvalue.deref().clone(), - RefValue((jvalue, _)) => jvalue.clone(), - RcValue((jvalue, _)) => jvalue.deref().clone(), + RefRef((jvalue, ..)) => jvalue.deref().clone(), + RefValue((jvalue, ..)) => jvalue.clone(), + RcValue((jvalue, ..)) => jvalue.deref().clone(), } } - fn as_tetraplets(&self) -> Vec { + fn as_tetraplets(&self) -> SecurityTetraplets { use super::IterableItem::*; // these clones are needed because rust-sdk allows passing arguments only by value match self { - RefRef((_, tetraplet)) => { + RefRef((_, tetraplet, _)) => { let tetraplet = tetraplet.deref().clone(); vec![tetraplet] } - RefValue((_, tetraplet)) => vec![(*tetraplet).clone()], - RcValue((_, tetraplet)) => vec![(*tetraplet).clone()], + RefValue((_, tetraplet, _)) => vec![tetraplet.clone()], + RcValue((_, tetraplet, _)) => vec![tetraplet.clone()], } } } diff --git a/air/src/execution/boxed_value/jvaluable/resolved_call_result.rs b/air/src/execution_step/boxed_value/jvaluable/resolved_call_result.rs similarity index 73% rename from air/src/execution/boxed_value/jvaluable/resolved_call_result.rs rename to air/src/execution_step/boxed_value/jvaluable/resolved_call_result.rs index b136a919..cc4d444a 100644 --- a/air/src/execution/boxed_value/jvaluable/resolved_call_result.rs +++ b/air/src/execution_step/boxed_value/jvaluable/resolved_call_result.rs @@ -16,9 +16,9 @@ use super::ExecutionResult; use super::JValuable; -use crate::contexts::execution::ResolvedCallResult; +use super::ResolvedCallResult; +use crate::execution_step::SecurityTetraplets; use crate::JValue; -use crate::SecurityTetraplet; use jsonpath_lib::select; @@ -34,20 +34,15 @@ impl JValuable for ResolvedCallResult { Ok(selected_jvalues) } - fn apply_json_path_with_tetraplets( - &self, - json_path: &str, - ) -> ExecutionResult<(Vec<&JValue>, Vec)> { + fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> { use super::ExecutionError::JValueJsonPathError as JsonPathError; is_json_path_allowed(&self.result)?; let selected_jvalues = select(&self.result, json_path) .map_err(|e| JsonPathError(self.result.deref().clone(), String::from(json_path), e))?; - let tetraplet = SecurityTetraplet { - triplet: self.triplet.clone(), - json_path: json_path.to_string(), - }; + let tetraplet = self.tetraplet.clone(); + tetraplet.borrow_mut().add_json_path(json_path); Ok((selected_jvalues, vec![tetraplet])) } @@ -60,13 +55,8 @@ impl JValuable for ResolvedCallResult { self.result.deref().clone() } - fn as_tetraplets(&self) -> Vec { - let tetraplet = SecurityTetraplet { - triplet: self.triplet.clone(), - json_path: String::new(), - }; - - vec![tetraplet] + fn as_tetraplets(&self) -> SecurityTetraplets { + vec![self.tetraplet.clone()] } } @@ -75,8 +65,8 @@ fn is_json_path_allowed(value: &JValue) -> ExecutionResult<()> { use crate::exec_err; match value { - JValue::Array(_) => return Ok(()), - JValue::Object(_) => return Ok(()), + JValue::Array(_) => Ok(()), + JValue::Object(_) => Ok(()), value => exec_err!(ExecutionError::JsonPathVariableTypeError(value.clone())), } } diff --git a/air/src/execution_step/boxed_value/jvaluable/stream.rs b/air/src/execution_step/boxed_value/jvaluable/stream.rs new file mode 100644 index 00000000..4d07fd1a --- /dev/null +++ b/air/src/execution_step/boxed_value/jvaluable/stream.rs @@ -0,0 +1,110 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionError::StreamJsonPathError; +use super::ExecutionResult; +use super::JValuable; +use crate::exec_err; +use crate::execution_step::boxed_value::Generation; +use crate::execution_step::boxed_value::Stream; +use crate::execution_step::SecurityTetraplets; +use crate::JValue; + +use jsonpath_lib::select_with_iter; + +use std::borrow::Cow; +use std::ops::Deref; + +#[derive(Debug)] +pub(crate) struct StreamJvaluableIngredients<'stream> { + pub(crate) stream: std::cell::Ref<'stream, Stream>, + pub(crate) generation: Generation, +} + +// TODO: this will be deleted soon, because it would be impossible to use streams without +// canonicalization as an arg of a call +impl JValuable for StreamJvaluableIngredients<'_> { + fn apply_json_path(&self, json_path: &str) -> ExecutionResult> { + let iter = self.iter()?.map(|v| v.result.deref()); + + let (selected_values, _) = select_with_iter(iter, json_path) + .map_err(|e| StreamJsonPathError(self.stream.deref().clone(), json_path.to_string(), e))?; + + Ok(selected_values) + } + + fn apply_json_path_with_tetraplets(&self, json_path: &str) -> ExecutionResult<(Vec<&JValue>, SecurityTetraplets)> { + let iter = self.iter()?.map(|v| v.result.deref()); + + let (selected_values, tetraplet_indices) = select_with_iter(iter, json_path) + .map_err(|e| StreamJsonPathError(self.stream.deref().clone(), json_path.to_string(), e))?; + + let mut tetraplets = Vec::with_capacity(tetraplet_indices.len()); + + for idx in tetraplet_indices.iter() { + let resolved_call = self.iter()?.nth(*idx).unwrap(); + let tetraplet = resolved_call.tetraplet.clone(); + tetraplet.borrow_mut().add_json_path(json_path); + tetraplets.push(tetraplet); + } + + Ok((selected_values, tetraplets)) + } + + fn as_jvalue(&self) -> Cow<'_, JValue> { + let jvalue = self.stream.deref().clone().as_jvalue(self.generation).unwrap(); + Cow::Owned(jvalue) + } + + fn into_jvalue(self: Box) -> JValue { + self.stream.as_jvalue(self.generation).unwrap() + } + + fn as_tetraplets(&self) -> SecurityTetraplets { + self.stream + .iter(self.generation) + .unwrap() + .map(|r| r.tetraplet.clone()) + .collect::>() + } +} + +use crate::execution_step::boxed_value::StreamIter; + +impl<'stream> StreamJvaluableIngredients<'stream> { + pub(crate) fn new(stream: std::cell::Ref<'stream, Stream>, generation: Generation) -> Self { + Self { stream, generation } + } + + pub(self) fn iter(&self) -> ExecutionResult> { + use super::ExecutionError::StreamDontHaveSuchGeneration; + + match self.stream.iter(self.generation) { + Some(iter) => Ok(iter), + None => { + let generation = match self.generation { + Generation::Nth(generation) => generation, + Generation::Last => unreachable!(), + }; + + exec_err!(StreamDontHaveSuchGeneration( + self.stream.deref().clone(), + generation as usize + )) + } + } + } +} diff --git a/air/src/execution_step/boxed_value/mod.rs b/air/src/execution_step/boxed_value/mod.rs new file mode 100644 index 00000000..1308c9e2 --- /dev/null +++ b/air/src/execution_step/boxed_value/mod.rs @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod iterable; +mod jvaluable; +mod scalar; +mod stream; +mod variable; + +pub(crate) use super::ExecutionError; +pub(crate) use iterable::*; +pub(crate) use jvaluable::*; +pub(crate) use scalar::ResolvedCallResult; +pub(crate) use scalar::Scalar; +pub(crate) use stream::Generation; +pub(crate) use stream::Stream; +pub(crate) use stream::StreamIter; +pub(crate) use variable::Variable; + +use super::ExecutionResult; diff --git a/air/src/contexts/execution_context/avalue.rs b/air/src/execution_step/boxed_value/scalar.rs similarity index 50% rename from air/src/contexts/execution_context/avalue.rs rename to air/src/execution_step/boxed_value/scalar.rs index d8b85a74..899e671a 100644 --- a/air/src/contexts/execution_context/avalue.rs +++ b/air/src/execution_step/boxed_value/scalar.rs @@ -14,14 +14,14 @@ * limitations under the License. */ -use crate::execution::FoldState; +use super::JValuable; +use crate::execution_step::FoldState; +use crate::execution_step::RSecurityTetraplet; use crate::JValue; -use crate::ResolvedTriplet; use serde::Deserialize; use serde::Serialize; -use std::cell::RefCell; use std::fmt::Display; use std::fmt::Formatter; use std::rc::Rc; @@ -29,28 +29,44 @@ use std::rc::Rc; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct ResolvedCallResult { pub result: Rc, - pub triplet: Rc, + pub tetraplet: RSecurityTetraplet, + pub trace_pos: usize, } -pub(crate) enum AValue<'i> { +pub(crate) enum Scalar<'i> { JValueRef(ResolvedCallResult), - JValueStreamRef(RefCell>), JValueFoldCursor(FoldState<'i>), } -impl<'i> Display for AValue<'i> { +impl<'i> Scalar<'i> { + pub(crate) fn to_jvaluable<'ctx>(&'ctx self) -> Box { + match self { + Scalar::JValueRef(value) => Box::new(value.clone()), + Scalar::JValueFoldCursor(fold_state) => { + let peeked_value = fold_state.iterable.peek().unwrap(); + Box::new(peeked_value) + } + } + } +} + +impl ResolvedCallResult { + pub(crate) fn new(result: Rc, tetraplet: RSecurityTetraplet, trace_pos: usize) -> Self { + Self { + result, + tetraplet, + trace_pos, + } + } +} + +impl<'i> Display for Scalar<'i> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - AValue::JValueRef(value) => write!(f, "{:?}", value)?, - AValue::JValueStreamRef(stream) => { - write!(f, "[ ")?; - for value in stream.borrow().iter() { - write!(f, "{:?} ", value)?; - } - write!(f, "]")?; - } - AValue::JValueFoldCursor(_) => { - write!(f, "cursor")?; + Scalar::JValueRef(value) => write!(f, "{:?}", value)?, + Scalar::JValueFoldCursor(cursor) => { + let iterable = &cursor.iterable; + write!(f, "cursor, current value: {:?}", iterable.peek())?; } } diff --git a/air/src/execution_step/boxed_value/stream.rs b/air/src/execution_step/boxed_value/stream.rs new file mode 100644 index 00000000..b5ce985b --- /dev/null +++ b/air/src/execution_step/boxed_value/stream.rs @@ -0,0 +1,205 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionError; +use super::ExecutionResult; +use super::ResolvedCallResult; +use crate::exec_err; +use crate::JValue; + +use std::fmt::Formatter; + +/// Streams are CRDT-like append only data structures. They are guaranteed to have the same order +/// of values on each peer. +/// +/// The first Vec represents generations, the second values in a generation. Generation is a set +/// of values that interpreter obtained from one particle. It means that number of generation on +/// a peer is equal to number of the interpreter runs in context of one particle. And each set of +/// obtained values from a current_data that were not present in prev_data becomes a new generation. +// TODO: make it non-pub after boxed value refactoring. +#[derive(Debug, Default, Clone)] +pub(crate) struct Stream(Vec>); + +impl Stream { + pub(crate) fn from_generations_count(count: usize) -> Self { + Self(vec![vec![]; count + 1]) + } + + pub(crate) fn from_value(value: ResolvedCallResult) -> Self { + Self(vec![vec![value]]) + } + + // if generation is None, value would be added to the last generation, otherwise it would + // be added to given generation + pub(crate) fn add_value(&mut self, value: ResolvedCallResult, generation: Generation) -> ExecutionResult { + let generation = match generation { + Generation::Last => self.0.len() - 1, + Generation::Nth(id) => id as usize, + }; + + if generation >= self.0.len() { + return exec_err!(ExecutionError::StreamDontHaveSuchGeneration(self.clone(), generation)); + } + + self.0[generation].push(value); + Ok(generation as u32) + } + + pub(crate) fn generations_count(&self) -> usize { + let generations_count = self.0.len(); + + // the last generation could be empty due to the logic of from_generations_count ctor + if generations_count > 0 && self.0[generations_count - 1].is_empty() { + generations_count - 1 + } else { + generations_count + } + } + + pub(crate) fn elements_count(&self, generation: Generation) -> Option { + match generation { + Generation::Nth(generation) if generation as usize > self.generations_count() => None, + Generation::Nth(generation) => Some(self.0.iter().take(generation as usize).map(|v| v.len()).sum()), + Generation::Last => Some(self.0.iter().map(|v| v.len()).sum()), + } + } + + pub(crate) fn is_empty(&self) -> bool { + if self.0.is_empty() { + return false; + } + + self.0.iter().all(|v| v.is_empty()) + } + + pub(crate) fn as_jvalue(&self, generation: Generation) -> Option { + use std::ops::Deref; + + let iter = self.iter(generation)?; + let jvalue_array = iter.map(|r| r.result.deref().clone()).collect::>(); + + Some(JValue::Array(jvalue_array)) + } + + pub(crate) fn iter(&self, generation: Generation) -> Option> { + let iter: Box> = match generation { + Generation::Nth(generation) if generation as usize >= self.generations_count() => return None, + Generation::Nth(generation) => Box::new(self.0.iter().take(generation as usize + 1).flat_map(|v| v.iter())), + Generation::Last => Box::new(self.0.iter().flat_map(|v| v.iter())), + }; + // unwrap is safe here, because generation's been already checked + let len = self.elements_count(generation).unwrap(); + + let iter = StreamIter { iter, len }; + + Some(iter) + } + + pub(crate) fn slice_iter(&self, generation: Generation) -> Option> { + let iter: Box> = match generation { + Generation::Nth(generation) if generation as usize >= self.generations_count() => return None, + Generation::Nth(generation) => Box::new(self.0.iter().take(generation as usize + 1).map(|v| v.as_slice())), + Generation::Last => Box::new(self.0.iter().map(|v| v.as_slice())), + }; + + let len = match generation { + Generation::Nth(generation) => generation as usize, + Generation::Last => self.0.len(), + }; + + let iter = StreamSliceIter { iter, len }; + + Some(iter) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum Generation { + Last, + Nth(u32), +} + +impl Generation { + pub(crate) fn from_option(raw_generation: Option) -> Self { + match raw_generation { + Some(generation) => Generation::Nth(generation), + None => Generation::Last, + } + } +} + +pub(crate) struct StreamIter<'result> { + iter: Box + 'result>, + len: usize, +} + +impl<'result> Iterator for StreamIter<'result> { + type Item = &'result ResolvedCallResult; + + fn next(&mut self) -> Option { + if self.len > 0 { + self.len -= 1; + } + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl<'result> ExactSizeIterator for StreamIter<'result> {} + +pub(crate) struct StreamSliceIter<'slice> { + iter: Box + 'slice>, + len: usize, +} + +impl<'slice> Iterator for StreamSliceIter<'slice> { + type Item = &'slice [ResolvedCallResult]; + + fn next(&mut self) -> Option { + if self.len > 0 { + self.len -= 1; + } + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +use std::fmt; + +impl fmt::Display for Stream { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if self.0.is_empty() { + return write!(f, "[]"); + } + + write!(f, "[ ")?; + for (id, generation) in self.0.iter().enumerate() { + write!(f, " -- {}: ", id)?; + for value in generation.iter() { + write!(f, "{:?}, ", value)?; + } + writeln!(f)?; + } + + write!(f, "]") + } +} diff --git a/air/src/execution_step/boxed_value/variable.rs b/air/src/execution_step/boxed_value/variable.rs new file mode 100644 index 00000000..e78fa8e5 --- /dev/null +++ b/air/src/execution_step/boxed_value/variable.rs @@ -0,0 +1,49 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::Generation; +use air_parser::ast::AstVariable; + +#[derive(Clone, Copy, Debug)] +pub(crate) enum Variable<'i> { + Scalar(&'i str), + Stream { name: &'i str, generation: Generation }, +} + +impl<'i> Variable<'i> { + pub(crate) fn from_ast(ast_variable: &AstVariable<'i>) -> Self { + match ast_variable { + AstVariable::Scalar(name) => Variable::Scalar(name), + AstVariable::Stream(name) => Variable::Stream { + name, + generation: Generation::Last, + }, + } + } + + #[allow(dead_code)] + pub(crate) fn from_ast_with_generation(ast_variable: &AstVariable<'i>, generation: Generation) -> Self { + match ast_variable { + AstVariable::Scalar(name) => Variable::Scalar(name), + AstVariable::Stream(name) => Variable::Stream { name, generation }, + } + } + + #[allow(dead_code)] + pub(crate) fn from_stream(name: &'i str, generation: Generation) -> Self { + Self::Stream { name, generation } + } +} diff --git a/air/src/execution/errors.rs b/air/src/execution_step/errors.rs similarity index 69% rename from air/src/execution/errors.rs rename to air/src/execution_step/errors.rs index b2338709..1ffcf84f 100644 --- a/air/src/execution/errors.rs +++ b/air/src/execution_step/errors.rs @@ -14,11 +14,16 @@ * limitations under the License. */ +mod catchable; + +pub(crate) use catchable::Catchable; + +use super::trace_handler::MergerApResult; +use super::trace_handler::TraceHandlerError; use super::Joinable; +use super::ResolvedCallResult; +use super::Stream; use crate::build_targets::CallServiceResult; -use crate::contexts::execution::ResolvedCallResult; -use crate::contexts::execution_trace::ExecutedState; -use crate::contexts::execution_trace::ExecutionTrace; use crate::JValue; use jsonpath_lib::JsonPathError; @@ -34,9 +39,9 @@ pub(crate) enum ExecutionError { #[error("call_service result '{0}' can't be serialized or deserialized with an error: {1}")] CallServiceResultDeError(CallServiceResult, SerdeJsonError), - /// Semantic errors in instructions. - #[error("{0}")] - InstructionError(String), + /// Semantic errors in a call instructions. + #[error("call should have service id specified by peer part or function part")] + IncorrectCallTriplet, /// An error is occurred while calling local service via call_service. #[error("Local service error, ret_code is {0}, error message is '{1}'")] @@ -54,9 +59,13 @@ pub(crate) enum ExecutionError { #[error("variable with path '{1}' not found in '{0}' with an error: '{2}'")] JValueJsonPathError(JValue, String, JsonPathError), + /// An error occurred while trying to apply json path to this stream generation with JValue's. + #[error("variable with path '{1}' not found in '{0:?}' with error: '{2}'")] + GenerationStreamJsonPathError(Vec, String, JsonPathError), + /// An error occurred while trying to apply json path to this stream with JValue's. #[error("variable with path '{1}' not found in '{0:?}' with error: '{2}'")] - JValueStreamJsonPathError(Vec, String, JsonPathError), + StreamJsonPathError(Stream, String, JsonPathError), /// Provided JValue has incompatible with target type. #[error("expected JValue type '{1}', but got '{0}' JValue")] @@ -78,35 +87,9 @@ pub(crate) enum ExecutionError { #[error("multiple fold states found for iterable '{0}'")] MultipleFoldStates(String), - /// Expected executed state of a different type. - #[error( - r#" - ====================================================================== - Below is a crucial debug information, please send this to the Fluence Labs: - - invalid executed state error: - current instruction: {instruction}, - expected state: {expected_state}, - actual_state: '{actual_state}', - current_trace: '{current_trace:?}', - new_trace: '{new_trace:?}', - current_subtree_size: {current_subtree_size}, - - ====================================================================== - "# - )] - InvalidExecutedState { - instruction: String, - expected_state: &'static str, - actual_state: ExecutedState, - current_trace: ExecutionTrace, - new_trace: ExecutionTrace, - current_subtree_size: usize, - }, - /// Errors encountered while shadowing non-scalar values. - #[error("variable with name '{0}' can't be shadowed, shadowing is supported only for scalar values")] - ShadowingError(String), + #[error("variable with name '{0}' can't be shadowed, shadowing isn't supported for iterables")] + IterableShadowing(String), /// This error type is produced by a match to notify xor that compared values aren't equal. #[error("match is used without corresponding xor")] @@ -126,6 +109,25 @@ pub(crate) enum ExecutionError { it could be applied only to streams and variables of array and object types" )] JsonPathVariableTypeError(JValue), + + /// Errors bubbled from a trace handler. + #[error("{0}")] + TraceError(#[from] TraceHandlerError), + + /// Errors occurred while insertion of a value inside stream that doesn't have corresponding generation. + #[error("stream {0:?} doesn't have generation with number {1}, probably a supplied to the interpreter data is corrupted")] + StreamDontHaveSuchGeneration(Stream, usize), + + /// Errors occurred when result from data doesn't match to a instruction, f.e. an instruction + /// could be applied to a stream, but result doesn't contain generation in a source position. + #[error("ap result {0:?} doesn't match corresponding instruction")] + ApResultNotCorrespondToInstr(MergerApResult), +} + +impl From for Rc { + fn from(trace_error: TraceHandlerError) -> Self { + Rc::new(ExecutionError::TraceError(trace_error)) + } } impl ExecutionError { @@ -134,23 +136,26 @@ impl ExecutionError { match self { CallServiceResultDeError(..) => 1, - InstructionError(_) => 2, + IncorrectCallTriplet => 2, LocalServiceError(..) => 3, VariableNotFound(_) => 4, MultipleVariablesFound(_) => 5, JValueJsonPathError(..) => 6, - JValueStreamJsonPathError(..) => 7, + GenerationStreamJsonPathError(..) => 7, IncompatibleJValueType(..) => 8, IncompatibleAValueType(..) => 9, MultipleValuesInJsonPath(_) => 10, FoldStateNotFound(_) => 11, MultipleFoldStates(_) => 12, - InvalidExecutedState { .. } => 13, - ShadowingError(_) => 14, - MatchWithoutXorError => 15, - MismatchWithoutXorError => 16, - FlatteningError(_) => 17, - JsonPathVariableTypeError(_) => 18, + IterableShadowing(_) => 13, + MatchWithoutXorError => 14, + MismatchWithoutXorError => 15, + FlatteningError(_) => 16, + JsonPathVariableTypeError(_) => 17, + StreamJsonPathError(..) => 18, + StreamDontHaveSuchGeneration(..) => 19, + ApResultNotCorrespondToInstr(_) => 20, + TraceError(_) => 21, } } } @@ -173,15 +178,23 @@ impl Joinable for ExecutionError { log_join!(" waiting for an argument with name '{}'", var_name); true } - JValueStreamJsonPathError(stream, json_path, _) => { + StreamJsonPathError(stream, json_path, _) => { log_join!(" waiting for an argument with path '{}' on stream '{:?}'", json_path, stream); true } + _ => false, } } } +impl Catchable for ExecutionError { + fn is_catchable(&self) -> bool { + // this kind is related to an invalid data and should treat as a non-catchable error + !matches!(self, ExecutionError::TraceError(_)) + } +} + impl From for ExecutionError { fn from(_: std::convert::Infallible) -> Self { unreachable!() diff --git a/air/src/execution_step/errors/catchable.rs b/air/src/execution_step/errors/catchable.rs new file mode 100644 index 00000000..9d4e7b68 --- /dev/null +++ b/air/src/execution_step/errors/catchable.rs @@ -0,0 +1,24 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// This trait is intended to differentiate between catchable and non-catchable error types. +/// Errors of the first type could be caught by xor, the second couldn't and should stop +/// AIR execution. This is needed to prevent some malicious data merging and manage +/// prev_data always in a valid state. +pub(crate) trait Catchable { + /// Return true, if error is catchable. + fn is_catchable(&self) -> bool; +} diff --git a/air/src/contexts/execution_context.rs b/air/src/execution_step/execution_context/context.rs similarity index 75% rename from air/src/contexts/execution_context.rs rename to air/src/execution_step/execution_context/context.rs index 1a3fd152..2b5a967d 100644 --- a/air/src/contexts/execution_context.rs +++ b/air/src/execution_step/execution_context/context.rs @@ -14,32 +14,33 @@ * limitations under the License. */ -mod avalue; -pub(crate) mod error_descriptor; - -pub(crate) use avalue::AValue; -pub(crate) use avalue::ResolvedCallResult; - -use crate::execution::ExecutionError; -use crate::SecurityTetraplet; -use error_descriptor::LastErrorDescriptor; -use error_descriptor::LastErrorWithTetraplets; +use super::InstrTracker; +use super::LastErrorDescriptor; +use super::LastErrorWithTetraplet; +use crate::execution_step::boxed_value::Scalar; +use crate::execution_step::boxed_value::Stream; +use std::cell::RefCell; use std::collections::HashMap; use std::collections::VecDeque; +use std::rc::Rc; /// Contains all necessary state needed to execute AIR script. #[derive(Default)] pub(crate) struct ExecutionCtx<'i> { - /// Contains all set variables. + /// Contains all scalars. // TODO: use shared string (Rc) to avoid copying. - pub data_cache: HashMap>, + pub scalars: HashMap>, + + /// Contains all streams. + // TODO: use shared string (Rc) to avoid copying. + pub streams: HashMap>, /// Set of peer public keys that should receive resulted data. pub next_peer_pks: Vec, /// PeerId of a peer executing this AIR script at the moment. - pub current_peer_id: String, + pub current_peer_id: Rc, /// PeerId of a peer send this AIR script. pub init_peer_id: String, @@ -55,17 +56,22 @@ pub(crate) struct ExecutionCtx<'i> { /// Indicates that previous executed subtree is complete. /// A subtree treats as a complete if all subtree elements satisfy the following rules: /// - at least one of par subtrees is completed - /// - at least one of xor substree is completed without an error + /// - at least one of xor subtrees is completed without an error /// - all of seq subtrees are completed /// - call executed successfully (executed state is Executed) pub subtree_complete: bool, /// List of met folds used to determine whether a variable can be shadowed. pub met_folds: VecDeque<&'i str>, + + /// Tracker of all met instructions. + pub tracker: InstrTracker, } impl<'i> ExecutionCtx<'i> { pub(crate) fn new(current_peer_id: String, init_peer_id: String) -> Self { + let current_peer_id = Rc::new(current_peer_id); + Self { current_peer_id, init_peer_id, @@ -75,9 +81,9 @@ impl<'i> ExecutionCtx<'i> { } } - pub(crate) fn last_error(&self) -> LastErrorWithTetraplets { + pub(crate) fn last_error(&self) -> LastErrorWithTetraplet { match &self.last_error { - Some(error_descriptor) => LastErrorWithTetraplets::from_error_descriptor(error_descriptor, self), + Some(error_descriptor) => LastErrorWithTetraplet::from_error_descriptor(error_descriptor, self), None => <_>::default(), } } @@ -89,7 +95,7 @@ use std::fmt::Formatter; impl<'i> Display for ExecutionCtx<'i> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { writeln!(f, "data cache:")?; - for (key, value) in self.data_cache.iter() { + for (key, value) in self.scalars.iter() { writeln!(f, " {} => {}", key, value)?; } writeln!(f, "current peer id: {}", self.current_peer_id)?; diff --git a/air/src/contexts/execution_context/error_descriptor.rs b/air/src/execution_step/execution_context/error_descriptor.rs similarity index 78% rename from air/src/contexts/execution_context/error_descriptor.rs rename to air/src/execution_step/execution_context/error_descriptor.rs index f2a5414e..a20f3485 100644 --- a/air/src/contexts/execution_context/error_descriptor.rs +++ b/air/src/execution_step/execution_context/error_descriptor.rs @@ -15,12 +15,14 @@ */ use super::ExecutionCtx; -use super::ExecutionError; -use super::SecurityTetraplet; +use crate::execution_step::ExecutionError; +use crate::execution_step::RSecurityTetraplet; +use crate::SecurityTetraplet; use serde::Deserialize; use serde::Serialize; +use std::cell::RefCell; use std::rc::Rc; /// This struct is intended to track the last arisen error. @@ -29,7 +31,7 @@ pub(crate) struct LastErrorDescriptor { pub(crate) error: Rc, pub(crate) instruction: String, pub(crate) peer_id: String, - pub(crate) tetraplet: Option, + pub(crate) tetraplet: Option, } /// This type is a serialization target for last error. It means that on the AIR script side @@ -48,21 +50,21 @@ pub struct LastError { /// Helper struct to return last error with tetraplets from the last_error ExecutionCtx method. #[derive(Debug, Default)] -pub(crate) struct LastErrorWithTetraplets { +pub(crate) struct LastErrorWithTetraplet { pub(crate) last_error: LastError, - pub(crate) tetraplets: Vec, + pub(crate) tetraplet: RSecurityTetraplet, } -impl<'s> LastErrorWithTetraplets { +impl<'s> LastErrorWithTetraplet { pub(crate) fn from_error_descriptor(descriptor: &LastErrorDescriptor, ctx: &ExecutionCtx<'_>) -> Self { let last_error = descriptor.serialize(); - let tetraplets = descriptor - .tetraplet - .clone() - .unwrap_or_else(|| SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone())); - let tetraplets = vec![tetraplets]; + let tetraplet = descriptor.tetraplet.clone().unwrap_or_else(|| { + Rc::new(RefCell::new(SecurityTetraplet::literal_tetraplet( + ctx.init_peer_id.clone(), + ))) + }); - Self { last_error, tetraplets } + Self { last_error, tetraplet } } } @@ -71,7 +73,7 @@ impl LastErrorDescriptor { error: Rc, instruction: String, peer_id: String, - tetraplet: Option, + tetraplet: Option, ) -> Self { Self { error, diff --git a/air/src/execution_step/execution_context/instructions_tracker.rs b/air/src/execution_step/execution_context/instructions_tracker.rs new file mode 100644 index 00000000..77b27ed3 --- /dev/null +++ b/air/src/execution_step/execution_context/instructions_tracker.rs @@ -0,0 +1,72 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#[derive(Default)] +#[allow(dead_code)] +pub(crate) struct InstrTracker { + pub(crate) ap: ApTracker, + pub(crate) call: CallTracker, + pub(crate) fold: FoldTracker, + pub(crate) match_count: usize, + pub(crate) mismatch_count: usize, + pub(crate) next_count: usize, + pub(crate) null_count: usize, + pub(crate) par: ParTracker, + pub(crate) seq_count: usize, + pub(crate) xor_count: usize, +} + +#[derive(Default)] +#[allow(dead_code)] +pub(crate) struct ApTracker { + pub(crate) seen_count: usize, + pub(crate) executed_count: usize, +} + +#[derive(Default)] +#[allow(dead_code)] +pub(crate) struct CallTracker { + pub(crate) seen_count: usize, + pub(crate) executed_count: usize, +} + +#[derive(Default)] +#[allow(dead_code)] +pub(crate) struct FoldTracker { + pub(crate) seen_scalar_count: usize, + pub(crate) seen_stream_count: usize, +} + +#[derive(Default)] +#[allow(dead_code)] +pub(crate) struct ParTracker { + pub(crate) seen_count: usize, + pub(crate) executed_count: usize, +} + +impl InstrTracker { + pub(crate) fn met_call(&mut self) { + self.call.seen_count += 1; + } + + pub(crate) fn met_executed_call(&mut self) { + self.call.executed_count += 1; + } + + pub(crate) fn met_fold_stream(&mut self) { + self.fold.seen_stream_count += 1; + } +} diff --git a/air/src/execution_step/execution_context/mod.rs b/air/src/execution_step/execution_context/mod.rs new file mode 100644 index 00000000..ca699245 --- /dev/null +++ b/air/src/execution_step/execution_context/mod.rs @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod context; +mod error_descriptor; +mod instructions_tracker; + +pub(crate) use context::ExecutionCtx; +pub use error_descriptor::LastError; +pub(crate) use error_descriptor::LastErrorDescriptor; +pub(crate) use error_descriptor::LastErrorWithTetraplet; +pub(crate) use instructions_tracker::*; diff --git a/air/src/execution/joinable.rs b/air/src/execution_step/joinable.rs similarity index 92% rename from air/src/execution/joinable.rs rename to air/src/execution_step/joinable.rs index 9a29dc31..00cb04ea 100644 --- a/air/src/execution/joinable.rs +++ b/air/src/execution_step/joinable.rs @@ -1,5 +1,5 @@ /* - * Copyright 2020 Fluence Labs Limited + * Copyright 2021 Fluence Labs Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,6 @@ /// /// At the moment, this trait's applied only to errors. pub(crate) trait Joinable { - /// Return true, if supplied object is joinable + /// Return true, if supplied object is joinable. fn is_joinable(&self) -> bool; } diff --git a/air/src/execution/mod.rs b/air/src/execution_step/mod.rs similarity index 60% rename from air/src/execution/mod.rs rename to air/src/execution_step/mod.rs index b86bd0d6..e5385976 100644 --- a/air/src/execution/mod.rs +++ b/air/src/execution_step/mod.rs @@ -17,18 +17,31 @@ mod air; mod boxed_value; mod errors; +pub(crate) mod execution_context; mod joinable; +mod trace_handler; mod utils; pub(super) use self::air::ExecutableInstruction; pub(super) use self::air::FoldState; +pub(super) use boxed_value::Generation; +pub(super) use boxed_value::ResolvedCallResult; +pub(super) use boxed_value::Scalar; +pub(super) use boxed_value::Stream; +pub(crate) use errors::Catchable; pub(super) use errors::ExecutionError; -pub(self) use joinable::Joinable; +pub(crate) use execution_context::ExecutionCtx; +use joinable::Joinable; +pub(crate) use trace_handler::TraceHandler; +use std::cell::RefCell; use std::rc::Rc; -pub(self) type ExecutionResult = std::result::Result>; -pub(self) use air_parser::ast::Variable; +type ExecutionResult = std::result::Result>; +type RSecurityTetraplet = Rc>; +type SecurityTetraplets = Vec; + +use air_parser::ast::AstVariable; #[macro_export] macro_rules! exec_err { diff --git a/air/src/execution_step/trace_handler/data_keeper/errors.rs b/air/src/execution_step/trace_handler/data_keeper/errors.rs new file mode 100644 index 00000000..0aeeea20 --- /dev/null +++ b/air/src/execution_step/trace_handler/data_keeper/errors.rs @@ -0,0 +1,44 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use thiserror::Error as ThisError; + +/// Errors arose out while accessing various interpreter data. +#[derive(ThisError, Debug, PartialEq, Eq)] +pub(crate) enum KeeperError { + /// Errors occurred when trace_len - trace_position < requested_subtrace_len. + #[error( + "executed trace has {trace_len} elements and current position is {trace_position},\ + but tried to set {requested_subtrace_len} subtrace_len" + )] + SetSubtraceLenFailed { + requested_subtrace_len: usize, + trace_position: usize, + trace_len: usize, + }, + + /// Errors occurred when + /// requested_subtrace_len != 0 && requested_pos + requested_subtrace_len > trace_len. + #[error( + "executed trace has {trace_len} elements,\ + but tried to set {requested_subtrace_len} subtrace_len and {requested_pos} position" + )] + SetSubtraceLenAndPosFailed { + requested_pos: usize, + requested_subtrace_len: usize, + trace_len: usize, + }, +} diff --git a/air/src/execution_step/trace_handler/data_keeper/keeper.rs b/air/src/execution_step/trace_handler/data_keeper/keeper.rs new file mode 100644 index 00000000..93401c9b --- /dev/null +++ b/air/src/execution_step/trace_handler/data_keeper/keeper.rs @@ -0,0 +1,72 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionTrace; +use super::MergeCtx; +use super::TraceSlider; + +use air_interpreter_data::InterpreterData; + +use std::collections::HashMap; + +/// Keeps all necessary data for merging. +#[derive(Debug, Default, PartialEq)] +pub(crate) struct DataKeeper { + pub(crate) prev_ctx: MergeCtx, + pub(crate) current_ctx: MergeCtx, + pub(crate) new_to_old_pos: HashMap, + pub(crate) result_trace: ExecutionTrace, +} + +impl DataKeeper { + pub(crate) fn from_data(prev_data: InterpreterData, current_data: InterpreterData) -> Self { + let prev_ctx = MergeCtx::from_data(prev_data); + let current_ctx = MergeCtx::from_data(current_data); + + Self { + prev_ctx, + current_ctx, + new_to_old_pos: <_>::default(), + result_trace: <_>::default(), + } + } + + pub(crate) fn result_states_count(&self) -> usize { + self.result_trace.len() + } + + pub(crate) fn prev_slider(&self) -> &TraceSlider { + &self.prev_ctx.slider + } + + pub(crate) fn prev_slider_mut(&mut self) -> &mut TraceSlider { + &mut self.prev_ctx.slider + } + + pub(crate) fn current_slider(&self) -> &TraceSlider { + &self.current_ctx.slider + } + + pub(crate) fn current_slider_mut(&mut self) -> &mut TraceSlider { + &mut self.current_ctx.slider + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub(crate) struct DataPositions { + pub(crate) prev_pos: Option, + pub(crate) current_pos: Option, +} diff --git a/air/src/execution_step/trace_handler/data_keeper/merge_ctx.rs b/air/src/execution_step/trace_handler/data_keeper/merge_ctx.rs new file mode 100644 index 00000000..2bb7c367 --- /dev/null +++ b/air/src/execution_step/trace_handler/data_keeper/merge_ctx.rs @@ -0,0 +1,83 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutionTrace; +use super::TraceSlider; + +use air_interpreter_data::InterpreterData; +use air_interpreter_data::StreamGenerations; + +use std::collections::HashMap; + +/// Contains all necessary information about data. +#[derive(Debug, Default, PartialEq)] +pub(crate) struct MergeCtx { + pub(crate) slider: TraceSlider, + pub(crate) streams: StreamGenerations, + /// This value is used to track the whole trace that each fold is described. + /// total_subtrace_len and subtrace_len from a slider are changed in the following way: + /// fold: + /// start: total = fold_states_count, subtrace_len = len of the first iteration + /// i iteration_end: total -= iteration_i len, subtrace_len = len of the i+1 iteration + /// end: total = 0 + /// par => total -= [left, right], new_subtrace_len = total - [left, right], pos += [left, right] + total_subtrace_len: usize, +} + +impl MergeCtx { + #[allow(dead_code)] + pub(crate) fn from_trace(trace: ExecutionTrace) -> Self { + let total_subtrace_len = trace.len(); + let slider = TraceSlider::new(trace); + + Self { + slider, + streams: HashMap::new(), + total_subtrace_len, + } + } + + pub(crate) fn from_data(data: InterpreterData) -> Self { + let total_subtrace_len = data.trace.len(); + let slider = TraceSlider::new(data.trace); + + Self { + slider, + streams: data.streams, + total_subtrace_len, + } + } + + pub(crate) fn stream_generation(&self, stream_name: &str) -> Option { + self.streams.get(stream_name).copied() + } + + pub(crate) fn set_total_subtrace_len(&mut self, total_subtrace_len: usize) { + if total_subtrace_len == 0 { + // setting empty subtrace_len is always possible + let _ = self.slider.set_subtrace_len(0); + } + + self.total_subtrace_len = total_subtrace_len; + } + + pub(crate) fn total_subtrace_len(&self) -> usize { + if self.total_subtrace_len < self.slider.seen_elements() { + return self.slider.subtrace_len(); + } + self.total_subtrace_len - self.slider.seen_elements() + } +} diff --git a/air/src/execution_step/trace_handler/data_keeper/mod.rs b/air/src/execution_step/trace_handler/data_keeper/mod.rs new file mode 100644 index 00000000..6e14b2ff --- /dev/null +++ b/air/src/execution_step/trace_handler/data_keeper/mod.rs @@ -0,0 +1,31 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod errors; +mod keeper; +mod merge_ctx; +mod trace_slider; + +pub(crate) use errors::KeeperError; +pub(crate) use keeper::DataKeeper; +pub(crate) use keeper::DataPositions; +pub(super) use merge_ctx::MergeCtx; +pub(super) use trace_slider::TraceSlider; + +pub(self) type KeeperResult = std::result::Result; + +use super::ExecutedState; +use super::ExecutionTrace; diff --git a/air/src/execution_step/trace_handler/data_keeper/trace_slider.rs b/air/src/execution_step/trace_handler/data_keeper/trace_slider.rs new file mode 100644 index 00000000..58f5421c --- /dev/null +++ b/air/src/execution_step/trace_handler/data_keeper/trace_slider.rs @@ -0,0 +1,109 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutedState; +use super::ExecutionTrace; +use super::KeeperError::*; +use super::KeeperResult; + +/// This slider is intended to slide on a subtrace inside provided trace. This subtrace +/// is identified by position and len. +// TODO: check for overflow +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub(crate) struct TraceSlider { + /// Trace that slider slide on. + trace: ExecutionTrace, + + /// Position of current subtrace inside trace. + position: usize, + + /// Length of a current subtrace. + subtrace_len: usize, + + /// Count of seen elements since the last position update. + seen_elements: usize, +} + +impl TraceSlider { + pub(super) fn new(trace: ExecutionTrace) -> Self { + let subtrace_len = trace.len(); + + Self { + trace, + subtrace_len, + ..<_>::default() + } + } + + /// Returns the next state if current interval length hasn't been reached + /// and None otherwise. + #[allow(clippy::suspicious_operation_groupings)] + pub(crate) fn next_state(&mut self) -> Option { + if self.seen_elements >= self.subtrace_len || self.position >= self.trace.len() { + return None; + } + + let result = self.trace[self.position].clone(); + self.position += 1; + self.seen_elements += 1; + Some(result) + } + + pub(crate) fn set_position_and_len(&mut self, position: usize, subtrace_len: usize) -> KeeperResult<()> { + // it's possible to set empty subtrace_len and inconsistent position + if subtrace_len != 0 && position + subtrace_len > self.trace.len() { + return Err(SetSubtraceLenAndPosFailed { + requested_pos: position, + requested_subtrace_len: subtrace_len, + trace_len: self.trace.len(), + }); + } + + self.position = position; + self.subtrace_len = subtrace_len; + self.seen_elements = 0; + + Ok(()) + } + + pub(crate) fn set_subtrace_len(&mut self, subtrace_len: usize) -> KeeperResult<()> { + let trace_remainder = self.trace.len() - self.position; + if trace_remainder < subtrace_len { + return Err(SetSubtraceLenFailed { + requested_subtrace_len: subtrace_len, + trace_position: self.position, + trace_len: self.trace.len(), + }); + } + + self.seen_elements = 0; + self.subtrace_len = subtrace_len; + + Ok(()) + } + + pub(crate) fn position(&self) -> usize { + self.position + } + + pub(crate) fn subtrace_len(&self) -> usize { + self.subtrace_len - self.seen_elements + } + + pub(crate) fn seen_elements(&self) -> usize { + self.seen_elements + } +} diff --git a/air/src/execution_step/trace_handler/errors.rs b/air/src/execution_step/trace_handler/errors.rs new file mode 100644 index 00000000..3b5eb0ea --- /dev/null +++ b/air/src/execution_step/trace_handler/errors.rs @@ -0,0 +1,35 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::data_keeper::KeeperError; +use super::merger::MergeError; +use super::state_automata::StateFSMError; + +use thiserror::Error as ThisError; + +/// Errors arose out of merging previous data with a new. +#[derive(ThisError, Debug)] +#[allow(clippy::enum_variant_names)] +pub(crate) enum TraceHandlerError { + #[error("{0}")] + KeeperError(#[from] KeeperError), + + #[error("{0}")] + MergeError(#[from] MergeError), + + #[error("{0}")] + StateFSMError(#[from] StateFSMError), +} diff --git a/air/src/execution_step/trace_handler/handler.rs b/air/src/execution_step/trace_handler/handler.rs new file mode 100644 index 00000000..d20aaaf2 --- /dev/null +++ b/air/src/execution_step/trace_handler/handler.rs @@ -0,0 +1,170 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use crate::log_targets::EXECUTED_STATE_CHANGING; +use merger::*; + +use air_interpreter_data::InterpreterData; +use air_parser::ast::CallOutputValue; + +#[derive(Debug, Default)] +pub(crate) struct TraceHandler { + pub(crate) data_keeper: DataKeeper, + fsm_keeper: FSMKeeper, +} + +impl TraceHandler { + pub(crate) fn from_data(prev_data: InterpreterData, current_data: InterpreterData) -> Self { + let data_keeper = DataKeeper::from_data(prev_data, current_data); + + Self { + data_keeper, + fsm_keeper: <_>::default(), + } + } + + /// Returns size of elements inside result trace and intended to provide + /// a position of next inserted elements. + pub(crate) fn trace_pos(&self) -> usize { + self.data_keeper.result_trace.len() + } + + pub(crate) fn into_result_trace(self) -> ExecutionTrace { + self.data_keeper.result_trace + } + + pub(crate) fn as_result_trace(&self) -> &ExecutionTrace { + &self.data_keeper.result_trace + } + + pub(crate) fn subtree_sizes(&self) -> (usize, usize) { + let prev_len = self.data_keeper.prev_slider().subtrace_len(); + let current_len = self.data_keeper.current_slider().subtrace_len(); + + (prev_len, current_len) + } +} + +impl TraceHandler { + /// Should be called at the beginning of a call execution. + pub(crate) fn meet_call_start( + &mut self, + output_value: &CallOutputValue<'_>, + ) -> TraceHandlerResult { + try_merge_next_state_as_call(&mut self.data_keeper, output_value).map_err(Into::into) + } + + /// Should be called when a call instruction was executed successfully. It adds the supplied + /// state to the result trace. + pub(crate) fn meet_call_end(&mut self, call_result: CallResult) { + log::trace!( + target: EXECUTED_STATE_CHANGING, + " adding new call executed state {:?}", + call_result + ); + self.data_keeper.result_trace.push(ExecutedState::Call(call_result)); + } +} + +impl TraceHandler { + pub(crate) fn meet_ap_start(&mut self) -> TraceHandlerResult { + try_merge_next_state_as_ap(&mut self.data_keeper).map_err(Into::into) + } + + pub(crate) fn meet_ap_end(&mut self, ap_result: ApResult) { + self.data_keeper.result_trace.push(ExecutedState::Ap(ap_result)); + } +} + +impl TraceHandler { + pub(crate) fn meet_par_start(&mut self) -> TraceHandlerResult<()> { + let ingredients = merger::try_merge_next_state_as_par(&mut self.data_keeper)?; + let par_fsm = ParFSM::from_left_started(ingredients, &mut self.data_keeper)?; + self.fsm_keeper.push_par(par_fsm); + + Ok(()) + } + + pub(crate) fn meet_par_subtree_end(&mut self, subtree_type: SubtreeType) -> TraceHandlerResult<()> { + match subtree_type { + SubtreeType::Left => { + let par_fsm = self.fsm_keeper.last_par()?; + par_fsm.left_completed(&mut self.data_keeper); + } + SubtreeType::Right => { + let par_fsm = self.fsm_keeper.pop_par()?; + par_fsm.right_completed(&mut self.data_keeper); + } + } + + Ok(()) + } +} + +impl TraceHandler { + pub(crate) fn meet_fold_start(&mut self, fold_id: usize) -> TraceHandlerResult<()> { + let ingredients = try_merge_next_state_as_fold(&mut self.data_keeper)?; + let fold_fsm = FoldFSM::from_fold_start(ingredients, &mut self.data_keeper)?; + self.fsm_keeper.add_fold(fold_id, fold_fsm); + + Ok(()) + } + + pub(crate) fn meet_iteration_start(&mut self, fold_id: usize, value_pos: usize) -> TraceHandlerResult<()> { + let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?; + fold_fsm.meet_iteration_start(value_pos, &mut self.data_keeper)?; + + Ok(()) + } + + pub(crate) fn meet_iteration_end(&mut self, fold_id: usize) -> TraceHandlerResult<()> { + let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?; + fold_fsm.meet_iteration_end(&mut self.data_keeper); + + Ok(()) + } + + pub(crate) fn meet_back_iterator(&mut self, fold_id: usize) -> TraceHandlerResult<()> { + let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?; + fold_fsm.meet_back_iterator(&mut self.data_keeper)?; + + Ok(()) + } + + pub(crate) fn meet_generation_end(&mut self, fold_id: usize) -> TraceHandlerResult<()> { + let fold_fsm = self.fsm_keeper.fold_mut(fold_id)?; + fold_fsm.meet_generation_end(&mut self.data_keeper); + + Ok(()) + } + + pub(crate) fn meet_fold_end(&mut self, fold_id: usize) -> TraceHandlerResult<()> { + let fold_fsm = self.fsm_keeper.extract_fold(fold_id)?; + fold_fsm.meet_fold_end(&mut self.data_keeper); + + Ok(()) + } + + pub(crate) fn fold_end_with_error(&mut self, fold_id: usize) { + let fold_fsm = match self.fsm_keeper.extract_fold(fold_id) { + Ok(fold_fsm) => fold_fsm, + // just passing here is ok, because error could be produced while fold initialization + Err(_) => return, + }; + fold_fsm.fold_end_with_error(&mut self.data_keeper); + } +} diff --git a/air/src/execution_step/trace_handler/merger/ap_merger.rs b/air/src/execution_step/trace_handler/merger/ap_merger.rs new file mode 100644 index 00000000..2ac88836 --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/ap_merger.rs @@ -0,0 +1,66 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +#[derive(Debug, Clone)] +pub(crate) enum MergerApResult { + /// There is no corresponding state in a trace for this call. + Empty, + + /// There was a state in at least one of the contexts. If there were two states in + /// both contexts, they were successfully merged. + ApResult { res_generation: Option }, +} + +pub(crate) fn try_merge_next_state_as_ap(data_keeper: &mut DataKeeper) -> MergeResult { + use ExecutedState::Ap; + + let prev_state = data_keeper.prev_slider_mut().next_state(); + let current_state = data_keeper.current_slider_mut().next_state(); + + let ap = match (prev_state, current_state) { + (Some(Ap(prev_ap)), _) => prev_ap, + // check that current state is Ap, but it's impossible to use it, because prev_data + // could not have streams with such generations + (None, Some(Ap(_))) => return Ok(MergerApResult::Empty), + (None, None) => return Ok(MergerApResult::Empty), + (prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "ap")), + }; + + to_merger_result(ap) +} + +macro_rules! to_maybe_generation { + ($ap_result:ident, $generations:expr, $error_ty:ident) => { + match $generations.len() { + 0 => None, + 1 => Some($generations[0]), + _ => { + let ap_error = super::ApResultError::$error_ty($ap_result); + return Err(super::MergeError::IncorrectApResult(ap_error)); + } + } + }; +} + +fn to_merger_result(ap_result: ApResult) -> MergeResult { + let res_generation = to_maybe_generation!(ap_result, &ap_result.res_generations, TooManyDstGenerations); + + let ap_result = MergerApResult::ApResult { res_generation }; + + Ok(ap_result) +} diff --git a/air/src/execution_step/trace_handler/merger/call_merger.rs b/air/src/execution_step/trace_handler/merger/call_merger.rs new file mode 100644 index 00000000..83008297 --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/call_merger.rs @@ -0,0 +1,120 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod call_result_constructor; +mod utils; + +use super::*; +use air_parser::ast::CallOutputValue; +use call_result_constructor::*; +use utils::*; + +#[derive(Debug, Clone)] +pub(crate) enum MergerCallResult { + /// There is no corresponding state in a trace for this call. + Empty, + + /// There was a state in at least one of the contexts. If there were two states in + /// both contexts, they were successfully merged. + CallResult { value: CallResult, trace_pos: usize }, +} + +pub(crate) fn try_merge_next_state_as_call( + data_keeper: &mut DataKeeper, + output_value: &CallOutputValue<'_>, +) -> MergeResult { + use ExecutedState::Call; + use PrepareScheme::*; + + let prev_state = data_keeper.prev_slider_mut().next_state(); + let current_state = data_keeper.current_slider_mut().next_state(); + let value_type = ValueType::from_output_value(output_value); + + let (prev_call, current_call) = match (prev_state, current_state) { + (Some(Call(prev_call)), Some(Call(current_call))) => (prev_call, current_call), + // this special case is needed to merge stream generation in a right way + (None, Some(Call(CallResult::Executed(value)))) => { + let call_result = merge_current_executed(value, value_type, data_keeper)?; + return Ok(prepare_call_result(call_result, Current, data_keeper)); + } + (None, Some(Call(current_call))) => return Ok(prepare_call_result(current_call, Current, data_keeper)), + (Some(Call(prev_call)), None) => return Ok(prepare_call_result(prev_call, Previous, data_keeper)), + (None, None) => return Ok(MergerCallResult::Empty), + (prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "call")), + }; + + let merged_call = merge_call_result(prev_call, current_call, value_type, data_keeper)?; + let call_result = prepare_call_result(merged_call, Both, data_keeper); + try_match_value_type(&call_result, value_type)?; + + Ok(call_result) +} + +fn merge_call_result( + prev_call: CallResult, + current_call: CallResult, + value_type: ValueType<'_>, + data_keeper: &DataKeeper, +) -> MergeResult { + use CallResult::*; + + let merged_state = match (prev_call, current_call) { + (prev @ CallServiceFailed(..), current @ CallServiceFailed(..)) => { + check_equal(&prev, ¤t)?; + prev + } + (RequestSentBy(_), current @ CallServiceFailed(..)) => current, + (prev @ CallServiceFailed(..), RequestSentBy(_)) => prev, + (prev @ RequestSentBy(_), current @ RequestSentBy(_)) => { + check_equal(&prev, ¤t)?; + prev + } + // this special case is needed to merge stream generation in a right way + (RequestSentBy(_), Executed(value)) => merge_current_executed(value, value_type, data_keeper)?, + (prev @ Executed(..), RequestSentBy(_)) => prev, + (Executed(prev_value), Executed(current_value)) => merge_executed(prev_value, current_value)?, + (prev_call, current_call) => return Err(CallResultError::incompatible_calls(prev_call, current_call)), + }; + + Ok(merged_state) +} + +#[derive(Debug, Copy, Clone)] +pub(crate) enum ValueType<'i> { + Scalar, + Stream(&'i str), +} + +impl<'i> ValueType<'i> { + pub(self) fn from_output_value(output_value: &'i CallOutputValue<'_>) -> Self { + use air_parser::ast::AstVariable; + + match output_value { + CallOutputValue::Variable(AstVariable::Stream(stream_name)) => ValueType::Stream(stream_name), + _ => ValueType::Scalar, + } + } +} + +use std::fmt; +impl fmt::Display for ValueType<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ValueType::Scalar => write!(f, "scalar"), + ValueType::Stream(stream_name) => write!(f, "${}", stream_name), + } + } +} diff --git a/air/src/execution_step/trace_handler/merger/call_merger/call_result_constructor.rs b/air/src/execution_step/trace_handler/merger/call_merger/call_result_constructor.rs new file mode 100644 index 00000000..35325a23 --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/call_merger/call_result_constructor.rs @@ -0,0 +1,46 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +pub(super) enum PrepareScheme { + Previous, + Current, + Both, +} + +pub(super) fn prepare_call_result( + value: CallResult, + scheme: PrepareScheme, + data_keeper: &mut DataKeeper, +) -> MergerCallResult { + let prev_pos = match scheme { + PrepareScheme::Previous | PrepareScheme::Both => Some(data_keeper.prev_slider().position() - 1), + PrepareScheme::Current => None, + }; + + let current_pos = match scheme { + PrepareScheme::Current | PrepareScheme::Both => Some(data_keeper.current_slider().position() - 1), + PrepareScheme::Previous => None, + }; + + let data_positions = DataPositions { prev_pos, current_pos }; + + let trace_pos = data_keeper.result_states_count(); + data_keeper.new_to_old_pos.insert(trace_pos, data_positions); + + MergerCallResult::CallResult { value, trace_pos } +} diff --git a/air/src/execution_step/trace_handler/merger/call_merger/utils.rs b/air/src/execution_step/trace_handler/merger/call_merger/utils.rs new file mode 100644 index 00000000..2c3ba65a --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/call_merger/utils.rs @@ -0,0 +1,108 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use crate::JValue; + +use std::rc::Rc; + +pub(super) fn merge_executed(prev_value: Value, current_value: Value) -> MergeResult { + match (&prev_value, ¤t_value) { + (Value::Scalar(_), Value::Scalar(_)) => { + are_scalars_equal(&prev_value, ¤t_value)?; + Ok(CallResult::Executed(prev_value)) + } + (Value::Stream { value: pr, .. }, Value::Stream { value: cr, .. }) => { + are_streams_equal(pr, cr, &prev_value, ¤t_value)?; + Ok(CallResult::Executed(prev_value)) + } + _ => Err(CallResultError::not_equal_values(prev_value, current_value)), + } +} + +fn are_scalars_equal(prev_value: &Value, current_value: &Value) -> MergeResult<()> { + if prev_value == current_value { + return Ok(()); + } + + Err(CallResultError::not_equal_values( + prev_value.clone(), + current_value.clone(), + )) +} + +fn are_streams_equal( + prev_result_value: &Rc, + current_result_value: &Rc, + prev_value: &Value, + current_value: &Value, +) -> MergeResult<()> { + // values from streams could have different generations and it's ok + if prev_result_value == current_result_value { + return Ok(()); + } + + Err(CallResultError::not_equal_values( + prev_value.clone(), + current_value.clone(), + )) +} + +/// Merging of value from only current data to a stream is a something special, because it's +/// needed to choose generation not from current data, but a maximum from streams on a current peer. +/// Maximum versions are tracked in data in a special field called streams. +pub(super) fn merge_current_executed( + value: Value, + value_type: ValueType<'_>, + data_keeper: &DataKeeper, +) -> MergeResult { + match (value, value_type) { + (scalar @ Value::Scalar(_), ValueType::Scalar) => Ok(CallResult::Executed(scalar)), + (Value::Stream { value, .. }, ValueType::Stream(stream_name)) => { + let generation = data_keeper.prev_ctx.stream_generation(stream_name).unwrap_or_default(); + let stream = Value::Stream { value, generation }; + Ok(CallResult::Executed(stream)) + } + (value, value_type) => Err(CallResultError::data_not_match(value, value_type)), + } +} + +pub(super) fn check_equal(prev_call: &CallResult, current_call: &CallResult) -> MergeResult<()> { + if prev_call != current_call { + Err(CallResultError::incompatible_calls( + prev_call.clone(), + current_call.clone(), + )) + } else { + Ok(()) + } +} + +pub(super) fn try_match_value_type(merged_call: &MergerCallResult, value_type: ValueType<'_>) -> MergeResult<()> { + if let MergerCallResult::CallResult { value, .. } = merged_call { + return match (value, value_type) { + (CallResult::Executed(value @ Value::Scalar(_)), ValueType::Stream(_)) => { + Err(CallResultError::data_not_match(value.clone(), value_type)) + } + (CallResult::Executed(value @ Value::Stream { .. }), ValueType::Scalar) => { + Err(CallResultError::data_not_match(value.clone(), value_type)) + } + _ => Ok(()), + }; + } + + Ok(()) +} diff --git a/air/src/execution_step/trace_handler/merger/errors.rs b/air/src/execution_step/trace_handler/merger/errors.rs new file mode 100644 index 00000000..f2d48c85 --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/errors.rs @@ -0,0 +1,146 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::call_merger::ValueType; +use super::ApResult; +use super::CallResult; +use super::ExecutedState; +use super::FoldResult; +use super::KeeperError; +use super::Value; + +use thiserror::Error as ThisError; + +/// Errors arose out of merging previous data with a new. +#[derive(ThisError, Debug)] +pub(crate) enum MergeError { + /// Errors occurred when previous and current executed states are incompatible. + #[error("previous and current data have incompatible states: '{0:?}' '{1:?}'")] + IncompatibleExecutedStates(ExecutedState, ExecutedState), + + /// Merger was expected to see other state that was obtained from one of traces + /// (the other state was absent). + #[error("state from {1} `{0:?}` is incompatible with expected {2}")] + DifferentExecutedStateExpected(ExecutedState, DataType, &'static str), + + #[error("{0:?} contains several subtraces with the same value_pos {1}")] + ManyRecordsWithSamePos(FoldResult, usize), + + /// Errors occurred when one of the fold subtrace lore doesn't contain 2 descriptors. + #[error("fold contains {0} sublore descriptors, but 2 is expected")] + FoldIncorrectSubtracesCount(usize), + + /// Errors bubbled from DataKeeper. + #[error("{0}")] + KeeperError(#[from] KeeperError), + + #[error("{0}")] + IncorrectApResult(#[from] ApResultError), + + #[error("{0}")] + IncorrectCallResult(#[from] CallResultError), +} + +#[derive(ThisError, Debug)] +pub(crate) enum ApResultError { + /// Error occurred when Ap results contains more then 1 generation in destination. + #[error("{0:?} ap result contains too many generations in destination")] + TooManyDstGenerations(ApResult), +} + +#[derive(ThisError, Debug)] +pub(crate) enum CallResultError { + #[error("values in call results are not equal: {prev_value:?} != {current_value:?}")] + ValuesNotEqual { prev_value: Value, current_value: Value }, + + /// Errors occurred when previous and current call results are incompatible. + #[error("previous and current call results are incompatible: '{prev_call:?}' '{current_call:?}'")] + IncompatibleCallResults { + prev_call: CallResult, + current_call: CallResult, + }, + + #[error("air scripts has the following value type '{air_type}' while data other '{data_value:?}'")] + DataNotMatchAIR { air_type: String, data_value: Value }, +} + +impl MergeError { + // shouldn't be called with both Nones + pub(crate) fn incompatible_states( + prev_state: Option, + current_state: Option, + expected_state: &'static str, + ) -> Self { + match (prev_state, current_state) { + (Some(prev_state), Some(current_state)) => { + MergeError::IncompatibleExecutedStates(prev_state, current_state) + } + (None, Some(current_state)) => { + MergeError::DifferentExecutedStateExpected(current_state, DataType::Current, expected_state) + } + (Some(prev_state), None) => { + MergeError::DifferentExecutedStateExpected(prev_state, DataType::Previous, expected_state) + } + (None, None) => unreachable!("shouldn't be called with both None"), + } + } +} + +// these impl methods allow construction of MergeError and are used to make code more clean +impl CallResultError { + pub(crate) fn not_equal_values(prev_value: Value, current_value: Value) -> MergeError { + let call_result_error = CallResultError::ValuesNotEqual { + prev_value, + current_value, + }; + + MergeError::IncorrectCallResult(call_result_error) + } + + pub(crate) fn incompatible_calls(prev_call: CallResult, current_call: CallResult) -> MergeError { + let call_result_error = CallResultError::IncompatibleCallResults { + prev_call, + current_call, + }; + + MergeError::IncorrectCallResult(call_result_error) + } + + pub(crate) fn data_not_match(data_value: Value, air_type: ValueType<'_>) -> MergeError { + let air_type = air_type.to_string(); + + let call_result_error = CallResultError::DataNotMatchAIR { air_type, data_value }; + + MergeError::IncorrectCallResult(call_result_error) + } +} + +#[derive(Clone, Copy, Debug)] +pub enum DataType { + Previous, + Current, +} + +use std::fmt; + +impl fmt::Display for DataType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DataType::Previous => write!(f, "previous"), + DataType::Current => write!(f, "current"), + } + } +} diff --git a/air/src/execution_step/trace_handler/merger/fold_merger.rs b/air/src/execution_step/trace_handler/merger/fold_merger.rs new file mode 100644 index 00000000..0a517a0b --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/fold_merger.rs @@ -0,0 +1,79 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod utils; + +use super::*; +pub(crate) use utils::*; + +#[derive(Debug, Default, Clone)] +pub(crate) struct MergerFoldResult { + pub(crate) prev_fold_lore: ResolvedFold, + pub(crate) current_fold_lore: ResolvedFold, +} + +pub(crate) fn try_merge_next_state_as_fold(data_keeper: &mut DataKeeper) -> MergeResult { + use ExecutedState::Fold; + + let prev_state = data_keeper.prev_slider_mut().next_state(); + let current_state = data_keeper.current_slider_mut().next_state(); + + let fold_result = match (prev_state, current_state) { + (Some(Fold(prev_fold)), Some(Fold(current_fold))) => { + MergerFoldResult::from_fold_results(&prev_fold, ¤t_fold) + } + (None, Some(Fold(current_fold))) => MergerFoldResult::from_fold_result(¤t_fold, MergeCtxType::Current), + (Some(Fold(prev_fold)), None) => MergerFoldResult::from_fold_result(&prev_fold, MergeCtxType::Previous), + (None, None) => return Ok(MergerFoldResult::default()), + (prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "fold")), + }?; + + Ok(fold_result) +} + +impl MergerFoldResult { + pub(self) fn from_fold_result(fold: &FoldResult, ctx_type: MergeCtxType) -> MergeResult { + let (prev_fold_lore, current_fold_lore) = match ctx_type { + MergeCtxType::Previous => { + let fold_lore = resolve_fold_lore(fold)?; + (fold_lore, <_>::default()) + } + MergeCtxType::Current => { + let fold_lore = resolve_fold_lore(fold)?; + (<_>::default(), fold_lore) + } + }; + + let merge_result = Self { + prev_fold_lore, + current_fold_lore, + }; + + Ok(merge_result) + } + + pub(self) fn from_fold_results(prev_fold: &FoldResult, current_fold: &FoldResult) -> MergeResult { + let prev_fold_lore = resolve_fold_lore(prev_fold)?; + let current_fold_lore = resolve_fold_lore(current_fold)?; + + let merge_result = Self { + prev_fold_lore, + current_fold_lore, + }; + + Ok(merge_result) + } +} diff --git a/air/src/execution_step/trace_handler/merger/fold_merger/utils.rs b/air/src/execution_step/trace_handler/merger/fold_merger/utils.rs new file mode 100644 index 00000000..56d200e7 --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/fold_merger/utils.rs @@ -0,0 +1,88 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use air_interpreter_data::FoldSubTraceLore; +use air_interpreter_data::SubTraceDesc; + +use std::collections::HashMap; + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub(crate) struct ResolvedFold { + pub(crate) lore: HashMap, + pub(crate) fold_states_count: usize, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct ResolvedSubTraceDescs { + pub(crate) before_subtrace: SubTraceDesc, + pub(crate) after_subtrace: SubTraceDesc, +} + +pub(super) fn resolve_fold_lore(fold: &FoldResult) -> MergeResult { + let mut lore = HashMap::with_capacity(fold.lore.len()); + let mut fold_states_count = 0usize; + + for subtrace_lore in fold.lore.iter() { + check_subtrace_lore(subtrace_lore)?; + + let resolved_descs = ResolvedSubTraceDescs { + before_subtrace: subtrace_lore.subtraces_desc[0], + after_subtrace: subtrace_lore.subtraces_desc[1], + }; + + fold_states_count += resolved_descs.len(); + + if lore.insert(subtrace_lore.value_pos as usize, resolved_descs).is_some() { + return Err(MergeError::ManyRecordsWithSamePos( + fold.clone(), + subtrace_lore.value_pos as usize, + )); + } + } + + let resolved_fold_lore = ResolvedFold::new(lore, fold_states_count); + Ok(resolved_fold_lore) +} + +fn check_subtrace_lore(subtrace_lore: &FoldSubTraceLore) -> MergeResult<()> { + // this limitation is due to current constraint on count of next inside one fold, + // for more info please see comments in the interpreter-data crate + const SUBTRACE_DESC_COUNT: usize = 2; + + if subtrace_lore.subtraces_desc.len() != SUBTRACE_DESC_COUNT { + return Err(MergeError::FoldIncorrectSubtracesCount( + subtrace_lore.subtraces_desc.len(), + )); + } + + Ok(()) +} + +impl ResolvedFold { + pub(crate) fn new(lore: HashMap, fold_states_count: usize) -> Self { + Self { + lore, + fold_states_count, + } + } +} + +impl ResolvedSubTraceDescs { + pub(crate) fn len(&self) -> usize { + self.before_subtrace.subtrace_len as usize + self.after_subtrace.subtrace_len as usize + } +} diff --git a/air/src/execution_step/trace_handler/merger/mod.rs b/air/src/execution_step/trace_handler/merger/mod.rs new file mode 100644 index 00000000..57c07110 --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/mod.rs @@ -0,0 +1,60 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod ap_merger; +mod call_merger; +mod errors; +mod fold_merger; +mod par_merger; + +pub(super) use ap_merger::try_merge_next_state_as_ap; +pub(crate) use ap_merger::MergerApResult; +pub(super) use call_merger::try_merge_next_state_as_call; +pub(crate) use call_merger::MergerCallResult; +pub(crate) use errors::ApResultError; +pub(crate) use errors::CallResultError; +pub(crate) use errors::MergeError; +pub(crate) use fold_merger::try_merge_next_state_as_fold; +pub(crate) use fold_merger::MergerFoldResult; +pub(crate) use fold_merger::ResolvedFold; +pub(crate) use fold_merger::ResolvedSubTraceDescs; +pub(crate) use par_merger::try_merge_next_state_as_par; +pub(crate) use par_merger::MergerParResult; + +type MergeResult = std::result::Result; + +use super::data_keeper::DataPositions; +use super::data_keeper::KeeperError; +use super::DataKeeper; + +use air_interpreter_data::*; + +#[derive(Debug, Copy, Clone)] +pub(crate) enum MergeCtxType { + Current, + Previous, +} + +use std::fmt; + +impl fmt::Display for MergeCtxType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MergeCtxType::Previous => write!(f, "previous"), + MergeCtxType::Current => write!(f, "current"), + } + } +} diff --git a/air/src/execution_step/trace_handler/merger/par_merger.rs b/air/src/execution_step/trace_handler/merger/par_merger.rs new file mode 100644 index 00000000..8092a062 --- /dev/null +++ b/air/src/execution_step/trace_handler/merger/par_merger.rs @@ -0,0 +1,62 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use ExecutedState::Par; + +#[derive(Default, Debug, Copy, Clone)] +pub(crate) struct MergerParResult { + pub(crate) prev_par: Option, + pub(crate) current_par: Option, +} + +pub(crate) fn try_merge_next_state_as_par(data_keeper: &mut DataKeeper) -> MergeResult { + let prev_state = data_keeper.prev_slider_mut().next_state(); + let current_state = data_keeper.current_slider_mut().next_state(); + + let result = match (prev_state, current_state) { + (Some(Par(prev_par)), Some(Par(current_par))) => MergerParResult::from_pars(prev_par, current_par), + (None, Some(Par(current_par))) => MergerParResult::from_current_par(current_par), + (Some(Par(prev_par)), None) => MergerParResult::from_prev_par(prev_par), + (None, None) => MergerParResult::default(), + (prev_state, current_state) => return Err(MergeError::incompatible_states(prev_state, current_state, "par")), + }; + + Ok(result) +} + +impl MergerParResult { + pub(self) fn from_pars(prev_par: ParResult, current_par: ParResult) -> Self { + Self { + prev_par: Some(prev_par), + current_par: Some(current_par), + } + } + + pub(self) fn from_prev_par(prev_par: ParResult) -> Self { + Self { + prev_par: Some(prev_par), + current_par: None, + } + } + + pub(self) fn from_current_par(current_par: ParResult) -> Self { + Self { + prev_par: None, + current_par: Some(current_par), + } + } +} diff --git a/air/src/execution_step/trace_handler/mod.rs b/air/src/execution_step/trace_handler/mod.rs new file mode 100644 index 00000000..8d139b02 --- /dev/null +++ b/air/src/execution_step/trace_handler/mod.rs @@ -0,0 +1,39 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod data_keeper; +mod errors; +mod handler; +mod merger; +mod state_automata; + +pub(crate) use errors::TraceHandlerError; +pub(crate) use handler::TraceHandler; +pub(crate) use merger::MergerApResult; +pub(crate) use merger::MergerCallResult; +pub(crate) use state_automata::SubtreeType; + +pub(crate) type TraceHandlerResult = std::result::Result; + +use air_interpreter_data::*; +use data_keeper::DataKeeper; +use merger::MergeCtxType; +use merger::MergerFoldResult; +use merger::ResolvedFold; +use merger::ResolvedSubTraceDescs; +use state_automata::FSMKeeper; +use state_automata::FoldFSM; +use state_automata::ParFSM; diff --git a/air/src/execution_step/trace_handler/state_automata/errors.rs b/air/src/execution_step/trace_handler/state_automata/errors.rs new file mode 100644 index 00000000..e1090507 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/errors.rs @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::KeeperError; +use super::ParResult; +use crate::execution_step::trace_handler::MergeCtxType; +use crate::execution_step::trace_handler::ResolvedFold; + +use thiserror::Error as ThisError; + +/// Errors arose out of merging previous data with a new. +#[derive(ThisError, Debug)] +pub(crate) enum StateFSMError { + /// Error occurred while trying to access or pop elements from an empty par queue. + #[error("par queue is empty, while par FSM is requested")] + ParQueueIsEmpty(), + + /// Errors occurred while trying to access or pop elements from queue, + /// which contains element of different type. + #[error("fold FSM for fold id {0} wasn't found")] + FoldFSMNotFound(usize), + + /// Errors occurred when ParResult.0 + ParResult.1 overflows. + #[error("overflow is occurred while calculating the entire len occupied by executed states corresponded to current par: '{0:?}'")] + ParLenOverflow(ParResult), + + /// Errors occurred when slider.position() + ParResult.0 + ParResult.1 overflows. + #[error("overflow is occurred while calculating the new position of a {2} slider for resolved par {0:?} and current position {1}'")] + ParPosOverflow(ParResult, usize, MergeCtxType), + + /// Errors occurred when ParResult.0 + ParResult.1 value is bigger than current subtree size. + #[error("underflow is occurred while calculating the new position of a {2} slider for resolved par {0:?} and current subtrace len {1}'")] + ParLenUnderflow(ParResult, usize, MergeCtxType), + + /// Errors occurred when {0}.fold_states_count + {1} overflows. + #[error("overflow is occurred while calculating the new position of a {2} slider for resolved fold {0:?} and current position {1}'")] + FoldPosOverflow(ResolvedFold, usize, MergeCtxType), + + /// Errors occurred when {1} - 1{0}.fold_states_count underflows. + #[error("underflow is occurred while calculating the new position of a {2} slider for resolved fold {0:?} and current subtrace len {1}'")] + FoldLenUnderflow(ResolvedFold, usize, MergeCtxType), + + /// Errors occurred while trying to set a total_subtrace_len that is less than + #[error("trying to set total_subtrace_len {0} that is less then len set before {1} for {2} ctx")] + TotalSubtraceLenIsLess(usize, usize, MergeCtxType), + + /// Errors bubbled from DataKeeper. + #[error("{0}")] + KeeperError(#[from] KeeperError), +} diff --git a/air/src/execution_step/trace_handler/state_automata/fold_fsm.rs b/air/src/execution_step/trace_handler/state_automata/fold_fsm.rs new file mode 100644 index 00000000..aab68463 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/fold_fsm.rs @@ -0,0 +1,164 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod lore_applier; +mod lore_ctor; +mod lore_ctor_queue; +mod state_handler; + +use super::*; +use lore_applier::*; +use lore_ctor::*; +use lore_ctor_queue::*; +use state_handler::CtxStateHandler; + +use air_interpreter_data::FoldLore; + +/// This FSM manages fold and keeps internally queue of lore ctors. +/// State transitioning functions must work in the following way: +/// meet_fold_start.1 -> +/// meet_generation_start.N -> +/// meet_next.M -> +/// meet_prev.M -> +/// meet_generation_end.N -> +/// meet_fold_end.1 +/// where .T means that this function should be called exactly T times. +#[derive(Debug, Default, Clone)] +pub(crate) struct FoldFSM { + prev_fold: ResolvedFold, + current_fold: ResolvedFold, + state_inserter: StateInserter, + ctor_queue: SubTraceLoreCtorQueue, + result_lore: FoldLore, + state_handler: CtxStateHandler, +} + +impl FoldFSM { + pub(crate) fn from_fold_start(fold_result: MergerFoldResult, data_keeper: &mut DataKeeper) -> FSMResult { + let state_inserter = StateInserter::from_keeper(data_keeper); + let state_updater = + CtxStateHandler::prepare(&fold_result.prev_fold_lore, &fold_result.current_fold_lore, data_keeper)?; + + data_keeper + .prev_ctx + .set_total_subtrace_len(fold_result.prev_fold_lore.fold_states_count); + data_keeper + .current_ctx + .set_total_subtrace_len(fold_result.current_fold_lore.fold_states_count); + + let fold_fsm = Self { + prev_fold: fold_result.prev_fold_lore, + current_fold: fold_result.current_fold_lore, + state_inserter, + state_handler: state_updater, + ..<_>::default() + }; + + Ok(fold_fsm) + } + + pub(crate) fn meet_iteration_start(&mut self, value_pos: usize, data_keeper: &mut DataKeeper) -> FSMResult<()> { + let (prev_pos, current_pos) = match data_keeper.new_to_old_pos.get(&value_pos) { + Some(DataPositions { prev_pos, current_pos }) => (prev_pos, current_pos), + None => return self.prepare(None, None, value_pos, data_keeper), + }; + + let prev_lore = prev_pos.map(|pos| self.prev_fold.lore.remove(&pos)).flatten(); + let current_lore = current_pos.map(|pos| self.current_fold.lore.remove(&pos)).flatten(); + + self.prepare(prev_lore, current_lore, value_pos, data_keeper) + } + + fn prepare( + &mut self, + prev_lore: Option, + current_lore: Option, + value_pos: usize, + data_keeper: &mut DataKeeper, + ) -> FSMResult<()> { + apply_fold_lore_before(data_keeper, &prev_lore, ¤t_lore)?; + + let ctor = SubTraceLoreCtor::from_before_start(value_pos, data_keeper); + self.ctor_queue.add_element(ctor, prev_lore, current_lore); + + Ok(()) + } + + pub(crate) fn meet_iteration_end(&mut self, data_keeper: &mut DataKeeper) { + self.ctor_queue.current().ctor.before_end(data_keeper); + } + + pub(crate) fn meet_back_iterator(&mut self, data_keeper: &mut DataKeeper) -> FSMResult<()> { + let back_traversal_started = self.ctor_queue.back_traversal_started(); + + let LoreCtorDesc { + ctor, + prev_lore, + current_lore, + } = self.ctor_queue.current(); + + if !back_traversal_started { + ctor.maybe_before_end(data_keeper); + ctor.after_start(data_keeper); + apply_fold_lore_after(data_keeper, prev_lore, current_lore)?; + self.ctor_queue.start_back_traverse(); + } else { + ctor.after_end(data_keeper); + self.ctor_queue.traverse_back(); + + let LoreCtorDesc { + ctor, + prev_lore, + current_lore, + } = self.ctor_queue.current(); + + ctor.after_start(data_keeper); + apply_fold_lore_after(data_keeper, prev_lore, current_lore)?; + } + + Ok(()) + } + + pub(crate) fn meet_generation_end(&mut self, data_keeper: &mut DataKeeper) { + self.ctor_queue.finish(data_keeper); + self.ctor_queue.end_back_traverse(); + + let fold_lore = self.ctor_queue.transform_to_lore(); + self.result_lore.extend(fold_lore); + } + + pub(crate) fn meet_fold_end(self, data_keeper: &mut DataKeeper) { + // TODO: check for prev and current lore emptiness + let fold_result = FoldResult { lore: self.result_lore }; + let state = ExecutedState::Fold(fold_result); + self.state_inserter.insert(data_keeper, state); + self.state_handler.set_final_states(data_keeper); + } + + pub(crate) fn fold_end_with_error(mut self, data_keeper: &mut DataKeeper) { + self.meet_generation_end(data_keeper); + self.meet_fold_end(data_keeper); + } +} + +#[derive(Clone, Copy)] +pub(self) enum ByNextPosition { + /// Represents executed states before next. + Before, + + /// Represents executed states after next. + After, +} diff --git a/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_applier.rs b/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_applier.rs new file mode 100644 index 00000000..6ff67d6e --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_applier.rs @@ -0,0 +1,72 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use ByNextPosition::*; +use MergeCtxType::*; + +/// Adjusts sliders accordingly to a before fold lore state. +pub(super) fn apply_fold_lore_before( + data_keeper: &mut DataKeeper, + prev_fold_lore: &Option, + current_fold_lore: &Option, +) -> FSMResult<()> { + apply_fold_lore(data_keeper, prev_fold_lore, Previous, Before)?; + apply_fold_lore(data_keeper, current_fold_lore, Current, Before) +} + +/// Adjusts sliders accordingly to an after fold lore state. +pub(super) fn apply_fold_lore_after( + data_keeper: &mut DataKeeper, + prev_fold_lore: &Option, + current_fold_lore: &Option, +) -> FSMResult<()> { + apply_fold_lore(data_keeper, prev_fold_lore, Previous, After)?; + apply_fold_lore(data_keeper, current_fold_lore, Current, After) +} + +fn apply_fold_lore( + data_keeper: &mut DataKeeper, + fold_lore: &Option, + ctx_type: MergeCtxType, + next_position: ByNextPosition, +) -> FSMResult<()> { + let fold_lore = match fold_lore { + Some(fold_lore) => fold_lore, + None => return Ok(()), + }; + + let slider = match ctx_type { + Previous => data_keeper.prev_slider_mut(), + Current => data_keeper.current_slider_mut(), + }; + + match next_position { + Before => { + slider.set_position_and_len( + fold_lore.before_subtrace.begin_pos as _, + fold_lore.before_subtrace.subtrace_len as _, + )?; + } + After => { + slider.set_position_and_len( + fold_lore.after_subtrace.begin_pos as _, + fold_lore.after_subtrace.subtrace_len as _, + )?; + } + } + Ok(()) +} diff --git a/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor.rs b/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor.rs new file mode 100644 index 00000000..c0539c70 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor.rs @@ -0,0 +1,146 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use air_interpreter_data::SubTraceDesc; + +/// This struct is a form of FSM and is intended to construct a fold subtrace lore element. +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] +pub(super) struct SubTraceLoreCtor { + value_pos: usize, + before_tracker: PositionsTracker, + after_tracker: PositionsTracker, + state: CtorState, +} + +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] +struct PositionsTracker { + pub(self) start_pos: usize, + pub(self) end_pos: usize, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum CtorState { + BeforeStarted, + BeforeCompleted, + AfterStarted, + AfterCompleted, +} + +impl SubTraceLoreCtor { + pub(super) fn from_before_start(value_pos: usize, data_keeper: &DataKeeper) -> Self { + let before_tracker = PositionsTracker { + start_pos: data_keeper.result_states_count(), + end_pos: 0, + }; + + Self { + value_pos, + before_tracker, + ..<_>::default() + } + } + + pub(super) fn before_end(&mut self, data_keeper: &DataKeeper) { + self.before_tracker.end_pos = data_keeper.result_states_count(); + self.state.next(); + } + + pub(super) fn maybe_before_end(&mut self, data_keeper: &DataKeeper) { + if !matches!(self.state, CtorState::BeforeStarted) { + return; + } + + self.before_tracker.end_pos = data_keeper.result_states_count(); + self.state.next(); + } + + pub(super) fn after_start(&mut self, data_keeper: &DataKeeper) { + self.after_tracker.start_pos = data_keeper.result_states_count(); + self.state.next(); + } + + pub(super) fn after_end(&mut self, data_keeper: &DataKeeper) { + self.after_tracker.end_pos = data_keeper.result_states_count(); + self.state.next(); + } + + pub(super) fn into_subtrace_lore(self) -> FoldSubTraceLore { + let before = SubTraceDesc { + begin_pos: self.before_tracker.start_pos as _, + subtrace_len: self.before_tracker.len() as _, + }; + + let after = SubTraceDesc { + begin_pos: self.after_tracker.start_pos as _, + subtrace_len: self.after_tracker.len() as _, + }; + + FoldSubTraceLore { + value_pos: self.value_pos as _, + subtraces_desc: vec![before, after], + } + } + + // this function should be called in a situation of early exit from fold, + // for more details see the comment above SubTraceLoreCtorQueue::finish(). + pub(super) fn finish(&mut self, data_keeper: &DataKeeper) { + use CtorState::*; + + match self.state { + BeforeStarted => { + self.before_end(data_keeper); + self.after_start(data_keeper); + self.after_end(data_keeper); + } + BeforeCompleted => { + self.after_start(data_keeper); + self.after_end(data_keeper); + } + AfterStarted => { + self.after_end(data_keeper); + } + AfterCompleted => {} + } + } +} + +impl PositionsTracker { + pub(self) fn len(&self) -> usize { + self.end_pos - self.start_pos + } +} + +impl Default for CtorState { + fn default() -> Self { + Self::BeforeStarted + } +} + +impl CtorState { + pub(self) fn next(&mut self) { + use CtorState::*; + + let next_state = match self { + BeforeStarted => BeforeCompleted, + BeforeCompleted => AfterStarted, + AfterStarted => AfterCompleted, + AfterCompleted => AfterCompleted, + }; + + *self = next_state; + } +} diff --git a/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor_queue.rs b/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor_queue.rs new file mode 100644 index 00000000..59df1d17 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/fold_fsm/lore_ctor_queue.rs @@ -0,0 +1,103 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::DataKeeper; +use super::FoldLore; +use super::ResolvedSubTraceDescs; +use super::SubTraceLoreCtor; + +/// This queue emulates behaviour of fold states traversal: +/// - at first states are traversal forward until the end of states +/// - then states are traversal backward the same times +#[derive(Debug, Default, Clone)] +pub(super) struct SubTraceLoreCtorQueue { + queue: Vec, + back_traversal_pos: usize, + back_traversal_started: bool, +} + +impl SubTraceLoreCtorQueue { + pub(super) fn current(&mut self) -> &mut LoreCtorDesc { + &mut self.queue[self.back_traversal_pos - 1] + } + + pub(super) fn add_element( + &mut self, + ctor: SubTraceLoreCtor, + prev_lore: Option, + current_lore: Option, + ) { + let new_element = LoreCtorDesc { + ctor, + prev_lore, + current_lore, + }; + self.queue.push(new_element); + self.back_traversal_pos += 1; + } + + pub(super) fn traverse_back(&mut self) { + self.back_traversal_pos -= 1; + } + + pub(super) fn start_back_traverse(&mut self) { + self.back_traversal_started = true; + } + + pub(super) fn end_back_traverse(&mut self) { + self.back_traversal_started = false; + } + + pub(super) fn back_traversal_started(&self) -> bool { + self.back_traversal_started + } + + pub(super) fn transform_to_lore(&mut self) -> FoldLore { + self.queue + .drain(..) + .map(|l| l.ctor.into_subtrace_lore()) + .collect::>() + } + + // this function should be called in a case of early exit from fold, f.e. + // in last error bubbling or join behaviour in the following situations: + // (fold iterable iterator + // (seq + // (call .. [joined_variable]) + // (next iterator) + // ) + // ) + // + // In such example next wouldn't be called and correspondingly all pushed to + // ctor queue states wouldn't be properly finished. This function serves such + // situations, having called from generation_end. + pub(super) fn finish(&mut self, data_keeper: &DataKeeper) { + // TODO: optimize it + for ctor in self.queue.iter_mut() { + ctor.ctor.finish(data_keeper); + } + + // set this to zero to correspond that all states were "observed" with back traversal + self.back_traversal_pos = 0; + } +} + +#[derive(Debug, Clone)] +pub(super) struct LoreCtorDesc { + pub(super) ctor: SubTraceLoreCtor, + pub(super) prev_lore: Option, + pub(super) current_lore: Option, +} diff --git a/air/src/execution_step/trace_handler/state_automata/fold_fsm/state_handler.rs b/air/src/execution_step/trace_handler/state_automata/fold_fsm/state_handler.rs new file mode 100644 index 00000000..3d0332f0 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/fold_fsm/state_handler.rs @@ -0,0 +1,68 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use crate::execution_step::trace_handler::MergeCtxType; +use crate::execution_step::trace_handler::ResolvedFold; + +/// This state updater manage to do the same thing as SubTreeStateUpdater in ParFSM, +/// for details please see its detailed comment. +#[derive(Debug, Default, Clone)] +pub(super) struct CtxStateHandler { + state_pair: CtxStatesPair, +} + +impl CtxStateHandler { + pub(super) fn prepare( + prev_fold: &ResolvedFold, + current_fold: &ResolvedFold, + data_keeper: &DataKeeper, + ) -> FSMResult { + let prev_state = compute_new_state(prev_fold, data_keeper, MergeCtxType::Previous)?; + let current_state = compute_new_state(current_fold, data_keeper, MergeCtxType::Current)?; + let state_pair = CtxStatesPair::new(prev_state, current_state); + + let updater = Self { state_pair }; + Ok(updater) + } + + pub(super) fn set_final_states(self, data_keeper: &mut DataKeeper) { + update_ctx_states(self.state_pair, data_keeper) + } +} + +fn compute_new_state(fold: &ResolvedFold, data_keeper: &DataKeeper, ctx_type: MergeCtxType) -> FSMResult { + let ctx = match ctx_type { + MergeCtxType::Previous => &data_keeper.prev_ctx, + MergeCtxType::Current => &data_keeper.current_ctx, + }; + + let current_position = ctx.slider.position(); + let current_len = ctx.slider.subtrace_len(); + + let pos = current_position + .checked_add(fold.fold_states_count) + .ok_or_else(|| StateFSMError::FoldPosOverflow(fold.clone(), current_position, ctx_type))?; + + let subtrace_len = current_len + .checked_sub(fold.fold_states_count) + .ok_or_else(|| StateFSMError::FoldLenUnderflow(fold.clone(), current_position, ctx_type))?; + + let total_subtrace_len = ctx.total_subtrace_len() - fold.fold_states_count; + + let state = CtxState::new(pos, subtrace_len, total_subtrace_len); + Ok(state) +} diff --git a/air/src/execution_step/trace_handler/state_automata/fsm_queue.rs b/air/src/execution_step/trace_handler/state_automata/fsm_queue.rs new file mode 100644 index 00000000..9a27f504 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/fsm_queue.rs @@ -0,0 +1,58 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::FSMResult; +use super::FoldFSM; +use super::ParFSM; +use super::StateFSMError; + +use std::collections::HashMap; + +#[derive(Debug, Default)] +pub(crate) struct FSMKeeper { + par_stack: Vec, + fold_map: HashMap, +} + +impl FSMKeeper { + pub(crate) fn push_par(&mut self, par_fsm: ParFSM) { + self.par_stack.push(par_fsm); + } + + pub(crate) fn add_fold(&mut self, fold_id: usize, fold_fsm: FoldFSM) { + self.fold_map.insert(fold_id, fold_fsm); + } + + pub(crate) fn last_par(&mut self) -> FSMResult<&mut ParFSM> { + self.par_stack.last_mut().ok_or(StateFSMError::ParQueueIsEmpty()) + } + + pub(crate) fn pop_par(&mut self) -> FSMResult { + self.par_stack.pop().ok_or(StateFSMError::ParQueueIsEmpty()) + } + + pub(crate) fn fold_mut(&mut self, fold_id: usize) -> FSMResult<&mut FoldFSM> { + self.fold_map + .get_mut(&fold_id) + .ok_or(StateFSMError::FoldFSMNotFound(fold_id)) + } + + pub(crate) fn extract_fold(&mut self, fold_id: usize) -> FSMResult { + self.fold_map + .remove(&fold_id) + .ok_or(StateFSMError::FoldFSMNotFound(fold_id)) + } +} diff --git a/air/src/execution_step/trace_handler/state_automata/mod.rs b/air/src/execution_step/trace_handler/state_automata/mod.rs new file mode 100644 index 00000000..7accaf4f --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/mod.rs @@ -0,0 +1,45 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod errors; +mod fold_fsm; +mod fsm_queue; +mod par_fsm; +mod state_inserter; +mod utils; + +pub(crate) use errors::StateFSMError; +pub(crate) use par_fsm::SubtreeType; +pub(crate) type FSMResult = std::result::Result; + +pub(super) use fold_fsm::FoldFSM; +pub(super) use fsm_queue::FSMKeeper; +pub(super) use par_fsm::ParFSM; + +use super::data_keeper::DataPositions; +use super::data_keeper::KeeperError; +use super::merger::MergerParResult; +use super::DataKeeper; +use super::ExecutedState; +use super::FoldResult; +use super::FoldSubTraceLore; +use super::MergeCtxType; +use super::MergerFoldResult; +use super::ParResult; +use super::ResolvedFold; +use super::ResolvedSubTraceDescs; +use state_inserter::StateInserter; +use utils::*; diff --git a/air/src/execution_step/trace_handler/state_automata/par_fsm.rs b/air/src/execution_step/trace_handler/state_automata/par_fsm.rs new file mode 100644 index 00000000..ef112d9f --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/par_fsm.rs @@ -0,0 +1,105 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod par_builder; +mod state_handler; + +use super::*; +use par_builder::ParBuilder; +use state_handler::CtxStateHandler; + +/// Manages a par state, its state transitioning functions must be called in the following way: +/// from_left_started +/// -> left_completed(_with_error) +/// -> right_started +/// -> right_completed(_with_error) +#[derive(Debug, Default, Clone)] +pub(crate) struct ParFSM { + prev_par: ParResult, + current_par: ParResult, + state_inserter: StateInserter, + state_handler: CtxStateHandler, + par_builder: ParBuilder, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub(crate) enum SubtreeType { + Left, + Right, +} + +impl ParFSM { + pub(crate) fn from_left_started(ingredients: MergerParResult, data_keeper: &mut DataKeeper) -> FSMResult { + // default is a par with empty left and right subtrees + let prev_par = ingredients.prev_par.unwrap_or_default(); + let current_par = ingredients.current_par.unwrap_or_default(); + + let state_inserter = StateInserter::from_keeper(data_keeper); + let state_updater = CtxStateHandler::prepare(prev_par, current_par, data_keeper)?; + let par_builder = ParBuilder::from_keeper(data_keeper, &state_inserter); + + let par_fsm = Self { + prev_par, + current_par, + state_inserter, + state_handler: state_updater, + par_builder, + }; + + par_fsm.prepare_sliders(data_keeper, SubtreeType::Left)?; + + Ok(par_fsm) + } + + pub(crate) fn left_completed(&mut self, data_keeper: &mut DataKeeper) { + self.par_builder.track(data_keeper, SubtreeType::Left); + self.state_handler.handle_subtree_end(data_keeper, SubtreeType::Left); + + // all invariants were checked in the ctor + let _ = self.prepare_sliders(data_keeper, SubtreeType::Right); + } + + pub(crate) fn right_completed(mut self, data_keeper: &mut DataKeeper) { + self.par_builder.track(data_keeper, SubtreeType::Right); + let state = self.par_builder.build(); + self.state_inserter.insert(data_keeper, state); + + self.state_handler.handle_subtree_end(data_keeper, SubtreeType::Right); + } + + fn prepare_sliders(&self, data_keeper: &mut DataKeeper, subtree_type: SubtreeType) -> FSMResult<()> { + let (prev_len, current_len) = match subtree_type { + SubtreeType::Left => (self.prev_par.left_size, self.current_par.left_size), + SubtreeType::Right => (self.prev_par.right_size, self.current_par.right_size), + }; + + data_keeper.prev_slider_mut().set_subtrace_len(prev_len as _)?; + data_keeper.current_slider_mut().set_subtrace_len(current_len as _)?; + + Ok(()) + } +} + +use std::fmt; + +impl fmt::Display for SubtreeType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SubtreeType::Left => write!(f, "left"), + SubtreeType::Right => write!(f, "right"), + } + } +} diff --git a/air/src/execution_step/trace_handler/state_automata/par_fsm/par_builder.rs b/air/src/execution_step/trace_handler/state_automata/par_fsm/par_builder.rs new file mode 100644 index 00000000..bd264dee --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/par_fsm/par_builder.rs @@ -0,0 +1,56 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +/// Tracks lens of data_keeper.result_trace to build resulted Par state at the end. +#[derive(Debug, Default, Clone)] +pub(super) struct ParBuilder { + saved_states_count: usize, + left_subtree_size: usize, + right_subtree_size: usize, +} + +impl ParBuilder { + // StateInserter here needs to guaranteed that ParBuilder creates after it, + // it must be so to right track a left subtree size + pub(super) fn from_keeper(data_keeper: &DataKeeper, _: &StateInserter) -> Self { + let saved_states_count = data_keeper.result_states_count(); + + Self { + saved_states_count, + left_subtree_size: 0, + right_subtree_size: 0, + } + } + + pub(super) fn track(&mut self, data_keeper: &DataKeeper, subtree_type: SubtreeType) { + let prev_states_count = self.saved_states_count; + let states_count = data_keeper.result_states_count(); + let resulted_states_count = states_count - prev_states_count; + + match subtree_type { + SubtreeType::Left => self.left_subtree_size = resulted_states_count, + SubtreeType::Right => self.right_subtree_size = resulted_states_count, + } + self.saved_states_count = data_keeper.result_trace.len(); + } + + pub(super) fn build(self) -> ExecutedState { + // TODO: check that usize could be converted into u32 + ExecutedState::par(self.left_subtree_size, self.right_subtree_size) + } +} diff --git a/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler.rs b/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler.rs new file mode 100644 index 00000000..831860de --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler.rs @@ -0,0 +1,121 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod new_states_calculation; +mod utils; + +use super::*; +use crate::execution_step::trace_handler::state_automata::par_fsm::state_handler::utils::compute_par_total_lens; +use new_states_calculation::compute_new_states; +use utils::prepare_total_lens; + +/// At the end of a Par execution it's needed to update subtrace_len and positions of both sliders. +/// +/// To see why it's really needed, imagine the following trace: +/// [par 9, 3] +/// [par 3, 5] <- left subtree of [par 9, 3] +/// [call rs 1] [call rs 2] [call rs 3] <- left subtree of [par 3, 5] +/// [call rs 4] [call rs 5] [call rs 6] [call rs 7] [call rs 8] <- right subtree of [par 3, 5] +/// [par 1, 1] <- right subtree of [par 9, 3] +/// [call e 9] <- left subtree of [par 1, 1] +/// [call e 10] <- right subtree of [par 1, 1] +/// +/// where +/// call rs N - request sent state of Nth call +/// call e N - executed state of Nth call +/// +/// and the following script: +/// (par +/// (xor +/// (par +/// (call 1-3) +/// (call 4-8) +/// ) +/// (null) <- here could be any non-fallible set of instructions +/// ) +/// (par +/// (call 9) +/// (call 10) +/// ) +/// ) +/// +/// Suppose that call 5 (corresponds to [call rs 5]) will fail (f.e. call_service returns a service +/// error). Since it's wrapped with xor, then right subtree of xor (null) will be executed. +/// After that next par will be executed. This par has corresponding state [par 1, 1] in a trace, +/// and to allow slider to pop it it's needed to set updated position in a proper way, because +/// otherwise [call rs 6] will be returned. +/// +/// This struct manages to save the updated lens and pos and update slider states to prevent +/// such situations. +/// +#[derive(Debug, Default, Clone, Copy)] +pub(super) struct CtxStateHandler { + left_pair: CtxStatesPair, + right_pair: CtxStatesPair, +} + +impl CtxStateHandler { + pub(super) fn prepare( + prev_par: ParResult, + current_par: ParResult, + data_keeper: &mut DataKeeper, + ) -> FSMResult { + let left_pair = prepare_left_pair(prev_par, current_par, data_keeper)?; + let right_pair = prepare_right_pair(prev_par, current_par, data_keeper)?; + + let handler = Self { left_pair, right_pair }; + + Ok(handler) + } + + pub(super) fn handle_subtree_end(self, data_keeper: &mut DataKeeper, subtree_type: SubtreeType) { + match subtree_type { + SubtreeType::Left => update_ctx_states(self.left_pair, data_keeper), + SubtreeType::Right => update_ctx_states(self.right_pair, data_keeper), + } + } +} + +fn prepare_left_pair( + prev_par: ParResult, + current_par: ParResult, + data_keeper: &mut DataKeeper, +) -> FSMResult { + let (prev_nibble, current_nibble) = compute_new_states(data_keeper, prev_par, current_par, SubtreeType::Left)?; + let prev_state = CtxState::from_nibble(prev_nibble, prev_nibble.subtrace_len); + let current_state = CtxState::from_nibble(current_nibble, current_nibble.subtrace_len); + let pair = CtxStatesPair::new(prev_state, current_state); + + Ok(pair) +} + +fn prepare_right_pair( + prev_par: ParResult, + current_par: ParResult, + data_keeper: &mut DataKeeper, +) -> FSMResult { + let (prev_par_len, current_par_len) = compute_par_total_lens(prev_par, current_par)?; + let (prev_total_len, current_total_len) = prepare_total_lens(prev_par_len, current_par_len, data_keeper)?; + + let prev_pos = data_keeper.prev_slider().position() + prev_par_len; + let current_pos = data_keeper.current_slider().position() + current_par_len; + + let prev_state = CtxState::new(prev_pos, prev_total_len, prev_total_len); + let current_state = CtxState::new(current_pos, current_total_len, current_total_len); + let pair = CtxStatesPair::new(prev_state, current_state); + + Ok(pair) +} diff --git a/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/new_states_calculation.rs b/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/new_states_calculation.rs new file mode 100644 index 00000000..ee617ed5 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/new_states_calculation.rs @@ -0,0 +1,59 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +pub(super) fn compute_new_states( + data_keeper: &DataKeeper, + prev_par: ParResult, + current_par: ParResult, + subtree_type: SubtreeType, +) -> FSMResult<(CtxStateNibble, CtxStateNibble)> { + let (prev_len, current_len) = match subtree_type { + SubtreeType::Left => (prev_par.left_size, current_par.left_size), + SubtreeType::Right => (prev_par.right_size, current_par.right_size), + }; + + let prev_nibble = compute_new_state(data_keeper, prev_len as usize, MergeCtxType::Previous, prev_par)?; + let current_nibble = compute_new_state(data_keeper, current_len as usize, MergeCtxType::Current, current_par)?; + + Ok((prev_nibble, current_nibble)) +} + +fn compute_new_state( + data_keeper: &DataKeeper, + par_subtree_len: usize, + ctx_type: MergeCtxType, + par: ParResult, +) -> FSMResult { + let slider = match ctx_type { + MergeCtxType::Previous => data_keeper.prev_slider(), + MergeCtxType::Current => data_keeper.current_slider(), + }; + + let pos = slider + .position() + .checked_add(par_subtree_len) + .ok_or_else(|| StateFSMError::ParPosOverflow(par, slider.position(), MergeCtxType::Previous))?; + + let subtrace_len = slider + .subtrace_len() + .checked_sub(par_subtree_len) + .ok_or_else(|| StateFSMError::ParLenUnderflow(par, slider.subtrace_len(), MergeCtxType::Current))?; + + let nibble = CtxStateNibble::new(pos, subtrace_len); + Ok(nibble) +} diff --git a/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/utils.rs b/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/utils.rs new file mode 100644 index 00000000..27436349 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/par_fsm/state_handler/utils.rs @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +pub(super) fn prepare_total_lens( + prev_size: usize, + current_size: usize, + data_keeper: &mut DataKeeper, +) -> FSMResult<(usize, usize)> { + sizes_suits(prev_size, current_size, data_keeper)?; + + // these lens should be set after end of a par + let prev_total_len = data_keeper.prev_ctx.total_subtrace_len() - prev_size; + let current_total_len = data_keeper.current_ctx.total_subtrace_len() - current_size; + + data_keeper.prev_ctx.set_total_subtrace_len(prev_size); + data_keeper.current_ctx.set_total_subtrace_len(current_size); + + Ok((prev_total_len, current_total_len)) +} + +pub(super) fn compute_par_total_lens(prev_par: ParResult, current_par: ParResult) -> FSMResult<(usize, usize)> { + let prev_par_len = prev_par.size().ok_or(StateFSMError::ParLenOverflow(prev_par))?; + let current_par_len = current_par.size().ok_or(StateFSMError::ParLenOverflow(prev_par))?; + + Ok((prev_par_len, current_par_len)) +} + +fn sizes_suits(prev_par_len: usize, current_par_len: usize, data_keeper: &DataKeeper) -> FSMResult<()> { + let prev_total_len = data_keeper.prev_ctx.total_subtrace_len(); + if prev_par_len > prev_total_len { + return Err(StateFSMError::TotalSubtraceLenIsLess( + prev_par_len, + prev_total_len, + MergeCtxType::Previous, + )); + } + + let current_total_len = data_keeper.current_ctx.total_subtrace_len(); + if current_par_len > current_total_len { + return Err(StateFSMError::TotalSubtraceLenIsLess( + current_par_len, + current_total_len, + MergeCtxType::Current, + )); + } + + Ok(()) +} diff --git a/air/src/execution_step/trace_handler/state_automata/state_inserter.rs b/air/src/execution_step/trace_handler/state_automata/state_inserter.rs new file mode 100644 index 00000000..3c546c1c --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/state_inserter.rs @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::DataKeeper; +use super::ExecutedState; + +/// This one is intended to optimize insertion in data to avoid insertion in a middle of it. +/// This is achieved by inserting a temporary state, track insert position and insert there +/// the final state. +#[derive(Debug, Default, Clone)] +pub(super) struct StateInserter { + position: usize, +} + +impl StateInserter { + pub(super) fn from_keeper(data_keeper: &mut DataKeeper) -> Self { + let position = data_keeper.result_trace.len(); + // this par is a temporary state + data_keeper.result_trace.push(ExecutedState::par(0, 0)); + + Self { position } + } + + pub(super) fn insert(self, data_keeper: &mut DataKeeper, state: ExecutedState) { + data_keeper.result_trace[self.position] = state; + } +} diff --git a/air/src/execution_step/trace_handler/state_automata/utils.rs b/air/src/execution_step/trace_handler/state_automata/utils.rs new file mode 100644 index 00000000..4d448ab8 --- /dev/null +++ b/air/src/execution_step/trace_handler/state_automata/utils.rs @@ -0,0 +1,92 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::DataKeeper; + +#[derive(Debug, Default, Clone, Copy)] +pub(super) struct CtxState { + pub(super) pos: usize, + pub(super) subtrace_len: usize, + pub(super) total_subtrace_len: usize, +} + +#[derive(Debug, Default, Clone, Copy)] +pub(super) struct CtxStatesPair { + pub(super) prev_state: CtxState, + pub(super) current_state: CtxState, +} + +#[derive(Clone, Copy, Debug, Default)] +pub(crate) struct CtxStateNibble { + pub(crate) pos: usize, + pub(crate) subtrace_len: usize, +} + +impl CtxState { + pub(super) fn new(pos: usize, subtrace_len: usize, total_subtrace_len: usize) -> Self { + Self { + pos, + subtrace_len, + total_subtrace_len, + } + } + + pub(super) fn from_nibble(nibble: CtxStateNibble, total_subtrace_len: usize) -> Self { + Self { + pos: nibble.pos, + subtrace_len: nibble.subtrace_len, + total_subtrace_len, + } + } +} + +impl CtxStatesPair { + pub(super) fn new(prev_state: CtxState, current_state: CtxState) -> Self { + Self { + prev_state, + current_state, + } + } +} + +impl CtxStateNibble { + pub(super) fn new(pos: usize, subtrace_len: usize) -> Self { + Self { pos, subtrace_len } + } +} + +pub(super) fn update_ctx_states(state_pair: CtxStatesPair, data_keeper: &mut DataKeeper) { + // these calls shouldn't produce a error, because sizes become less and + // they have been already checked in a state updater ctor. It's important + // to make it in a such way, because this function could be called from + // error_exit that shouldn't fail. + let prev_state = state_pair.prev_state; + let current_state = state_pair.current_state; + + let _ = data_keeper + .prev_slider_mut() + .set_position_and_len(prev_state.pos, prev_state.subtrace_len); + data_keeper + .prev_ctx + .set_total_subtrace_len(prev_state.total_subtrace_len); + + let _ = data_keeper + .current_slider_mut() + .set_position_and_len(current_state.pos, current_state.subtrace_len); + data_keeper + .current_ctx + .set_total_subtrace_len(current_state.subtrace_len); +} diff --git a/air/src/execution/utils/mod.rs b/air/src/execution_step/utils/mod.rs similarity index 82% rename from air/src/execution/utils/mod.rs rename to air/src/execution_step/utils/mod.rs index 6e417bcc..18b1f63d 100644 --- a/air/src/execution/utils/mod.rs +++ b/air/src/execution_step/utils/mod.rs @@ -16,6 +16,6 @@ mod resolve; -pub(crate) use resolve::get_variable_name; -pub(crate) use resolve::resolve_to_args; -pub(crate) use resolve::resolve_to_jvaluable; +pub(crate) use resolve::*; + +use super::SecurityTetraplets; diff --git a/air/src/execution_step/utils/resolve.rs b/air/src/execution_step/utils/resolve.rs new file mode 100644 index 00000000..9c62dc6a --- /dev/null +++ b/air/src/execution_step/utils/resolve.rs @@ -0,0 +1,165 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::SecurityTetraplets; +use crate::execution_step::boxed_value::JValuable; +use crate::execution_step::boxed_value::Variable; +use crate::execution_step::execution_context::ExecutionCtx; +use crate::execution_step::execution_context::LastErrorWithTetraplet; +use crate::execution_step::ExecutionError; +use crate::execution_step::ExecutionResult; +use crate::JValue; +use crate::SecurityTetraplet; + +use air_parser::ast::AstVariable; +use air_parser::ast::CallInstrArgValue; +use air_parser::ast::LastErrorPath; +use serde_json::json; +use std::cell::RefCell; +use std::rc::Rc; + +/// Resolve value to called function arguments. +pub(crate) fn resolve_to_args<'i>( + value: &CallInstrArgValue<'i>, + ctx: &ExecutionCtx<'i>, +) -> ExecutionResult<(JValue, SecurityTetraplets)> { + match value { + CallInstrArgValue::InitPeerId => prepare_const(ctx.init_peer_id.clone(), ctx), + CallInstrArgValue::LastError(path) => prepare_last_error(path, ctx), + CallInstrArgValue::Literal(value) => prepare_const(value.to_string(), ctx), + CallInstrArgValue::Boolean(value) => prepare_const(*value, ctx), + CallInstrArgValue::Number(value) => prepare_const(value, ctx), + CallInstrArgValue::EmptyArray => prepare_const(json!([]), ctx), + CallInstrArgValue::Variable(variable) => { + let variable = Variable::from_ast(variable); + prepare_variable(variable, ctx) + } + CallInstrArgValue::JsonPath(json_path) => { + let variable = Variable::from_ast(&json_path.variable); + apply_json_path(variable, json_path.path, json_path.should_flatten, ctx) + } + } +} + +#[allow(clippy::unnecessary_wraps)] +pub(crate) fn prepare_const( + arg: impl Into, + ctx: &ExecutionCtx<'_>, +) -> ExecutionResult<(JValue, SecurityTetraplets)> { + let jvalue = arg.into(); + let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone()); + let tetraplet = Rc::new(RefCell::new(tetraplet)); + + Ok((jvalue, vec![tetraplet])) +} + +#[allow(clippy::unnecessary_wraps)] +pub(crate) fn prepare_last_error( + path: &LastErrorPath, + ctx: &ExecutionCtx<'_>, +) -> ExecutionResult<(JValue, SecurityTetraplets)> { + let LastErrorWithTetraplet { + last_error, + tetraplet: tetraplets, + } = ctx.last_error(); + let jvalue = match path { + LastErrorPath::Instruction => JValue::String(last_error.instruction), + LastErrorPath::Message => JValue::String(last_error.msg), + LastErrorPath::PeerId => JValue::String(last_error.peer_id), + LastErrorPath::None => json!(last_error), + }; + + Ok((jvalue, vec![tetraplets])) +} + +fn prepare_variable<'i>( + variable: Variable<'_>, + ctx: &ExecutionCtx<'i>, +) -> ExecutionResult<(JValue, SecurityTetraplets)> { + let resolved = resolve_variable(variable, ctx)?; + let tetraplets = resolved.as_tetraplets(); + let jvalue = resolved.into_jvalue(); + + Ok((jvalue, tetraplets)) +} + +pub(crate) fn resolve_variable<'ctx, 'i>( + variable: Variable<'_>, + ctx: &'ctx ExecutionCtx<'i>, +) -> ExecutionResult> { + use crate::execution_step::boxed_value::StreamJvaluableIngredients; + + match variable { + Variable::Scalar(name) => scalar_to_jvaluable(name, ctx), + Variable::Stream { name, generation } => { + match ctx.streams.get(name) { + Some(stream) => { + let ingredients = StreamJvaluableIngredients::new(stream.borrow(), generation); + Ok(Box::new(ingredients)) + } + // return an empty stream for not found stream + // here it ignores the join behaviour + None => Ok(Box::new(())), + } + } + } +} + +pub(crate) fn resolve_ast_variable<'ctx, 'i>( + variable: &AstVariable<'_>, + ctx: &'ctx ExecutionCtx<'i>, +) -> ExecutionResult> { + let variable = Variable::from_ast(variable); + resolve_variable(variable, ctx) +} + +pub(crate) fn apply_json_path<'i>( + variable: Variable<'_>, + json_path: &str, + should_flatten: bool, + ctx: &ExecutionCtx<'i>, +) -> ExecutionResult<(JValue, SecurityTetraplets)> { + let resolved = resolve_variable(variable, ctx)?; + let (jvalue, tetraplets) = resolved.apply_json_path_with_tetraplets(json_path)?; + + let jvalue = if should_flatten { + if jvalue.len() > 1 { + let jvalue = jvalue.into_iter().cloned().collect::>(); + return crate::exec_err!(ExecutionError::FlatteningError(JValue::Array(jvalue))); + } + jvalue[0].clone() + } else { + let jvalue = jvalue.into_iter().cloned().collect::>(); + JValue::Array(jvalue) + }; + + Ok((jvalue, tetraplets)) +} + +/// Constructs jvaluable result from scalars by name. +fn scalar_to_jvaluable<'name, 'i, 'ctx>( + name: &'name str, + ctx: &'ctx ExecutionCtx<'i>, +) -> ExecutionResult> { + use ExecutionError::VariableNotFound; + + let value = ctx + .scalars + .get(name) + .ok_or_else(|| VariableNotFound(name.to_string()))?; + + Ok(value.to_jvaluable()) +} diff --git a/air/src/lib.rs b/air/src/lib.rs index d6564139..1f96a6ae 100644 --- a/air/src/lib.rs +++ b/air/src/lib.rs @@ -27,26 +27,22 @@ )] mod build_targets; -mod contexts; -mod execution; -mod preparation; +mod execution_step; +mod preparation_step; -mod air; pub mod log_targets; +mod runner; -pub use crate::contexts::execution::LastError; pub use air_interpreter_interface::InterpreterOutcome; pub use air_interpreter_interface::INTERPRETER_SUCCESS; +pub use execution_step::execution_context::LastError; pub use polyplets::ResolvedTriplet; pub use polyplets::SecurityTetraplet; -pub use crate::air::execute_air; +pub use crate::runner::execute_air; -pub mod execution_trace { - pub use crate::contexts::execution_trace::CallResult; - pub use crate::contexts::execution_trace::ExecutedState; - pub use crate::contexts::execution_trace::ExecutionTrace; - pub use crate::contexts::execution_trace::ParResult; +pub mod interpreter_data { + pub use air_interpreter_data::*; } pub mod parser { diff --git a/air/src/log_targets.rs b/air/src/log_targets.rs index d9eed367..4e98bdd0 100644 --- a/air/src/log_targets.rs +++ b/air/src/log_targets.rs @@ -14,25 +14,22 @@ * limitations under the License. */ -/// Print out each instruction name at the beginning of its execution. +/// Print out each instruction name at the beginning of its execution_step. pub const INSTRUCTION: &str = "instruction"; -/// Print out data cache at the beginning of each instruction execution. +/// Print out data cache at the beginning of each instruction execution_step. pub const DATA_CACHE: &str = "data_cache"; -/// Print out next_peer_pks at the beginning of each instruction execution. +/// Print out next_peer_pks at the beginning of each instruction execution_step. pub const NEXT_PEER_PKS: &str = "next_peer_pks"; -/// Print out subtree_complete value at the beginning of each instruction execution. +/// Print out subtree_complete value at the beginning of each instruction execution_step. pub const SUBTREE_COMPLETE: &str = "subtree_complete"; -/// Print out current executed trace at the beginning of each instruction execution. -pub const EXECUTED_TRACE: &str = "executed_trace"; - -/// Print out count of element in the current subtree at the beginning of each instruction execution. +/// Print out count of element in the current subtree at the beginning of each instruction execution_step. pub const SUBTREE_ELEMENTS: &str = "subtree_elements_count"; -/// Print out state of data cache at the beginning of each instruction execution. +/// Print out state of data cache at the beginning of each instruction execution_step. pub const NEW_EXECUTED_TRACE: &str = "new_executed_trace"; /// Print out logs at the executed states merging stage. @@ -41,23 +38,22 @@ pub const EXECUTED_TRACE_MERGE: &str = "executed_trace_merge"; /// Print out running arguments and params of a script. pub const RUN_PARAMS: &str = "initial_params"; -/// Print out state of data cache at the beginning of each instruction execution. +/// Print out state of data cache at the beginning of each instruction execution_step. pub const EXECUTED_STATE_CHANGING: &str = "executed_state_changing"; /// Print log if call is postponed due the join behaviour. pub const JOIN_BEHAVIOUR: &str = "join_behaviour"; /// This map should be used by rust-sdk logger that allows print only necessary targets by id. -pub const TARGET_MAP: [(&str, i32); 11] = [ +pub const TARGET_MAP: [(&str, i32); 10] = [ (INSTRUCTION, 1 << 1), (DATA_CACHE, 1 << 2), (NEXT_PEER_PKS, 1 << 3), (SUBTREE_COMPLETE, 1 << 4), - (EXECUTED_TRACE, 1 << 5), - (SUBTREE_ELEMENTS, 1 << 6), - (NEW_EXECUTED_TRACE, 1 << 7), - (EXECUTED_TRACE_MERGE, 1 << 8), - (RUN_PARAMS, 1 << 9), + (SUBTREE_ELEMENTS, 1 << 5), + (NEW_EXECUTED_TRACE, 1 << 6), + (EXECUTED_TRACE_MERGE, 1 << 7), + (RUN_PARAMS, 1 << 8), (EXECUTED_STATE_CHANGING, 1 << 9), (JOIN_BEHAVIOUR, 1 << 10), ]; diff --git a/air/src/preparation/data_merging.rs b/air/src/preparation/data_merging.rs deleted file mode 100644 index 8cc1e69b..00000000 --- a/air/src/preparation/data_merging.rs +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::CallResult; -use super::DataMergingError; -use super::ExecutedState; -use super::ExecutionTrace; -use super::ParResult; -use crate::log_targets::EXECUTED_TRACE_MERGE; - -type MergeResult = Result; - -pub(super) fn merge_execution_traces( - mut prev_trace: ExecutionTrace, - mut current_trace: ExecutionTrace, -) -> MergeResult { - let mut merged_trace = ExecutionTrace::new(); - - let prev_subtree_size = prev_trace.len(); - let current_subtree_size = current_trace.len(); - - merge_subtree( - &mut prev_trace, - prev_subtree_size, - &mut current_trace, - current_subtree_size, - &mut merged_trace, - )?; - - log::trace!(target: EXECUTED_TRACE_MERGE, "merged trace: {:?}", merged_trace); - - Ok(merged_trace) -} - -fn merge_subtree( - prev_trace: &mut ExecutionTrace, - mut prev_subtree_size: usize, - current_trace: &mut ExecutionTrace, - mut current_subtree_size: usize, - result_trace: &mut ExecutionTrace, -) -> MergeResult<()> { - use DataMergingError::ExecutedTraceTooSmall; - use DataMergingError::IncompatibleExecutedStates; - use ExecutedState::*; - - loop { - let prev_state = if prev_subtree_size != 0 { - prev_subtree_size -= 1; - prev_trace.pop_front() - } else { - None - }; - - let current_state = if current_subtree_size != 0 { - current_subtree_size -= 1; - current_trace.pop_front() - } else { - None - }; - - match (prev_state, current_state) { - (Some(Call(prev_call)), Some(Call(call))) => { - let resulted_call = merge_call(prev_call, call)?; - result_trace.push_back(Call(resulted_call)); - } - (Some(Par(ParResult(prev_left, prev_right))), Some(Par(ParResult(current_left, current_right)))) => { - let par_position = result_trace.len(); - // place temporary Par value to avoid insert in the middle - result_trace.push_back(ExecutedState::par(0, 0)); - - let before_result_len = result_trace.len(); - - merge_subtree(prev_trace, prev_left, current_trace, current_left, result_trace)?; - let left_par_size = result_trace.len() - before_result_len; - - merge_subtree(prev_trace, prev_right, current_trace, current_right, result_trace)?; - let right_par_size = result_trace.len() - left_par_size - before_result_len; - - // update temporary Par with final values - result_trace[par_position] = ExecutedState::par(left_par_size, right_par_size); - - prev_subtree_size -= prev_left + prev_right; - current_subtree_size -= current_left + current_right; - } - (None, Some(s)) => { - if current_trace.len() < current_subtree_size { - return Err(ExecutedTraceTooSmall(current_trace.len(), current_subtree_size)); - } - - result_trace.push_back(s); - result_trace.extend(current_trace.drain(..current_subtree_size)); - break; - } - (Some(s), None) => { - if prev_trace.len() < prev_subtree_size { - return Err(ExecutedTraceTooSmall(prev_trace.len(), prev_subtree_size)); - } - - result_trace.push_back(s); - result_trace.extend(prev_trace.drain(..prev_subtree_size)); - break; - } - (None, None) => break, - // this match arn represents (Call, Par) and (Par, Call) states - (Some(prev_state), Some(current_state)) => { - return Err(IncompatibleExecutedStates(prev_state, current_state)) - } - } - } - - Ok(()) -} - -fn merge_call(prev_call_result: CallResult, current_call_result: CallResult) -> MergeResult { - use super::CallResult::*; - use super::DataMergingError::IncompatibleCallResults; - - match (&prev_call_result, ¤t_call_result) { - (CallServiceFailed(prev_ret_code, prev_err_msg), CallServiceFailed(ret_code, err_msg)) => { - if prev_ret_code != ret_code || prev_err_msg != err_msg { - return Err(IncompatibleCallResults(prev_call_result, current_call_result)); - } - Ok(current_call_result) - } - (RequestSentBy(_), CallServiceFailed(..)) => Ok(current_call_result), - (CallServiceFailed(..), RequestSentBy(_)) => Ok(prev_call_result), - (RequestSentBy(prev_sender), RequestSentBy(sender)) => { - if prev_sender != sender { - return Err(IncompatibleCallResults(prev_call_result, current_call_result)); - } - - Ok(prev_call_result) - } - (RequestSentBy(_), Executed(..)) => Ok(current_call_result), - (Executed(..), RequestSentBy(_)) => Ok(prev_call_result), - (Executed(prev_result), Executed(result)) => { - if prev_result != result { - return Err(IncompatibleCallResults(prev_call_result, current_call_result)); - } - - Ok(prev_call_result) - } - (CallServiceFailed(..), Executed(..)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)), - (Executed(..), CallServiceFailed(..)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)), - } -} diff --git a/air/src/preparation/errors.rs b/air/src/preparation/errors.rs deleted file mode 100644 index bcc99d28..00000000 --- a/air/src/preparation/errors.rs +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use super::CallResult; -use super::ExecutedState; - -use serde_json::Error as SerdeJsonError; -use thiserror::Error as ThisError; - -use std::env::VarError; -use std::error::Error; - -/// Errors happened during the interpreter preparation step. -#[derive(Debug)] -pub enum PreparationError { - /// Error occurred while parsing AIR script - AIRParseError(String), - - /// Errors occurred on executed trace deserialization. - ExecutedTraceDeError(SerdeJsonError, Vec), - - /// Point out that error is occured while getting current peer id. - CurrentPeerIdEnvError(VarError), - - /// Errors occurred while merging previous and current data. - StateMergingError(DataMergingError), -} - -/// Errors arose out of merging previous data with a new. -#[derive(ThisError, Debug)] -pub enum DataMergingError { - /// Errors occurred when previous and current executed states are incompatible. - #[error("previous and current data have incompatible states: '{0:?}' '{1:?}'")] - IncompatibleExecutedStates(ExecutedState, ExecutedState), - - /// Errors occurred when previous and current call results are incompatible. - #[error("previous and current call results are incompatible: '{0:?}' '{1:?}'")] - IncompatibleCallResults(CallResult, CallResult), - - /// Errors occurred when executed trace contains less elements then corresponding Par has. - #[error("executed trace has {0} elements, but {1} requires by Par")] - ExecutedTraceTooSmall(usize, usize), -} - -impl Error for PreparationError {} - -impl PreparationError { - pub(crate) fn to_error_code(&self) -> u32 { - use DataMergingError::*; - use PreparationError::*; - - match self { - AIRParseError(_) => 1, - ExecutedTraceDeError(..) => 2, - CurrentPeerIdEnvError(_) => 3, - StateMergingError(IncompatibleExecutedStates(..)) => 4, - StateMergingError(IncompatibleCallResults(..)) => 5, - StateMergingError(ExecutedTraceTooSmall(..)) => 6, - } - } -} - -use std::fmt; - -impl fmt::Display for PreparationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - use PreparationError::*; - - match self { - AIRParseError(err_msg) => write!(f, "air can't be parsed:\n{}", err_msg), - ExecutedTraceDeError(serde_error, executed_trace) => { - fn print_error( - f: &mut fmt::Formatter<'_>, - trace: impl std::fmt::Debug, - serde_error: &SerdeJsonError, - ) -> Result<(), fmt::Error> { - write!( - f, - "an error occurred while executed trace deserialization on '{:?}': {:?}", - trace, serde_error - ) - } - - match String::from_utf8(executed_trace.to_vec()) { - Ok(str) => print_error(f, str, serde_error), - Err(e) => print_error(f, e.into_bytes(), serde_error), - } - } - CurrentPeerIdEnvError(err) => write!(f, "current peer id can't be obtained: {:?}", err), - StateMergingError(err) => write!(f, "{}", err), - } - } -} - -impl From for PreparationError { - fn from(err: DataMergingError) -> Self { - Self::StateMergingError(err) - } -} - -impl From for PreparationError { - fn from(_: std::convert::Infallible) -> Self { - unreachable!() - } -} - -impl From for DataMergingError { - fn from(_: std::convert::Infallible) -> Self { - unreachable!() - } -} diff --git a/air/src/preparation/mod.rs b/air/src/preparation/mod.rs deleted file mode 100644 index 18071ba0..00000000 --- a/air/src/preparation/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -mod data_merging; -mod errors; -mod preparation; -#[cfg(tests)] -mod tests; - -pub(crate) use errors::DataMergingError; -pub(crate) use errors::PreparationError; -pub(crate) use preparation::prepare; -pub(crate) use preparation::PreparationDescriptor; - -pub(self) use crate::contexts::execution::*; -pub(self) use crate::contexts::execution_trace::CallResult; -pub(self) use crate::contexts::execution_trace::ExecutedState; -pub(self) use crate::contexts::execution_trace::ExecutionTrace; -pub(self) use crate::contexts::execution_trace::ExecutionTraceCtx; -pub(self) use crate::contexts::execution_trace::ParResult; -pub(self) use data_merging::merge_execution_traces; diff --git a/air/src/preparation/tests.rs b/air/src/preparation/tests.rs deleted file mode 100644 index 6e466ec5..00000000 --- a/air/src/preparation/tests.rs +++ /dev/null @@ -1,128 +0,0 @@ -use super::merge_execution_traces; -use crate::JValue; - -use air_test_utils::executed_state::*; -use air_test_utils::ExecutionTrace; - -use std::rc::Rc; - -#[test] -fn merge_call_states_1() { - let mut prev_trace = vec![ - par(1, 1), - request_sent_by("peer_1"), - scalar_jvalue(JValue::Null), - par(1, 1), - request_sent_by("peer_3"), - scalar_jvalue(JValue::Null), - ]; - - let current_trace = vec![ - par(1, 1), - scalar_jvalue(JValue::Null), - request_sent_by("peer_2"), - par(1, 1), - scalar_jvalue(JValue::Null), - request_sent_by("peer_4"), - ]; - - let actual_merged_trace = - merge_execution_traces(prev_trace.into(), current_trace.into()).expect("merging should be successful"); - - let expected_merged_trace = vec![ - par(1, 1), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - par(1, 1), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - ]; - - assert_eq!(actual_merged_trace, expected_merged_trace); -} - -#[test] -fn merge_call_states_2() { - let prev_trace = vec![ - par(1, 0), - request_sent_by("peer_1"), - par(1, 1), - request_sent_by("peer_2"), - scalar_jvalue(JValue::Null), - ]; - - let current_trace = vec![ - par(2, 2), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - request_sent_by("peer_1"), - par(1, 1), - scalar_jvalue(JValue::Null), - request_sent_by("peer_2"), - ]; - - let actual_merged_trace = - merge_execution_traces(prev_trace.into(), current_trace.into()).expect("merging should be successful"); - - let expected_merged_trace = vec![ - par(2, 2), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - par(1, 1), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - ]; - - assert_eq!(actual_merged_trace, expected_merged_trace); -} - -#[test] -fn merge_call_states_3() { - let prev_trace = vec![ - scalar_jvalue(JValue::Null), - par(2, 0), - par(1, 0), - request_sent_by("peer_1"), - par(1, 2), - request_sent_by("peer_1"), - scalar_jvalue(JValue::Null), - request_sent_by("peer_1"), - ]; - - let current_trace = vec![ - scalar_jvalue(JValue::Null), - par(3, 3), - par(1, 1), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - par(1, 1), - scalar_jvalue(JValue::Null), - request_sent_by("peer_1"), - par(1, 1), - scalar_jvalue(JValue::Null), - request_sent_by("peer_1"), - ]; - - let actual_merged_trace = - merge_execution_traces(prev_trace.into(), current_trace.into()).expect("merging should be successful"); - - let expected_merged_trace = vec![ - scalar_jvalue(JValue::Null), - par(3, 3), - par(1, 1), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - par(1, 1), - scalar_jvalue(JValue::Null), - request_sent_by("peer_1"), - par(1, 2), - scalar_jvalue(JValue::Null), - scalar_jvalue(JValue::Null), - request_sent_by("peer_1"), - ]; - - assert_eq!(actual_merged_trace, expected_merged_trace); -} diff --git a/air/src/preparation_step/errors.rs b/air/src/preparation_step/errors.rs new file mode 100644 index 00000000..c6255c0f --- /dev/null +++ b/air/src/preparation_step/errors.rs @@ -0,0 +1,50 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use serde_json::Error as SerdeJsonError; +use thiserror::Error as ThisError; + +use air_interpreter_data::DATA_FORMAT_VERSION; +use std::env::VarError; + +/// Errors happened during the interpreter preparation_step step. +#[derive(Debug, ThisError)] +pub enum PreparationError { + /// Error occurred while parsing AIR script + #[error("air can't be parsed:\n{0}")] + AIRParseError(String), + + /// Errors occurred on executed trace deserialization. + #[error("an error occurred while executed trace deserialization on {1:?}:\n{0:?}.\ + Probably it's a data of an old version that couldn't be converted to '{}'", *DATA_FORMAT_VERSION)] + DataDeFailed(SerdeJsonError, Vec), + + /// Error occurred while getting current peer id. + #[error("current peer id can't be obtained: {0:?}")] + CurrentPeerIdEnvError(VarError), +} + +impl PreparationError { + pub(crate) fn to_error_code(&self) -> u32 { + use PreparationError::*; + + match self { + AIRParseError(_) => 1, + DataDeFailed(..) => 2, + CurrentPeerIdEnvError(_) => 3, + } + } +} diff --git a/air/src/execution/boxed_value/mod.rs b/air/src/preparation_step/mod.rs similarity index 78% rename from air/src/execution/boxed_value/mod.rs rename to air/src/preparation_step/mod.rs index f942a52f..c7dd5a85 100644 --- a/air/src/execution/boxed_value/mod.rs +++ b/air/src/preparation_step/mod.rs @@ -14,11 +14,9 @@ * limitations under the License. */ -mod iterable; -mod jvaluable; +mod errors; +mod preparation; -pub(crate) use super::ExecutionError; -pub(crate) use iterable::*; -pub(crate) use jvaluable::*; - -pub(self) use super::ExecutionResult; +pub(crate) use errors::PreparationError; +pub(crate) use preparation::prepare; +pub(crate) use preparation::PreparationDescriptor; diff --git a/air/src/preparation/preparation.rs b/air/src/preparation_step/preparation.rs similarity index 52% rename from air/src/preparation/preparation.rs rename to air/src/preparation_step/preparation.rs index b6e3b88d..b12b1d4a 100644 --- a/air/src/preparation/preparation.rs +++ b/air/src/preparation_step/preparation.rs @@ -14,46 +14,34 @@ * limitations under the License. */ -use super::merge_execution_traces; -use super::ExecutionCtx; -use super::ExecutionTrace; -use super::ExecutionTraceCtx; use super::PreparationError; use crate::build_targets::get_current_peer_id; +use crate::execution_step::ExecutionCtx; +use crate::execution_step::Stream; +use crate::execution_step::TraceHandler; use crate::log_targets::RUN_PARAMS; +use air_interpreter_data::InterpreterData; use air_parser::ast::Instruction; type PreparationResult = Result; -/// Represents result of the preparation step. +/// Represents result of the preparation_step step. pub(crate) struct PreparationDescriptor<'ctx, 'i> { pub(crate) exec_ctx: ExecutionCtx<'ctx>, - pub(crate) trace_ctx: ExecutionTraceCtx, + pub(crate) trace_handler: TraceHandler, pub(crate) air: Instruction<'i>, } /// Parse and prepare supplied data and AIR script. pub(crate) fn prepare<'i>( prev_data: &[u8], - data: &[u8], + current_data: &[u8], raw_air: &'i str, init_peer_id: String, ) -> PreparationResult> { - fn to_executed_trace(raw_data: &[u8]) -> PreparationResult { - use PreparationError::ExecutedTraceDeError as CallDeError; - - // treat empty string as an empty executed trace allows abstracting from - // the internal format for empty data. - if raw_data.is_empty() { - Ok(ExecutionTrace::new()) - } else { - serde_json::from_slice(&raw_data).map_err(|err| CallDeError(err, raw_data.to_vec())) - } - } - - let prev_trace = to_executed_trace(prev_data)?; - let trace = to_executed_trace(data)?; + let prev_data = try_to_data(prev_data)?; + let current_data = try_to_data(current_data)?; let air: Instruction<'i> = *air_parser::parse(raw_air).map_err(PreparationError::AIRParseError)?; @@ -61,33 +49,46 @@ pub(crate) fn prepare<'i>( target: RUN_PARAMS, "air: {:?}\nprev_trace: {:?}\ncurrent_trace: {:?}", air, - prev_trace, - trace + prev_data, + current_data ); - let (exec_ctx, trace_ctx) = make_contexts(prev_trace, trace, init_peer_id)?; + let exec_ctx = make_exec_ctx(init_peer_id, &prev_data)?; + let trace_handler = TraceHandler::from_data(prev_data, current_data); + let result = PreparationDescriptor { exec_ctx, - trace_ctx, + trace_handler, air, }; Ok(result) } -/// Make execution and execution trace contexts from supplied data. -/// Internally, it unites variable from previous and current data and merges executed traces. -fn make_contexts( - prev_trace: ExecutionTrace, - trace: ExecutionTrace, - init_peer_id: String, -) -> PreparationResult<(ExecutionCtx<'static>, ExecutionTraceCtx)> { +fn try_to_data(raw_data: &[u8]) -> PreparationResult { + use PreparationError::DataDeFailed; + + InterpreterData::try_from_slice(raw_data).map_err(|err| DataDeFailed(err, raw_data.to_vec())) +} + +fn make_exec_ctx(init_peer_id: String, prev_data: &InterpreterData) -> PreparationResult> { let current_peer_id = get_current_peer_id().map_err(PreparationError::CurrentPeerIdEnvError)?; log::trace!(target: RUN_PARAMS, "current peer id {}", current_peer_id); - let exec_ctx = ExecutionCtx::new(current_peer_id, init_peer_id); - let current_trace = merge_execution_traces(prev_trace, trace)?; - let trace_ctx = ExecutionTraceCtx::new(current_trace); + let mut ctx = ExecutionCtx::new(current_peer_id, init_peer_id); + create_streams(&mut ctx, prev_data); - Ok((exec_ctx, trace_ctx)) + Ok(ctx) +} + +fn create_streams(ctx: &mut ExecutionCtx<'_>, prev_data: &InterpreterData) { + use std::cell::RefCell; + + for (stream_name, generation_count) in prev_data.streams.iter() { + let new_stream = Stream::from_generations_count(*generation_count as usize); + let new_stream = RefCell::new(new_stream); + + // it's impossible to have duplicates of streams in data because of HashMap in data + ctx.streams.insert(stream_name.to_string(), new_stream); + } } diff --git a/air/src/air.rs b/air/src/runner.rs similarity index 59% rename from air/src/air.rs rename to air/src/runner.rs index de3a50d8..81d05a42 100644 --- a/air/src/air.rs +++ b/air/src/runner.rs @@ -16,9 +16,10 @@ mod outcome; -use crate::execution::ExecutableInstruction; -use crate::preparation::prepare; -use crate::preparation::PreparationDescriptor; +use crate::execution_step::Catchable; +use crate::execution_step::ExecutableInstruction; +use crate::preparation_step::prepare; +use crate::preparation_step::PreparationDescriptor; use air_interpreter_interface::InterpreterOutcome; @@ -42,17 +43,25 @@ fn execute_air_impl( ) -> Result { let PreparationDescriptor { mut exec_ctx, - mut trace_ctx, + mut trace_handler, air, - } = prepare(&prev_data, &data, air.as_str(), init_peer_id) + } = match prepare(&prev_data, &data, air.as_str(), init_peer_id) { + Ok(desc) => desc, // return the initial data in case of errors - .map_err(|e| outcome::from_preparation_error(data, e))?; + Err(error) => return Err(outcome::from_preparation_error(prev_data, error)), + }; - air.execute(&mut exec_ctx, &mut trace_ctx) + // match here is used instead of map_err, because the compiler can't determine that + // they are exclusive and would treat exec_ctx and trace_handler as moved + match air.execute(&mut exec_ctx, &mut trace_handler) { + Ok(_) => {} + // return the old data in case of any trace errors + Err(e) if !e.is_catchable() => return Err(outcome::from_trace_error(prev_data, e)), // return new collected trace in case of errors - .map_err(|e| outcome::from_execution_error(&trace_ctx.new_trace, exec_ctx.next_peer_pks.clone(), e))?; + Err(e) => return Err(outcome::from_execution_error(exec_ctx, trace_handler, e)), + } - let outcome = outcome::from_path_and_peers(&trace_ctx.new_trace, exec_ctx.next_peer_pks); + let outcome = outcome::from_success_result(exec_ctx, trace_handler); Ok(outcome) } diff --git a/air/src/runner/outcome.rs b/air/src/runner/outcome.rs new file mode 100644 index 00000000..7988f819 --- /dev/null +++ b/air/src/runner/outcome.rs @@ -0,0 +1,118 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::execution_step::ExecutionCtx; +use crate::execution_step::ExecutionError; +use crate::execution_step::Stream; +use crate::execution_step::TraceHandler; +use crate::preparation_step::PreparationError; +use crate::InterpreterOutcome; +use crate::INTERPRETER_SUCCESS; + +use air_interpreter_data::InterpreterData; +use air_interpreter_data::StreamGenerations; + +use std::cell::RefCell; +use std::collections::HashMap; +use std::hash::Hash; +use std::rc::Rc; + +const EXECUTION_ERRORS_START_ID: i32 = 1000; + +/// Create InterpreterOutcome from supplied execution context and trace handler, +/// set ret_code to INTERPRETER_SUCCESS. +pub(crate) fn from_success_result(exec_ctx: ExecutionCtx<'_>, trace_handler: TraceHandler) -> InterpreterOutcome { + let streams = extract_stream_generations(exec_ctx.streams); + let data = InterpreterData::from_execution_result(trace_handler.into_result_trace(), streams); + let data = serde_json::to_vec(&data).expect("default serializer shouldn't fail"); + + let next_peer_pks = dedup(exec_ctx.next_peer_pks); + + InterpreterOutcome { + ret_code: INTERPRETER_SUCCESS, + error_message: String::new(), + data, + next_peer_pks, + } +} + +/// Create InterpreterOutcome from supplied data and error, +/// set ret_code based on the error. +pub(crate) fn from_preparation_error(data: impl Into>, err: PreparationError) -> InterpreterOutcome { + let ret_code = err.to_error_code() as i32; + let data = data.into(); + + InterpreterOutcome { + ret_code, + error_message: format!("{}", err), + data, + next_peer_pks: vec![], + } +} + +/// Create InterpreterOutcome from supplied data and error, +/// set ret_code based on the error. +pub(crate) fn from_trace_error(data: impl Into>, err: Rc) -> InterpreterOutcome { + let ret_code = err.to_error_code() as i32; + let ret_code = EXECUTION_ERRORS_START_ID + ret_code; + let data = data.into(); + + InterpreterOutcome { + ret_code, + error_message: format!("{}", err), + data, + next_peer_pks: vec![], + } +} + +/// Create InterpreterOutcome from supplied execution context, trace handler, and error, +/// set ret_code based on the error. +pub(crate) fn from_execution_error( + exec_ctx: ExecutionCtx<'_>, + trace_handler: TraceHandler, + err: Rc, +) -> InterpreterOutcome { + let ret_code = err.to_error_code() as i32; + let ret_code = EXECUTION_ERRORS_START_ID + ret_code; + + let streams = extract_stream_generations(exec_ctx.streams); + let data = InterpreterData::from_execution_result(trace_handler.into_result_trace(), streams); + let data = serde_json::to_vec(&data).expect("default serializer shouldn't fail"); + + let next_peer_pks = dedup(exec_ctx.next_peer_pks); + + InterpreterOutcome { + ret_code, + error_message: format!("{}", err), + data, + next_peer_pks, + } +} + +/// Deduplicate values in a supplied vector. +fn dedup(mut vec: Vec) -> Vec { + use std::collections::HashSet; + + let set: HashSet<_> = vec.drain(..).collect(); + set.into_iter().collect() +} + +fn extract_stream_generations(streams: HashMap>) -> StreamGenerations { + streams + .into_iter() + .map(|(name, stream)| (name, stream.borrow().generations_count() as u32)) + .collect::<_>() +} diff --git a/air/tests/test_module/instructions/ap.rs b/air/tests/test_module/instructions/ap.rs new file mode 100644 index 00000000..df5b08da --- /dev/null +++ b/air/tests/test_module/instructions/ap.rs @@ -0,0 +1,194 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use air_test_utils::*; +use serde_json::json; + +#[test] +fn ap_with_scalars() { + let vm_1_peer_id = "vm_1_peer_id"; + let test_value = "scalar_2"; + let mut vm_1 = create_avm( + set_variable_call_service(json!({ "field": test_value }).to_string()), + vm_1_peer_id, + ); + + let vm_2_peer_id = "vm_2_peer_id"; + let mut vm_2 = create_avm(echo_string_call_service(), vm_2_peer_id); + + let script = format!( + r#" + (seq + (seq + (call "{}" ("" "") ["scalar_1_result"] scalar_1) + (ap scalar_1.$.field! scalar_2) + ) + (call "{}" ("" "") [scalar_2]) + ) + "#, + vm_1_peer_id, vm_2_peer_id + ); + + let result = checked_call_vm!(vm_1, "", &script, "", ""); + let result = checked_call_vm!(vm_2, "", script, "", result.data); + + let actual_trace = trace_from_result(&result); + let expected_state = vec![ + executed_state::scalar(json!({ "field": test_value })), + executed_state::scalar_string(test_value), + ]; + + assert_eq!(actual_trace, expected_state); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn ap_with_string_literal() { + let vm_1_peer_id = "vm_1_peer_id"; + let mut vm_1 = create_avm(echo_call_service(), vm_1_peer_id); + + let script = format!( + r#" + (seq + (ap "some_string" $stream) + (call "{}" ("" "") [$stream]) + ) + "#, + vm_1_peer_id + ); + + let result = checked_call_vm!(vm_1, "", script, "", ""); + + let actual_trace = trace_from_result(&result); + let expected_state = vec![ + executed_state::ap(Some(0)), + executed_state::scalar(json!(["some_string"])), + ]; + + assert_eq!(actual_trace, expected_state); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn ap_with_bool_literal() { + let vm_1_peer_id = "vm_1_peer_id"; + let mut vm_1 = create_avm(echo_call_service(), vm_1_peer_id); + + let script = format!( + r#" + (seq + (ap true $stream) + (call "{}" ("" "") [$stream]) + ) + "#, + vm_1_peer_id + ); + + let result = checked_call_vm!(vm_1, "", script, "", ""); + + let actual_trace = trace_from_result(&result); + let expected_state = vec![executed_state::ap(Some(0)), executed_state::scalar(json!([true]))]; + + assert_eq!(actual_trace, expected_state); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn ap_with_number_literal() { + let vm_1_peer_id = "vm_1_peer_id"; + let mut vm_1 = create_avm(echo_call_service(), vm_1_peer_id); + + let script = format!( + r#" + (seq + (ap 100 $stream) + (call "{}" ("" "") [$stream]) + ) + "#, + vm_1_peer_id + ); + + let result = checked_call_vm!(vm_1, "", script, "", ""); + + let actual_trace = trace_from_result(&result); + let expected_state = vec![executed_state::ap(Some(0)), executed_state::scalar(json!([100]))]; + + assert_eq!(actual_trace, expected_state); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn ap_with_last_error() { + let vm_1_peer_id = "vm_1_peer_id"; + let mut vm_1 = create_avm(echo_call_service(), vm_1_peer_id); + + let script = format!( + r#" + (seq + (ap %last_error%.$.msg $stream) + (call "{}" ("" "") [$stream]) + ) + "#, + vm_1_peer_id + ); + + let result = checked_call_vm!(vm_1, "", script, "", ""); + + let actual_trace = trace_from_result(&result); + let expected_state = vec![executed_state::ap(Some(0)), executed_state::scalar(json!([""]))]; + + assert_eq!(actual_trace, expected_state); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn ap_with_dst_stream() { + let vm_1_peer_id = "vm_1_peer_id"; + let test_value = "scalar_2"; + let mut vm_1 = create_avm( + set_variable_call_service(json!({ "field": test_value }).to_string()), + vm_1_peer_id, + ); + + let vm_2_peer_id = "vm_2_peer_id"; + let mut vm_2 = create_avm(echo_call_service(), vm_2_peer_id); + + let script = format!( + r#" + (seq + (seq + (call "{}" ("" "") ["scalar_1_result"] scalar_1) + (ap scalar_1 $stream) + ) + (call "{}" ("" "") [$stream]) + ) + "#, + vm_1_peer_id, vm_2_peer_id + ); + + let result = checked_call_vm!(vm_1, "", &script, "", ""); + let result = checked_call_vm!(vm_2, "", script, "", result.data); + + let actual_trace = trace_from_result(&result); + let expected_state = vec![ + executed_state::scalar(json!({ "field": test_value })), + executed_state::ap(Some(0)), + executed_state::scalar(json!([{ "field": test_value }])), + ]; + + assert_eq!(actual_trace, expected_state); + assert!(result.next_peer_pks.is_empty()); +} diff --git a/air/tests/test_module/instructions/call.rs b/air/tests/test_module/instructions/call.rs index 94822c41..065eec04 100644 --- a/air/tests/test_module/instructions/call.rs +++ b/air/tests/test_module/instructions/call.rs @@ -15,13 +15,14 @@ */ use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::echo_string_call_service; use air_test_utils::executed_state; use air_test_utils::set_variable_call_service; +use air_test_utils::trace_from_result; use air_test_utils::unit_call_service; use air_test_utils::CallServiceClosure; -use air_test_utils::ExecutionTrace; use air_test_utils::IValue; use air_test_utils::NEVec; @@ -32,8 +33,8 @@ fn current_peer_id_call() { let vm_peer_id = String::from("test_peer_id"); let mut vm = create_avm(unit_call_service(), vm_peer_id.clone()); - let service_id = String::from("local_service_id"); - let function_name = String::from("local_fn_name"); + let service_id = "local_service_id"; + let function_name = "local_fn_name"; let script = format!( r#" (call %init_peer_id% ("{}" "{}") [] result_name) @@ -41,14 +42,14 @@ fn current_peer_id_call() { service_id, function_name ); - let res = call_vm!(vm, vm_peer_id.clone(), script.clone(), "[]", "[]"); - let call_path: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be a valid json"); + let result = checked_call_vm!(vm, &vm_peer_id, script, [], []); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("test"); - assert_eq!(call_path.len(), 1); - assert_eq!(call_path[0], expected_state); - assert!(res.next_peer_pks.is_empty()); + assert_eq!(actual_trace.len(), 1); + assert_eq!(actual_trace[0], expected_state); + assert!(result.next_peer_pks.is_empty()); let script = format!( r#" @@ -57,18 +58,18 @@ fn current_peer_id_call() { vm_peer_id, service_id, function_name ); - let res = call_vm!(vm, "asd", script.clone(), "[]", "[]"); + let result = checked_call_vm!(vm, "asd", script.clone(), "", ""); // test that empty string for data works - let res_with_empty_string = call_vm!(vm, "asd", script, "", ""); - assert_eq!(res_with_empty_string, res); + let result_with_empty_string = checked_call_vm!(vm, "asd", script, "", ""); + assert_eq!(result_with_empty_string, result); } // Check that specifying remote peer id in call will result its appearing in next_peer_pks. #[test] fn remote_peer_id_call() { let some_local_peer_id = String::from("some_local_peer_id"); - let mut vm = create_avm(echo_string_call_service(), some_local_peer_id.clone()); + let mut vm = create_avm(echo_string_call_service(), &some_local_peer_id); let remote_peer_id = String::from("some_remote_peer_id"); let script = format!( @@ -76,14 +77,14 @@ fn remote_peer_id_call() { remote_peer_id ); - let res = call_vm!(vm, "asd", script, "[]", "[]"); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be a valid json"); + let result = checked_call_vm!(vm, "asd", script, "", ""); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::request_sent_by(some_local_peer_id); assert_eq!(actual_trace.len(), 1); assert_eq!(actual_trace[0], expected_state); - assert_eq!(res.next_peer_pks, vec![remote_peer_id]); + assert_eq!(result.next_peer_pks, vec![remote_peer_id]); } // Check that setting variables works as expected. @@ -92,19 +93,17 @@ fn variables() { let mut vm = create_avm(unit_call_service(), "remote_peer_id"); let mut set_variable_vm = create_avm(set_variable_call_service(r#""remote_peer_id""#), "set_variable"); - let script = format!( - r#" + let script = r#" (seq (call "set_variable" ("some_service_id" "local_fn_name") [] remote_peer_id) (call remote_peer_id ("some_service_id" "local_fn_name") [] result_name) ) - "#, - ); + "#; - let res = call_vm!(set_variable_vm, "asd", script.clone(), "[]", "[]"); - let res = call_vm!(vm, "asd", script, "[]", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); - assert!(res.next_peer_pks.is_empty()); + assert!(result.next_peer_pks.is_empty()); } // Check that duplicate variables are impossible. @@ -112,19 +111,17 @@ fn variables() { fn duplicate_variables() { let mut vm = create_avm(unit_call_service(), "some_peer_id"); - let script = format!( - r#" + let script = r#" (seq (call "some_peer_id" ("some_service_id" "local_fn_name") [] modules) (call "some_peer_id" ("some_service_id" "local_fn_name") [] modules) ) - "#, - ); + "#; - let res = call_vm!(vm, "asd", script, "", ""); + let result = call_vm!(vm, "asd", script, "", ""); - assert_eq!(res.ret_code, 1005); - assert!(res.next_peer_pks.is_empty()); + assert_eq!(result.ret_code, 1005); + assert!(result.next_peer_pks.is_empty()); } // Check that string literals can be used as call parameters. @@ -162,10 +159,10 @@ fn string_parameters() { set_variable_vm_peer_id, service_id, function_name, vm_peer_id, service_id, function_name ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "[]", "[]"); - let res = call_vm!(vm, "asd", script, "[]", res.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be a valid json"); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string_array(vec!["arg1", "arg2", "arg3_value"]); assert_eq!(actual_trace.len(), 2); diff --git a/air/tests/test_module/instructions/fold.rs b/air/tests/test_module/instructions/fold.rs index 075ece09..e23c83c5 100644 --- a/air/tests/test_module/instructions/fold.rs +++ b/air/tests/test_module/instructions/fold.rs @@ -15,13 +15,14 @@ */ use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::echo_number_call_service; use air_test_utils::echo_string_call_service; use air_test_utils::executed_state; use air_test_utils::set_variable_call_service; +use air_test_utils::trace_from_result; use air_test_utils::AVMError; -use air_test_utils::ExecutionTrace; use air_test_utils::InterpreterOutcome; use serde_json::json; @@ -31,8 +32,7 @@ fn lfold() { let mut vm = create_avm(echo_number_call_service(), "A"); let mut set_variable_vm = create_avm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); - let lfold = String::from( - r#" + let lfold = r#" (seq (call "set_variable" ("" "") [] Iterable) (fold Iterable i @@ -41,19 +41,19 @@ fn lfold() { (next i) ) ) - )"#, - ); + )"#; - let res = call_vm!(set_variable_vm, "", lfold.clone(), "[]", "[]"); - let res = call_vm!(vm, "", lfold, "[]", res.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); + let result = checked_call_vm!(set_variable_vm, "", lfold, "", ""); + let result = checked_call_vm!(vm, "", lfold, "", result.data); + + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string_array(vec!["1", "2", "3", "4", "5"]); assert_eq!(actual_trace.len(), 6); assert_eq!(actual_trace[0], expected_state); for i in 1..=5 { - let expected_state = executed_state::stream_number(i, "$acc"); + let expected_state = executed_state::stream_number(i, 0); assert_eq!(actual_trace[i], expected_state); } } @@ -63,8 +63,7 @@ fn rfold() { let mut vm = create_avm(echo_number_call_service(), "A"); let mut set_variable_vm = create_avm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); - let rfold = String::from( - r#" + let rfold = r#" (seq (call "set_variable" ("" "") [] Iterable) (fold Iterable i @@ -73,20 +72,19 @@ fn rfold() { (call "A" ("" "") [i] $acc) ) ) - )"#, - ); + )"#; - let res = call_vm!(set_variable_vm, "", rfold.clone(), "[]", "[]"); - let res = call_vm!(vm, "", rfold, "[]", res.data); + let result = checked_call_vm!(set_variable_vm, "", rfold, "", ""); + let result = checked_call_vm!(vm, "", rfold, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); + let actual_trace = trace_from_result(&result); assert_eq!(actual_trace.len(), 6); let expected_state = executed_state::scalar_string_array(vec!["1", "2", "3", "4", "5"]); assert_eq!(actual_trace[0], expected_state); for i in 1..=5 { - let expected_state = executed_state::stream_number(6 - i, "$acc"); + let expected_state = executed_state::stream_number(6 - i, 0); assert_eq!(actual_trace[i], expected_state); } } @@ -96,8 +94,7 @@ fn inner_fold() { let mut vm = create_avm(echo_number_call_service(), "A"); let mut set_variable_vm = create_avm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); - let script = String::from( - r#" + let script = r#" (seq (seq (call "set_variable" ("" "") [] Iterable1) @@ -114,13 +111,12 @@ fn inner_fold() { (next i) ) ) - )"#, - ); + )"#; - let res = call_vm!(set_variable_vm, "", script.clone(), "[]", "[]"); - let res = call_vm!(vm, "", script, "[]", res.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); + let result = checked_call_vm!(set_variable_vm, "", script, "", ""); + let result = checked_call_vm!(vm, "", script, "", result.data); + let actual_trace = trace_from_result(&result); assert_eq!(actual_trace.len(), 27); let expected_state = executed_state::scalar_string_array(vec!["1", "2", "3", "4", "5"]); @@ -129,7 +125,7 @@ fn inner_fold() { for i in 1..=5 { for j in 1..=5 { - let expected_state = executed_state::stream_number(i, "$acc"); + let expected_state = executed_state::stream_number(i, 0); assert_eq!(actual_trace[1 + 5 * (i - 1) + j], expected_state); } } @@ -139,8 +135,7 @@ fn inner_fold() { fn inner_fold_with_same_iterator() { let mut vm = create_avm(set_variable_call_service(r#"["1","2","3","4","5"]"#), "set_variable"); - let script = String::from( - r#" + let script = r#" (seq (seq (call "set_variable" ("" "") [] Iterable1) @@ -157,12 +152,11 @@ fn inner_fold_with_same_iterator() { (next i) ) ) - )"#, - ); + )"#; - let res = call_vm!(vm, "", script, "[]", "[]"); + let result = call_vm!(vm, "", script, "", ""); - assert_eq!(res.ret_code, 1012); + assert_eq!(result.ret_code, 1012); } #[test] @@ -170,8 +164,7 @@ fn empty_fold() { let mut vm = create_avm(echo_number_call_service(), "A"); let mut set_variable_vm = create_avm(set_variable_call_service(r#"[]"#), "set_variable"); - let empty_fold = String::from( - r#" + let empty_fold = r#" (seq (call "set_variable" ("" "") [] Iterable) (fold Iterable i @@ -180,13 +173,13 @@ fn empty_fold() { (next i) ) ) - )"#, - ); + )"#; - let res = call_vm!(set_variable_vm, "", empty_fold.clone(), "[]", "[]"); - let res = call_vm!(vm, "", empty_fold, "[]", res.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); - let expected_state = executed_state::scalar_jvalue(json!([])); + let result = checked_call_vm!(set_variable_vm, "", empty_fold, "", ""); + let result = checked_call_vm!(vm, "", empty_fold, "", result.data); + + let actual_trace = trace_from_result(&result); + let expected_state = executed_state::scalar(json!([])); assert_eq!(actual_trace.len(), 1); assert_eq!(actual_trace[0], expected_state); @@ -208,12 +201,13 @@ fn empty_fold_json_path() { ) )"#; - let res = call_vm!(set_variable_vm, "", empty_fold, "", ""); - let res = call_vm!(vm, "", empty_fold, "", res.data); - let res: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); + let result = checked_call_vm!(set_variable_vm, "", empty_fold, "", ""); + let result = checked_call_vm!(vm, "", empty_fold, "", result.data); - assert_eq!(res.len(), 1); - assert_eq!(res[0], executed_state::scalar_jvalue(json!({ "messages": [] }))); + let actual_trace = trace_from_result(&result); + let expected_trace = vec![executed_state::scalar(json!({ "messages": [] }))]; + + assert_eq!(actual_trace, expected_trace); } // Check that fold works with the join behaviour without hanging up. @@ -222,8 +216,7 @@ fn fold_with_join() { let mut vm = create_avm(echo_number_call_service(), "A"); let mut set_variable_vm = create_avm(set_variable_call_service(r#"["1","2"]"#), "set_variable"); - let fold_with_join = String::from( - r#" + let fold_with_join = r#" (seq (call "set_variable" ("" "") [] iterable) (par @@ -235,14 +228,13 @@ fn fold_with_join() { ) ) ) - )"#, - ); + )"#; - let res = call_vm!(set_variable_vm, "", &fold_with_join, "", ""); - let res = call_vm!(vm, "", fold_with_join, "", res.data); - let res: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); + let result = checked_call_vm!(set_variable_vm, "", fold_with_join, "", ""); + let result = checked_call_vm!(vm, "", fold_with_join, "", result.data); - assert_eq!(res.len(), 3); + let actual_trace = trace_from_result(&result); + assert_eq!(actual_trace.len(), 3); } #[test] @@ -253,8 +245,7 @@ fn json_path() { "set_variable", ); - let lfold = String::from( - r#" + let lfold = r#" (seq (call "set_variable" ("" "") [] iterable) (fold iterable.$.array! i @@ -263,19 +254,19 @@ fn json_path() { (next i) ) ) - )"#, - ); + )"#; - let res = call_vm!(set_variable_vm, "", lfold.clone(), "[]", "[]"); - let res = call_vm!(vm, "", lfold, "[]", res.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); - let expected_state = executed_state::scalar_jvalue(json!({ "array": ["1", "2", "3", "4", "5"] })); + let result = checked_call_vm!(set_variable_vm, "", lfold, "", ""); + let result = checked_call_vm!(vm, "", lfold, "", result.data); + + let actual_trace = trace_from_result(&result); + let expected_state = executed_state::scalar(json!({ "array": ["1", "2", "3", "4", "5"] })); assert_eq!(actual_trace.len(), 6); assert_eq!(actual_trace[0], expected_state); for i in 1..=5 { - let expected_state = executed_state::stream_number(i, "$acc"); + let expected_state = executed_state::stream_number(i, 0); assert_eq!(actual_trace[i], expected_state); } } @@ -288,8 +279,7 @@ fn shadowing() { let mut vm_a = create_avm(echo_string_call_service(), "A"); let mut vm_b = create_avm(echo_string_call_service(), "B"); - let script = String::from( - r#" + let script = r#" (seq (seq (call "set_variable" ("" "") [] iterable1) @@ -315,18 +305,17 @@ fn shadowing() { (next i) ) ) - )"#, - ); + )"#; - let res = call_vm!(set_variables_vm, "", script.clone(), "[]", "[]"); - let res = call_vm!(vm_a, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_b, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_a, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_b, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_a, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_b, "", script, "[]", res.data); + let result = checked_call_vm!(set_variables_vm, "", script, "", ""); + let result = checked_call_vm!(vm_a, "", script, "", result.data); + let result = checked_call_vm!(vm_b, "", script, "", result.data); + let result = checked_call_vm!(vm_a, "", script, "", result.data); + let result = checked_call_vm!(vm_b, "", script, "", result.data); + let result = checked_call_vm!(vm_a, "", script, "", result.data); + let result = checked_call_vm!(vm_b, "", script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); + let actual_trace = trace_from_result(&result); let expected_trace = vec![ scalar_string_array(vec!["1", "2"]), scalar_string_array(vec!["1", "2"]), @@ -354,17 +343,16 @@ fn shadowing_scope() { let mut vm_a = create_avm(echo_string_call_service(), "A"); let mut vm_b = create_avm(echo_string_call_service(), "B"); - let res = call_vm!(set_variables_vm, "", script.clone(), "[]", "[]"); - let res = call_vm!(vm_a, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_b, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_a, "", script.clone(), "[]", res.data); - let res = call_vm!(vm_b, "", script.clone(), "[]", res.data); + let result = checked_call_vm!(set_variables_vm, "", script.clone(), "", ""); + let result = checked_call_vm!(vm_a, "", script.clone(), "", result.data); + let result = checked_call_vm!(vm_b, "", script.clone(), "", result.data); + let result = checked_call_vm!(vm_a, "", script.clone(), "", result.data); + let result = checked_call_vm!(vm_b, "", script.clone(), "", result.data); - vm_a.call_with_prev_data("", script, "[]", res.data) + vm_a.call_with_prev_data("", script, "", result.data) } - let variable_shadowing_script = String::from( - r#" + let variable_shadowing_script = r#" (seq (seq (call "set_variable" ("" "") [] iterable1) @@ -390,11 +378,11 @@ fn shadowing_scope() { (next i) ) ) - )"#, - ); + )"#; - let res = execute_script(variable_shadowing_script).unwrap(); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid executed trace"); + let result = execute_script(String::from(variable_shadowing_script)).unwrap(); + + let actual_trace = trace_from_result(&result); let expected_trace = vec![ scalar_string_array(vec!["1", "2"]), scalar_string_array(vec!["1", "2"]), diff --git a/air/tests/test_module/instructions/match_.rs b/air/tests/test_module/instructions/match_.rs index 9ed3119e..5e40afc0 100644 --- a/air/tests/test_module/instructions/match_.rs +++ b/air/tests/test_module/instructions/match_.rs @@ -14,11 +14,7 @@ * limitations under the License. */ -use air_test_utils::call_vm; -use air_test_utils::create_avm; -use air_test_utils::echo_string_call_service; -use air_test_utils::executed_state; -use air_test_utils::ExecutionTrace; +use air_test_utils::*; #[test] fn match_equal() { @@ -45,10 +41,10 @@ fn match_equal() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("result_1"); assert_eq!(actual_trace.len(), 3); @@ -80,10 +76,10 @@ fn match_not_equal() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("result_2"); assert_eq!(actual_trace.len(), 3); @@ -112,10 +108,10 @@ fn match_with_string() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("result_1"); assert_eq!(actual_trace.len(), 2); @@ -144,10 +140,10 @@ fn match_with_init_peer_id() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, local_peer_id, script.clone(), "", ""); - let res = call_vm!(vm, local_peer_id, script, "", res.data); + let result = checked_call_vm!(set_variable_vm, local_peer_id, &script, "", ""); + let result = checked_call_vm!(vm, local_peer_id, script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let actual_trace = trace_from_result(&result); let expected_executed_call_result = executed_state::scalar_string("result_1"); assert_eq!(actual_trace.len(), 2); @@ -167,8 +163,8 @@ fn match_with_equal_numbers() { (null) )"; - let res = call_vm!(vm, "asd", script, "", ""); - assert_eq!(res.ret_code, 0); + let result = checked_call_vm!(vm, "asd", script, "", ""); + assert_eq!(result.ret_code, 0); } #[test] @@ -193,14 +189,14 @@ fn match_without_xor() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script.clone(), "", res.data); + let result = call_vm!(set_variable_vm, "", &script, "", ""); + let result = call_vm!(vm, "", &script, "", result.data); - assert_eq!(res.ret_code, 1015); + assert_eq!(result.ret_code, 1014); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = call_vm!(vm, "", script, "", result.data); - assert_eq!(res.ret_code, 1015); + assert_eq!(result.ret_code, 1014); } #[test] @@ -236,11 +232,10 @@ fn match_with_two_xors() { local_peer_id, local_peer_id_2 ); - let res = call_vm!(vm, "", script, "", ""); - let mut trace: ExecutionTrace = - serde_json::from_slice(&res.data).expect("the interpreter should provide correct trace"); + let result = checked_call_vm!(vm, "", script, "", ""); + let mut actual_trace = trace_from_result(&result); let expected_executed_call_result = executed_state::request_sent_by(local_peer_id); - assert_eq!(res.ret_code, 0); - assert_eq!(trace.pop_back().unwrap(), expected_executed_call_result); + + assert_eq!(actual_trace.pop().unwrap(), expected_executed_call_result); } diff --git a/air/tests/test_module/instructions/mismatch.rs b/air/tests/test_module/instructions/mismatch.rs index 730db3c9..fdb8dcdb 100644 --- a/air/tests/test_module/instructions/mismatch.rs +++ b/air/tests/test_module/instructions/mismatch.rs @@ -15,10 +15,11 @@ */ use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::echo_string_call_service; use air_test_utils::executed_state; -use air_test_utils::ExecutionTrace; +use air_test_utils::trace_from_result; #[test] fn mismatch_equal() { @@ -45,10 +46,10 @@ fn mismatch_equal() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("result_2"); assert_eq!(actual_trace.len(), 3); @@ -80,10 +81,10 @@ fn mismatch_not_equal() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("result_1"); assert_eq!(actual_trace.len(), 3); @@ -112,10 +113,10 @@ fn mismatch_with_string() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("result_2"); assert_eq!(actual_trace.len(), 2); @@ -144,14 +145,14 @@ fn mismatch_without_xor() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script.clone(), "", res.data); + let result = call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = call_vm!(vm, "asd", &script, "", result.data); - assert_eq!(res.ret_code, 1016); + assert_eq!(result.ret_code, 1015); - let res = call_vm!(vm, "asd", script, "", res.data); + let result = call_vm!(vm, "asd", script, "", result.data); - assert_eq!(res.ret_code, 1016); + assert_eq!(result.ret_code, 1015); } #[test] @@ -187,11 +188,11 @@ fn mismatch_with_two_xors() { local_peer_id, local_peer_id_2 ); - let res = call_vm!(vm, "", script, "", ""); - let mut trace: ExecutionTrace = - serde_json::from_slice(&res.data).expect("the interpreter should provide correct trace"); + let result = checked_call_vm!(vm, "", script, "", ""); + let mut actual_trace = trace_from_result(&result); let expected_executed_call_result = executed_state::request_sent_by(local_peer_id); - assert_eq!(res.ret_code, 0); - assert_eq!(trace.pop_back().unwrap(), expected_executed_call_result); + + assert_eq!(result.ret_code, 0); + assert_eq!(actual_trace.pop().unwrap(), expected_executed_call_result); } diff --git a/air/tests/test_module/instructions/mod.rs b/air/tests/test_module/instructions/mod.rs index e5490703..62467ca0 100644 --- a/air/tests/test_module/instructions/mod.rs +++ b/air/tests/test_module/instructions/mod.rs @@ -14,6 +14,7 @@ * limitations under the License. */ +mod ap; mod call; mod fold; mod match_; diff --git a/air/tests/test_module/instructions/par.rs b/air/tests/test_module/instructions/par.rs index a4049889..4b979b00 100644 --- a/air/tests/test_module/instructions/par.rs +++ b/air/tests/test_module/instructions/par.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::unit_call_service; @@ -24,20 +24,19 @@ fn par_remote_remote() { let mut vm = create_avm(unit_call_service(), ""); - let script = String::from( - r#" + let script = r#" (par (call "remote_peer_id_1" ("local_service_id" "local_fn_name") [] result_name) (call "remote_peer_id_2" ("service_id" "fn_name") [] g) - )"#, - ); + )"#; - let mut res = call_vm!(vm, "", script, "[]", "[]"); + let mut result = checked_call_vm!(vm, "", script, "", ""); - let peers_result: HashSet<_> = res.next_peer_pks.drain(..).collect(); - let peers_right: HashSet<_> = maplit::hashset!(String::from("remote_peer_id_1"), String::from("remote_peer_id_2")); + let actual_peers: HashSet<_> = result.next_peer_pks.drain(..).collect(); + let expected_peers: HashSet<_> = + maplit::hashset!(String::from("remote_peer_id_1"), String::from("remote_peer_id_2")); - assert_eq!(peers_result, peers_right); + assert_eq!(actual_peers, expected_peers); } #[test] @@ -54,7 +53,7 @@ fn par_local_remote() { local_peer_id ); - let res = call_vm!(vm, "", script, "[]", "[]"); + let result = checked_call_vm!(vm, "", script, "", ""); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]); + assert_eq!(result.next_peer_pks, vec![String::from("remote_peer_id_2")]); } diff --git a/air/tests/test_module/instructions/seq.rs b/air/tests/test_module/instructions/seq.rs index 8670a090..7f1c573a 100644 --- a/air/tests/test_module/instructions/seq.rs +++ b/air/tests/test_module/instructions/seq.rs @@ -14,31 +14,31 @@ * limitations under the License. */ -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::executed_state; +use air_test_utils::raw_data_from_trace; use air_test_utils::unit_call_service; #[test] fn seq_remote_remote() { let mut vm = create_avm(unit_call_service(), ""); - let script = String::from( - r#" + let script = r#" (seq (call "remote_peer_id_1" ("local_service_id" "local_fn_name") [] result_name) (call "remote_peer_id_2" ("service_id" "fn_name") [] g) - )"#, - ); + )"#; - let res = call_vm!(vm, "asd", script.clone(), "", ""); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_1")]); + let result = checked_call_vm!(vm, "asd", script, "", ""); + assert_eq!(result.next_peer_pks, vec![String::from("remote_peer_id_1")]); - let initial_data = vec![executed_state::scalar_string("")]; - let initial_data = serde_json::to_string(&initial_data).expect("default serializer shouldn't fail"); + let initial_trace = vec![executed_state::scalar_string("")]; + let initial_data = raw_data_from_trace(initial_trace); - let res = call_vm!(vm, "asd", script, "", initial_data); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id_2")]); + let result = checked_call_vm!(vm, "asd", script, "", initial_data); + + assert_eq!(result.next_peer_pks, vec![String::from("remote_peer_id_2")]); } #[test] @@ -56,6 +56,6 @@ fn seq_local_remote() { local_peer_id, remote_peer_id ); - let res = call_vm!(vm, "asd", script, "[]", "[]"); - assert_eq!(res.next_peer_pks, vec![remote_peer_id]); + let result = checked_call_vm!(vm, "asd", script, "", ""); + assert_eq!(result.next_peer_pks, vec![remote_peer_id]); } diff --git a/air/tests/test_module/instructions/xor.rs b/air/tests/test_module/instructions/xor.rs index df7b85ea..97017629 100644 --- a/air/tests/test_module/instructions/xor.rs +++ b/air/tests/test_module/instructions/xor.rs @@ -14,12 +14,12 @@ * limitations under the License. */ -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::echo_string_call_service; use air_test_utils::executed_state; use air_test_utils::fallible_call_service; -use air_test_utils::ExecutionTrace; +use air_test_utils::trace_from_result; #[test] fn xor() { @@ -36,8 +36,9 @@ fn xor() { local_peer_id, ); - let res = call_vm!(vm, "asd", script, "[]", "[]"); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let result = checked_call_vm!(vm, "asd", script, "", ""); + + let actual_trace = trace_from_result(&result); let expected_call_result = executed_state::scalar_string("res"); assert_eq!(actual_trace.len(), 2); @@ -53,9 +54,9 @@ fn xor() { local_peer_id ); - let res = call_vm!(vm, "asd", script, "[]", "[]"); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let result = checked_call_vm!(vm, "asd", script, "", ""); + let actual_trace = trace_from_result(&result); assert_eq!(actual_trace.len(), 1); assert_eq!(actual_trace[0], expected_call_result); } @@ -77,8 +78,9 @@ fn xor_var_not_found() { local_peer_id, ); - let res = call_vm!(vm, "asd", script, "[]", "[]"); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let result = checked_call_vm!(vm, "asd", script, "", ""); + + let actual_trace = trace_from_result(&result); assert_eq!(actual_trace[0], executed_state::par(1, 0)); assert_eq!(actual_trace[1], executed_state::request_sent_by(local_peer_id)); } @@ -105,9 +107,10 @@ fn xor_multiple_variables_found() { set_variables_peer_id, local_peer_id, some_string, expected_string ); - let res = call_vm!(set_variables_vm, "asd", script.clone(), "[]", "[]"); - let res = call_vm!(vm, "asd", script, "[]", res.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let result = checked_call_vm!(set_variables_vm, "asd", &script, "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); + + let actual_trace = trace_from_result(&result); let some_string_call_result = executed_state::scalar_string(some_string); let expected_string_call_result = executed_state::scalar_string(expected_string); @@ -128,13 +131,13 @@ fn xor_par() { r#" (xor (par - (seq - (call "{0}" ("service_id_2" "local_fn_name") [] result_1) - (call "{0}" ("service_id_2" "local_fn_name") [] result_2) + (par + (call "{0}" ("service_id_1" "local_fn_name") [] result_1) + (call "{0}" ("service_id_1" "local_fn_name") [] result_2) ) (par (call "{0}" ("service_id_1" "local_fn_name") [] result_3) - (call "{0}" ("service_id_2" "local_fn_name") [] result_4) + (call "{0}" ("service_id_1" "local_fn_name") [] result_4) ) ) (seq @@ -145,25 +148,28 @@ fn xor_par() { local_peer_id ); - let result = call_vm!(vm, "asd", script.clone(), "[]", "[]"); - let actual_trace: ExecutionTrace = serde_json::from_slice(&result.data).expect("should be valid json"); + let result = checked_call_vm!(vm, "asd", &script, "", ""); + let actual_trace = trace_from_result(&result); - let res = String::from("res"); + let scalar_result = String::from("res"); let expected_trace = vec![ - par(2, 2), - scalar_string(&res), - scalar_string(&res), - par(1, 0), + par(3, 3), + par(1, 1), service_failed(1, "error"), - scalar_string(&res), - scalar_string(&res), + service_failed(1, "error"), + par(1, 1), + service_failed(1, "error"), + service_failed(1, "error"), + scalar_string(&scalar_result), + scalar_string(&scalar_result), ]; assert_eq!(actual_trace, expected_trace); - let result = call_vm!(vm, "asd", script, "[]", result.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&result.data).expect("should be valid json"); + let result = checked_call_vm!(vm, "asd", script, "", result.data); + + let actual_trace = trace_from_result(&result); assert_eq!(actual_trace, expected_trace); } @@ -185,10 +191,10 @@ fn last_error_with_xor() { faillible_peer_id, local_peer_id, ); - let res = call_vm!(faillible_vm, "asd", script.clone(), "", ""); - let res = call_vm!(vm, "asd", script, "", res.data); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let result = checked_call_vm!(faillible_vm, "asd", script.clone(), "", ""); + let result = checked_call_vm!(vm, "asd", script, "", result.data); + let actual_trace = trace_from_result(&result); let expected_state = executed_state::scalar_string("Local service error, ret_code is 1, error message is 'error'"); assert_eq!(actual_trace[1], expected_state); diff --git a/air/tests/test_module/integration/air_basic.rs b/air/tests/test_module/integration/air_basic.rs index 663b6d4e..2514e7c3 100644 --- a/air/tests/test_module/integration/air_basic.rs +++ b/air/tests/test_module/integration/air_basic.rs @@ -14,11 +14,11 @@ * limitations under the License. */ -use air::execution_trace::ExecutionTrace; -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::executed_state; use air_test_utils::set_variables_call_service; +use air_test_utils::trace_from_result; use air_test_utils::unit_call_service; use air_test_utils::CallServiceClosure; use air_test_utils::IValue; @@ -28,8 +28,8 @@ use serde_json::json; #[test] fn seq_par_call() { - let vm_peer_id = String::from("some_peer_id"); - let mut vm = create_avm(unit_call_service(), vm_peer_id.clone()); + let vm_peer_id = "some_peer_id"; + let mut vm = create_avm(unit_call_service(), vm_peer_id); let script = format!( r#" @@ -43,8 +43,8 @@ fn seq_par_call() { vm_peer_id ); - let res = call_vm!(vm, "asd", script, "[]", "[]"); - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("interpreter should return valid json"); + let result = checked_call_vm!(vm, "asd", script, "", ""); + let actual_trace = trace_from_result(&result); let test_string = "test"; let expected_trace = vec![ @@ -55,13 +55,13 @@ fn seq_par_call() { ]; assert_eq!(actual_trace, expected_trace); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); + assert_eq!(result.next_peer_pks, vec![String::from("remote_peer_id")]); } #[test] fn par_par_call() { - let vm_peer_id = String::from("some_peer_id"); - let mut vm = create_avm(unit_call_service(), vm_peer_id.clone()); + let vm_peer_id = "some_peer_id"; + let mut vm = create_avm(unit_call_service(), vm_peer_id); let script = format!( r#" @@ -75,9 +75,8 @@ fn par_par_call() { vm_peer_id, ); - let res = call_vm!(vm, "asd", script, "[]", "[]"); - let resulted_trace: ExecutionTrace = - serde_json::from_slice(&res.data).expect("interpreter should return valid json"); + let result = checked_call_vm!(vm, "asd", script, "", ""); + let actual_trace = trace_from_result(&result); let test_string = "test"; let expected_trace = vec![ @@ -88,8 +87,8 @@ fn par_par_call() { executed_state::scalar_string(test_string), ]; - assert_eq!(resulted_trace, expected_trace); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); + assert_eq!(actual_trace, expected_trace); + assert_eq!(result.next_peer_pks, vec![String::from("remote_peer_id")]); } #[test] @@ -145,17 +144,18 @@ fn create_service() { let script = include_str!("./scripts/create_service.clj"); - let res = call_vm!(set_variables_vm, "init_peer_id", script.clone(), "[]", "[]"); - let res = call_vm!(vm, "init_peer_id", script, "[]", res.data); + let result = checked_call_vm!(set_variables_vm, "init_peer_id", script.clone(), "", ""); + let result = checked_call_vm!(vm, "init_peer_id", script, "", result.data); let add_module_response = "add_module response"; let add_blueprint_response = "add_blueprint response"; let create_response = "create response"; - let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be a correct json"); + + let actual_trace = trace_from_result(&result); let expected_trace = vec![ - executed_state::scalar_jvalue(module_bytes), - executed_state::scalar_jvalue(module_config), - executed_state::scalar_jvalue(blueprint), + executed_state::scalar(module_bytes), + executed_state::scalar(module_config), + executed_state::scalar(blueprint), executed_state::scalar_string(add_module_response), executed_state::scalar_string(add_blueprint_response), executed_state::scalar_string(create_response), @@ -163,5 +163,5 @@ fn create_service() { ]; assert_eq!(actual_trace, expected_trace); - assert_eq!(res.next_peer_pks, vec![String::from("remote_peer_id")]); + assert_eq!(result.next_peer_pks, vec![String::from("remote_peer_id")]); } diff --git a/air/tests/test_module/integration/ap_with_fold.rs b/air/tests/test_module/integration/ap_with_fold.rs new file mode 100644 index 00000000..1f43b8b7 --- /dev/null +++ b/air/tests/test_module/integration/ap_with_fold.rs @@ -0,0 +1,73 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use air_test_utils::*; +use serde_json::json; + +#[test] +fn ap_with_fold() { + let nums: Vec = (1..10).map(|i| i.to_string()).collect(); + let vec = vec![nums.clone(), nums.clone(), nums.clone()]; + let elems: Vec<(String, Vec>)> = vec![ + ("a".into(), vec.clone()), + ("a".into(), vec.clone()), + ("a".into(), vec.clone()), + ("a".into(), vec.clone()), + ("a".into(), vec), + ]; + let set_variable_id = "set_variable_peer_id"; + let mut set_variable_vm = create_avm(set_variable_call_service(json!(elems).to_string()), set_variable_id); + + let local_vm_peer_id = "local_peer_id"; + let mut local_vm = create_avm(unit_call_service(), local_vm_peer_id); + + let script = format!( + r#" + (seq + (call "{0}" ("" "") [] permutations) + (seq + (seq + (fold permutations pair + (seq + (fold pair.$.[1]! peer_ids + (seq + (ap peer_ids $inner) + (next peer_ids) + ) + ) + (next pair) + ) + ) + (fold $inner ns + (next ns) + ) + ) + (seq + (call "{1}" ("op" "noop") []) + (call "{1}" ("return" "") [$inner]) + ) + ) + ) + "#, + set_variable_id, local_vm_peer_id, + ); + + let result = checked_call_vm!(set_variable_vm, "", &script, "", ""); + assert_eq!(result.next_peer_pks, vec![local_vm_peer_id.to_string()]); + + let result = checked_call_vm!(local_vm, "", &script, "", result.data); + assert!(result.next_peer_pks.is_empty()); +} diff --git a/air/tests/test_module/integration/call_evidence_basic.rs b/air/tests/test_module/integration/call_evidence_basic.rs new file mode 100644 index 00000000..08ead514 --- /dev/null +++ b/air/tests/test_module/integration/call_evidence_basic.rs @@ -0,0 +1,427 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use air_test_utils::checked_call_vm; +use air_test_utils::create_avm; +use air_test_utils::echo_number_call_service; +use air_test_utils::executed_state::*; +use air_test_utils::raw_data_from_trace; +use air_test_utils::trace_from_result; +use air_test_utils::unit_call_service; +use air_test_utils::CallServiceClosure; +use air_test_utils::IValue; +use air_test_utils::NEVec; + +use serde_json::json; + +#[test] +fn executed_trace_seq_par_call() { + let local_peer_id = "local_peer_id"; + let mut vm = create_avm(unit_call_service(), local_peer_id); + + let script = format!( + r#" + (seq + (par + (call "{0}" ("local_service_id" "local_fn_name") [] result_1) + (call "remote_peer_id" ("service_id" "fn_name") [] g) + ) + (call "{0}" ("local_service_id" "local_fn_name") [] result_2) + )"#, + local_peer_id + ); + + let initial_trace = vec![par(1, 1), scalar_string("test"), scalar_string("test")]; + let initial_data = raw_data_from_trace(initial_trace); + + let result = checked_call_vm!(vm, "asd", script, "", initial_data); + let actual_trace = trace_from_result(&result); + + let test_string = "test"; + + let expected_trace = vec![ + par(1, 1), + scalar_string(test_string), + scalar_string(test_string), + scalar_string(test_string), + ]; + + assert_eq!(actual_trace, expected_trace); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn executed_trace_par_par_call() { + let local_peer_id = "local_peer_id"; + let mut vm = create_avm(unit_call_service(), local_peer_id); + + let script = format!( + r#" + (par + (par + (call "{0}" ("local_service_id" "local_fn_name") [] result_1) + (call "remote_peer_id" ("service_id" "fn_name") [] g) + ) + (call "{0}" ("local_service_id" "local_fn_name") [] result_2) + )"#, + local_peer_id, + ); + + let initial_state = vec![ + par(2, 1), + par(1, 0), + request_sent_by("peer_id_1"), + scalar_string("test"), + ]; + + let initial_data = raw_data_from_trace(initial_state); + + let result = checked_call_vm!(vm, "asd", &script, "", initial_data); + let actual_trace = trace_from_result(&result); + + let test_string = "test"; + let expected_trace = vec![ + par(3, 1), + par(1, 1), + scalar_string(test_string), + request_sent_by(local_peer_id), + scalar_string(test_string), + ]; + + assert_eq!(actual_trace, expected_trace); + assert_eq!(result.next_peer_pks, vec![String::from("remote_peer_id")]); + + let initial_state = vec![ + par(3, 0), + par(1, 1), + request_sent_by("peer_id_1"), + request_sent_by(local_peer_id), + ]; + + let initial_data = raw_data_from_trace(initial_state); + + let result = checked_call_vm!(vm, "asd", script, "", initial_data); + let actual_trace = trace_from_result(&result); + + assert_eq!(actual_trace, expected_trace); +} + +#[test] +fn executed_trace_seq_seq() { + let peer_id_1 = String::from("12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er"); + let peer_id_2 = String::from("12D3KooWAzJcYitiZrerycVB4Wryrx22CFKdDGx7c4u31PFdfTbR"); + let mut vm1 = create_avm(unit_call_service(), peer_id_1.clone()); + let mut vm2 = create_avm(unit_call_service(), peer_id_2.clone()); + + let script = format!( + r#" + (seq + (call "{}" ("identity" "") [] void0) + (seq + (call "{}" ("add_blueprint" "") [] blueprint_id) + (call "{}" ("addBlueprint-14d8488e-d10d-474d-96b2-878f6a7d74c8" "") [] void1) + ) + ) + "#, + peer_id_1, peer_id_1, peer_id_2 + ); + + let result = checked_call_vm!(vm2, "asd", script.clone(), "", ""); + assert_eq!(result.next_peer_pks, vec![peer_id_1.clone()]); + + let result = checked_call_vm!(vm1, "asd", script.clone(), "", result.data); + assert_eq!(result.next_peer_pks, vec![peer_id_2.clone()]); + + let result = checked_call_vm!(vm2, "asd", script, "", result.data); + + let actual_trace = trace_from_result(&result); + + let test_string = "test"; + let expected_trace = vec![ + scalar_string(test_string), + scalar_string(test_string), + scalar_string(test_string), + ]; + + assert_eq!(actual_trace, expected_trace); +} + +#[test] +fn executed_trace_create_service() { + let module = "greeting"; + let module_config = json!( + { + "name": module, + "mem_pages_count": 100, + "logger_enabled": true, + "wasi": { + "envs": json!({}), + "preopened_files": vec!["/tmp"], + "mapped_dirs": json!({}), + } + } + ); + + let module_bytes = json!([1, 2]); + let blueprint = json!({ "name": "blueprint", "dependencies": [module]}); + + let add_module_response = String::from("add_module response"); + let add_blueprint_response = String::from("add_blueprint response"); + let create_response = String::from("create response"); + + let call_service: CallServiceClosure = Box::new(move |args| -> Option { + let builtin_service = match &args.function_args[0] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let response = match builtin_service.as_str() { + "add_module" => add_module_response.clone(), + "add_blueprint" => add_blueprint_response.clone(), + "create" => create_response.clone(), + _ => String::from("unknown response"), + }; + + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(format!("\"{}\"", response))]).unwrap(), + )) + }); + + let mut vm = create_avm(call_service, "A"); + + let script = include_str!("./scripts/create_service.clj"); + + let add_module_response = String::from("add_module response"); + let add_blueprint_response = String::from("add_blueprint response"); + let create_response = String::from("create response"); + let expected_trace = vec![ + scalar(module_bytes), + scalar(module_config), + scalar(blueprint), + scalar_string(add_module_response), + scalar_string(add_blueprint_response), + scalar_string(create_response), + scalar_string("test"), + ]; + let initial_data = raw_data_from_trace(expected_trace.clone()); + + let result = checked_call_vm!(vm, "init_peer_id", script, "", initial_data); + + let actual_trace = trace_from_result(&result); + + assert_eq!(actual_trace, expected_trace); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn executed_trace_par_seq_fold_call() { + let return_numbers_call_service: CallServiceClosure = Box::new(|_| -> Option { + Some(IValue::Record( + NEVec::new(vec![ + IValue::S32(0), + IValue::String(String::from( + "[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]", + )), + ]) + .unwrap(), + )) + }); + + let mut vm1 = create_avm(return_numbers_call_service, "some_peer_id_1"); + let mut vm2 = create_avm(echo_number_call_service(), "some_peer_id_2"); + let mut vm3 = create_avm(unit_call_service(), "some_peer_id_3"); + + let script = String::from( + r#" + (par + (seq + (call "some_peer_id_1" ("local_service_id" "local_fn_name") [] IterableResultPeer1) + (fold IterableResultPeer1 i + (par + (call "some_peer_id_2" ("local_service_id" "local_fn_name") [i] $acc) + (next i) + ) + ) + ) + (call "some_peer_id_3" ("local_service_id" "local_fn_name") [] result_2) + )"#, + ); + + let result = checked_call_vm!(vm2, "asd", script.clone(), "", ""); + let result = checked_call_vm!(vm1, "asd", script.clone(), "", result.data); + let mut data = result.data; + + for _ in 0..100 { + let result = checked_call_vm!(vm2, "asd", script.clone(), "", data); + data = result.data; + } + + let result = checked_call_vm!(vm3, "asd", script, "", data); + let actual_trace = trace_from_result(&result); + + let generation = 0; + let expected_trace = vec![ + par(21, 1), + scalar_string_array(vec!["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]), + par(1, 18), + stream_number(1, generation), + par(1, 16), + stream_number(2, generation), + par(1, 14), + stream_number(3, generation), + par(1, 12), + stream_number(4, generation), + par(1, 10), + stream_number(5, generation), + par(1, 8), + stream_number(6, generation), + par(1, 6), + stream_number(7, generation), + par(1, 4), + stream_number(8, generation), + par(1, 2), + stream_number(9, generation), + par(1, 0), + stream_number(10, generation), + scalar_string("test"), + ]; + + assert_eq!(actual_trace, expected_trace); + assert!(result.next_peer_pks.is_empty()); +} + +#[test] +fn executed_trace_par_seq_fold_in_cycle_call() { + let return_numbers_call_service: CallServiceClosure = Box::new(|_| -> Option { + Some(IValue::Record( + NEVec::new(vec![ + IValue::S32(0), + IValue::String(String::from( + "[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]", + )), + ]) + .unwrap(), + )) + }); + + let mut vm1 = create_avm(return_numbers_call_service, "some_peer_id_1"); + let mut vm2 = create_avm(echo_number_call_service(), "some_peer_id_2"); + let mut vm3 = create_avm(unit_call_service(), "some_peer_id_3"); + + let script = r#" + (par + (seq + (call "some_peer_id_1" ("local_service_id" "local_fn_name") [] IterableResultPeer1) + (fold IterableResultPeer1 i + (par + (call "some_peer_id_2" ("local_service_id" "local_fn_name") [i] $acc) + (next i) + ) + ) + ) + (call "some_peer_id_3" ("local_service_id" "local_fn_name") [] result_2) + )"#; + + let mut data = vec![]; + + for _ in 0..100 { + let result = checked_call_vm!(vm1, "asd", script, "", data); + let result = checked_call_vm!(vm2, "asd", script, "", result.data); + let result = checked_call_vm!(vm3, "asd", script, "", result.data); + + let actual_trace = trace_from_result(&result); + + let generation = 0; + let expected_trace = vec![ + par(21, 1), + scalar_string_array(vec!["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]), + par(1, 18), + stream_number(1, generation), + par(1, 16), + stream_number(2, generation), + par(1, 14), + stream_number(3, generation), + par(1, 12), + stream_number(4, generation), + par(1, 10), + stream_number(5, generation), + par(1, 8), + stream_number(6, generation), + par(1, 6), + stream_number(7, generation), + par(1, 4), + stream_number(8, generation), + par(1, 2), + stream_number(9, generation), + par(1, 0), + stream_number(10, generation), + scalar_string("test"), + ]; + + assert_eq!(actual_trace, expected_trace); + + data = result.data; + } +} + +#[test] +fn executed_trace_seq_par_seq_seq() { + let peer_id_1 = String::from("12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er"); + let peer_id_2 = String::from("12D3KooWAzJcYitiZrerycVB4Wryrx22CFKdDGx7c4u31PFdfTbR"); + let mut vm1 = create_avm(unit_call_service(), peer_id_1.clone()); + let mut vm2 = create_avm(unit_call_service(), peer_id_2.clone()); + let script = format!( + r#" + (seq + (par + (seq + (call "{}" ("" "") [] result_1) + (call "{}" ("" "") [] result_2) + ) + (seq + (call "{}" ("" "") [] result_3) + (call "{}" ("" "") [] result_4) + ) + ) + (call "{}" ("" "") [] result_5) + ) + "#, + peer_id_1, peer_id_2, peer_id_2, peer_id_1, peer_id_2 + ); + + let result = checked_call_vm!(vm2, "asd", script.clone(), "", ""); + assert_eq!(result.next_peer_pks, vec![peer_id_1.clone()]); + + let result = checked_call_vm!(vm1, "asd", script.clone(), "", result.data); + assert_eq!(result.next_peer_pks, vec![peer_id_2.clone()]); + + let result = checked_call_vm!(vm2, "asd", script, "", result.data); + + let actual_trace = trace_from_result(&result); + + let service_result_string = "test"; + let executed_trace = vec![ + par(2, 2), + scalar_string(service_result_string), + scalar_string(service_result_string), + scalar_string(service_result_string), + scalar_string(service_result_string), + scalar_string(service_result_string), + ]; + + assert_eq!(actual_trace, executed_trace); + assert!(result.next_peer_pks.is_empty()); +} diff --git a/air/tests/test_module/integration/dashboard.rs b/air/tests/test_module/integration/dashboard.rs index 18c6774a..c6fb5819 100644 --- a/air/tests/test_module/integration/dashboard.rs +++ b/air/tests/test_module/integration/dashboard.rs @@ -14,11 +14,12 @@ * limitations under the License. */ -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; +use air_test_utils::CallServiceClosure; use air_test_utils::IValue; use air_test_utils::NEVec; -use air_test_utils::{CallServiceClosure, AVM}; +use air_test_utils::AVM; use std::cell::RefCell; use std::collections::HashSet; @@ -213,28 +214,34 @@ fn dashboard() { .collect::>(); // -> client 1 - let client_1_res = call_vm!(client, client_id.clone(), script.clone(), "", ""); - let next_peer_pks = into_hashset(client_1_res.next_peer_pks); + let client_1_result = checked_call_vm!(client, client_id.clone(), script.clone(), "", ""); + let next_peer_pks = into_hashset(client_1_result.next_peer_pks); let mut all_peer_pks = into_hashset(known_peer_ids.clone()); all_peer_pks.insert(relay_id.clone()); assert_eq!(next_peer_pks, all_peer_pks); // client 1 -> relay 1 - let relay_1_res = call_vm!(relay, client_id.clone(), script.clone(), client_1_res.data.clone(), ""); - let next_peer_pks = into_hashset(relay_1_res.next_peer_pks.clone()); + let relay_1_result = checked_call_vm!( + relay, + client_id.clone(), + script.clone(), + client_1_result.data.clone(), + "" + ); + let next_peer_pks = into_hashset(relay_1_result.next_peer_pks.clone()); all_peer_pks.remove(&relay_id); all_peer_pks.insert(client_id.clone()); assert_eq!(next_peer_pks, all_peer_pks); // relay 1 -> client 2 - let client_2_res = call_vm!( + let client_2_result = checked_call_vm!( client, client_id.clone(), script.clone(), - client_1_res.data.clone(), - relay_1_res.data.clone() + client_1_result.data.clone(), + relay_1_result.data.clone() ); - assert!(client_2_res.next_peer_pks.is_empty()); + assert!(client_2_result.next_peer_pks.is_empty()); assert_eq!( *all_info.borrow(), String::from( @@ -242,40 +249,40 @@ fn dashboard() { ) ); - let mut relay_2_res = relay_1_res.clone(); - let mut client_3_res = client_2_res.clone(); + let mut relay_2_result = relay_1_result.clone(); + let mut client_3_result = client_2_result.clone(); // peers 1 -> relay 2 -> client 3 for avm in known_peers.iter_mut() { let prev_result = std::mem::replace(&mut avm.prev_result, vec![]); - let known_peer_res = call_vm!( + let known_peer_result = checked_call_vm!( avm.vm, client_id.clone(), script.clone(), prev_result, - client_1_res.data.clone() + client_1_result.data.clone() ); - assert_eq!(known_peer_res.next_peer_pks, vec![relay_id.clone()]); + assert_eq!(known_peer_result.next_peer_pks, vec![relay_id.clone()]); - avm.prev_result = known_peer_res.data; + avm.prev_result = known_peer_result.data; - relay_2_res = call_vm!( + relay_2_result = checked_call_vm!( relay, client_id.clone(), script.clone(), - relay_2_res.data.clone(), + relay_2_result.data.clone(), avm.prev_result.clone() ); - assert_eq!(relay_2_res.next_peer_pks, vec![client_id.clone()]); + assert_eq!(relay_2_result.next_peer_pks, vec![client_id.clone()]); - client_3_res = call_vm!( + client_3_result = checked_call_vm!( client, client_id.clone(), script.clone(), - client_3_res.data.clone(), - relay_2_res.data.clone() + client_3_result.data.clone(), + relay_2_result.data.clone() ); - assert!(client_3_res.next_peer_pks.is_empty()); + assert!(client_3_result.next_peer_pks.is_empty()); assert_eq!( *all_info.borrow(), format!( @@ -288,45 +295,45 @@ fn dashboard() { all_peer_pks.remove(&client_id); all_peer_pks.insert(relay_id.clone()); - let mut relay_3_res = relay_2_res.clone(); - let mut client_4_res = client_3_res.clone(); + let mut relay_3_result = relay_2_result.clone(); + let mut client_4_result = client_3_result.clone(); // peers 2 -> relay 3 -> client 4 for avm in known_peers.iter_mut() { let prev_result = std::mem::replace(&mut avm.prev_result, vec![]); - let known_peer_res = call_vm!( + let known_peer_result = checked_call_vm!( avm.vm, client_id.clone(), script.clone(), prev_result, - relay_1_res.data.clone() + relay_1_result.data.clone() ); all_peer_pks.remove(&avm.peer_id); - let next_peer_pks = into_hashset(known_peer_res.next_peer_pks.clone()); + let next_peer_pks = into_hashset(known_peer_result.next_peer_pks.clone()); assert_eq!(next_peer_pks, all_peer_pks); all_peer_pks.insert(avm.peer_id.clone()); - avm.prev_result = known_peer_res.data; + avm.prev_result = known_peer_result.data; - relay_3_res = call_vm!( + relay_3_result = checked_call_vm!( relay, client_id.clone(), script.clone(), - relay_3_res.data.clone(), + relay_3_result.data.clone(), avm.prev_result.clone() ); - assert_eq!(relay_3_res.next_peer_pks, vec![client_id.clone()]); + assert_eq!(relay_3_result.next_peer_pks, vec![client_id.clone()]); // client -> peers -> relay -> client - client_4_res = call_vm!( + client_4_result = checked_call_vm!( client, client_id.clone(), script.clone(), - client_4_res.data.clone(), - relay_3_res.data.clone() + client_4_result.data.clone(), + relay_3_result.data.clone() ); - assert!(client_4_res.next_peer_pks.is_empty()); + assert!(client_4_result.next_peer_pks.is_empty()); assert_eq!( *all_info.borrow(), format!( @@ -336,8 +343,8 @@ fn dashboard() { ) } - let mut relay_4_res = relay_3_res.clone(); - let mut client_5_res = client_4_res.clone(); + let mut relay_4_result = relay_3_result.clone(); + let mut client_5_result = client_4_result.clone(); // peers 2 -> peers 3 -> relay 4 -> client 5 for i in 0..known_peers.len() { @@ -348,29 +355,30 @@ fn dashboard() { let prev_data = known_peers[j].prev_result.clone(); let data = known_peers[i].prev_result.clone(); - let known_peer_i_j_res = call_vm!(known_peers[j].vm, client_id.clone(), script.clone(), prev_data, data); - assert_eq!(known_peer_i_j_res.next_peer_pks, vec![relay_id.clone()]); + let known_peer_i_j_result = + checked_call_vm!(known_peers[j].vm, client_id.clone(), script.clone(), prev_data, data); + assert_eq!(known_peer_i_j_result.next_peer_pks, vec![relay_id.clone()]); - known_peers[j].prev_result = known_peer_i_j_res.data; + known_peers[j].prev_result = known_peer_i_j_result.data; - relay_4_res = call_vm!( + relay_4_result = checked_call_vm!( relay, client_id.clone(), script.clone(), - relay_4_res.data.clone(), + relay_4_result.data.clone(), known_peers[j].prev_result.clone() ); - assert_eq!(relay_4_res.next_peer_pks, vec![client_id.clone()]); + assert_eq!(relay_4_result.next_peer_pks, vec![client_id.clone()]); // client -> peers -> relay -> client - client_5_res = call_vm!( + client_5_result = checked_call_vm!( client, client_id.clone(), script.clone(), - client_5_res.data.clone(), - relay_4_res.data.clone() + client_5_result.data.clone(), + relay_4_result.data.clone() ); - assert!(client_5_res.next_peer_pks.is_empty()); + assert!(client_5_result.next_peer_pks.is_empty()); assert_eq!( *all_info.borrow(), format!( diff --git a/air/tests/test_module/integration/data_merge.rs b/air/tests/test_module/integration/data_merge.rs index d35e01c2..f15c3a61 100644 --- a/air/tests/test_module/integration/data_merge.rs +++ b/air/tests/test_module/integration/data_merge.rs @@ -14,18 +14,11 @@ * limitations under the License. */ -use pretty_assertions::assert_eq; +// use pretty_assertions::assert_eq; use serde_json::json; -use air_test_utils::call_vm; -use air_test_utils::create_avm; -use air_test_utils::executed_state; -use air_test_utils::set_variable_call_service; -use air_test_utils::set_variables_call_service; -use air_test_utils::CallServiceClosure; -use air_test_utils::ExecutionTrace; -use air_test_utils::IValue; -use air_test_utils::NEVec; +use air_test_utils::*; +use std::collections::HashMap; type JValue = serde_json::Value; @@ -33,25 +26,20 @@ type JValue = serde_json::Value; fn data_merge() { use executed_state::*; - let neighborhood_call_service1: CallServiceClosure = Box::new(|_| -> Option { - Some(IValue::Record( - NEVec::new(vec![IValue::S32(0), IValue::String(String::from("[\"A\", \"B\"]"))]).unwrap(), - )) - }); + let set_variable_id = "set_variable"; + let vm_1_id = "A"; + let vm_2_id = "B"; - let neighborhood_call_service2: CallServiceClosure = Box::new(|_| -> Option { - Some(IValue::Record( - NEVec::new(vec![IValue::S32(0), IValue::String(String::from("[\"A\", \"B\"]"))]).unwrap(), - )) - }); + let mut set_variable = create_avm( + set_variable_call_service(json!([vm_1_id, vm_2_id]).to_string()), + set_variable_id, + ); + let mut vm1 = create_avm(return_string_call_service(vm_1_id), vm_1_id); + let mut vm2 = create_avm(return_string_call_service(vm_2_id), vm_2_id); - let mut vm1 = create_avm(neighborhood_call_service1, "A"); - let mut vm2 = create_avm(neighborhood_call_service2, "B"); - - let script = String::from( - r#" + let script = r#" (seq - (call %init_peer_id% ("neighborhood" "") [] neighborhood) + (call "set_variable" ("neighborhood" "") [] neighborhood) (seq (seq (fold neighborhood i @@ -73,99 +61,93 @@ fn data_merge() { ) ) ) - "#, - ); + "#; // little hack here with init_peer_id to execute the first call from both VMs - let res1 = call_vm!(vm1, "A", script.clone(), "[]", "[]"); - let res2 = call_vm!(vm2, "B", script.clone(), "[]", "[]"); - let res3 = call_vm!(vm1, "asd", script.clone(), res1.data.clone(), res2.data.clone()); - let res4 = call_vm!(vm2, "asd", script, res1.data.clone(), res2.data.clone()); + let result_0 = checked_call_vm!(set_variable, "", script, "", ""); + let result_1 = checked_call_vm!(vm1, "", script, "", result_0.data.clone()); + let result_2 = checked_call_vm!(vm2, "", script, "", result_0.data); + let result_3 = checked_call_vm!(vm1, "", script, result_1.data.clone(), result_2.data.clone()); + let result_4 = checked_call_vm!(vm2, "", script, result_1.data.clone(), result_2.data.clone()); - let actual_trace_1: ExecutionTrace = - serde_json::from_slice(&res1.data).expect("interpreter should return valid json"); + let actual_trace_1 = trace_from_result(&result_1); let expected_trace_1 = vec![ scalar_string_array(vec!["A", "B"]), par(1, 2), - stream_string_array(vec!["A", "B"], "$void"), + stream_string(vm_1_id, 0), par(1, 0), - request_sent_by("A"), + request_sent_by(set_variable_id), par(1, 2), - stream_string_array(vec!["A", "B"], "$providers"), + stream_string(vm_1_id, 0), par(1, 0), - request_sent_by("A"), - stream_string_array(vec!["A", "B"], "$void"), - request_sent_by("A"), + request_sent_by(vm_1_id), + stream_string(vm_1_id, 0), + request_sent_by(vm_1_id), ]; assert_eq!(actual_trace_1, expected_trace_1); - assert_eq!(res1.next_peer_pks, vec![String::from("B")]); + assert_eq!(result_1.next_peer_pks, vec![String::from("B")]); - let actual_trace_2: ExecutionTrace = - serde_json::from_slice(&res2.data).expect("interpreter should return valid json"); + let actual_trace_2 = trace_from_result(&result_2); let expected_trace_2 = vec![ scalar_string_array(vec!["A", "B"]), par(1, 2), - request_sent_by("B"), + request_sent_by(set_variable_id), par(1, 0), - stream_string_array(vec!["A", "B"], "$void"), + stream_string(vm_2_id, 0), par(1, 2), - request_sent_by("B"), + request_sent_by(vm_2_id), par(1, 0), - stream_string_array(vec!["A", "B"], "$providers"), - request_sent_by("B"), + stream_string(vm_2_id, 0), + request_sent_by(vm_2_id), ]; assert_eq!(actual_trace_2, expected_trace_2); - assert_eq!(res2.next_peer_pks, vec![String::from("A")]); + assert_eq!(result_2.next_peer_pks, vec![String::from("A")]); - let actual_trace_3: ExecutionTrace = - serde_json::from_slice(&res3.data).expect("interpreter should return valid json"); + let actual_trace_3 = trace_from_result(&result_3); let expected_trace_3 = vec![ scalar_string_array(vec!["A", "B"]), par(1, 2), - stream_string_array(vec!["A", "B"], "$void"), + stream_string(vm_1_id, 0), par(1, 0), - stream_string_array(vec!["A", "B"], "$void"), + stream_string(vm_2_id, 1), par(1, 2), - stream_string_array(vec!["A", "B"], "$providers"), + stream_string(vm_1_id, 0), par(1, 0), - stream_string_array(vec!["A", "B"], "$providers"), - stream_string_array(vec!["A", "B"], "$void"), - request_sent_by("A"), + stream_string(vm_2_id, 1), + stream_string(vm_1_id, 0), + request_sent_by(vm_1_id), ]; assert_eq!(actual_trace_3, expected_trace_3); - assert!(res3.next_peer_pks.is_empty()); + assert!(result_3.next_peer_pks.is_empty()); - let actual_trace_4: ExecutionTrace = - serde_json::from_slice(&res4.data).expect("interpreter should return valid json"); + let actual_trace_4 = trace_from_result(&result_4); let expected_trace_4 = vec![ scalar_string_array(vec!["A", "B"]), par(1, 2), - stream_string_array(vec!["A", "B"], "$void"), + stream_string(vm_1_id, 0), par(1, 0), - stream_string_array(vec!["A", "B"], "$void"), + stream_string(vm_2_id, 1), par(1, 2), - stream_string_array(vec!["A", "B"], "$providers"), + stream_string(vm_1_id, 0), par(1, 0), - stream_string_array(vec!["A", "B"], "$providers"), - stream_string_array(vec!["A", "B"], "$void"), - scalar_string_array(vec!["A", "B"]), + stream_string(vm_2_id, 1), + stream_string(vm_1_id, 0), + scalar_string(vm_2_id), ]; assert_eq!(actual_trace_4, expected_trace_4); - assert!(res4.next_peer_pks.is_empty()); + assert!(result_4.next_peer_pks.is_empty()); } #[test] fn acc_merge() { - env_logger::init(); - let neighborhood_call_service: CallServiceClosure = Box::new(|args| -> Option { let args_count = match &args.function_args[1] { IValue::String(str) => str, @@ -216,46 +198,143 @@ fn acc_merge() { "#, ); - let res = call_vm!(vm1, "asd", script.clone(), "[]", "[]"); - call_vm!(vm2, "asd", script, "[]", res.data); + let result = checked_call_vm!(vm1, "asd", script.clone(), "", ""); + checked_call_vm!(vm2, "asd", script, "", result.data); } #[test] -#[ignore] fn fold_merge() { + use std::ops::Deref; + let set_variable_vm_id = "set_variable"; let local_vm_id = "local_vm"; let variables = maplit::hashmap! { - "stream1".to_string() => r#"["s1", "s2", "s3"]"#.to_string(), - "stream2".to_string() => r#"["s4", "s5", "s6"]"#.to_string(), + "stream_1".to_string() => json!(["peer_0", "peer_1", "peer_2", "peer_3"]).to_string(), + "stream_2".to_string() => json!(["peer_4", "peer_5", "peer_6"]).to_string(), }; - - let local_vm_service_call: CallServiceClosure = Box::new(|args| -> Option { - let args = match &args.function_args[2] { - IValue::String(str) => str, - _ => unreachable!(), - }; - println!("args {:?}", args); - - Some(IValue::Record( - NEVec::new(vec![IValue::S32(0), IValue::String(format!("{}", args))]).unwrap(), - )) - }); - let mut set_variable_vm = create_avm(set_variables_call_service(variables), set_variable_vm_id); - let mut local_vm = create_avm(local_vm_service_call, local_vm_id); let script = format!( include_str!("./scripts/inner_folds_v1.clj"), set_variable_vm_id, local_vm_id ); - let res = call_vm!(set_variable_vm, "", script.clone(), "", ""); - let res = call_vm!(local_vm, "", script, "", res.data); + let set_variable_result = checked_call_vm!(set_variable_vm, "", &script, "", ""); - let _actual_trace: ExecutionTrace = - serde_json::from_slice(&res.data).expect("interpreter should return valid json"); + let mut local_vms = Vec::with_capacity(7); + let mut local_vms_results = Vec::with_capacity(7); + for vm_id in 0..7 { + let peer_id = format!("peer_{}", vm_id); + let mut vm = create_avm(echo_string_call_service(), peer_id); + let result = checked_call_vm!( + vm, + "", + &script, + set_variable_result.data.clone(), + set_variable_result.data.clone() + ); - // println!("res is {:?}", actual_trace); + local_vms.push(vm); + local_vms_results.push(result); + } + + let mut local_vm = create_avm(echo_string_call_service(), local_vm_id); + let result_1 = checked_call_vm!(local_vm, "", &script, "", local_vms_results[0].data.clone()); + let result_2 = checked_call_vm!( + local_vm, + "", + &script, + result_1.data.clone(), + local_vms_results[3].data.clone() + ); + let result_3 = checked_call_vm!( + local_vm, + "", + &script, + result_2.data.clone(), + local_vms_results[4].data.clone() + ); + let result_4 = checked_call_vm!( + local_vm, + "", + &script, + result_3.data.clone(), + local_vms_results[5].data.clone() + ); + + let result_5 = checked_call_vm!( + local_vm, + "", + &script, + result_4.data.clone(), + local_vms_results[1].data.clone() + ); + + let result_6 = checked_call_vm!( + local_vm, + "", + &script, + result_5.data.clone(), + local_vms_results[2].data.clone() + ); + + let result_7 = checked_call_vm!( + local_vm, + "", + &script, + result_6.data.clone(), + local_vms_results[6].data.clone() + ); + + let data = InterpreterData::try_from_slice(&result_7.data).expect("data should be well-formed"); + let stream_1_generations = data + .streams + .get("$stream_1") + .expect("$stream_1 should presence in data"); + let stream_2_generations = data + .streams + .get("$stream_2") + .expect("$stream_2 should presence in data"); + + assert_eq!(*stream_1_generations, 4); + assert_eq!(*stream_2_generations, 3); + + let mut fold_states_count = 0; + let mut calls_count = HashMap::new(); + + for state in data.trace.iter() { + if matches!(state, ExecutedState::Fold(_)) { + fold_states_count += 1; + } + + if let ExecutedState::Fold(fold) = state { + for subtrace_lore in fold.lore.iter() { + let value_pos = subtrace_lore.value_pos as usize; + if let ExecutedState::Call(CallResult::Executed(value)) = &data.trace[value_pos] { + let value = match value { + Value::Scalar(value) => value, + Value::Stream { value, .. } => value, + }; + + if let JValue::String(var_name) = value.deref() { + let current_count: usize = calls_count.get(var_name).map(|v| *v).unwrap_or_default(); + calls_count.insert(var_name, current_count + 1); + } + } + } + } + } + + // $stream_1 contains 4 generation each with 2 elements, it means that inner fold for $stream_2 + // runs 2*4 times and produces 2*4 fold states, and 1 state comes from fold for $stream_1 + assert_eq!(fold_states_count, 2 * 4 + 1); + + for (call_result, call_count) in calls_count { + if call_result.as_str() < "peer_4" { + assert_eq!(call_count, 2); + } else { + assert_eq!(call_count, 16); + } + } } diff --git a/air/tests/test_module/integration/empty_array.rs b/air/tests/test_module/integration/empty_array.rs new file mode 100644 index 00000000..a3894ae3 --- /dev/null +++ b/air/tests/test_module/integration/empty_array.rs @@ -0,0 +1,42 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use air_test_utils::*; + +use serde_json::json; + +#[test] +fn empty_array() { + let vm_peer_id = "some_peer_id"; + let mut vm = create_avm(echo_call_service(), vm_peer_id); + + let script = format!( + r#" + (seq + (call "{0}" ("" "") [[]] result) + (call "{0}" ("" "") [result]) + )"#, + vm_peer_id + ); + + let result = checked_call_vm!(vm, "", script, "", ""); + let actual_trace = trace_from_result(&result); + + let expected_trace = vec![executed_state::scalar(json!([])), executed_state::scalar(json!([]))]; + + assert_eq!(actual_trace, expected_trace); + assert!(result.next_peer_pks.is_empty()); +} diff --git a/air/tests/test_module/integration/flattening.rs b/air/tests/test_module/integration/flattening.rs index 4b2a2c27..f46b83e2 100644 --- a/air/tests/test_module/integration/flattening.rs +++ b/air/tests/test_module/integration/flattening.rs @@ -15,6 +15,7 @@ */ use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::set_variable_call_service; use air_test_utils::CallServiceClosure; @@ -96,10 +97,10 @@ fn flattening_scalar_arrays() { set_variable_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(local_vm, "asd", script.clone(), "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", ""); + let result = checked_call_vm!(local_vm, "asd", script.clone(), "", result.data); - assert_eq!(res.ret_code, 0); + assert_eq!(result.ret_code, 0); assert_eq!( closure_call_args.service_id_var, Rc::new(RefCell::new("local_service_id".to_string())) @@ -112,6 +113,7 @@ fn flattening_scalar_arrays() { } #[test] +#[ignore] fn flattening_streams() { let stream_value = json!( {"peer_id" : "local_peer_id", "service_id": "local_service_id", "function_name": "local_function_name", "args": [0, 1]} @@ -146,10 +148,10 @@ fn flattening_streams() { set_variable_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(local_vm, "asd", script.clone(), "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", ""); + let result = checked_call_vm!(local_vm, "asd", script.clone(), "", result.data); - assert_eq!(res.ret_code, 0); + assert_eq!(result.ret_code, 0); assert_eq!( closure_call_args.service_id_var, Rc::new(RefCell::new("local_service_id".to_string())) @@ -185,14 +187,15 @@ fn flattening_empty_values() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(local_vm, "asd", script.clone(), "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", ""); + let result = checked_call_vm!(local_vm, "asd", script.clone(), "", result.data); - assert_eq!(res.ret_code, 0); + assert_eq!(result.ret_code, 0); assert_eq!(closure_call_args.args_var, Rc::new(RefCell::new(vec![]))); } #[test] +#[ignore] fn test_handling_non_flattening_values() { let stream_value = json!( {"peer_id" : "local_peer_id", "service_id": "local_service_id", "function_name": "local_function_name", "args": [0, 1]} @@ -227,12 +230,12 @@ fn test_handling_non_flattening_values() { set_variable_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(local_vm, "asd", script.clone(), "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = call_vm!(local_vm, "asd", &script, "", result.data); - assert_eq!(res.ret_code, 1017); + assert_eq!(result.ret_code, 1017); assert_eq!( - res.error_message, + result.error_message, String::from( r#"jvalue '[{"peer_id":"local_peer_id","service_id":"local_service_id","function_name":"local_function_name","args":[0,1]},{"peer_id":"local_peer_id","service_id":"local_service_id","function_name":"local_function_name","args":[0,1]},{"peer_id":"local_peer_id","service_id":"local_service_id","function_name":"local_function_name","args":[0,1]}]' can't be flattened, to be flattened a jvalue should have an array type and consist of zero or one values"# ) diff --git a/air/tests/test_module/integration/join.rs b/air/tests/test_module/integration/join.rs index 9ff42a05..5154f175 100644 --- a/air/tests/test_module/integration/join.rs +++ b/air/tests/test_module/integration/join.rs @@ -14,10 +14,10 @@ * limitations under the License. */ -use air::execution_trace::ExecutionTrace; -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::executed_state; +use air_test_utils::trace_from_result; use air_test_utils::unit_call_service; use air_test_utils::CallServiceClosure; use air_test_utils::IValue; @@ -45,8 +45,7 @@ fn join_chat() { let mut client_1 = create_avm(unit_call_service(), "A"); let mut client_2 = create_avm(unit_call_service(), "B"); - let script = String::from( - r#" + let script = r#" (seq (call "Relay1" ("identity" "") [] $void1) (seq @@ -65,130 +64,120 @@ fn join_chat() { ) ) ) - "#, - ); + "#; - let client_1_res = call_vm!(client_1, "asd", script.clone(), "[]", "[]"); - - let client_1_actual_trace: ExecutionTrace = - serde_json::from_slice(&client_1_res.data).expect("interpreter should return valid json"); + let client_1_result = checked_call_vm!(client_1, "asd", script, "", ""); + let client_1_actual_trace = trace_from_result(&client_1_result); let client_1_expected_trace = vec![executed_state::request_sent_by("A")]; assert_eq!(client_1_actual_trace, client_1_expected_trace); - assert_eq!(client_1_res.next_peer_pks, vec![String::from("Relay1")]); + assert_eq!(client_1_result.next_peer_pks, vec![String::from("Relay1")]); - let relay_1_res = call_vm!(relay_1, "asd", script.clone(), client_1_res.data, "[]"); - - let relay_1_actual_trace: ExecutionTrace = - serde_json::from_slice(&relay_1_res.data).expect("interpreter should return valid json"); + let relay_1_result = checked_call_vm!(relay_1, "asd", script.clone(), client_1_result.data, ""); + let relay_1_actual_trace = trace_from_result(&relay_1_result); let relay_1_expected_trace = vec![ - executed_state::stream_string("test", "void1"), + executed_state::stream_string("test", 0), executed_state::request_sent_by("Relay1"), ]; assert_eq!(relay_1_actual_trace, relay_1_expected_trace); - assert_eq!(relay_1_res.next_peer_pks, vec![String::from("Remote")]); + assert_eq!(relay_1_result.next_peer_pks, vec![String::from("Remote")]); - let remote_res = call_vm!(remote, "asd", script.clone(), relay_1_res.data, "[]"); - - let remote_actual_trace: ExecutionTrace = - serde_json::from_slice(&remote_res.data).expect("interpreter should return valid json"); + let remote_result = checked_call_vm!(remote, "asd", script.clone(), relay_1_result.data, ""); + let remote_actual_trace = trace_from_result(&remote_result); let remote_expected_trace = vec![ - executed_state::stream_string("test", "void1"), - executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), "void2"), - executed_state::scalar_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]])), + executed_state::stream_string("test", 0), + executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0), + executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])), executed_state::par(1, 2), executed_state::request_sent_by("Remote"), executed_state::par(1, 0), executed_state::request_sent_by("Remote"), ]; - let remote_res_next_peer_pks: HashSet<_> = remote_res.next_peer_pks.iter().map(|s| s.as_str()).collect(); + let remote_result_next_peer_pks: HashSet<_> = remote_result.next_peer_pks.iter().map(|s| s.as_str()).collect(); let next_peer_pks_right = maplit::hashset! { "Relay1", "Relay2", }; assert_eq!(remote_actual_trace, remote_expected_trace); - assert_eq!(remote_res_next_peer_pks, next_peer_pks_right); + assert_eq!(remote_result_next_peer_pks, next_peer_pks_right); - let relay_1_res = call_vm!(relay_1, "asd", script.clone(), remote_res.data.clone(), "[]"); + let relay_1_result = checked_call_vm!(relay_1, "asd", script.clone(), remote_result.data.clone(), ""); + + let relay_1_actual_trace = trace_from_result(&relay_1_result); - let relay_1_actual_trace: ExecutionTrace = - serde_json::from_slice(&relay_1_res.data).expect("interpreter should return valid json"); let relay_1_expected_trace = vec![ - executed_state::stream_string("test", "void1"), - executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), "void2"), - executed_state::scalar_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]])), + executed_state::stream_string("test", 0), + executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0), + executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])), executed_state::par(2, 2), - executed_state::stream_string("test", "void"), + executed_state::stream_string("test", 0), executed_state::request_sent_by("Relay1"), executed_state::par(1, 0), executed_state::request_sent_by("Remote"), ]; assert_eq!(relay_1_actual_trace, relay_1_expected_trace); - assert_eq!(relay_1_res.next_peer_pks, vec![String::from("A")]); + assert_eq!(relay_1_result.next_peer_pks, vec![String::from("A")]); - let client_1_res = call_vm!(client_1, "asd", script.clone(), relay_1_res.data, "[]"); + let client_1_result = checked_call_vm!(client_1, "asd", script.clone(), relay_1_result.data, ""); - let client_1_actual_trace: ExecutionTrace = - serde_json::from_slice(&client_1_res.data).expect("interpreter should return valid json"); + let client_1_actual_trace = trace_from_result(&client_1_result); let client_1_expected_trace = vec![ - executed_state::stream_string("test", "void1"), - executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), "void2"), - executed_state::scalar_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]])), + executed_state::stream_string("test", 0), + executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0), + executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])), executed_state::par(2, 2), - executed_state::stream_string("test", "void"), - executed_state::stream_string("test", "void3"), + executed_state::stream_string("test", 0), + executed_state::stream_string("test", 0), executed_state::par(1, 0), executed_state::request_sent_by("Remote"), ]; assert_eq!(client_1_actual_trace, client_1_expected_trace); - assert_eq!(client_1_res.next_peer_pks, Vec::::new()); + assert_eq!(client_1_result.next_peer_pks, Vec::::new()); - let relay_2_res = call_vm!(relay_2, "asd", script.clone(), remote_res.data, "[]"); + let relay_2_result = checked_call_vm!(relay_2, "asd", script.clone(), remote_result.data, ""); - let relay_2_actual_trace: ExecutionTrace = - serde_json::from_slice(&relay_2_res.data).expect("interpreter should return valid json"); + let relay_2_actual_trace = trace_from_result(&relay_2_result); let relay_2_expected_trace = vec![ - executed_state::stream_string("test", "void1"), - executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), "void2"), - executed_state::scalar_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]])), + executed_state::stream_string("test", 0), + executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0), + executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])), executed_state::par(1, 3), executed_state::request_sent_by("Remote"), executed_state::par(2, 0), - executed_state::stream_string("test", "void"), + executed_state::stream_string("test", 0), executed_state::request_sent_by("Relay2"), ]; assert_eq!(relay_2_actual_trace, relay_2_expected_trace); - assert_eq!(relay_2_res.next_peer_pks, vec![String::from("B")]); + assert_eq!(relay_2_result.next_peer_pks, vec![String::from("B")]); - let client_2_res = call_vm!(client_2, "asd", script, relay_2_res.data, "[]"); + let client_2_result = checked_call_vm!(client_2, "asd", script, relay_2_result.data, ""); - let client_2_actual_trace: ExecutionTrace = - serde_json::from_slice(&client_2_res.data).expect("interpreter should return valid json"); + let client_2_actual_trace = trace_from_result(&client_2_result); let client_2_expected_trace = vec![ - executed_state::stream_string("test", "void1"), - executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), "void2"), - executed_state::scalar_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]])), + executed_state::stream_string("test", 0), + executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0), + executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])), executed_state::par(1, 3), executed_state::request_sent_by("Remote"), executed_state::par(2, 0), - executed_state::stream_string("test", "void"), - executed_state::stream_string("test", "void3"), + executed_state::stream_string("test", 0), + executed_state::stream_string("test", 0), ]; assert_eq!(client_2_actual_trace, client_2_expected_trace); - assert_eq!(client_2_res.next_peer_pks, Vec::::new()); + assert_eq!(client_2_result.next_peer_pks, Vec::::new()); } #[test] @@ -203,8 +192,7 @@ fn join() { let mut remote = create_avm(members_call_service1, "Remote"); let mut client_1 = create_avm(unit_call_service(), "A"); - let script = String::from( - r#" + let script = r#" (seq (call "Relay1" ("identity" "") [] $void1) (seq @@ -220,31 +208,29 @@ fn join() { ) ) ) - "#, - ); + "#; - let client_1_res = call_vm!(client_1, "asd", script.clone(), "[]", "[]"); - let relay_1_res = call_vm!(relay_1, "asd", script.clone(), client_1_res.data, "[]"); - let remote_res = call_vm!(remote, "asd", script.clone(), relay_1_res.data, "[]"); - let relay_1_res = call_vm!(relay_1, "asd", script.clone(), remote_res.data, "[]"); - let client_1_res = call_vm!(client_1, "asd", script, relay_1_res.data, "[]"); + let client_1_result = checked_call_vm!(client_1, "asd", script, "", ""); + let relay_1_result = checked_call_vm!(relay_1, "asd", script, client_1_result.data, ""); + let remote_result = checked_call_vm!(remote, "asd", script, relay_1_result.data, ""); + let relay_1_result = checked_call_vm!(relay_1, "asd", script, remote_result.data, ""); + let client_1_result = checked_call_vm!(client_1, "asd", script, relay_1_result.data, ""); - let client_1_actual_trace: ExecutionTrace = - serde_json::from_slice(&client_1_res.data).expect("interpreter should return valid json"); + let client_1_actual_trace = trace_from_result(&client_1_result); let client_1_expected_trace = vec![ - executed_state::stream_string("test", "void1"), - executed_state::scalar_jvalue(json!([["A"], ["B"]])), + executed_state::stream_string("test", 0), + executed_state::scalar(json!([["A"], ["B"]])), executed_state::par(2, 3), - executed_state::stream_string("test", "void"), - executed_state::stream_string("test", "void3"), + executed_state::stream_string("test", 0), + executed_state::stream_string("test", 0), executed_state::par(2, 0), - executed_state::stream_string("test", "void"), - executed_state::stream_string("test", "void3"), + executed_state::stream_string("test", 0), + executed_state::stream_string("test", 0), ]; assert_eq!(client_1_actual_trace, client_1_expected_trace); - assert_eq!(client_1_res.next_peer_pks, Vec::::new()); + assert_eq!(client_1_result.next_peer_pks, Vec::::new()); } #[test] @@ -262,8 +248,7 @@ fn init_peer_id() { let mut client_1 = create_avm(unit_call_service(), "A"); let mut initiator = create_avm(unit_call_service(), initiator_peer_id.clone()); - let script = String::from( - r#"(seq + let script = r#"(seq (seq (call "Relay1" ("identity" "") []) (seq @@ -281,40 +266,20 @@ fn init_peer_id() { ) (call %init_peer_id% ("identity" "") []) ) - "#, - ); + "#; - let initiator_1_res = call_vm!(initiator, initiator_peer_id.clone(), script.clone(), "", ""); - let client_1_res = call_vm!( - client_1, - initiator_peer_id.clone(), - script.clone(), - initiator_1_res.data, - "" - ); - let relay_1_res = call_vm!( - relay_1, - initiator_peer_id.clone(), - script.clone(), - client_1_res.data, - "" - ); - let remote_res = call_vm!(remote, initiator_peer_id.clone(), script.clone(), relay_1_res.data, ""); - let relay_1_res = call_vm!(relay_1, initiator_peer_id.clone(), script.clone(), remote_res.data, ""); - let client_1_res = call_vm!( - client_1, - initiator_peer_id.clone(), - script.clone(), - relay_1_res.data, - "" - ); + let initiator_1_result = checked_call_vm!(initiator, &initiator_peer_id, script, "", ""); + let client_1_result = checked_call_vm!(client_1, &initiator_peer_id, script, initiator_1_result.data, ""); + let relay_1_result = checked_call_vm!(relay_1, &initiator_peer_id, script, client_1_result.data, ""); + let remote_result = checked_call_vm!(remote, &initiator_peer_id, script, relay_1_result.data, ""); + let relay_1_result = checked_call_vm!(relay_1, &initiator_peer_id, script, remote_result.data, ""); + let client_1_result = checked_call_vm!(client_1, &initiator_peer_id, script, relay_1_result.data, ""); - let client_1_actual_trace: ExecutionTrace = - serde_json::from_slice(&client_1_res.data).expect("interpreter should return valid json"); + let client_1_actual_trace = trace_from_result(&client_1_result); let client_1_expected_trace = vec![ executed_state::scalar_string("test"), - executed_state::scalar_jvalue(json!([["A"], ["B"]])), + executed_state::scalar(json!([["A"], ["B"]])), executed_state::par(2, 3), executed_state::scalar_string("test"), executed_state::scalar_string("test"), @@ -325,16 +290,15 @@ fn init_peer_id() { ]; assert_eq!(client_1_actual_trace, client_1_expected_trace); - assert_eq!(client_1_res.next_peer_pks, vec![initiator_peer_id.clone()]); + assert_eq!(client_1_result.next_peer_pks, vec![initiator_peer_id.clone()]); - let initiator_1_res = call_vm!(initiator, initiator_peer_id, script, client_1_res.data, ""); + let initiator_1_result = checked_call_vm!(initiator, initiator_peer_id, script, client_1_result.data, ""); - let initiator_1_actual_trace: ExecutionTrace = - serde_json::from_slice(&initiator_1_res.data).expect("interpreter should return valid json"); + let initiator_1_actual_trace = trace_from_result(&initiator_1_result); let initiator_1_expected_trace = vec![ executed_state::scalar_string("test"), - executed_state::scalar_jvalue(json!([["A"], ["B"]])), + executed_state::scalar(json!([["A"], ["B"]])), executed_state::par(2, 3), executed_state::scalar_string("test"), executed_state::scalar_string("test"), @@ -345,5 +309,5 @@ fn init_peer_id() { ]; assert_eq!(initiator_1_actual_trace, initiator_1_expected_trace); - assert_eq!(initiator_1_res.next_peer_pks, Vec::::new()); + assert_eq!(initiator_1_result.next_peer_pks, Vec::::new()); } diff --git a/air/tests/test_module/integration/join_behaviour.rs b/air/tests/test_module/integration/join_behaviour.rs index 615dba3d..e3576428 100644 --- a/air/tests/test_module/integration/join_behaviour.rs +++ b/air/tests/test_module/integration/join_behaviour.rs @@ -15,10 +15,11 @@ */ use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::set_variables_call_service; +use air_test_utils::trace_from_result; use air_test_utils::unit_call_service; -use air_test_utils::ExecutionTrace; use serde_json::json; @@ -65,10 +66,10 @@ fn dont_wait_on_json_path() { ); let init_peer_id = "asd"; - let res = call_vm!(set_variable_vm, init_peer_id, &script, "", ""); - let res = call_vm!(local_vm, init_peer_id, script, "", res.data); + let result = checked_call_vm!(set_variable_vm, init_peer_id, &script, "", ""); + let result = checked_call_vm!(local_vm, init_peer_id, script, "", result.data); - assert_eq!(res.next_peer_pks, vec![init_peer_id.to_string()]); + assert_eq!(result.next_peer_pks, vec![init_peer_id.to_string()]); } #[test] @@ -85,11 +86,11 @@ fn wait_on_stream_json_path_by_id() { local_peer_id ); - let res = call_vm!(local_vm, "", non_join_stream_script, "", ""); - let trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let result = checked_call_vm!(local_vm, "", non_join_stream_script, "", ""); + let actual_trace = trace_from_result(&result); - assert_eq!(res.ret_code, 0); - assert_eq!(trace.len(), 3); + assert_eq!(result.ret_code, 0); + assert_eq!(actual_trace.len(), 3); let join_stream_script = format!( r#" @@ -100,11 +101,11 @@ fn wait_on_stream_json_path_by_id() { local_peer_id ); - let res = call_vm!(local_vm, "", join_stream_script, "", ""); - let trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json"); + let result = checked_call_vm!(local_vm, "", join_stream_script, "", ""); + let actual_trace = trace_from_result(&result); - assert_eq!(res.ret_code, 0); - assert_eq!(trace.len(), 2); // par and the first call emit traces, second call doesn't + assert_eq!(result.ret_code, 0); + assert_eq!(actual_trace.len(), 2); // par and the first call emit traces, second call doesn't } #[test] @@ -136,32 +137,38 @@ fn dont_wait_on_json_path_on_scalars() { let script = format!( r#" (seq - (seq - (call "{0}" ("" "") ["array"] array) - (call "{0}" ("" "") ["object"] object) - ) - (par - (call "{1}" ("" "") [array.$.[5]!] auth_result) - (call "{2}" ("" "") [object.$.non_exist_path]) - ) + (call "{0}" ("" "") ["array"] array) + (call "{1}" ("" "") [array.$.[5]!] auth_result) ) "#, - set_variable_peer_id, array_consumer_peer_id, object_consumer_peer_id, + set_variable_peer_id, array_consumer_peer_id, ); let init_peer_id = "asd"; - let res = call_vm!(set_variable_vm, init_peer_id, &script, "", ""); - let array_res = call_vm!(array_consumer, init_peer_id, &script, "", res.data.clone()); - assert_eq!(array_res.ret_code, 1006); + let result = call_vm!(set_variable_vm, init_peer_id, &script, "", ""); + let array_result = call_vm!(array_consumer, init_peer_id, &script, "", result.data.clone()); + assert_eq!(array_result.ret_code, 1006); assert_eq!( - array_res.error_message, + array_result.error_message, r#"variable with path '$.[5]' not found in '[1,2,3,4,5]' with an error: 'json value not set'"# ); - let object_res = call_vm!(object_consumer, init_peer_id, script, "", res.data); - assert_eq!(object_res.ret_code, 1006); + let script = format!( + r#" + (seq + (call "{0}" ("" "") ["object"] object) + (call "{1}" ("" "") [object.$.non_exist_path]) + ) + "#, + set_variable_peer_id, object_consumer_peer_id, + ); + + let init_peer_id = "asd"; + let result = call_vm!(set_variable_vm, init_peer_id, &script, "", ""); + let object_result = call_vm!(object_consumer, init_peer_id, script, "", result.data); + assert_eq!(object_result.ret_code, 1006); assert_eq!( - object_res.error_message, + object_result.error_message, r#"variable with path '$.non_exist_path' not found in '{"err_msg":"","is_authenticated":1,"ret_code":0}' with an error: 'json value not set'"# ); } diff --git a/air/tests/test_module/integration/json_path.rs b/air/tests/test_module/integration/json_path.rs index 4ffd4eb5..94b33ce8 100644 --- a/air/tests/test_module/integration/json_path.rs +++ b/air/tests/test_module/integration/json_path.rs @@ -15,6 +15,7 @@ */ use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::echo_string_call_service; @@ -36,8 +37,8 @@ fn json_path_not_allowed_for_non_objects_and_arrays() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", &script, "", ""); - let res = call_vm!(local_vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let result = call_vm!(local_vm, "asd", script, "", result.data); - assert_eq!(res.ret_code, 1018); + assert_eq!(result.ret_code, 1017); } diff --git a/air/tests/test_module/integration/last_error.rs b/air/tests/test_module/integration/last_error.rs index e08e4db8..2861364a 100644 --- a/air/tests/test_module/integration/last_error.rs +++ b/air/tests/test_module/integration/last_error.rs @@ -16,7 +16,7 @@ use air::LastError; use air::SecurityTetraplet; -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; use air_test_utils::fallible_call_service; use air_test_utils::unit_call_service; @@ -81,9 +81,9 @@ fn last_error_tetraplets() { set_variable_peer_id, fallible_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", script.clone(), "", ""); - let res = call_vm!(fallible_vm, "asd", script.clone(), "", res.data); - let _ = call_vm!(local_vm, "asd", script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", script.clone(), "", ""); + let result = checked_call_vm!(fallible_vm, "asd", script.clone(), "", result.data); + let _ = checked_call_vm!(local_vm, "asd", script, "", result.data); let actual_value = (*args.borrow()).as_ref().unwrap().clone(); assert_eq!( @@ -135,8 +135,8 @@ fn not_clear_last_error_in_match() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", &script, "", ""); - let _ = call_vm!(local_vm, "asd", &script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let _ = checked_call_vm!(local_vm, "asd", &script, "", result.data); let actual_value = (*args.borrow()).as_ref().unwrap().clone(); assert_eq!(actual_value.instruction, ""); @@ -175,8 +175,8 @@ fn not_clear_last_error_in_mismatch() { set_variable_peer_id, local_peer_id ); - let res = call_vm!(set_variable_vm, "asd", &script, "", ""); - let _ = call_vm!(local_vm, "asd", &script, "", res.data); + let result = checked_call_vm!(set_variable_vm, "asd", &script, "", ""); + let _ = checked_call_vm!(local_vm, "asd", &script, "", result.data); let actual_value = (*args.borrow()).as_ref().unwrap().clone(); assert_eq!(actual_value.instruction, ""); @@ -207,8 +207,8 @@ fn track_current_peer_id() { fallible_peer_id, local_peer_id ); - let res = call_vm!(fallible_vm, "asd", &script, "", ""); - let _ = call_vm!(local_vm, "asd", script, "", res.data); + let result = checked_call_vm!(fallible_vm, "asd", &script, "", ""); + let _ = checked_call_vm!(local_vm, "asd", script, "", result.data); let actual_value = (*args.borrow()).as_ref().unwrap().clone(); assert_eq!(actual_value.peer_id, fallible_peer_id); diff --git a/air/tests/test_module/integration/mod.rs b/air/tests/test_module/integration/mod.rs index 1d463394..e683f896 100644 --- a/air/tests/test_module/integration/mod.rs +++ b/air/tests/test_module/integration/mod.rs @@ -15,12 +15,17 @@ */ mod air_basic; +mod ap_with_fold; +mod call_evidence_basic; mod dashboard; mod data_merge; +mod empty_array; mod flattening; mod join; mod join_behaviour; mod json_path; mod last_error; mod network_explore; +mod security_tetraplets; mod streams; +mod streams_early_exit; diff --git a/air/tests/test_module/integration/network_explore.rs b/air/tests/test_module/integration/network_explore.rs index 06962f1a..9369c48f 100644 --- a/air/tests/test_module/integration/network_explore.rs +++ b/air/tests/test_module/integration/network_explore.rs @@ -14,64 +14,110 @@ * limitations under the License. */ -use air_test_utils::call_vm; -use air_test_utils::create_avm; -use air_test_utils::set_variables_call_service; +use air_test_utils::*; use serde_json::json; #[test] fn network_explore() { - let relay_id = String::from("12D3KooWSpr929UacQSTUWQeK7CQPhcW2TSmLrGTdE1NcFWkuCvY"); - let client_id = String::from("12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck"); + let relay_id = "relay_id"; + let client_id = "client_id"; let set_variables_state = maplit::hashmap!( - String::from("relay") => json!(relay_id).to_string(), - String::from("client") => json!(client_id).to_string(), + "relay".to_string() => json!(relay_id).to_string(), + "client".to_string() => json!(client_id).to_string(), ); let client_call_service = set_variables_call_service(set_variables_state); - let mut client = create_avm( - client_call_service, - "12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck", - ); + let mut client = create_avm(client_call_service, client_id); + + let client_1_id = "client_1_id"; + let client_2_id = "client_2_id"; + let client_3_id = "client_3_id"; - let client_1_id = String::from("12D3KooWFX27Tg3cNJkFk3W2iapnyRhwfwdQ4ZiTucsy1Go3MSGL"); - let client_2_id = String::from("12D3KooWGNJoFmCNEHq8NpunB4pZSUh9UBHM53W1NwE7gM8L3RjZ"); let relay_call_service = - air_test_utils::set_variable_call_service(format!(r#"["{}", "{}"]"#, client_1_id, client_2_id)); - let mut relay = create_avm(relay_call_service, relay_id.clone()); + air_test_utils::set_variable_call_service(json!([client_1_id, client_2_id, client_3_id, relay_id]).to_string()); + let mut relay = create_avm(relay_call_service, relay_id); let client_1_call_service = - air_test_utils::set_variable_call_service(format!(r#"["{}", "{}"]"#, relay_id, client_2_id)); - let mut client_1 = create_avm(client_1_call_service, client_1_id.clone()); + air_test_utils::set_variable_call_service(json!([client_1_id, client_3_id, relay_id, client_2_id]).to_string()); + let mut client_1 = create_avm(client_1_call_service, client_1_id); let client_2_call_service = - air_test_utils::set_variable_call_service(format!(r#"["{}", "{}"]"#, relay_id, client_1_id)); - let mut client_2 = create_avm(client_2_call_service, client_2_id.clone()); + air_test_utils::set_variable_call_service(json!([relay_id, client_3_id, client_1_id, client_2_id]).to_string()); + let mut client_2 = create_avm(client_2_call_service, client_2_id); + + let client_3_call_service = + air_test_utils::set_variable_call_service(json!([relay_id, client_3_id, client_1_id, client_2_id]).to_string()); + let mut client_3 = create_avm(client_3_call_service, client_3_id); let script = include_str!("./scripts/network_explore.clj"); - let client_res = call_vm!(client, "", script, "[]", "[]"); - assert_eq!(client_res.next_peer_pks, vec![relay_id.clone()]); + let client_result = checked_call_vm!(client, "", script, "", ""); + assert_next_pks!(&client_result.next_peer_pks, &[relay_id]); - let relay_res = call_vm!(relay, "", script, "", client_res.data); - assert_eq!(relay_res.next_peer_pks, vec![client_1_id.clone()]); + let relay_result = checked_call_vm!(relay, "", script, "", client_result.data.clone()); + assert_next_pks!(&relay_result.next_peer_pks, &[client_1_id]); - let client_1_res = call_vm!(client_1, "", script, "", relay_res.data.clone()); - assert_eq!(client_1_res.next_peer_pks, vec![client_2_id.clone()]); + let client_1_result = checked_call_vm!(client_1, "", script, "", relay_result.data.clone()); + assert_next_pks!(&client_1_result.next_peer_pks, &[client_2_id]); - let client_2_res = call_vm!(client_2, "", script, "", client_1_res.data.clone()); - assert_eq!(client_2_res.next_peer_pks, vec![relay_id.clone()]); + let client_2_result = checked_call_vm!(client_2, "", script, "", client_1_result.data.clone()); + assert_next_pks!(&client_2_result.next_peer_pks, &[client_3_id]); - let relay_res = call_vm!(relay, "", script, relay_res.data, client_2_res.data.clone()); - assert_eq!(relay_res.next_peer_pks, vec![client_2_id.clone()]); + let client_3_result = checked_call_vm!(client_3, "", script, "", client_2_result.data.clone()); + assert_next_pks!(&client_3_result.next_peer_pks, &[relay_id]); - let client_2_res = call_vm!(client_2, "", script, client_2_res.data, relay_res.data.clone()); - assert_eq!(client_2_res.next_peer_pks, vec![relay_id.clone()]); + let relay_result = checked_call_vm!(relay, "", script, relay_result.data, client_3_result.data.clone()); + assert_next_pks!(&relay_result.next_peer_pks, &[client_1_id]); - let relay_res = call_vm!(relay, "", script, relay_res.data, client_2_res.data); - assert_eq!(relay_res.next_peer_pks, vec![client_1_id.clone()]); + let client_1_result = checked_call_vm!(client_1, "", script, client_1_result.data, relay_result.data.clone()); + assert_next_pks!(&client_1_result.next_peer_pks, &[client_3_id]); - let client_1_res = call_vm!(client_1, "", script, client_1_res.data, relay_res.data); - assert_eq!(client_1_res.next_peer_pks, Vec::::new()); + let client_3_result = checked_call_vm!(client_3, "", script, client_3_result.data, client_1_result.data.clone()); + assert_next_pks!(&client_3_result.next_peer_pks, &[relay_id]); + + let relay_result = checked_call_vm!(relay, "", script, relay_result.data, client_3_result.data.clone()); + assert_next_pks!(&relay_result.next_peer_pks, &[client_2_id]); + + let client_2_result = checked_call_vm!(client_2, "", script, client_2_result.data, relay_result.data.clone()); + assert_next_pks!(&client_2_result.next_peer_pks, &[relay_id]); + + let relay_result = checked_call_vm!(relay, "", script, relay_result.data, client_2_result.data.clone()); + assert_next_pks!(&relay_result.next_peer_pks, &[client_3_id]); + + let client_3_result = checked_call_vm!(client_3, "", script, client_3_result.data, relay_result.data.clone()); + assert_next_pks!(&client_3_result.next_peer_pks, &[client_1_id]); + + let client_1_result = checked_call_vm!(client_1, "", script, client_1_result.data, client_3_result.data.clone()); + assert_next_pks!(&client_1_result.next_peer_pks, &[client_2_id]); + + let client_2_result = checked_call_vm!(client_2, "", script, client_2_result.data, client_1_result.data.clone()); + assert_next_pks!(&client_2_result.next_peer_pks, &[relay_id]); + + let relay_result = checked_call_vm!(relay, "", script, relay_result.data, client_2_result.data.clone()); + assert_next_pks!(&relay_result.next_peer_pks, &[client_3_id]); + + let client_3_result = checked_call_vm!(client_3, "", script, client_3_result.data, relay_result.data.clone()); + assert_next_pks!(&client_3_result.next_peer_pks, &[client_1_id]); + + let client_1_result = checked_call_vm!(client_1, "", script, client_1_result.data, client_3_result.data.clone()); + assert_next_pks!(&client_1_result.next_peer_pks, &[client_2_id]); + + let client_2_result = checked_call_vm!(client_2, "", script, client_2_result.data, client_1_result.data.clone()); + assert_next_pks!(&client_2_result.next_peer_pks, &[client_1_id]); + + let client_1_result = checked_call_vm!(client_1, "", script, client_1_result.data, client_2_result.data.clone()); + assert_next_pks!(&client_1_result.next_peer_pks, &[client_2_id]); + + let client_2_result = checked_call_vm!(client_2, "", script, client_2_result.data, client_1_result.data.clone()); + assert_next_pks!(&client_2_result.next_peer_pks, &[client_3_id]); + + let client_3_result = checked_call_vm!(client_3, "", script, client_3_result.data, client_2_result.data.clone()); + assert_next_pks!(&client_3_result.next_peer_pks, &[relay_id]); + + let relay_result = checked_call_vm!(relay, "", script, relay_result.data, client_3_result.data.clone()); + assert_next_pks!(&relay_result.next_peer_pks, &[client_id]); + + let client_result = checked_call_vm!(client, "", script, client_result.data, relay_result.data.clone()); + assert_next_pks!(&client_result.next_peer_pks, &[]); } diff --git a/air/tests/test_module/integration/scripts/fold_early_exit.clj b/air/tests/test_module/integration/scripts/fold_early_exit.clj new file mode 100644 index 00000000..5d76c493 --- /dev/null +++ b/air/tests/test_module/integration/scripts/fold_early_exit.clj @@ -0,0 +1,74 @@ +(seq + (seq + (seq + (seq + (seq + (call "{0}" ("" "") ["stream_1"] stream_1_ingredients) + (call "{0}" ("" "") ["stream_2"] stream_2_ingredients) + ) + (call "{0}" ("" "") ["stream_3"] stream_3_ingredients) + ) + (call "{0}" ("" "") ["stream_4"] stream_4_ingredients) + ) + (seq + (seq + (seq + (fold stream_1_ingredients v1 + (seq + (call "{1}" ("" "") [v1] $stream_1) + (next v1) + ) + ) + (fold stream_2_ingredients v2 + (seq + (call "{1}" ("" "") [v2] $stream_2) + (next v2) + ) + ) + ) + (fold stream_3_ingredients v3 + (seq + (call "{1}" ("" "") [v3] $stream_3) + (next v3) + ) + ) + ) + (fold stream_4_ingredients v4 + (seq + (call "{1}" ("" "") [v4] $stream_4) + (next v4) + ) + ) + ) + ) + (par + (xor + (fold $stream_1 v1 + (seq + (fold $stream_2 v2 + (seq + (seq + (fold $stream_3 v3 + (seq + (fold $stream_4 v4 + (seq + (call "{2}" ("" "") []) + (next v4) + ) + ) + (next v3) + ) + ) + (call "{3}" ("error" "") []) ; will trigger an error + ) + (next v2) + ) + ) + (next v1) + ) + ) + (call "{4}" ("" "") [%last_error%]) + ) + (call "{5}" ("" "") []) + ) + ) diff --git a/air/tests/test_module/integration/scripts/fold_par_early_exit.clj b/air/tests/test_module/integration/scripts/fold_par_early_exit.clj new file mode 100644 index 00000000..f4ba4fec --- /dev/null +++ b/air/tests/test_module/integration/scripts/fold_par_early_exit.clj @@ -0,0 +1,74 @@ +(seq + (seq + (seq + (seq + (seq + (call "{0}" ("" "") ["stream_1"] stream_1_ingredients) + (call "{0}" ("" "") ["stream_2"] stream_2_ingredients) + ) + (call "{0}" ("" "") ["stream_3"] stream_3_ingredients) + ) + (call "{0}" ("" "") ["stream_4"] stream_4_ingredients) + ) + (seq + (seq + (seq + (fold stream_1_ingredients v1 + (seq + (call "{1}" ("" "") [v1] $stream_1) + (next v1) + ) + ) + (fold stream_2_ingredients v2 + (seq + (call "{1}" ("" "") [v2] $stream_2) + (next v2) + ) + ) + ) + (fold stream_3_ingredients v3 + (seq + (call "{1}" ("" "") [v3] $stream_3) + (next v3) + ) + ) + ) + (fold stream_4_ingredients v4 + (seq + (call "{1}" ("" "") [v4] $stream_4) + (next v4) + ) + ) + ) + ) + (par + (xor + (fold $stream_1 v1 + (par + (fold $stream_2 v2 + (par + (par + (fold $stream_3 v3 + (par + (fold $stream_4 v4 + (par + (call "{2}" ("" "") []) + (next v4) + ) + ) + (next v3) + ) + ) + (call "{3}" ("error" "") []) ; will trigger an error + ) + (next v2) + ) + ) + (next v1) + ) + ) + (call "{4}" ("" "") [%last_error%]) + ) + (call "{5}" ("" "") []) + ) + ) diff --git a/air/tests/test_module/integration/scripts/inner_folds_v1.clj b/air/tests/test_module/integration/scripts/inner_folds_v1.clj index 0796fa83..b4646582 100644 --- a/air/tests/test_module/integration/scripts/inner_folds_v1.clj +++ b/air/tests/test_module/integration/scripts/inner_folds_v1.clj @@ -1,33 +1,42 @@ (seq (seq - (call "{0}" ("" "") ["stream1"] stream1) - (call "{0}" ("" "") ["stream2"] stream2) + (call "{0}" ("" "") ["stream_1"] stream_peers_1) + (call "{0}" ("" "") ["stream_2"] stream_peers_2) ) - (fold stream1 v1 - (seq - (fold stream2 v2 - (seq - (call "{1}" ("" "") [v1 v2] stream_out1[]) + (seq + (par + (fold stream_peers_1 v1 + (par + (seq + (call v1 ("" "") [v1] $stream_1) + (call v1 ("" "") [v1] $stream_1) + ) + (next v1) + ) + ) + (fold stream_peers_2 v2 + (par + (seq + (call v2 ("" "") [v2] $stream_2) + (call v2 ("" "") [v2] $stream_2) + ) + (next v2) + ) + ) + ) + (fold $stream_1 v1 + (seq + (fold $stream_2 v2 (seq - (call "{1}" ("" "") [v1 v2] stream_out2[]) (seq - + (call "{1}" ("" "") [v1 v2]) (next v2) - (seq - (call "{1}" ("" "") [v1 v2] stream_out3[]) - (seq - (call "{1}" ("" "") [v1 v2] stream_out4[]) - (seq - (call "{1}" ("" "") [v1 v2] stream_out5[]) - (call "{1}" ("" "") [v1 v2] stream_out6[]) - ) - ) - ) ) + (call "{1}" ("" "") [v1 v2]) ) ) - ) - (next v1) + (next v1) + ) ) - ) + ) ) diff --git a/air/tests/test_module/integration/scripts/network_explore.clj b/air/tests/test_module/integration/scripts/network_explore.clj index cfff62bc..97c849b6 100644 --- a/air/tests/test_module/integration/scripts/network_explore.clj +++ b/air/tests/test_module/integration/scripts/network_explore.clj @@ -1,7 +1,8 @@ +(seq (seq (seq - (call "12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck" ("" "") ["relay"] relay) - (call "12D3KooWEDU1WwGtvHUKpGCaMjhcLWyCUq3MQiRKZBLLFcBVVMck" ("" "") ["client"] client) + (call "client_id" ("" "") ["relay"] relay) + (call "client_id" ("" "") ["client"] client) ) (seq (call relay ("dht" "neighborhood") [relay] neighs_top) @@ -25,4 +26,9 @@ ) ) ) + ) + (seq + (call relay ("op" "identity") []) + (call client ("return" "") [$services $neighs_inner neighs_top]) + ) ) \ No newline at end of file diff --git a/air/tests/test_module/integration/scripts/par_early_exit.clj b/air/tests/test_module/integration/scripts/par_early_exit.clj new file mode 100644 index 00000000..ba204227 --- /dev/null +++ b/air/tests/test_module/integration/scripts/par_early_exit.clj @@ -0,0 +1,31 @@ +(seq + (xor + (seq + (call "{0}" ("" "") []) ;; initiator that should send data to stream generators + (par + (seq + (par + (par + (par + (par + (par + (call "{1}" ("" "") [] $stream) + (call "{2}" ("" "") [] $stream) + ) + (call "{1}" ("" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{3}" ("error" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{3}" ("error" "") [] $stream) + ) + (call "{3}" ("error" "") [] $stream) + ) + ) + (null) + ) + (call "{0}" ("" "") []) ;; this one is needed to check check that sliders switched correctly + ) \ No newline at end of file diff --git a/air/tests/test_module/integration/scripts/stream_fold_merging_v0.clj b/air/tests/test_module/integration/scripts/stream_fold_merging_v0.clj new file mode 100644 index 00000000..0704b308 --- /dev/null +++ b/air/tests/test_module/integration/scripts/stream_fold_merging_v0.clj @@ -0,0 +1,33 @@ +(seq + (seq + (call "{0}" ("" "") []) ;; initiator that should send data to stream generators + (par + (par + (par + (par + (par + (par + (call "{1}" ("" "") [] $stream) + (call "{2}" ("" "") [] $stream) + ) + (call "{1}" ("" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{1}" ("" "") [] $stream) + ) + (call "{2}" ("" "") [] $stream) + ) + ) + (fold $stream v + (seq + (seq + (call "{4}" ("" "") [v]) + (call "{4}" ("" "") [v]) + ) + (next v) + ) + ) + ) diff --git a/air/tests/test_module/integration/scripts/stream_fold_merging_v1.clj b/air/tests/test_module/integration/scripts/stream_fold_merging_v1.clj new file mode 100644 index 00000000..de4e98e4 --- /dev/null +++ b/air/tests/test_module/integration/scripts/stream_fold_merging_v1.clj @@ -0,0 +1,33 @@ +(seq + (seq + (call "{0}" ("" "") []) ;; initiator that should send data to stream generators + (par + (par + (par + (par + (par + (par + (call "{1}" ("" "") [] $stream) + (call "{2}" ("" "") [] $stream) + ) + (call "{1}" ("" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{1}" ("" "") [] $stream) + ) + (call "{2}" ("" "") [] $stream) + ) + ) + (fold $stream v + (seq + (seq + (call "{4}" ("" "") [v]) + (next v) + ) + (call "{4}" ("" "") [v]) + ) + ) + ) diff --git a/air/tests/test_module/integration/scripts/stream_fold_merging_v2.clj b/air/tests/test_module/integration/scripts/stream_fold_merging_v2.clj new file mode 100644 index 00000000..9c687740 --- /dev/null +++ b/air/tests/test_module/integration/scripts/stream_fold_merging_v2.clj @@ -0,0 +1,33 @@ +(seq + (seq + (call "{0}" ("" "") []) ;; initiator that should send data to stream generators + (par + (par + (par + (par + (par + (par + (call "{1}" ("" "") [] $stream) + (call "{2}" ("" "") [] $stream) + ) + (call "{1}" ("" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{3}" ("" "") [] $stream) + ) + (call "{1}" ("" "") [] $stream) + ) + (call "{2}" ("" "") [] $stream) + ) + ) + (fold $stream v + (seq + (seq + (next v) + (call "{4}" ("" "") [v]) + ) + (call "{4}" ("" "") [v]) + ) + ) + ) diff --git a/air/tests/test_module/integration/security_tetraplets.rs b/air/tests/test_module/integration/security_tetraplets.rs new file mode 100644 index 00000000..cacca7f9 --- /dev/null +++ b/air/tests/test_module/integration/security_tetraplets.rs @@ -0,0 +1,309 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use air::ResolvedTriplet; +use air::SecurityTetraplet; +use air_test_utils::checked_call_vm; +use air_test_utils::create_avm; +use air_test_utils::executed_state; +use air_test_utils::CallServiceClosure; +use air_test_utils::IValue; +use air_test_utils::NEVec; + +use std::cell::RefCell; +use std::rc::Rc; + +type ArgTetraplets = Vec>; + +fn arg_host_function() -> (CallServiceClosure, Rc>) { + let arg_tetraplets = Rc::new(RefCell::new(ArgTetraplets::new())); + + let arg_tetraplets_inner = arg_tetraplets.clone(); + let host_function: CallServiceClosure = Box::new(move |args| -> Option { + let tetraplets = match &args.function_args[3] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let de_tetraplets: ArgTetraplets = + serde_json::from_str(tetraplets).expect("json deserialization shouldn't fail"); + *arg_tetraplets_inner.borrow_mut() = de_tetraplets; + + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(tetraplets.clone())]).unwrap(), + )) + }); + + (host_function, arg_tetraplets) +} + +#[test] +fn simple_fold() { + let return_numbers_call_service: CallServiceClosure = Box::new(|_| -> Option { + Some(IValue::Record( + NEVec::new(vec![ + IValue::S32(0), + IValue::String(String::from( + "[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]", + )), + ]) + .unwrap(), + )) + }); + + let set_variable_vm_peer_id = String::from("some_peer_id_1"); + let mut set_variable_vm = create_avm(return_numbers_call_service, set_variable_vm_peer_id.clone()); + + let mut client_vms = Vec::new(); + for i in 1..=10 { + let (arg_host_func, arg_tetraplets) = arg_host_function(); + let vm = create_avm(arg_host_func, i.to_string()); + client_vms.push((vm, arg_tetraplets)) + } + + let service_id = String::from("some_service_id"); + let function_name = String::from("some_function_name"); + let script = format!( + r#" + (seq + (call "{}" ("{}" "{}") [] IterableResultPeer1) + (fold IterableResultPeer1 i + (par + (call i ("local_service_id" "local_fn_name") [i "some_text_literal"] $acc) + (next i) + ) + ) + ) + "#, + set_variable_vm_peer_id, service_id, function_name + ); + + let init_peer_id = String::from("some_init_peer_id"); + let result = checked_call_vm!(set_variable_vm, init_peer_id.clone(), script.clone(), "", ""); + let mut data = result.data; + + let first_arg_triplet = ResolvedTriplet { + peer_pk: set_variable_vm_peer_id, + service_id, + function_name, + }; + let first_arg_tetraplet = SecurityTetraplet { + triplet: first_arg_triplet, + json_path: String::new(), + }; + + let second_arg_triplet = ResolvedTriplet { + peer_pk: init_peer_id.clone(), + service_id: String::new(), + function_name: String::new(), + }; + let second_arg_tetraplet = SecurityTetraplet { + triplet: second_arg_triplet, + json_path: String::new(), + }; + + let expected_tetraplets = vec![vec![first_arg_tetraplet], vec![second_arg_tetraplet]]; + let expected_tetraplets = Rc::new(RefCell::new(expected_tetraplets)); + for i in 0..10 { + let result = checked_call_vm!(client_vms[i].0, init_peer_id.clone(), script.clone(), "", data); + data = result.data; + + assert_eq!(client_vms[i].1, expected_tetraplets); + } +} + +#[test] +fn fold_json_path() { + let return_numbers_call_service: CallServiceClosure = Box::new(|_| -> Option { + Some(IValue::Record( + NEVec::new(vec![ + IValue::S32(0), + IValue::String(String::from( + "{\"arg\": [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"]}", + )), + ]) + .unwrap(), + )) + }); + + let set_variable_vm_peer_id = String::from("some_peer_id_1"); + let mut set_variable_vm = create_avm(return_numbers_call_service, set_variable_vm_peer_id.clone()); + + let (arg_host_func, arg_tetraplets) = arg_host_function(); + let client_peer_id = String::from("client_id"); + let mut client_vm = create_avm(arg_host_func, client_peer_id.clone()); + + let service_id = String::from("some_service_id"); + let function_name = String::from("some_function_name"); + let script = format!( + r#" + (seq + (call "{}" ("{}" "{}") [] IterableResultPeer1) + (fold IterableResultPeer1.$.arg i + (par + (call "{}" ("local_service_id" "local_fn_name") [i "some_text_literal"] $acc) + (next i) + ) + ) + ) + "#, + set_variable_vm_peer_id, service_id, function_name, client_peer_id + ); + + let init_peer_id = String::from("some_init_peer_id"); + let result = checked_call_vm!(set_variable_vm, init_peer_id.clone(), script.clone(), "", ""); + + let first_arg_triplet = ResolvedTriplet { + peer_pk: set_variable_vm_peer_id, + service_id, + function_name, + }; + let first_arg_tetraplet = SecurityTetraplet { + triplet: first_arg_triplet, + json_path: String::from("$.arg"), + }; + + let second_arg_triplet = ResolvedTriplet { + peer_pk: init_peer_id.clone(), + service_id: String::new(), + function_name: String::new(), + }; + let second_arg_tetraplet = SecurityTetraplet { + triplet: second_arg_triplet, + json_path: String::new(), + }; + + let expected_tetraplets = vec![vec![first_arg_tetraplet], vec![second_arg_tetraplet]]; + let expected_tetraplets = Rc::new(RefCell::new(expected_tetraplets)); + checked_call_vm!(client_vm, init_peer_id.clone(), script.clone(), "", result.data); + assert_eq!(arg_tetraplets, expected_tetraplets); +} + +use fluence_app_service::AppService; +use fluence_app_service::AppServiceConfig; +use fluence_app_service::FaaSConfig; +use fluence_app_service::ModuleDescriptor; + +use air_test_utils::trace_from_result; +use std::path::PathBuf; + +fn construct_service_config(module_name: impl Into) -> AppServiceConfig { + let module_name = module_name.into(); + let module_path = format!("./tests/security_tetraplets/{}/target/wasm32-wasi/debug/", module_name); + + let module_descriptor = ModuleDescriptor { + file_name: module_name.clone() + ".wasm", + import_name: module_name, + ..<_>::default() + }; + + let faas_config = FaaSConfig { + modules_dir: Some(PathBuf::from(module_path)), + modules_config: vec![module_descriptor], + default_modules_config: None, + }; + + let service_base_dir = std::env::temp_dir(); + + let config = AppServiceConfig { + service_base_dir, + faas_config, + }; + + config +} + +#[test] +#[ignore] +fn tetraplet_with_wasm_modules() { + use marine_rs_sdk::CallParameters; + use marine_rs_sdk::SecurityTetraplet as SDKTetraplet; + + let auth_module_name = String::from("auth_module"); + let auth_service_config = construct_service_config(auth_module_name.clone()); + let auth_service = AppService::new(auth_service_config, auth_module_name.clone(), <_>::default()).unwrap(); + + let log_module_name = String::from("log_storage"); + let log_service_config = construct_service_config(log_module_name.clone()); + let log_service = AppService::new(log_service_config, log_module_name.clone(), <_>::default()).unwrap(); + + let services = maplit::hashmap!( + "auth" => auth_service, + "log_storage" => log_service, + ); + let services = Rc::new(RefCell::new(services)); + + let services_inner = services.clone(); + const ADMIN_PEER_PK: &str = "12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE1"; + let host_func: CallServiceClosure = Box::new(move |args| -> Option { + let args = &args.function_args; + + let service_id = match &args[0] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let function_name = match &args[1] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let service_args = match &args[2] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let tetraplets = match &args[3] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let tetraplets: Vec> = serde_json::from_str(tetraplets).unwrap(); + + let mut call_parameters = CallParameters::default(); + call_parameters.init_peer_id = ADMIN_PEER_PK.to_string(); + call_parameters.tetraplets = tetraplets; + + let service_args = serde_json::from_str(service_args).unwrap(); + let mut service = services_inner.borrow_mut(); + let service: &mut AppService = service.get_mut(service_id.as_str()).unwrap(); + + let result = service.call(function_name, service_args, call_parameters).unwrap(); + + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(result.to_string())]).unwrap(), + )) + }); + + let local_peer_id = "local_peer_id"; + let script = format!( + r#" + (seq + (call "{0}" ("auth" "is_authorized") [] auth_result) + (call "{0}" ("log_storage" "delete") [auth_result.$.is_authorized "1"]) + ) + "#, + local_peer_id, + ); + + let mut vm = create_avm(host_func, local_peer_id); + + let result = checked_call_vm!(vm, ADMIN_PEER_PK, script, "", ""); + let actual_trace = trace_from_result(&result); + let expected_state = executed_state::scalar_string("Ok"); + + assert_eq!(actual_trace[1], expected_state) +} diff --git a/air/tests/test_module/integration/security_tetraplets/auth_module/Cargo.lock b/air/tests/test_module/integration/security_tetraplets/auth_module/Cargo.lock index cf5a0182..6d48596f 100644 --- a/air/tests/test_module/integration/security_tetraplets/auth_module/Cargo.lock +++ b/air/tests/test_module/integration/security_tetraplets/auth_module/Cargo.lock @@ -13,8 +13,9 @@ dependencies = [ [[package]] name = "air" -version = "0.9.0" +version = "0.10.0" dependencies = [ + "air-interpreter-data", "air-interpreter-interface", "air-parser", "boolinator", @@ -28,9 +29,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "air-interpreter-data" +version = "0.1.0" +dependencies = [ + "once_cell", + "semver", + "serde", + "serde_json", +] + [[package]] name = "air-interpreter-interface" -version = "0.5.0" +version = "0.5.1" dependencies = [ "fluence", "fluence-it-types", @@ -275,9 +286,9 @@ checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "fluence" -version = "0.6.5" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd1f159a4da6aef89e8e4a0bf061a8031d669d03d9928266942581a52df03f56" +checksum = "88b09e1cd11a51ba4d169db347d009fe41ece2714eef4d5df720343733a1d5a6" dependencies = [ "fluence-sdk-main", "marine-macro", @@ -297,9 +308,9 @@ dependencies = [ [[package]] name = "fluence-sdk-main" -version = "0.6.4" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a5d1ca20ada064379d959a9a82f9c006e4d9388533cf06010186fef6dd583b" +checksum = "68d93cde99e1494e11755a39b93863333397245c9959c774fe3bebd9e4143879" dependencies = [ "log", "marine-macro", @@ -385,9 +396,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jsonpath_lib-fl" -version = "0.2.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e81233a3c2e1f4579f1fdb856eeec115dcb23817374268212ebad691bd53e664" +checksum = "33dcf980221b25366e8f0df601cf0df6ffcc97242cbbe4d139a79a7f0de5107f" dependencies = [ "array_tool", "env_logger", @@ -451,18 +462,18 @@ dependencies = [ [[package]] name = "marine-macro" -version = "0.6.4" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1652b6ac1bbdde9a66c16c8a2f9cd34d005a1f1b211a538c5b28764faa6ef4" +checksum = "f63d927851847cc3dd9e3bd0f10bdeb313859d4822d5b5f650d9d34d461ed419" dependencies = [ "marine-macro-impl", ] [[package]] name = "marine-macro-impl" -version = "0.6.4" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fee75eaf1a97ee9fe2d382c0537c06a79e5d7ab9d81bda6cb263fb8fd1a15a" +checksum = "fb504be4a90e229ab453c7369cc8a9063acec819f3397802eea719cd0a232be1" dependencies = [ "proc-macro2", "quote", @@ -474,9 +485,9 @@ dependencies = [ [[package]] name = "marine-timestamp-macro" -version = "0.6.2" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6feb612ccd3fd39ec3d50c9a1a96885e1fd32d36a92cf674a0fbe6f7c452613" +checksum = "5994c7db5567d21609f2a2e5a40d9d4564f86c17ca35b2d77007152619b9d7fc" dependencies = [ "chrono", "quote", @@ -522,6 +533,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + [[package]] name = "petgraph" version = "0.5.1" @@ -549,7 +566,7 @@ checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d" [[package]] name = "polyplets" -version = "0.1.0" +version = "0.1.1" dependencies = [ "fluence", "serde", @@ -637,6 +654,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "semver" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f3aac57ee7f3272d8395c6e4f502f434f0e289fcd62876f70daa008c20dcabe" +dependencies = [ + "serde", +] + [[package]] name = "serde" version = "1.0.118" diff --git a/air/tests/test_module/integration/security_tetraplets/log_storage/Cargo.lock b/air/tests/test_module/integration/security_tetraplets/log_storage/Cargo.lock index 0434ce08..a5f65aca 100644 --- a/air/tests/test_module/integration/security_tetraplets/log_storage/Cargo.lock +++ b/air/tests/test_module/integration/security_tetraplets/log_storage/Cargo.lock @@ -13,8 +13,9 @@ dependencies = [ [[package]] name = "air" -version = "0.9.0" +version = "0.10.0" dependencies = [ + "air-interpreter-data", "air-interpreter-interface", "air-parser", "boolinator", @@ -28,9 +29,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "air-interpreter-data" +version = "0.1.0" +dependencies = [ + "once_cell", + "semver", + "serde", + "serde_json", +] + [[package]] name = "air-interpreter-interface" -version = "0.5.0" +version = "0.5.1" dependencies = [ "fluence", "fluence-it-types", @@ -267,9 +278,9 @@ checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "fluence" -version = "0.6.5" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd1f159a4da6aef89e8e4a0bf061a8031d669d03d9928266942581a52df03f56" +checksum = "88b09e1cd11a51ba4d169db347d009fe41ece2714eef4d5df720343733a1d5a6" dependencies = [ "fluence-sdk-main", "marine-macro", @@ -289,9 +300,9 @@ dependencies = [ [[package]] name = "fluence-sdk-main" -version = "0.6.4" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a5d1ca20ada064379d959a9a82f9c006e4d9388533cf06010186fef6dd583b" +checksum = "68d93cde99e1494e11755a39b93863333397245c9959c774fe3bebd9e4143879" dependencies = [ "log", "marine-macro", @@ -377,9 +388,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jsonpath_lib-fl" -version = "0.2.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e81233a3c2e1f4579f1fdb856eeec115dcb23817374268212ebad691bd53e664" +checksum = "33dcf980221b25366e8f0df601cf0df6ffcc97242cbbe4d139a79a7f0de5107f" dependencies = [ "array_tool", "env_logger", @@ -451,18 +462,18 @@ dependencies = [ [[package]] name = "marine-macro" -version = "0.6.4" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1652b6ac1bbdde9a66c16c8a2f9cd34d005a1f1b211a538c5b28764faa6ef4" +checksum = "f63d927851847cc3dd9e3bd0f10bdeb313859d4822d5b5f650d9d34d461ed419" dependencies = [ "marine-macro-impl", ] [[package]] name = "marine-macro-impl" -version = "0.6.4" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fee75eaf1a97ee9fe2d382c0537c06a79e5d7ab9d81bda6cb263fb8fd1a15a" +checksum = "fb504be4a90e229ab453c7369cc8a9063acec819f3397802eea719cd0a232be1" dependencies = [ "proc-macro2", "quote", @@ -474,9 +485,9 @@ dependencies = [ [[package]] name = "marine-timestamp-macro" -version = "0.6.2" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6feb612ccd3fd39ec3d50c9a1a96885e1fd32d36a92cf674a0fbe6f7c452613" +checksum = "5994c7db5567d21609f2a2e5a40d9d4564f86c17ca35b2d77007152619b9d7fc" dependencies = [ "chrono", "quote", @@ -522,6 +533,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + [[package]] name = "petgraph" version = "0.5.1" @@ -549,7 +566,7 @@ checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d" [[package]] name = "polyplets" -version = "0.1.0" +version = "0.1.1" dependencies = [ "fluence", "serde", @@ -637,6 +654,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "semver" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f3aac57ee7f3272d8395c6e4f502f434f0e289fcd62876f70daa008c20dcabe" +dependencies = [ + "serde", +] + [[package]] name = "serde" version = "1.0.118" diff --git a/air/tests/test_module/integration/streams.rs b/air/tests/test_module/integration/streams.rs index 8653c528..6e46a6c6 100644 --- a/air/tests/test_module/integration/streams.rs +++ b/air/tests/test_module/integration/streams.rs @@ -14,12 +14,18 @@ * limitations under the License. */ -use air_test_utils::call_vm; +use air_test_utils::checked_call_vm; use air_test_utils::create_avm; +use air_test_utils::executed_state; +use air_test_utils::set_variable_call_service; +use air_test_utils::trace_from_result; +use air_test_utils::unit_call_service; use air_test_utils::CallServiceClosure; use air_test_utils::IValue; use air_test_utils::NEVec; +use air_test_utils::SubTraceDesc; +use serde_json::json; use serde_json::Value as JValue; #[test] @@ -51,5 +57,425 @@ fn empty_stream() { (null) )"#; - let _ = call_vm!(vm, "", script, "", ""); + let _ = checked_call_vm!(vm, "", script, "", ""); +} + +#[test] +fn stream_merging_v0() { + let initiator_id = "initiator_id"; + let setter_1_id = "setter_1"; + let setter_2_id = "setter_2"; + let setter_3_id = "setter_3"; + let executor_id = "stream_executor"; + + let mut initiator = create_avm(unit_call_service(), initiator_id); + let mut setter_1 = create_avm(set_variable_call_service(json!("1").to_string()), setter_1_id); + let mut setter_2 = create_avm(set_variable_call_service(json!("2").to_string()), setter_2_id); + let mut setter_3 = create_avm(set_variable_call_service(json!("3").to_string()), setter_3_id); + let mut executor = create_avm(unit_call_service(), executor_id); + + let script = format!( + include_str!("scripts/stream_fold_merging_v0.clj"), + initiator_id, setter_1_id, setter_2_id, setter_3_id, executor_id + ); + + let initiator_result = checked_call_vm!(initiator, "", &script, "", ""); + let setter_1_res = checked_call_vm!(setter_1, "", &script, "", initiator_result.data.clone()); + let setter_2_res = checked_call_vm!(setter_2, "", &script, "", initiator_result.data.clone()); + let setter_3_res = checked_call_vm!(setter_3, "", &script, "", initiator_result.data); + + let executor_result_1 = checked_call_vm!(executor, "", &script, "", setter_1_res.data); + let actual_trace_1 = trace_from_result(&executor_result_1); + + let test_value = "test"; + let expected_trace_1 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(9, SubTraceDesc::new(17, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(12, SubTraceDesc::new(19, 2), SubTraceDesc::new(21, 0)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_1, expected_trace_1); + + let executor_result_2 = checked_call_vm!(executor, "", &script, executor_result_1.data.clone(), setter_2_res.data); + let actual_trace_2 = trace_from_result(&executor_result_2); + + let expected_trace_2 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(9, SubTraceDesc::new(17, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(12, SubTraceDesc::new(19, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(8, SubTraceDesc::new(21, 2), SubTraceDesc::new(25, 0)), + executed_state::subtrace_lore(13, SubTraceDesc::new(23, 2), SubTraceDesc::new(25, 0)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_2, expected_trace_2); + + let executor_result_3 = checked_call_vm!(executor, "", &script, executor_result_2.data.clone(), setter_3_res.data); + let actual_trace_3 = trace_from_result(&executor_result_3); + + let expected_trace_3 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("3", 2), + executed_state::stream_string("3", 2), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(9, SubTraceDesc::new(17, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(12, SubTraceDesc::new(19, 2), SubTraceDesc::new(21, 0)), + executed_state::subtrace_lore(8, SubTraceDesc::new(21, 2), SubTraceDesc::new(25, 0)), + executed_state::subtrace_lore(13, SubTraceDesc::new(23, 2), SubTraceDesc::new(25, 0)), + executed_state::subtrace_lore(10, SubTraceDesc::new(25, 2), SubTraceDesc::new(29, 0)), + executed_state::subtrace_lore(11, SubTraceDesc::new(27, 2), SubTraceDesc::new(29, 0)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_3, expected_trace_3); +} + +#[test] +fn stream_merging_v1() { + let initiator_id = "initiator_id"; + let setter_1_id = "setter_1"; + let setter_2_id = "setter_2"; + let setter_3_id = "setter_3"; + let executor_id = "stream_executor"; + + let mut initiator = create_avm(unit_call_service(), initiator_id); + let mut setter_1 = create_avm(set_variable_call_service(json!("1").to_string()), setter_1_id); + let mut setter_2 = create_avm(set_variable_call_service(json!("2").to_string()), setter_2_id); + let mut setter_3 = create_avm(set_variable_call_service(json!("3").to_string()), setter_3_id); + let mut executor = create_avm(unit_call_service(), executor_id); + + let script = format!( + include_str!("scripts/stream_fold_merging_v1.clj"), + initiator_id, setter_1_id, setter_2_id, setter_3_id, executor_id + ); + + let initiator_result = checked_call_vm!(initiator, "", &script, "", ""); + let setter_1_res = checked_call_vm!(setter_1, "", &script, "", initiator_result.data.clone()); + let setter_2_res = checked_call_vm!(setter_2, "", &script, "", initiator_result.data.clone()); + let setter_3_res = checked_call_vm!(setter_3, "", &script, "", initiator_result.data); + + let executor_result_1 = checked_call_vm!(executor, "", &script, "", setter_1_res.data); + let actual_trace_1 = trace_from_result(&executor_result_1); + + let test_value = "test"; + let expected_trace_1 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 1), SubTraceDesc::new(20, 1)), + executed_state::subtrace_lore(9, SubTraceDesc::new(16, 1), SubTraceDesc::new(19, 1)), + executed_state::subtrace_lore(12, SubTraceDesc::new(17, 1), SubTraceDesc::new(18, 1)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_1, expected_trace_1); + + let executor_result_2 = checked_call_vm!(executor, "", &script, executor_result_1.data.clone(), setter_2_res.data); + let actual_trace_2 = trace_from_result(&executor_result_2); + + let expected_trace_2 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 1), SubTraceDesc::new(20, 1)), + executed_state::subtrace_lore(9, SubTraceDesc::new(16, 1), SubTraceDesc::new(19, 1)), + executed_state::subtrace_lore(12, SubTraceDesc::new(17, 1), SubTraceDesc::new(18, 1)), + executed_state::subtrace_lore(8, SubTraceDesc::new(21, 1), SubTraceDesc::new(24, 1)), + executed_state::subtrace_lore(13, SubTraceDesc::new(22, 1), SubTraceDesc::new(23, 1)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_2, expected_trace_2); + + let executor_result_3 = checked_call_vm!(executor, "", &script, executor_result_2.data.clone(), setter_3_res.data); + let actual_trace_3 = trace_from_result(&executor_result_3); + + let expected_trace_3 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("3", 2), + executed_state::stream_string("3", 2), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 1), SubTraceDesc::new(20, 1)), + executed_state::subtrace_lore(9, SubTraceDesc::new(16, 1), SubTraceDesc::new(19, 1)), + executed_state::subtrace_lore(12, SubTraceDesc::new(17, 1), SubTraceDesc::new(18, 1)), + executed_state::subtrace_lore(8, SubTraceDesc::new(21, 1), SubTraceDesc::new(24, 1)), + executed_state::subtrace_lore(13, SubTraceDesc::new(22, 1), SubTraceDesc::new(23, 1)), + executed_state::subtrace_lore(10, SubTraceDesc::new(25, 1), SubTraceDesc::new(28, 1)), + executed_state::subtrace_lore(11, SubTraceDesc::new(26, 1), SubTraceDesc::new(27, 1)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_3, expected_trace_3); +} + +#[test] +fn stream_merging_v2() { + let initiator_id = "initiator_id"; + let setter_1_id = "setter_1"; + let setter_2_id = "setter_2"; + let setter_3_id = "setter_3"; + let executor_id = "stream_executor"; + + let mut initiator = create_avm(unit_call_service(), initiator_id); + let mut setter_1 = create_avm(set_variable_call_service(json!("1").to_string()), setter_1_id); + let mut setter_2 = create_avm(set_variable_call_service(json!("2").to_string()), setter_2_id); + let mut setter_3 = create_avm(set_variable_call_service(json!("3").to_string()), setter_3_id); + let mut executor = create_avm(unit_call_service(), executor_id); + + let script = format!( + include_str!("scripts/stream_fold_merging_v2.clj"), + initiator_id, setter_1_id, setter_2_id, setter_3_id, executor_id + ); + + let initiator_result = checked_call_vm!(initiator, "", &script, "", ""); + let setter_1_res = checked_call_vm!(setter_1, "", &script, "", initiator_result.data.clone()); + let setter_2_res = checked_call_vm!(setter_2, "", &script, "", initiator_result.data.clone()); + let setter_3_res = checked_call_vm!(setter_3, "", &script, "", initiator_result.data); + + let executor_result_1 = checked_call_vm!(executor, "", &script, "", setter_1_res.data); + let actual_trace_1 = trace_from_result(&executor_result_1); + + let test_value = "test"; + let expected_trace_1 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 0), SubTraceDesc::new(19, 2)), + executed_state::subtrace_lore(9, SubTraceDesc::new(15, 0), SubTraceDesc::new(17, 2)), + executed_state::subtrace_lore(12, SubTraceDesc::new(15, 0), SubTraceDesc::new(15, 2)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_1, expected_trace_1); + + let executor_result_2 = checked_call_vm!(executor, "", &script, executor_result_1.data.clone(), setter_2_res.data); + let actual_trace_2 = trace_from_result(&executor_result_2); + + let expected_trace_2 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::stream_string("1", 0), + executed_state::request_sent_by(initiator_id), + executed_state::request_sent_by(initiator_id), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 0), SubTraceDesc::new(19, 2)), + executed_state::subtrace_lore(9, SubTraceDesc::new(15, 0), SubTraceDesc::new(17, 2)), + executed_state::subtrace_lore(12, SubTraceDesc::new(15, 0), SubTraceDesc::new(15, 2)), + executed_state::subtrace_lore(8, SubTraceDesc::new(21, 0), SubTraceDesc::new(23, 2)), + executed_state::subtrace_lore(13, SubTraceDesc::new(21, 0), SubTraceDesc::new(21, 2)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_2, expected_trace_2); + + let executor_result_3 = checked_call_vm!(executor, "", &script, executor_result_2.data.clone(), setter_3_res.data); + let actual_trace_3 = trace_from_result(&executor_result_3); + + let expected_trace_3 = vec![ + executed_state::scalar_string(test_value), + executed_state::par(11, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("3", 2), + executed_state::stream_string("3", 2), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(7, SubTraceDesc::new(15, 0), SubTraceDesc::new(19, 2)), + executed_state::subtrace_lore(9, SubTraceDesc::new(15, 0), SubTraceDesc::new(17, 2)), + executed_state::subtrace_lore(12, SubTraceDesc::new(15, 0), SubTraceDesc::new(15, 2)), + executed_state::subtrace_lore(8, SubTraceDesc::new(21, 0), SubTraceDesc::new(23, 2)), + executed_state::subtrace_lore(13, SubTraceDesc::new(21, 0), SubTraceDesc::new(21, 2)), + executed_state::subtrace_lore(10, SubTraceDesc::new(25, 0), SubTraceDesc::new(27, 2)), + executed_state::subtrace_lore(11, SubTraceDesc::new(25, 0), SubTraceDesc::new(25, 2)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + assert_eq!(actual_trace_3, expected_trace_3); } diff --git a/air/tests/test_module/integration/streams_early_exit.rs b/air/tests/test_module/integration/streams_early_exit.rs new file mode 100644 index 00000000..d0bbfcaa --- /dev/null +++ b/air/tests/test_module/integration/streams_early_exit.rs @@ -0,0 +1,306 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use air_test_utils::*; + +use pretty_assertions::assert_eq; +use serde_json::json; + +#[test] +fn par_early_exit() { + let init_peer_id = "init_peer_id"; + let setter_1_id = "setter_1"; + let setter_2_id = "setter_2"; + let setter_3_id = "setter_3"; + + let mut init = create_avm(unit_call_service(), init_peer_id); + let mut setter_1 = create_avm(set_variable_call_service(json!("1").to_string()), setter_1_id); + let mut setter_2 = create_avm(set_variable_call_service(json!("2").to_string()), setter_2_id); + let mut setter_3 = create_avm(fallible_call_service("error"), setter_3_id); + + let script = format!( + include_str!("scripts/par_early_exit.clj"), + init_peer_id, setter_1_id, setter_2_id, setter_3_id + ); + + let init_result_1 = checked_call_vm!(init, "", &script, "", ""); + let setter_1_res = checked_call_vm!(setter_1, "", &script, "", init_result_1.data.clone()); + let setter_2_res = checked_call_vm!(setter_2, "", &script, "", init_result_1.data.clone()); + let setter_3_res = checked_call_vm!(setter_3, "", &script, "", init_result_1.data.clone()); + let actual_trace_3 = trace_from_result(&setter_3_res); + + let expected_trace = vec![ + executed_state::scalar_string("test"), + executed_state::par(12, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::request_sent_by(init_peer_id), + executed_state::request_sent_by(init_peer_id), + executed_state::request_sent_by(init_peer_id), + executed_state::stream_string("res", 0), + executed_state::service_failed(1, "error"), + executed_state::stream_string("res", 0), + executed_state::service_failed(1, "error"), + executed_state::service_failed(1, "error"), + executed_state::request_sent_by(setter_3_id), + ]; + assert_eq!(actual_trace_3, expected_trace); + + let init_result_2 = checked_call_vm!(init, "", &script, init_result_1.data.clone(), setter_1_res.data.clone()); + let init_result_3 = checked_call_vm!(init, "", &script, init_result_2.data.clone(), setter_2_res.data.clone()); + let init_result_4 = checked_call_vm!(init, "", &script, init_result_3.data.clone(), setter_3_res.data.clone()); + let actual_trace_4 = trace_from_result(&init_result_4); + + let expected_trace = vec![ + executed_state::scalar_string("test"), + executed_state::par(12, 1), + executed_state::par(9, 1), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("2", 1), + executed_state::stream_string("1", 0), + executed_state::stream_string("res", 2), + executed_state::service_failed(1, "error"), + executed_state::stream_string("res", 2), + executed_state::service_failed(1, "error"), + executed_state::service_failed(1, "error"), + executed_state::scalar_string("test"), + ]; + assert_eq!(actual_trace_4, expected_trace); + + let setter_3_malicious_trace = vec![ + executed_state::scalar_string("test"), + executed_state::par(10, 0), + executed_state::par(9, 0), + executed_state::par(7, 1), + executed_state::par(5, 1), + executed_state::par(3, 1), + executed_state::par(1, 1), + executed_state::request_sent_by(init_peer_id), + executed_state::request_sent_by(init_peer_id), + executed_state::stream_string("non_exist_value", 0), + executed_state::stream_string("res", 0), + executed_state::service_failed(1, "error"), + executed_state::request_sent_by(setter_3_id), + ]; + let setter_3_malicious_data = raw_data_from_trace(setter_3_malicious_trace); + let init_result_5 = call_vm!(init, "", &script, init_result_3.data.clone(), setter_3_malicious_data); + assert_eq!(init_result_5.ret_code, 1021); + + let actual_trace = trace_from_result(&init_result_5); + let expected_trace = trace_from_result(&init_result_3); + assert_eq!(actual_trace, expected_trace); +} + +#[test] +fn fold_early_exit() { + let variables_setter_id = "set_variable_id"; + let stream_setter_id = "stream_setter_id"; + let fold_executor_id = "fold_executor_id"; + let error_trigger_id = "error_trigger_id"; + let last_error_receiver_id = "last_error_receiver_id"; + let last_peer_checker_id = "last_peer_checker_id"; + + let variables = maplit::hashmap!( + "stream_1".to_string() => json!(["a1", "a2"]).to_string(), + "stream_2".to_string() => json!(["b1", "b2"]).to_string(), + "stream_3".to_string() => json!(["c1", "c2"]).to_string(), + "stream_4".to_string() => json!(["d1", "d2"]).to_string(), + ); + + let mut variables_setter = create_avm(set_variables_call_service(variables), variables_setter_id); + let mut stream_setter = create_avm(echo_string_call_service(), stream_setter_id); + let mut fold_executor = create_avm(unit_call_service(), fold_executor_id); + let mut error_trigger = create_avm(fallible_call_service("error"), error_trigger_id); + let mut last_error_receiver = create_avm(unit_call_service(), last_error_receiver_id); + let mut last_peer_checker = create_avm(unit_call_service(), last_peer_checker_id); + + let script = format!( + include_str!("scripts/fold_early_exit.clj"), + variables_setter_id, + stream_setter_id, + fold_executor_id, + error_trigger_id, + last_error_receiver_id, + last_peer_checker_id + ); + + let variables_setter_result = checked_call_vm!(variables_setter, "", &script, "", ""); + let stream_setter_result = checked_call_vm!(stream_setter, "", &script, "", variables_setter_result.data); + let fold_executor_result = checked_call_vm!(fold_executor, "", &script, "", stream_setter_result.data); + let error_trigger_result = checked_call_vm!(error_trigger, "", &script, "", fold_executor_result.data); + let last_error_receiver_result = checked_call_vm!(last_error_receiver, "", &script, "", error_trigger_result.data); + let last_peer_checker_result = + checked_call_vm!(last_peer_checker, "", &script, "", last_error_receiver_result.data); + let actual_trace = trace_from_result(&last_peer_checker_result); + + let test_value = "test"; + let expected_trace = vec![ + executed_state::scalar_string_array(vec!["a1", "a2"]), + executed_state::scalar_string_array(vec!["b1", "b2"]), + executed_state::scalar_string_array(vec!["c1", "c2"]), + executed_state::scalar_string_array(vec!["d1", "d2"]), + executed_state::stream_string("a1", 0), + executed_state::stream_string("a2", 0), + executed_state::stream_string("b1", 0), + executed_state::stream_string("b2", 0), + executed_state::stream_string("c1", 0), + executed_state::stream_string("c2", 0), + executed_state::stream_string("d1", 0), + executed_state::stream_string("d2", 0), + executed_state::par(11, 1), + executed_state::fold(vec![executed_state::subtrace_lore( + 4, + SubTraceDesc::new(14, 9), + SubTraceDesc::new(23, 0), + )]), + executed_state::fold(vec![executed_state::subtrace_lore( + 6, + SubTraceDesc::new(15, 8), + SubTraceDesc::new(23, 0), + )]), + executed_state::fold(vec![ + executed_state::subtrace_lore(8, SubTraceDesc::new(16, 3), SubTraceDesc::new(22, 0)), + executed_state::subtrace_lore(9, SubTraceDesc::new(19, 3), SubTraceDesc::new(22, 0)), + ]), + executed_state::fold(vec![ + executed_state::subtrace_lore(10, SubTraceDesc::new(17, 1), SubTraceDesc::new(19, 0)), + executed_state::subtrace_lore(11, SubTraceDesc::new(18, 1), SubTraceDesc::new(19, 0)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::fold(vec![ + executed_state::subtrace_lore(10, SubTraceDesc::new(20, 1), SubTraceDesc::new(22, 0)), + executed_state::subtrace_lore(11, SubTraceDesc::new(21, 1), SubTraceDesc::new(22, 0)), + ]), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + executed_state::service_failed(1, "error"), + executed_state::scalar_string(test_value), + executed_state::scalar_string(test_value), + ]; + + assert_eq!(actual_trace, expected_trace); +} + +#[test] +fn fold_par_early_exit() { + let variables_setter_id = "set_variable_id"; + let stream_setter_id = "stream_setter_id"; + let fold_executor_id = "fold_executor_id"; + let error_trigger_id = "error_trigger_id"; + let last_error_receiver_id = "last_error_receiver_id"; + let last_peer_checker_id = "last_peer_checker_id"; + + let variables = maplit::hashmap!( + "stream_1".to_string() => json!(["a1", "a2"]).to_string(), + "stream_2".to_string() => json!(["b1", "b2"]).to_string(), + "stream_3".to_string() => json!(["c1", "c2"]).to_string(), + "stream_4".to_string() => json!(["d1", "d2"]).to_string(), + ); + + let mut variables_setter = create_avm(set_variables_call_service(variables), variables_setter_id); + let mut stream_setter = create_avm(echo_string_call_service(), stream_setter_id); + let mut fold_executor = create_avm(unit_call_service(), fold_executor_id); + let mut error_trigger = create_avm(fallible_call_service("error"), error_trigger_id); + let mut last_error_receiver = create_avm(unit_call_service(), last_error_receiver_id); + let mut last_peer_checker = create_avm(unit_call_service(), last_peer_checker_id); + + let script = format!( + include_str!("scripts/fold_par_early_exit.clj"), + variables_setter_id, + stream_setter_id, + fold_executor_id, + error_trigger_id, + last_error_receiver_id, + last_peer_checker_id + ); + + let variables_setter_result = checked_call_vm!(variables_setter, "", &script, "", ""); + let stream_setter_result = checked_call_vm!(stream_setter, "", &script, "", variables_setter_result.data); + let fold_executor_result = checked_call_vm!(fold_executor, "", &script, "", stream_setter_result.data); + let error_trigger_result = checked_call_vm!(error_trigger, "", &script, "", fold_executor_result.data); + let last_error_receiver_result = checked_call_vm!(last_error_receiver, "", &script, "", error_trigger_result.data); + let last_peer_checker_result = + checked_call_vm!(last_peer_checker, "", &script, "", last_error_receiver_result.data); + let actual_trace = trace_from_result(&last_peer_checker_result); + + let test_value = "test"; + let expected_trace = vec![ + executed_state::scalar_string_array(vec!["a1", "a2"]), + executed_state::scalar_string_array(vec!["b1", "b2"]), + executed_state::scalar_string_array(vec!["c1", "c2"]), + executed_state::scalar_string_array(vec!["d1", "d2"]), + executed_state::stream_string("a1", 0), + executed_state::stream_string("a2", 0), + executed_state::stream_string("b1", 0), + executed_state::stream_string("b2", 0), + executed_state::stream_string("c1", 0), + executed_state::stream_string("c2", 0), + executed_state::stream_string("d1", 0), + executed_state::stream_string("d2", 0), + executed_state::par(69, 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(4, SubTraceDesc::new(14, 34), SubTraceDesc::new(82, 0)), + executed_state::subtrace_lore(5, SubTraceDesc::new(48, 34), SubTraceDesc::new(82, 0)), + ]), + executed_state::par(33, 34), + executed_state::fold(vec![ + executed_state::subtrace_lore(6, SubTraceDesc::new(16, 16), SubTraceDesc::new(48, 0)), + executed_state::subtrace_lore(7, SubTraceDesc::new(32, 16), SubTraceDesc::new(48, 0)), + ]), + executed_state::par(15, 16), + executed_state::par(13, 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(8, SubTraceDesc::new(19, 6), SubTraceDesc::new(31, 0)), + executed_state::subtrace_lore(9, SubTraceDesc::new(25, 6), SubTraceDesc::new(31, 0)), + ]), + executed_state::par(5, 6), + executed_state::fold(vec![ + executed_state::subtrace_lore(10, SubTraceDesc::new(21, 2), SubTraceDesc::new(25, 0)), + executed_state::subtrace_lore(11, SubTraceDesc::new(23, 2), SubTraceDesc::new(25, 0)), + ]), + executed_state::par(1, 2), + executed_state::scalar_string(test_value), + executed_state::par(1, 0), + executed_state::scalar_string(test_value), + executed_state::par(5, 0), + executed_state::fold(vec![ + executed_state::subtrace_lore(10, SubTraceDesc::new(27, 2), SubTraceDesc::new(31, 0)), + executed_state::subtrace_lore(11, SubTraceDesc::new(29, 2), SubTraceDesc::new(31, 0)), + ]), + executed_state::par(1, 2), + executed_state::scalar_string(test_value), + executed_state::par(1, 0), + executed_state::scalar_string(test_value), + executed_state::service_failed(1, "error"), + executed_state::par(15, 0), + executed_state::par(13, 1), + executed_state::fold(vec![ + executed_state::subtrace_lore(8, SubTraceDesc::new(35, 6), SubTraceDesc::new(47, 0)), + executed_state::subtrace_lore(9, SubTraceDesc::new(41, 6), SubTraceDesc::new(47, 0)), + ]), + ]; + let trace_len = expected_trace.len(); + + assert_eq!(&actual_trace[0..trace_len], expected_trace); +} diff --git a/avm/server/Cargo.toml b/avm/server/Cargo.toml index 7a4a5f03..058267a0 100644 --- a/avm/server/Cargo.toml +++ b/avm/server/Cargo.toml @@ -16,7 +16,7 @@ air-interpreter-interface = { version = "0.6.0", path = "../../crates/interprete thiserror = "1.0.24" maplit = "1.0.2" -serde_json = "1.0.60" +serde_json = "=1.0.61" serde = "=1.0.118" log = "0.4.14" parking_lot = "0.11.1" diff --git a/avm/server/src/avm.rs b/avm/server/src/avm.rs index 0ffb2531..5bbe94e5 100644 --- a/avm/server/src/avm.rs +++ b/avm/server/src/avm.rs @@ -55,7 +55,7 @@ impl DerefMut for SendSafeFaaS { } /// Information about the particle that is being executed by the interpreter at the moment -#[derive(Debug, Default, Clone)] +#[derive(Clone, Debug, Default)] pub struct ParticleParameters { pub init_user_id: String, pub particle_id: String, @@ -169,15 +169,15 @@ impl AVM { } fn prepare_args( - prev_data: Vec, + prev_data: impl Into>, data: impl Into>, - init_user_id: String, + init_user_id: impl Into, air: impl Into, ) -> Vec { vec![ - IValue::String(init_user_id), + IValue::String(init_user_id.into()), IValue::String(air.into()), - IValue::ByteArray(prev_data), + IValue::ByteArray(prev_data.into()), IValue::ByteArray(data.into()), ] } @@ -292,7 +292,7 @@ impl AVM { prev_data: impl Into>, data: impl Into>, ) -> Result { - let args = prepare_args(prev_data.into(), data, init_user_id.into(), air); + let args = prepare_args(prev_data, data, init_user_id, air); let result = self.faas diff --git a/avm/server/src/config.rs b/avm/server/src/config.rs index 5bb9aa4c..fcce77dc 100644 --- a/avm/server/src/config.rs +++ b/avm/server/src/config.rs @@ -30,7 +30,7 @@ pub struct AVMConfig { pub current_peer_id: String, /// Path to a folder contains prev data. - /// AVM uses it to store data obtained after interpreter execution, and load it as a prev_data by particle_id. + /// AVM uses it to store data obtained after interpreter execution_step, and load it as a prev_data by particle_id. pub particle_data_store: PathBuf, /// Path to a directory to store shared directories called Particle File Vault. diff --git a/crates/air-parser/Cargo.toml b/crates/air-parser/Cargo.toml index e980dac7..f9979cfc 100644 --- a/crates/air-parser/Cargo.toml +++ b/crates/air-parser/Cargo.toml @@ -7,13 +7,13 @@ license = "Apache-2.0" publish = false [build-dependencies] -lalrpop = "0.19.5" +lalrpop = "0.19.6" [dependencies] -lalrpop-util = "0.19.5" -regex = "1.4.1" -codespan = "0.9.5" -codespan-reporting = "0.9.5" +lalrpop-util = "0.19.6" +regex = "1.5.4" +codespan = "0.11.1" +codespan-reporting = "0.11.1" multimap = "0.8.3" # TODO: hide serde behind a feature diff --git a/crates/air-parser/src/parser/air.lalrpop b/crates/air-parser/src/parser/air.lalrpop index 7c103522..0fb09423 100644 --- a/crates/air-parser/src/parser/air.lalrpop +++ b/crates/air-parser/src/parser/air.lalrpop @@ -1,12 +1,10 @@ use crate::parser::ast::*; use crate::parser::air_parser::make_flattened_error; +use crate::parser::air_parser::make_stream_iterable_error; use crate::parser::ParserError; use crate::parser::VariableValidator; use crate::parser::Span; use crate::parser::lexer::Token; -use crate::parser::lexer::Number; -use crate::parser::lexer::LastErrorPath; -use crate::parser::lexer::Variable; use lalrpop_util::ErrorRecovery; use std::rc::Rc; @@ -18,7 +16,7 @@ pub AIR = Instr; Instr: Box> = { "(" call ")" => { - let output = output.unwrap_or(CallOutputValue::None); + let output = output.map(CallOutputValue::Variable).unwrap_or(CallOutputValue::None); let args = Rc::new(args); let call = Call { peer_part, function_part, args, output }; let span = Span { left, right }; @@ -27,18 +25,49 @@ Instr: Box> = { Box::new(Instruction::Call(call)) }, + "(" ap ")" => { + if let ApArgument::JsonPath(json_path) = &arg { + if let AstVariable::Stream(_) = &json_path.variable { + let token = Token::VariableWithJsonPath(json_path.variable.clone(), json_path.path, json_path.should_flatten); + errors.push(make_stream_iterable_error(left, token, right)); + }; + + // Due the json path constraints json path should be flattened in a apply arguments. + if !json_path.should_flatten { + let token = Token::VariableWithJsonPath(json_path.variable.clone(), json_path.path, json_path.should_flatten); + errors.push(make_flattened_error(left, token, right)); + } + } + + let apply = Ap::new(arg, res); + let span = Span { left, right }; + validator.met_ap(&apply, span); + + Box::new(Instruction::Ap(apply)) + }, + "(" seq ")" => Box::new(Instruction::Seq(Seq(l, r))), "(" par ")" => Box::new(Instruction::Par(Par(l, r))), "(" null ")" => Box::new(Instruction::Null(Null)), - "(" fold ")" => { + "(" fold ")" => { let instruction = Rc::new(*i); - let fold = Fold { iterable, iterator, instruction }; + let fold = FoldScalar { iterable, iterator, instruction }; let span = Span { left, right }; - validator.met_fold(&fold, span); + validator.met_fold_scalar(&fold, span); - Box::new(Instruction::Fold(fold)) + Box::new(Instruction::FoldScalar(fold)) }, + + "(" fold ")" => { + let instruction = Rc::new(*i); + let fold = FoldStream { stream_name: stream, iterator, instruction }; + let span = Span { left, right }; + validator.met_fold_stream(&fold, span); + + Box::new(Instruction::FoldStream(fold)) + }, + "(" next ")" => { let next = Next(i); let span = Span { left, right }; @@ -82,9 +111,9 @@ PeerPart: PeerPart<'input> = { "(" ")" => PeerPart::PeerPkWithServiceId(pid, sid), } -Output: CallOutputValue<'input> = { - => CallOutputValue::Variable(Variable::Scalar(a)), - => CallOutputValue::Variable(Variable::Stream(s)), +Output: AstVariable<'input> = { + => AstVariable::Scalar(a), + => AstVariable::Stream(s), }; Function = CallInstrValue; @@ -93,18 +122,19 @@ ServiceId = CallInstrValue; CallInstrValue: CallInstrValue<'input> = { => CallInstrValue::Literal(l), - => CallInstrValue::Variable(Variable::Scalar(a)), - => CallInstrValue::Variable(Variable::Stream(s)), - => { + => CallInstrValue::Variable(AstVariable::Scalar(a)), + => CallInstrValue::Variable(AstVariable::Stream(s)), + => { let variable = j.0; let path = j.1; let should_flatten = j.2; // Due the json path constraints json path should be flattened in a call triplet. if !should_flatten { let token = Token::VariableWithJsonPath(variable.clone(), path, should_flatten); - errors.push(make_flattened_error(l, token, r)); + errors.push(make_flattened_error(left, token, right)); } - CallInstrValue::JsonPath { variable, path, should_flatten } + + CallInstrValue::JsonPath(JsonPath::new(variable, path, should_flatten)) }, InitPeerId => CallInstrValue::InitPeerId, } @@ -113,29 +143,56 @@ Arg = CallInstrArgValue; CallInstrArgValue: CallInstrArgValue<'input> = { => CallInstrArgValue::Literal(s), - => CallInstrArgValue::Variable(Variable::Scalar(v)), - => CallInstrArgValue::Variable(Variable::Stream(v)), - => CallInstrArgValue::JsonPath { variable: j.0, path: j.1, should_flatten: j.2 }, + => CallInstrArgValue::Variable(AstVariable::Scalar(v)), + => CallInstrArgValue::Variable(AstVariable::Stream(v)), + => CallInstrArgValue::JsonPath(JsonPath::new(j.0, j.1, j.2)), => CallInstrArgValue::Number(n), => CallInstrArgValue::Boolean(b), InitPeerId => CallInstrArgValue::InitPeerId, + EmptyArray => CallInstrArgValue::EmptyArray, => CallInstrArgValue::LastError(p), } -Iterable: IterableValue<'input> = { - => IterableValue::Variable(Variable::Scalar(v)), - => IterableValue::Variable(Variable::Stream(v)), - => IterableValue::JsonPath { variable: j.0, path: j.1, should_flatten: j.2 }, +ApArgument: ApArgument<'input> = { + => ApArgument::ScalarVariable(a), + => ApArgument::JsonPath(JsonPath::new(j.0, j.1, j.2)), + => ApArgument::Number(n), + => ApArgument::Boolean(b), + EmptyArray => ApArgument::EmptyArray, + => ApArgument::Literal(s), + => ApArgument::LastError(p), +}; + +ScalarIterable: IterableScalarValue<'input> = { + => IterableScalarValue::ScalarVariable(v), + => { + use crate::parser::air::AstVariable::*; + + let variable = j.0; + let path = j.1; + let should_flatten = j.2; + + let scalar_name = match variable { + Stream(name) => { + let token = Token::VariableWithJsonPath(variable, path, should_flatten); + errors.push(make_stream_iterable_error(l, token, r)); + name + } + Scalar(name) => name, + }; + IterableScalarValue::JsonPath { scalar_name, path, should_flatten } + } } Matchable: MatchableValue<'input> = { InitPeerId => MatchableValue::InitPeerId, - => MatchableValue::Variable(Variable::Scalar(v)), - => MatchableValue::Variable(Variable::Stream(v)), + => MatchableValue::Variable(AstVariable::Scalar(v)), + => MatchableValue::Variable(AstVariable::Stream(v)), => MatchableValue::Literal(s), => MatchableValue::Boolean(b), => MatchableValue::Number(n), - => MatchableValue::JsonPath { variable: j.0, path: j.1, should_flatten: j.2 }, + EmptyArray => MatchableValue::EmptyArray, + => MatchableValue::JsonPath(JsonPath::new(j.0, j.1, j.2)), } extern { @@ -147,11 +204,12 @@ extern { ")" => Token::CloseRoundBracket, "[" => Token::OpenSquareBracket, "]" => Token::CloseSquareBracket, + EmptyArray => Token::SquareBrackets, Literal => Token::StringLiteral(<&'input str>), Alphanumeric => Token::Alphanumeric(<&'input str>), Stream => Token::Stream(<&'input str>), - JsonPath => Token::VariableWithJsonPath(>, <&'input str>, ), + JsonPath => Token::VariableWithJsonPath(>, <&'input str>, ), Number => Token::Number(), Boolean => Token::Boolean(), @@ -159,6 +217,7 @@ extern { LastError => Token::LastError(), call => Token::Call, + ap => Token::Ap, seq => Token::Seq, par => Token::Par, null => Token::Null, diff --git a/crates/air-parser/src/parser/air.rs b/crates/air-parser/src/parser/air.rs index 53291ebb..f60bc206 100644 --- a/crates/air-parser/src/parser/air.rs +++ b/crates/air-parser/src/parser/air.rs @@ -1,14 +1,12 @@ // auto-generated: "lalrpop 0.19.6" -// sha3: 829f4457beb3225fd27138a974f7b38a6c5f2b8672caca7dc588d2315ec560 +// sha3: ec6297654486ff6653229497083c43cbdeadeb5545b73675ba8f2f9665737ec use crate::parser::ast::*; use crate::parser::air_parser::make_flattened_error; +use crate::parser::air_parser::make_stream_iterable_error; use crate::parser::ParserError; use crate::parser::VariableValidator; use crate::parser::Span; use crate::parser::lexer::Token; -use crate::parser::lexer::Number; -use crate::parser::lexer::LastErrorPath; -use crate::parser::lexer::Variable; use lalrpop_util::ErrorRecovery; use std::rc::Rc; #[allow(unused_extern_crates)] @@ -24,13 +22,11 @@ mod __parse__AIR { use crate::parser::ast::*; use crate::parser::air_parser::make_flattened_error; + use crate::parser::air_parser::make_stream_iterable_error; use crate::parser::ParserError; use crate::parser::VariableValidator; use crate::parser::Span; use crate::parser::lexer::Token; - use crate::parser::lexer::Number; - use crate::parser::lexer::LastErrorPath; - use crate::parser::lexer::Variable; use lalrpop_util::ErrorRecovery; use std::rc::Rc; #[allow(unused_extern_crates)] @@ -46,7 +42,7 @@ mod __parse__AIR { Variant0(Token<'input>), Variant1(&'input str), Variant2(bool), - Variant3((Variable<'input>, &'input str, bool)), + Variant3((AstVariable<'input>, &'input str, bool)), Variant4(LastErrorPath), Variant5(Number), Variant6(__lalrpop_util::ErrorRecovery, ParserError>), @@ -54,197 +50,230 @@ mod __parse__AIR { Variant8(alloc::vec::Vec>), Variant9(usize), Variant10(Box>), - Variant11(Vec>), - Variant12(CallInstrValue<'input>), - Variant13(FunctionPart<'input>), - Variant14(IterableValue<'input>), + Variant11(ApArgument<'input>), + Variant12(Vec>), + Variant13(CallInstrValue<'input>), + Variant14(FunctionPart<'input>), Variant15(MatchableValue<'input>), - Variant16(CallOutputValue<'input>), - Variant17(core::option::Option>), + Variant16(AstVariable<'input>), + Variant17(core::option::Option>), Variant18(PeerPart<'input>), + Variant19(IterableScalarValue<'input>), } const __ACTION: &[i8] = &[ // State 0 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 1 - 10, 0, 0, 0, 34, 0, 35, 36, 0, 37, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 36, 37, 0, 38, 39, 40, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 2 - 0, 0, 0, 0, 40, 0, 0, 41, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 44, 0, 0, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 3 - 0, 0, 0, 0, 43, 44, 45, 46, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 4 - 0, 0, 0, 0, 43, 44, 45, 46, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 54, 55, 56, 57, 0, 58, 59, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 5 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 0, 0, 53, 54, 55, 56, 57, 0, 58, 59, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 6 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 7 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 8 - 17, 0, 0, 0, 34, 0, 35, 36, 0, 37, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 9 - 0, 0, 0, 0, 34, 0, 35, 36, 0, 37, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 10 - 0, 0, 0, 0, 43, 44, 45, 46, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 44, 0, 0, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 11 - 0, 0, 0, 0, 43, 44, 45, 46, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 44, 0, 0, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 12 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 0, 0, 53, 54, 55, 56, 57, 0, 58, 59, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 13 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 0, 0, 53, 54, 55, 56, 57, 0, 58, 59, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 14 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 15 - 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 16 - 0, 0, 0, 0, 34, 0, 35, 36, 0, 37, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 17 - 0, 0, 0, 0, 34, 0, 35, 36, 0, 37, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 18 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 0, 0, 44, 0, 0, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 19 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 0, 0, 44, 0, 0, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 20 - 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 21 - 0, 67, 0, 0, 68, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 22 - 0, 0, 0, 72, 73, 74, 75, 76, 77, 78, 79, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 23 - 0, 0, 0, 0, 34, 0, 35, 36, 0, 37, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, // State 24 - 0, 0, 0, 88, 73, 74, 75, 76, 77, 78, 79, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 83, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 25 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 26 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 44, 0, 0, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 27 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 30, 31, 6, 7, 8, 0, + 0, 0, 0, 104, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 28 - -38, -38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 29 - 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 30 - 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 33, 34, 7, 8, 9, 0, // State 31 - -53, 0, 0, 0, -53, 0, -53, -53, 0, -53, 0, -53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -48, -48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -48, // State 32 - -54, 0, 0, 0, -54, 0, -54, -54, 0, -54, 0, -54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 33 - -21, -21, -21, 0, -21, 0, -21, -21, 0, -21, 0, -21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 34 - -24, -24, -24, 0, -24, 0, -24, -24, 0, -24, 0, -24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 35 - -23, -23, -23, 0, -23, 0, -23, -23, 0, -23, 0, -23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -12, 0, 0, 0, 0, 0, 0, 0, -12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 36 - -20, -20, -20, 0, -20, 0, -20, -20, 0, -20, 0, -20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 37 - -22, -22, -22, 0, -22, 0, -22, -22, 0, -22, 0, -22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -10, 0, 0, 0, 0, 0, 0, 0, -10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 38 - 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 39 - 0, 0, 0, 0, -39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -14, 0, 0, 0, 0, 0, 0, 0, -14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 40 - 0, 0, 0, 0, -41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 41 - 0, 0, 0, 0, -40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -61, 0, 0, 0, -61, 0, 0, -61, -61, 0, -61, 0, -61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 42 - -43, 0, 0, 0, -43, -43, -43, -43, 0, -43, -43, -43, 0, 0, 0, 0, 0, 0, 0, 0, 0, -43, + -62, 0, 0, 0, -62, 0, 0, -62, -62, 0, -62, 0, -62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 43 - -46, 0, 0, 0, -46, -46, -46, -46, 0, -46, -46, -46, 0, 0, 0, 0, 0, 0, 0, 0, 0, -46, + -29, -29, -29, 0, -29, 0, 0, -29, -29, 0, -29, 0, -29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 44 - -42, 0, 0, 0, -42, -42, -42, -42, 0, -42, -42, -42, 0, 0, 0, 0, 0, 0, 0, 0, 0, -42, + -32, -32, -32, 0, -32, 0, 0, -32, -32, 0, -32, 0, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 45 - -48, 0, 0, 0, -48, -48, -48, -48, 0, -48, -48, -48, 0, 0, 0, 0, 0, 0, 0, 0, 0, -48, + -31, -31, -31, 0, -31, 0, 0, -31, -31, 0, -31, 0, -31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 46 - -45, 0, 0, 0, -45, -45, -45, -45, 0, -45, -45, -45, 0, 0, 0, 0, 0, 0, 0, 0, 0, -45, + -28, -28, -28, 0, -28, 0, 0, -28, -28, 0, -28, 0, -28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 47 - -47, 0, 0, 0, -47, -47, -47, -47, 0, -47, -47, -47, 0, 0, 0, 0, 0, 0, 0, 0, 0, -47, + -30, -30, -30, 0, -30, 0, 0, -30, -30, 0, -30, 0, -30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 48 - -44, 0, 0, 0, -44, -44, -44, -44, 0, -44, -44, -44, 0, 0, 0, 0, 0, 0, 0, 0, 0, -44, + 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 49 - 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 50 - -32, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -32, + 0, 0, 0, 0, -65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 51 - 0, -27, -27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 52 - 0, 0, -25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -50, 0, 0, 0, -50, -50, -50, -50, -50, 0, -50, -50, -50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -50, // State 53 - -34, -34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -34, + -53, 0, 0, 0, -53, -53, -53, -53, -53, 0, -53, -53, -53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -53, // State 54 - 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -55, 0, 0, 0, -55, -55, -55, -55, -55, 0, -55, -55, -55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -55, // State 55 - 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -49, 0, 0, 0, -49, -49, -49, -49, -49, 0, -49, -49, -49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -49, // State 56 - 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -56, 0, 0, 0, -56, -56, -56, -56, -56, 0, -56, -56, -56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -56, // State 57 - 0, -56, 0, 0, -56, 0, -56, -56, 0, -56, 0, -56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -52, 0, 0, 0, -52, -52, -52, -52, -52, 0, -52, -52, -52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -52, // State 58 - 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -54, 0, 0, 0, -54, -54, -54, -54, -54, 0, -54, -54, -54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -54, // State 59 - 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -51, 0, 0, 0, -51, -51, -51, -51, -51, 0, -51, -51, -51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -51, // State 60 - 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 61 - 0, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -41, -41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -41, // State 62 - -31, -31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -31, + 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 63 - -30, -30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -30, + 0, -57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 64 - -35, -35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -35, + 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 65 - 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -35, -35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 66 - -29, -29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -29, + 0, 0, -33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 67 - 0, -49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -44, -44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -44, // State 68 - 0, -50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 69 - 0, 0, 0, -4, -4, -4, -4, -4, -4, -4, -4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 70 - 0, 0, 0, -9, -9, -9, -9, -9, -9, -9, -9, -9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 71 - 0, -10, 0, 0, -10, 0, 0, 0, 0, 0, 0, -10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -38, -38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, // State 72 - 0, 0, 0, -13, -13, -13, -13, -13, -13, -13, -13, -13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -66, 0, 0, -66, 0, 0, -66, -66, 0, -66, 0, -66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 73 - 0, 0, 0, -17, -17, -17, -17, -17, -17, -17, -17, -17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 74 - 0, 0, 0, -18, -18, -18, -18, -18, -18, -18, -18, -18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 75 - 0, 0, 0, -15, -15, -15, -15, -15, -15, -15, -15, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 76 - 0, 0, 0, -19, -19, -19, -19, -19, -19, -19, -19, -19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 77 - 0, 0, 0, -12, -12, -12, -12, -12, -12, -12, -12, -12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 78 - 0, 0, 0, -16, -16, -16, -16, -16, -16, -16, -16, -16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -40, -40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -40, // State 79 - 0, 0, 0, -14, -14, -14, -14, -14, -14, -14, -14, -14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -39, -39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -39, // State 80 - 0, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -45, -45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -45, // State 81 - -55, 0, 0, 0, -55, 0, -55, -55, 0, -55, 0, -55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 82 - -33, -33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -33, + -37, -37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -37, // State 83 - -36, -36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -36, + 0, 0, 0, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 84 - -37, -37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -37, + 0, 0, 0, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 85 - -28, -28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -28, + 0, -17, 0, 0, -17, 0, 0, 0, 0, 0, 0, 0, -17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 86 - 0, 0, 0, -5, -5, -5, -5, -5, -5, -5, -5, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 87 - 0, -11, 0, 0, -11, 0, 0, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // State 88 - 0, 0, -26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 89 + 0, 0, 0, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 90 + 0, 0, 0, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 91 + 0, 0, 0, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 92 + 0, 0, 0, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 93 + 0, 0, 0, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 94 + 0, 0, 0, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 95 + 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 96 + -63, 0, 0, 0, -63, 0, 0, -63, -63, 0, -63, 0, -63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 97 + -42, -42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -42, + // State 98 + -43, -43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -43, + // State 99 + -46, -46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -46, + // State 100 + -47, -47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -47, + // State 101 + -36, -36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -36, + // State 102 + 0, 0, 0, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 103 + 0, -18, 0, 0, -18, 0, 0, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // State 104 + 0, 0, -34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; fn __action(state: i8, integer: usize) -> i8 { - __ACTION[(state as usize) * 22 + integer] + __ACTION[(state as usize) * 24 + integer] } const __EOF_ACTION: &[i8] = &[ // State 0 @@ -298,19 +327,19 @@ mod __parse__AIR { // State 24 0, // State 25 - -57, + 0, // State 26 - -8, + 0, // State 27 0, // State 28 - -38, + -67, // State 29 - 0, + -8, // State 30 0, // State 31 - 0, + -48, // State 32 0, // State 33 @@ -348,13 +377,13 @@ mod __parse__AIR { // State 49 0, // State 50 - -32, + 0, // State 51 0, // State 52 0, // State 53 - -34, + 0, // State 54 0, // State 55 @@ -370,19 +399,19 @@ mod __parse__AIR { // State 60 0, // State 61 - 0, + -41, // State 62 - -31, + 0, // State 63 - -30, + 0, // State 64 - -35, + 0, // State 65 0, // State 66 - -29, - // State 67 0, + // State 67 + -44, // State 68 0, // State 69 @@ -390,7 +419,7 @@ mod __parse__AIR { // State 70 0, // State 71 - 0, + -38, // State 72 0, // State 73 @@ -404,76 +433,113 @@ mod __parse__AIR { // State 77 0, // State 78 - 0, + -40, // State 79 - 0, + -39, // State 80 - 0, + -45, // State 81 0, // State 82 - -33, - // State 83 - -36, - // State 84 -37, + // State 83 + 0, + // State 84 + 0, // State 85 - -28, + 0, // State 86 0, // State 87 0, // State 88 0, + // State 89 + 0, + // State 90 + 0, + // State 91 + 0, + // State 92 + 0, + // State 93 + 0, + // State 94 + 0, + // State 95 + 0, + // State 96 + 0, + // State 97 + -42, + // State 98 + -43, + // State 99 + -46, + // State 100 + -47, + // State 101 + -36, + // State 102 + 0, + // State 103 + 0, + // State 104 + 0, ]; fn __goto(state: i8, nt: usize) -> i8 { match nt { - 2 => 24, - 5 => 25, - 6 => match state { - 24 => 86, - _ => 69, + 2 => 27, + 5 => 28, + 6 => 9, + 7 => match state { + 27 => 102, + _ => 83, }, - 7 => 21, - 8 => 70, - 9 => match state { - 8 | 23 => 51, - 16..=17 => 57, - _ => 31, - }, - 10 => 15, - 11 => match state { - 23 => 80, - _ => 52, + 8 => 24, + 9 => 84, + 10 => match state { + 10 | 26 => 65, + 18..=19 => 72, + _ => 41, }, + 11 => 17, 12 => match state { - 6 => 13, - 7 => 14, - 0 => 26, - 12 => 54, - 13 => 55, - 14 => 56, - 18 => 59, - 19 => 60, - 20 => 61, + 26 => 95, + _ => 66, + }, + 13 => match state { + 7 => 15, + 8 => 16, + 0 => 29, + 14 => 68, + 15 => 69, + 16 => 70, + 20 => 74, + 21 => 75, + 22 => 76, + 23 => 77, + _ => 14, + }, + 14 => match state { + 5 => 13, + 12 => 22, + 13 => 23, _ => 12, }, - 13 => 38, - 14 => match state { - 4 => 11, - 10 => 19, - 11 => 20, - _ => 10, + 15 => match state { + 24 => 81, + _ => 62, }, - 15 => 65, 17 => match state { - 1 => 32, - _ => 17, + 2 => 42, + _ => 19, }, - 18 => 8, - 19 => match state { - 17 => 58, - _ => 23, + 18 => 10, + 19 => 48, + 20 => match state { + 19 => 73, + _ => 26, }, _ => 0, } @@ -486,12 +552,14 @@ mod __parse__AIR { r###""]""###, r###"Alphanumeric"###, r###"Boolean"###, + r###"EmptyArray"###, r###"InitPeerId"###, r###"JsonPath"###, r###"LastError"###, r###"Literal"###, r###"Number"###, r###"Stream"###, + r###"ap"###, r###"call"###, r###"fold"###, r###"match_"###, @@ -555,7 +623,7 @@ mod __parse__AIR { #[inline] fn error_action(&self, state: i8) -> i8 { - __action(state, 22 - 1) + __action(state, 24 - 1) } #[inline] @@ -628,21 +696,23 @@ mod __parse__AIR { Token::CloseSquareBracket if true => Some(3), Token::Alphanumeric(_) if true => Some(4), Token::Boolean(_) if true => Some(5), - Token::InitPeerId if true => Some(6), - Token::VariableWithJsonPath(_, _, _) if true => Some(7), - Token::LastError(_) if true => Some(8), - Token::StringLiteral(_) if true => Some(9), - Token::Number(_) if true => Some(10), - Token::Stream(_) if true => Some(11), - Token::Call if true => Some(12), - Token::Fold if true => Some(13), - Token::Match if true => Some(14), - Token::MisMatch if true => Some(15), - Token::Next if true => Some(16), - Token::Null if true => Some(17), - Token::Par if true => Some(18), - Token::Seq if true => Some(19), - Token::Xor if true => Some(20), + Token::SquareBrackets if true => Some(6), + Token::InitPeerId if true => Some(7), + Token::VariableWithJsonPath(_, _, _) if true => Some(8), + Token::LastError(_) if true => Some(9), + Token::StringLiteral(_) if true => Some(10), + Token::Number(_) if true => Some(11), + Token::Stream(_) if true => Some(12), + Token::Ap if true => Some(13), + Token::Call if true => Some(14), + Token::Fold if true => Some(15), + Token::Match if true => Some(16), + Token::MisMatch if true => Some(17), + Token::Next if true => Some(18), + Token::Null if true => Some(19), + Token::Par if true => Some(20), + Token::Seq if true => Some(21), + Token::Xor if true => Some(22), _ => None, } } @@ -657,8 +727,8 @@ mod __parse__AIR { ) -> __Symbol<'input> { match __token_index { - 0 | 1 | 2 | 3 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 => __Symbol::Variant0(__token), - 4 | 9 | 11 => match __token { + 0 | 1 | 2 | 3 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 => __Symbol::Variant0(__token), + 4 | 10 | 12 => match __token { Token::Alphanumeric(__tok0) | Token::StringLiteral(__tok0) | Token::Stream(__tok0) if true => __Symbol::Variant1(__tok0), _ => unreachable!(), }, @@ -666,15 +736,15 @@ mod __parse__AIR { Token::Boolean(__tok0) if true => __Symbol::Variant2(__tok0), _ => unreachable!(), }, - 7 => match __token { + 8 => match __token { Token::VariableWithJsonPath(__tok0, __tok1, __tok2) if true => __Symbol::Variant3((__tok0, __tok1, __tok2)), _ => unreachable!(), }, - 8 => match __token { + 9 => match __token { Token::LastError(__tok0) if true => __Symbol::Variant4(__tok0), _ => unreachable!(), }, - 10 => match __token { + 11 => match __token { Token::Number(__tok0) if true => __Symbol::Variant5(__tok0), _ => unreachable!(), }, @@ -750,62 +820,62 @@ mod __parse__AIR { } 9 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 2, - nonterminal_produced: 7, + states_to_pop: 1, + nonterminal_produced: 6, } } 10 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 3, - nonterminal_produced: 7, + states_to_pop: 1, + nonterminal_produced: 6, } } 11 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 8, + nonterminal_produced: 6, } } 12 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 8, + nonterminal_produced: 6, } } 13 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 8, + nonterminal_produced: 6, } } 14 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 8, + nonterminal_produced: 6, } } 15 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 8, + nonterminal_produced: 7, } } 16 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, + states_to_pop: 2, nonterminal_produced: 8, } } 17 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, + states_to_pop: 3, nonterminal_produced: 8, } } 18 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 8, + nonterminal_produced: 9, } } 19 => { @@ -841,196 +911,256 @@ mod __parse__AIR { 24 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 10, + nonterminal_produced: 9, } } 25 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 4, - nonterminal_produced: 10, + states_to_pop: 1, + nonterminal_produced: 9, } } 26 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 11, + nonterminal_produced: 9, } } 27 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 7, - nonterminal_produced: 12, + states_to_pop: 1, + nonterminal_produced: 10, } } 28 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 6, - nonterminal_produced: 12, + states_to_pop: 1, + nonterminal_produced: 10, } } 29 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 5, - nonterminal_produced: 12, + states_to_pop: 1, + nonterminal_produced: 10, } } 30 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 5, - nonterminal_produced: 12, + states_to_pop: 1, + nonterminal_produced: 10, } } 31 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 3, - nonterminal_produced: 12, + states_to_pop: 1, + nonterminal_produced: 10, } } 32 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 6, - nonterminal_produced: 12, + states_to_pop: 1, + nonterminal_produced: 11, } } 33 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 4, - nonterminal_produced: 12, + nonterminal_produced: 11, } } 34 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 5, + states_to_pop: 1, nonterminal_produced: 12, } } 35 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 6, - nonterminal_produced: 12, + states_to_pop: 7, + nonterminal_produced: 13, } } 36 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 6, - nonterminal_produced: 12, + nonterminal_produced: 13, } } 37 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, - nonterminal_produced: 12, + states_to_pop: 5, + nonterminal_produced: 13, } } 38 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, + states_to_pop: 5, nonterminal_produced: 13, } } 39 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, + states_to_pop: 5, nonterminal_produced: 13, } } 40 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, + states_to_pop: 3, nonterminal_produced: 13, } } 41 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, - nonterminal_produced: 14, + states_to_pop: 6, + nonterminal_produced: 13, } } 42 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, - nonterminal_produced: 14, + states_to_pop: 6, + nonterminal_produced: 13, } } 43 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, - nonterminal_produced: 14, + states_to_pop: 4, + nonterminal_produced: 13, } } 44 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, - nonterminal_produced: 14, + states_to_pop: 5, + nonterminal_produced: 13, } } 45 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, - nonterminal_produced: 14, + states_to_pop: 6, + nonterminal_produced: 13, } } 46 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 1, - nonterminal_produced: 14, + states_to_pop: 6, + nonterminal_produced: 13, } } 47 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 14, + nonterminal_produced: 13, } } 48 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 15, + nonterminal_produced: 14, } } 49 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 15, + nonterminal_produced: 14, } } 50 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 16, + nonterminal_produced: 14, } } 51 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 0, - nonterminal_produced: 16, + states_to_pop: 1, + nonterminal_produced: 14, } } 52 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 17, + nonterminal_produced: 14, } } 53 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, - nonterminal_produced: 18, + nonterminal_produced: 14, } } 54 => { __state_machine::SimulatedReduce::Reduce { - states_to_pop: 4, - nonterminal_produced: 18, + states_to_pop: 1, + nonterminal_produced: 14, } } 55 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 14, + } + } + 56 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 15, + } + } + 57 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 15, + } + } + 58 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 16, + } + } + 59 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 0, + nonterminal_produced: 16, + } + } + 60 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 17, + } + } + 61 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 18, + } + } + 62 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 4, + nonterminal_produced: 18, + } + } + 63 => { __state_machine::SimulatedReduce::Reduce { states_to_pop: 1, nonterminal_produced: 19, } } - 56 => __state_machine::SimulatedReduce::Accept, + 64 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 19, + } + } + 65 => { + __state_machine::SimulatedReduce::Reduce { + states_to_pop: 1, + nonterminal_produced: 20, + } + } + 66 => __state_machine::SimulatedReduce::Accept, _ => panic!("invalid reduction index {}", __reduce_index) } } @@ -1296,6 +1426,36 @@ mod __parse__AIR { __reduce55(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) } 56 => { + __reduce56(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 57 => { + __reduce57(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 58 => { + __reduce58(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 59 => { + __reduce59(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 60 => { + __reduce60(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 61 => { + __reduce61(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 62 => { + __reduce62(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 63 => { + __reduce63(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 64 => { + __reduce64(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 65 => { + __reduce65(input, errors, validator, __lookahead_start, __symbols, core::marker::PhantomData::<(&(), &(), &())>) + } + 66 => { // __AIR = AIR => ActionFn(0); let __sym0 = __pop_Variant10(__symbols); let __start = __sym0.0.clone(); @@ -1320,13 +1480,35 @@ mod __parse__AIR { 'input, >( __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, (Variable<'input>, &'input str, bool), usize) + ) -> (usize, (AstVariable<'input>, &'input str, bool), usize) { match __symbols.pop() { Some((__l, __Symbol::Variant3(__v), __r)) => (__l, __v, __r), _ => __symbol_type_mismatch() } } + fn __pop_Variant11< + 'input, + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> + ) -> (usize, ApArgument<'input>, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant11(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } + fn __pop_Variant16< + 'input, + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> + ) -> (usize, AstVariable<'input>, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant16(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } fn __pop_Variant10< 'input, >( @@ -1349,33 +1531,11 @@ mod __parse__AIR { _ => __symbol_type_mismatch() } } - fn __pop_Variant12< - 'input, - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, CallInstrValue<'input>, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant12(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } - fn __pop_Variant16< - 'input, - >( - __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, CallOutputValue<'input>, usize) - { - match __symbols.pop() { - Some((__l, __Symbol::Variant16(__v), __r)) => (__l, __v, __r), - _ => __symbol_type_mismatch() - } - } fn __pop_Variant13< 'input, >( __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, FunctionPart<'input>, usize) + ) -> (usize, CallInstrValue<'input>, usize) { match __symbols.pop() { Some((__l, __Symbol::Variant13(__v), __r)) => (__l, __v, __r), @@ -1386,13 +1546,24 @@ mod __parse__AIR { 'input, >( __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, IterableValue<'input>, usize) + ) -> (usize, FunctionPart<'input>, usize) { match __symbols.pop() { Some((__l, __Symbol::Variant14(__v), __r)) => (__l, __v, __r), _ => __symbol_type_mismatch() } } + fn __pop_Variant19< + 'input, + >( + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> + ) -> (usize, IterableScalarValue<'input>, usize) + { + match __symbols.pop() { + Some((__l, __Symbol::Variant19(__v), __r)) => (__l, __v, __r), + _ => __symbol_type_mismatch() + } + } fn __pop_Variant4< 'input, >( @@ -1448,14 +1619,14 @@ mod __parse__AIR { _ => __symbol_type_mismatch() } } - fn __pop_Variant11< + fn __pop_Variant12< 'input, >( __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> ) -> (usize, Vec>, usize) { match __symbols.pop() { - Some((__l, __Symbol::Variant11(__v), __r)) => (__l, __v, __r), + Some((__l, __Symbol::Variant12(__v), __r)) => (__l, __v, __r), _ => __symbol_type_mismatch() } } @@ -1496,7 +1667,7 @@ mod __parse__AIR { 'input, >( __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)> - ) -> (usize, core::option::Option>, usize) + ) -> (usize, core::option::Option>, usize) { match __symbols.pop() { Some((__l, __Symbol::Variant17(__v), __r)) => (__l, __v, __r), @@ -1538,11 +1709,11 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // () = Arg => ActionFn(48); + // () = Arg => ActionFn(58); let __sym0 = __pop_Variant7(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action48::<>(input, errors, validator, __sym0); + let __nt = super::__action58::<>(input, errors, validator, __sym0); __symbols.push((__start, __Symbol::Variant7(__nt), __end)); (1, 0) } @@ -1559,10 +1730,10 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // ()* = => ActionFn(46); + // ()* = => ActionFn(56); let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); let __end = __start.clone(); - let __nt = super::__action46::<>(input, errors, validator, &__start, &__end); + let __nt = super::__action56::<>(input, errors, validator, &__start, &__end); __symbols.push((__start, __Symbol::Variant8(__nt), __end)); (0, 1) } @@ -1579,11 +1750,11 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // ()* = ()+ => ActionFn(47); + // ()* = ()+ => ActionFn(57); let __sym0 = __pop_Variant8(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action47::<>(input, errors, validator, __sym0); + let __nt = super::__action57::<>(input, errors, validator, __sym0); __symbols.push((__start, __Symbol::Variant8(__nt), __end)); (1, 1) } @@ -1600,11 +1771,11 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // ()+ = Arg => ActionFn(55); + // ()+ = Arg => ActionFn(65); let __sym0 = __pop_Variant7(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action55::<>(input, errors, validator, __sym0); + let __nt = super::__action65::<>(input, errors, validator, __sym0); __symbols.push((__start, __Symbol::Variant8(__nt), __end)); (1, 2) } @@ -1621,13 +1792,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // ()+ = ()+, Arg => ActionFn(56); + // ()+ = ()+, Arg => ActionFn(66); assert!(__symbols.len() >= 2); let __sym1 = __pop_Variant7(__symbols); let __sym0 = __pop_Variant8(__symbols); let __start = __sym0.0.clone(); let __end = __sym1.2.clone(); - let __nt = super::__action56::<>(input, errors, validator, __sym0, __sym1); + let __nt = super::__action66::<>(input, errors, validator, __sym0, __sym1); __symbols.push((__start, __Symbol::Variant8(__nt), __end)); (2, 2) } @@ -1644,10 +1815,10 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // @L = => ActionFn(52); + // @L = => ActionFn(62); let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); let __end = __start.clone(); - let __nt = super::__action52::<>(input, errors, validator, &__start, &__end); + let __nt = super::__action62::<>(input, errors, validator, &__start, &__end); __symbols.push((__start, __Symbol::Variant9(__nt), __end)); (0, 3) } @@ -1664,10 +1835,10 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // @R = => ActionFn(49); + // @R = => ActionFn(59); let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); let __end = __start.clone(); - let __nt = super::__action49::<>(input, errors, validator, &__start, &__end); + let __nt = super::__action59::<>(input, errors, validator, &__start, &__end); __symbols.push((__start, __Symbol::Variant9(__nt), __end)); (0, 4) } @@ -1705,12 +1876,12 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Arg = CallInstrArgValue => ActionFn(27); - let __sym0 = __pop_Variant7(__symbols); + // ApArgument = Alphanumeric => ActionFn(39); + let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action27::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant7(__nt), __end)); + let __nt = super::__action39::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant11(__nt), __end)); (1, 6) } pub(crate) fn __reduce9< @@ -1726,15 +1897,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Args = "[", "]" => ActionFn(57); - assert!(__symbols.len() >= 2); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // ApArgument = JsonPath => ActionFn(40); + let __sym0 = __pop_Variant3(__symbols); let __start = __sym0.0.clone(); - let __end = __sym1.2.clone(); - let __nt = super::__action57::<>(input, errors, validator, __sym0, __sym1); + let __end = __sym0.2.clone(); + let __nt = super::__action40::<>(input, errors, validator, __sym0); __symbols.push((__start, __Symbol::Variant11(__nt), __end)); - (2, 7) + (1, 6) } pub(crate) fn __reduce10< 'err, @@ -1749,16 +1918,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Args = "[", ()+, "]" => ActionFn(58); - assert!(__symbols.len() >= 3); - let __sym2 = __pop_Variant0(__symbols); - let __sym1 = __pop_Variant8(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // ApArgument = Number => ActionFn(41); + let __sym0 = __pop_Variant5(__symbols); let __start = __sym0.0.clone(); - let __end = __sym2.2.clone(); - let __nt = super::__action58::<>(input, errors, validator, __sym0, __sym1, __sym2); + let __end = __sym0.2.clone(); + let __nt = super::__action41::<>(input, errors, validator, __sym0); __symbols.push((__start, __Symbol::Variant11(__nt), __end)); - (3, 7) + (1, 6) } pub(crate) fn __reduce11< 'err, @@ -1773,13 +1939,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = Literal => ActionFn(28); - let __sym0 = __pop_Variant1(__symbols); + // ApArgument = Boolean => ActionFn(42); + let __sym0 = __pop_Variant2(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action28::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + let __nt = super::__action42::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant11(__nt), __end)); + (1, 6) } pub(crate) fn __reduce12< 'err, @@ -1794,13 +1960,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = Alphanumeric => ActionFn(29); - let __sym0 = __pop_Variant1(__symbols); + // ApArgument = EmptyArray => ActionFn(43); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action29::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + let __nt = super::__action43::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant11(__nt), __end)); + (1, 6) } pub(crate) fn __reduce13< 'err, @@ -1815,13 +1981,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = Stream => ActionFn(30); + // ApArgument = Literal => ActionFn(44); let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action30::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + let __nt = super::__action44::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant11(__nt), __end)); + (1, 6) } pub(crate) fn __reduce14< 'err, @@ -1836,13 +2002,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = JsonPath => ActionFn(31); - let __sym0 = __pop_Variant3(__symbols); + // ApArgument = LastError => ActionFn(45); + let __sym0 = __pop_Variant4(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action31::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + let __nt = super::__action45::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant11(__nt), __end)); + (1, 6) } pub(crate) fn __reduce15< 'err, @@ -1857,13 +2023,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = Number => ActionFn(32); - let __sym0 = __pop_Variant5(__symbols); + // Arg = CallInstrArgValue => ActionFn(29); + let __sym0 = __pop_Variant7(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action32::<>(input, errors, validator, __sym0); + let __nt = super::__action29::<>(input, errors, validator, __sym0); __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + (1, 7) } pub(crate) fn __reduce16< 'err, @@ -1878,13 +2044,15 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = Boolean => ActionFn(33); - let __sym0 = __pop_Variant2(__symbols); + // Args = "[", "]" => ActionFn(67); + assert!(__symbols.len() >= 2); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action33::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + let __end = __sym1.2.clone(); + let __nt = super::__action67::<>(input, errors, validator, __sym0, __sym1); + __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + (2, 8) } pub(crate) fn __reduce17< 'err, @@ -1899,13 +2067,16 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = InitPeerId => ActionFn(34); + // Args = "[", ()+, "]" => ActionFn(68); + assert!(__symbols.len() >= 3); + let __sym2 = __pop_Variant0(__symbols); + let __sym1 = __pop_Variant8(__symbols); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action34::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + let __end = __sym2.2.clone(); + let __nt = super::__action68::<>(input, errors, validator, __sym0, __sym1, __sym2); + __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + (3, 8) } pub(crate) fn __reduce18< 'err, @@ -1920,13 +2091,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrArgValue = LastError => ActionFn(35); - let __sym0 = __pop_Variant4(__symbols); + // CallInstrArgValue = Literal => ActionFn(30); + let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action35::<>(input, errors, validator, __sym0); + let __nt = super::__action30::<>(input, errors, validator, __sym0); __symbols.push((__start, __Symbol::Variant7(__nt), __end)); - (1, 8) + (1, 9) } pub(crate) fn __reduce19< 'err, @@ -1941,12 +2112,12 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrValue = Literal => ActionFn(22); + // CallInstrArgValue = Alphanumeric => ActionFn(31); let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action22::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + let __nt = super::__action31::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); (1, 9) } pub(crate) fn __reduce20< @@ -1962,12 +2133,12 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrValue = Alphanumeric => ActionFn(23); + // CallInstrArgValue = Stream => ActionFn(32); let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action23::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + let __nt = super::__action32::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); (1, 9) } pub(crate) fn __reduce21< @@ -1983,12 +2154,12 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrValue = Stream => ActionFn(24); - let __sym0 = __pop_Variant1(__symbols); + // CallInstrArgValue = JsonPath => ActionFn(33); + let __sym0 = __pop_Variant3(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action24::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + let __nt = super::__action33::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); (1, 9) } pub(crate) fn __reduce22< @@ -2004,12 +2175,12 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrValue = JsonPath => ActionFn(65); - let __sym0 = __pop_Variant3(__symbols); + // CallInstrArgValue = Number => ActionFn(34); + let __sym0 = __pop_Variant5(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action65::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + let __nt = super::__action34::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); (1, 9) } pub(crate) fn __reduce23< @@ -2025,12 +2196,12 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // CallInstrValue = InitPeerId => ActionFn(26); - let __sym0 = __pop_Variant0(__symbols); + // CallInstrArgValue = Boolean => ActionFn(35); + let __sym0 = __pop_Variant2(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action26::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + let __nt = super::__action35::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); (1, 9) } pub(crate) fn __reduce24< @@ -2046,13 +2217,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // FPart = Function => ActionFn(13); - let __sym0 = __pop_Variant12(__symbols); + // CallInstrArgValue = InitPeerId => ActionFn(36); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action13::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant13(__nt), __end)); - (1, 10) + let __nt = super::__action36::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); + (1, 9) } pub(crate) fn __reduce25< 'err, @@ -2067,17 +2238,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // FPart = "(", ServiceId, Function, ")" => ActionFn(14); - assert!(__symbols.len() >= 4); - let __sym3 = __pop_Variant0(__symbols); - let __sym2 = __pop_Variant12(__symbols); - let __sym1 = __pop_Variant12(__symbols); + // CallInstrArgValue = EmptyArray => ActionFn(37); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym3.2.clone(); - let __nt = super::__action14::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3); - __symbols.push((__start, __Symbol::Variant13(__nt), __end)); - (4, 10) + let __end = __sym0.2.clone(); + let __nt = super::__action37::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); + (1, 9) } pub(crate) fn __reduce26< 'err, @@ -2092,13 +2259,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Function = CallInstrValue => ActionFn(19); - let __sym0 = __pop_Variant12(__symbols); + // CallInstrArgValue = LastError => ActionFn(38); + let __sym0 = __pop_Variant4(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action19::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); - (1, 11) + let __nt = super::__action38::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant7(__nt), __end)); + (1, 9) } pub(crate) fn __reduce27< 'err, @@ -2113,20 +2280,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", call, PeerPart, FPart, Args, Output, ")" => ActionFn(71); - assert!(__symbols.len() >= 7); - let __sym6 = __pop_Variant0(__symbols); - let __sym5 = __pop_Variant16(__symbols); - let __sym4 = __pop_Variant11(__symbols); - let __sym3 = __pop_Variant13(__symbols); - let __sym2 = __pop_Variant18(__symbols); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // CallInstrValue = Literal => ActionFn(24); + let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); - let __end = __sym6.2.clone(); - let __nt = super::__action71::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5, __sym6); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (7, 12) + let __end = __sym0.2.clone(); + let __nt = super::__action24::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 10) } pub(crate) fn __reduce28< 'err, @@ -2141,19 +2301,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", call, PeerPart, FPart, Args, ")" => ActionFn(72); - assert!(__symbols.len() >= 6); - let __sym5 = __pop_Variant0(__symbols); - let __sym4 = __pop_Variant11(__symbols); - let __sym3 = __pop_Variant13(__symbols); - let __sym2 = __pop_Variant18(__symbols); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // CallInstrValue = Alphanumeric => ActionFn(25); + let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); - let __end = __sym5.2.clone(); - let __nt = super::__action72::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (6, 12) + let __end = __sym0.2.clone(); + let __nt = super::__action25::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 10) } pub(crate) fn __reduce29< 'err, @@ -2168,18 +2322,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", seq, Instr, Instr, ")" => ActionFn(3); - assert!(__symbols.len() >= 5); - let __sym4 = __pop_Variant0(__symbols); - let __sym3 = __pop_Variant10(__symbols); - let __sym2 = __pop_Variant10(__symbols); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // CallInstrValue = Stream => ActionFn(26); + let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); - let __end = __sym4.2.clone(); - let __nt = super::__action3::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (5, 12) + let __end = __sym0.2.clone(); + let __nt = super::__action26::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 10) } pub(crate) fn __reduce30< 'err, @@ -2194,18 +2343,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", par, Instr, Instr, ")" => ActionFn(4); - assert!(__symbols.len() >= 5); - let __sym4 = __pop_Variant0(__symbols); - let __sym3 = __pop_Variant10(__symbols); - let __sym2 = __pop_Variant10(__symbols); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // CallInstrValue = JsonPath => ActionFn(78); + let __sym0 = __pop_Variant3(__symbols); let __start = __sym0.0.clone(); - let __end = __sym4.2.clone(); - let __nt = super::__action4::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (5, 12) + let __end = __sym0.2.clone(); + let __nt = super::__action78::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 10) } pub(crate) fn __reduce31< 'err, @@ -2220,16 +2364,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", null, ")" => ActionFn(5); - assert!(__symbols.len() >= 3); - let __sym2 = __pop_Variant0(__symbols); - let __sym1 = __pop_Variant0(__symbols); + // CallInstrValue = InitPeerId => ActionFn(28); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym2.2.clone(); - let __nt = super::__action5::<>(input, errors, validator, __sym0, __sym1, __sym2); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (3, 12) + let __end = __sym0.2.clone(); + let __nt = super::__action28::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 10) } pub(crate) fn __reduce32< 'err, @@ -2244,19 +2385,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", fold, Iterable, Alphanumeric, Instr, ")" => ActionFn(67); - assert!(__symbols.len() >= 6); - let __sym5 = __pop_Variant0(__symbols); - let __sym4 = __pop_Variant10(__symbols); - let __sym3 = __pop_Variant1(__symbols); - let __sym2 = __pop_Variant14(__symbols); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // FPart = Function => ActionFn(15); + let __sym0 = __pop_Variant13(__symbols); let __start = __sym0.0.clone(); - let __end = __sym5.2.clone(); - let __nt = super::__action67::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (6, 12) + let __end = __sym0.2.clone(); + let __nt = super::__action15::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant14(__nt), __end)); + (1, 11) } pub(crate) fn __reduce33< 'err, @@ -2271,17 +2406,17 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", next, Alphanumeric, ")" => ActionFn(68); + // FPart = "(", ServiceId, Function, ")" => ActionFn(16); assert!(__symbols.len() >= 4); let __sym3 = __pop_Variant0(__symbols); - let __sym2 = __pop_Variant1(__symbols); - let __sym1 = __pop_Variant0(__symbols); + let __sym2 = __pop_Variant13(__symbols); + let __sym1 = __pop_Variant13(__symbols); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); let __end = __sym3.2.clone(); - let __nt = super::__action68::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (4, 12) + let __nt = super::__action16::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3); + __symbols.push((__start, __Symbol::Variant14(__nt), __end)); + (4, 11) } pub(crate) fn __reduce34< 'err, @@ -2296,18 +2431,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", xor, Instr, Instr, ")" => ActionFn(8); - assert!(__symbols.len() >= 5); - let __sym4 = __pop_Variant0(__symbols); - let __sym3 = __pop_Variant10(__symbols); - let __sym2 = __pop_Variant10(__symbols); - let __sym1 = __pop_Variant0(__symbols); - let __sym0 = __pop_Variant0(__symbols); + // Function = CallInstrValue => ActionFn(21); + let __sym0 = __pop_Variant13(__symbols); let __start = __sym0.0.clone(); - let __end = __sym4.2.clone(); - let __nt = super::__action8::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4); - __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (5, 12) + let __end = __sym0.2.clone(); + let __nt = super::__action21::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 12) } pub(crate) fn __reduce35< 'err, @@ -2322,19 +2452,20 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", match_, Matchable, Matchable, Instr, ")" => ActionFn(69); - assert!(__symbols.len() >= 6); - let __sym5 = __pop_Variant0(__symbols); - let __sym4 = __pop_Variant10(__symbols); - let __sym3 = __pop_Variant15(__symbols); - let __sym2 = __pop_Variant15(__symbols); + // Instr = "(", call, PeerPart, FPart, Args, Output, ")" => ActionFn(87); + assert!(__symbols.len() >= 7); + let __sym6 = __pop_Variant0(__symbols); + let __sym5 = __pop_Variant16(__symbols); + let __sym4 = __pop_Variant12(__symbols); + let __sym3 = __pop_Variant14(__symbols); + let __sym2 = __pop_Variant18(__symbols); let __sym1 = __pop_Variant0(__symbols); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym5.2.clone(); - let __nt = super::__action69::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); + let __end = __sym6.2.clone(); + let __nt = super::__action87::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5, __sym6); __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (6, 12) + (7, 13) } pub(crate) fn __reduce36< 'err, @@ -2349,19 +2480,19 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = "(", mismatch, Matchable, Matchable, Instr, ")" => ActionFn(70); + // Instr = "(", call, PeerPart, FPart, Args, ")" => ActionFn(88); assert!(__symbols.len() >= 6); let __sym5 = __pop_Variant0(__symbols); - let __sym4 = __pop_Variant10(__symbols); - let __sym3 = __pop_Variant15(__symbols); - let __sym2 = __pop_Variant15(__symbols); + let __sym4 = __pop_Variant12(__symbols); + let __sym3 = __pop_Variant14(__symbols); + let __sym2 = __pop_Variant18(__symbols); let __sym1 = __pop_Variant0(__symbols); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); let __end = __sym5.2.clone(); - let __nt = super::__action70::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); + let __nt = super::__action88::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (6, 12) + (6, 13) } pub(crate) fn __reduce37< 'err, @@ -2376,13 +2507,18 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Instr = error => ActionFn(11); - let __sym0 = __pop_Variant6(__symbols); + // Instr = "(", ap, ApArgument, Output, ")" => ActionFn(80); + assert!(__symbols.len() >= 5); + let __sym4 = __pop_Variant0(__symbols); + let __sym3 = __pop_Variant16(__symbols); + let __sym2 = __pop_Variant11(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action11::<>(input, errors, validator, __sym0); + let __end = __sym4.2.clone(); + let __nt = super::__action80::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4); __symbols.push((__start, __Symbol::Variant10(__nt), __end)); - (1, 12) + (5, 13) } pub(crate) fn __reduce38< 'err, @@ -2397,13 +2533,18 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Iterable = Alphanumeric => ActionFn(36); - let __sym0 = __pop_Variant1(__symbols); + // Instr = "(", seq, Instr, Instr, ")" => ActionFn(4); + assert!(__symbols.len() >= 5); + let __sym4 = __pop_Variant0(__symbols); + let __sym3 = __pop_Variant10(__symbols); + let __sym2 = __pop_Variant10(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action36::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant14(__nt), __end)); - (1, 13) + let __end = __sym4.2.clone(); + let __nt = super::__action4::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (5, 13) } pub(crate) fn __reduce39< 'err, @@ -2418,13 +2559,18 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Iterable = Stream => ActionFn(37); - let __sym0 = __pop_Variant1(__symbols); + // Instr = "(", par, Instr, Instr, ")" => ActionFn(5); + assert!(__symbols.len() >= 5); + let __sym4 = __pop_Variant0(__symbols); + let __sym3 = __pop_Variant10(__symbols); + let __sym2 = __pop_Variant10(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action37::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant14(__nt), __end)); - (1, 13) + let __end = __sym4.2.clone(); + let __nt = super::__action5::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (5, 13) } pub(crate) fn __reduce40< 'err, @@ -2439,13 +2585,16 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Iterable = JsonPath => ActionFn(38); - let __sym0 = __pop_Variant3(__symbols); + // Instr = "(", null, ")" => ActionFn(6); + assert!(__symbols.len() >= 3); + let __sym2 = __pop_Variant0(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action38::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant14(__nt), __end)); - (1, 13) + let __end = __sym2.2.clone(); + let __nt = super::__action6::<>(input, errors, validator, __sym0, __sym1, __sym2); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (3, 13) } pub(crate) fn __reduce41< 'err, @@ -2460,13 +2609,19 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Matchable = InitPeerId => ActionFn(39); + // Instr = "(", fold, ScalarIterable, Alphanumeric, Instr, ")" => ActionFn(81); + assert!(__symbols.len() >= 6); + let __sym5 = __pop_Variant0(__symbols); + let __sym4 = __pop_Variant10(__symbols); + let __sym3 = __pop_Variant1(__symbols); + let __sym2 = __pop_Variant19(__symbols); + let __sym1 = __pop_Variant0(__symbols); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action39::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant15(__nt), __end)); - (1, 14) + let __end = __sym5.2.clone(); + let __nt = super::__action81::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (6, 13) } pub(crate) fn __reduce42< 'err, @@ -2481,13 +2636,19 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Matchable = Alphanumeric => ActionFn(40); - let __sym0 = __pop_Variant1(__symbols); + // Instr = "(", fold, Stream, Alphanumeric, Instr, ")" => ActionFn(82); + assert!(__symbols.len() >= 6); + let __sym5 = __pop_Variant0(__symbols); + let __sym4 = __pop_Variant10(__symbols); + let __sym3 = __pop_Variant1(__symbols); + let __sym2 = __pop_Variant1(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action40::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant15(__nt), __end)); - (1, 14) + let __end = __sym5.2.clone(); + let __nt = super::__action82::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (6, 13) } pub(crate) fn __reduce43< 'err, @@ -2502,13 +2663,17 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Matchable = Stream => ActionFn(41); - let __sym0 = __pop_Variant1(__symbols); + // Instr = "(", next, Alphanumeric, ")" => ActionFn(83); + assert!(__symbols.len() >= 4); + let __sym3 = __pop_Variant0(__symbols); + let __sym2 = __pop_Variant1(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action41::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant15(__nt), __end)); - (1, 14) + let __end = __sym3.2.clone(); + let __nt = super::__action83::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (4, 13) } pub(crate) fn __reduce44< 'err, @@ -2523,13 +2688,18 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Matchable = Literal => ActionFn(42); - let __sym0 = __pop_Variant1(__symbols); + // Instr = "(", xor, Instr, Instr, ")" => ActionFn(10); + assert!(__symbols.len() >= 5); + let __sym4 = __pop_Variant0(__symbols); + let __sym3 = __pop_Variant10(__symbols); + let __sym2 = __pop_Variant10(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action42::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant15(__nt), __end)); - (1, 14) + let __end = __sym4.2.clone(); + let __nt = super::__action10::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (5, 13) } pub(crate) fn __reduce45< 'err, @@ -2544,13 +2714,19 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Matchable = Boolean => ActionFn(43); - let __sym0 = __pop_Variant2(__symbols); + // Instr = "(", match_, Matchable, Matchable, Instr, ")" => ActionFn(84); + assert!(__symbols.len() >= 6); + let __sym5 = __pop_Variant0(__symbols); + let __sym4 = __pop_Variant10(__symbols); + let __sym3 = __pop_Variant15(__symbols); + let __sym2 = __pop_Variant15(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action43::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant15(__nt), __end)); - (1, 14) + let __end = __sym5.2.clone(); + let __nt = super::__action84::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (6, 13) } pub(crate) fn __reduce46< 'err, @@ -2565,13 +2741,19 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Matchable = Number => ActionFn(44); - let __sym0 = __pop_Variant5(__symbols); + // Instr = "(", mismatch, Matchable, Matchable, Instr, ")" => ActionFn(85); + assert!(__symbols.len() >= 6); + let __sym5 = __pop_Variant0(__symbols); + let __sym4 = __pop_Variant10(__symbols); + let __sym3 = __pop_Variant15(__symbols); + let __sym2 = __pop_Variant15(__symbols); + let __sym1 = __pop_Variant0(__symbols); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym0.2.clone(); - let __nt = super::__action44::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant15(__nt), __end)); - (1, 14) + let __end = __sym5.2.clone(); + let __nt = super::__action85::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3, __sym4, __sym5); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (6, 13) } pub(crate) fn __reduce47< 'err, @@ -2586,13 +2768,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Matchable = JsonPath => ActionFn(45); - let __sym0 = __pop_Variant3(__symbols); + // Instr = error => ActionFn(13); + let __sym0 = __pop_Variant6(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action45::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant15(__nt), __end)); - (1, 14) + let __nt = super::__action13::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant10(__nt), __end)); + (1, 13) } pub(crate) fn __reduce48< 'err, @@ -2607,13 +2789,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Output = Alphanumeric => ActionFn(17); - let __sym0 = __pop_Variant1(__symbols); + // Matchable = InitPeerId => ActionFn(48); + let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action17::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant16(__nt), __end)); - (1, 15) + let __nt = super::__action48::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) } pub(crate) fn __reduce49< 'err, @@ -2628,13 +2810,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Output = Stream => ActionFn(18); + // Matchable = Alphanumeric => ActionFn(49); let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action18::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant16(__nt), __end)); - (1, 15) + let __nt = super::__action49::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) } pub(crate) fn __reduce50< 'err, @@ -2649,13 +2831,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Output? = Output => ActionFn(50); - let __sym0 = __pop_Variant16(__symbols); + // Matchable = Stream => ActionFn(50); + let __sym0 = __pop_Variant1(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); let __nt = super::__action50::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant17(__nt), __end)); - (1, 16) + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) } pub(crate) fn __reduce51< 'err, @@ -2670,12 +2852,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // Output? = => ActionFn(51); - let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); - let __end = __start.clone(); - let __nt = super::__action51::<>(input, errors, validator, &__start, &__end); - __symbols.push((__start, __Symbol::Variant17(__nt), __end)); - (0, 16) + // Matchable = Literal => ActionFn(51); + let __sym0 = __pop_Variant1(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action51::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) } pub(crate) fn __reduce52< 'err, @@ -2690,13 +2873,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // PeerId = CallInstrValue => ActionFn(20); - let __sym0 = __pop_Variant12(__symbols); + // Matchable = Boolean => ActionFn(52); + let __sym0 = __pop_Variant2(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action20::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); - (1, 17) + let __nt = super::__action52::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) } pub(crate) fn __reduce53< 'err, @@ -2711,13 +2894,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // PeerPart = PeerId => ActionFn(15); - let __sym0 = __pop_Variant12(__symbols); + // Matchable = Number => ActionFn(53); + let __sym0 = __pop_Variant5(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action15::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant18(__nt), __end)); - (1, 18) + let __nt = super::__action53::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) } pub(crate) fn __reduce54< 'err, @@ -2732,17 +2915,13 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // PeerPart = "(", PeerId, ServiceId, ")" => ActionFn(16); - assert!(__symbols.len() >= 4); - let __sym3 = __pop_Variant0(__symbols); - let __sym2 = __pop_Variant12(__symbols); - let __sym1 = __pop_Variant12(__symbols); + // Matchable = EmptyArray => ActionFn(54); let __sym0 = __pop_Variant0(__symbols); let __start = __sym0.0.clone(); - let __end = __sym3.2.clone(); - let __nt = super::__action16::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3); - __symbols.push((__start, __Symbol::Variant18(__nt), __end)); - (4, 18) + let __end = __sym0.2.clone(); + let __nt = super::__action54::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) } pub(crate) fn __reduce55< 'err, @@ -2757,14 +2936,227 @@ mod __parse__AIR { _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, ) -> (usize, usize) { - // ServiceId = CallInstrValue => ActionFn(21); - let __sym0 = __pop_Variant12(__symbols); + // Matchable = JsonPath => ActionFn(55); + let __sym0 = __pop_Variant3(__symbols); let __start = __sym0.0.clone(); let __end = __sym0.2.clone(); - let __nt = super::__action21::<>(input, errors, validator, __sym0); - __symbols.push((__start, __Symbol::Variant12(__nt), __end)); + let __nt = super::__action55::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant15(__nt), __end)); + (1, 14) + } + pub(crate) fn __reduce56< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // Output = Alphanumeric => ActionFn(19); + let __sym0 = __pop_Variant1(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action19::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant16(__nt), __end)); + (1, 15) + } + pub(crate) fn __reduce57< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // Output = Stream => ActionFn(20); + let __sym0 = __pop_Variant1(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action20::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant16(__nt), __end)); + (1, 15) + } + pub(crate) fn __reduce58< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // Output? = Output => ActionFn(60); + let __sym0 = __pop_Variant16(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action60::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant17(__nt), __end)); + (1, 16) + } + pub(crate) fn __reduce59< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // Output? = => ActionFn(61); + let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); + let __end = __start.clone(); + let __nt = super::__action61::<>(input, errors, validator, &__start, &__end); + __symbols.push((__start, __Symbol::Variant17(__nt), __end)); + (0, 16) + } + pub(crate) fn __reduce60< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // PeerId = CallInstrValue => ActionFn(22); + let __sym0 = __pop_Variant13(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action22::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 17) + } + pub(crate) fn __reduce61< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // PeerPart = PeerId => ActionFn(17); + let __sym0 = __pop_Variant13(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action17::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant18(__nt), __end)); + (1, 18) + } + pub(crate) fn __reduce62< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // PeerPart = "(", PeerId, ServiceId, ")" => ActionFn(18); + assert!(__symbols.len() >= 4); + let __sym3 = __pop_Variant0(__symbols); + let __sym2 = __pop_Variant13(__symbols); + let __sym1 = __pop_Variant13(__symbols); + let __sym0 = __pop_Variant0(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym3.2.clone(); + let __nt = super::__action18::<>(input, errors, validator, __sym0, __sym1, __sym2, __sym3); + __symbols.push((__start, __Symbol::Variant18(__nt), __end)); + (4, 18) + } + pub(crate) fn __reduce63< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // ScalarIterable = Alphanumeric => ActionFn(46); + let __sym0 = __pop_Variant1(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action46::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant19(__nt), __end)); (1, 19) } + pub(crate) fn __reduce64< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // ScalarIterable = JsonPath => ActionFn(86); + let __sym0 = __pop_Variant3(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action86::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant19(__nt), __end)); + (1, 19) + } + pub(crate) fn __reduce65< + 'err, + 'input, + 'v, + >( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __lookahead_start: Option<&usize>, + __symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>, + _: core::marker::PhantomData<(&'err (), &'input (), &'v ())>, + ) -> (usize, usize) + { + // ServiceId = CallInstrValue => ActionFn(23); + let __sym0 = __pop_Variant13(__symbols); + let __start = __sym0.0.clone(); + let __end = __sym0.2.clone(); + let __nt = super::__action23::<>(input, errors, validator, __sym0); + __symbols.push((__start, __Symbol::Variant13(__nt), __end)); + (1, 20) + } } pub use self::__parse__AIR::AIRParser; @@ -2813,13 +3205,13 @@ fn __action2< (_, peer_part, _): (usize, PeerPart<'input>, usize), (_, function_part, _): (usize, FunctionPart<'input>, usize), (_, args, _): (usize, Vec>, usize), - (_, output, _): (usize, core::option::Option>, usize), + (_, output, _): (usize, core::option::Option>, usize), (_, _, _): (usize, Token<'input>, usize), (_, right, _): (usize, usize, usize), ) -> Box> { { - let output = output.unwrap_or(CallOutputValue::None); + let output = output.map(CallOutputValue::Variable).unwrap_or(CallOutputValue::None); let args = Rc::new(args); let call = Call { peer_part, function_part, args, output }; let span = Span { left, right }; @@ -2838,14 +3230,35 @@ fn __action3< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, + (_, left, _): (usize, usize, usize), (_, _, _): (usize, Token<'input>, usize), (_, _, _): (usize, Token<'input>, usize), - (_, l, _): (usize, Box>, usize), - (_, r, _): (usize, Box>, usize), + (_, arg, _): (usize, ApArgument<'input>, usize), + (_, res, _): (usize, AstVariable<'input>, usize), (_, _, _): (usize, Token<'input>, usize), + (_, right, _): (usize, usize, usize), ) -> Box> { - Box::new(Instruction::Seq(Seq(l, r))) + { + if let ApArgument::JsonPath(json_path) = &arg { + if let AstVariable::Stream(_) = &json_path.variable { + let token = Token::VariableWithJsonPath(json_path.variable.clone(), json_path.path, json_path.should_flatten); + errors.push(make_stream_iterable_error(left, token, right)); + }; + + // Due the json path constraints json path should be flattened in a apply arguments. + if !json_path.should_flatten { + let token = Token::VariableWithJsonPath(json_path.variable.clone(), json_path.path, json_path.should_flatten); + errors.push(make_flattened_error(left, token, right)); + } + } + + let apply = Ap::new(arg, res); + let span = Span { left, right }; + validator.met_ap(&apply, span); + + Box::new(Instruction::Ap(apply)) + } } #[allow(unused_variables)] @@ -2864,7 +3277,7 @@ fn __action4< (_, _, _): (usize, Token<'input>, usize), ) -> Box> { - Box::new(Instruction::Par(Par(l, r))) + Box::new(Instruction::Seq(Seq(l, r))) } #[allow(unused_variables)] @@ -2872,6 +3285,25 @@ fn __action5< 'err, 'input, 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, _, _): (usize, Token<'input>, usize), + (_, _, _): (usize, Token<'input>, usize), + (_, l, _): (usize, Box>, usize), + (_, r, _): (usize, Box>, usize), + (_, _, _): (usize, Token<'input>, usize), +) -> Box> +{ + Box::new(Instruction::Par(Par(l, r))) +} + +#[allow(unused_variables)] +fn __action6< + 'err, + 'input, + 'v, >( input: &'input str, errors: &'err mut Vec, ParserError>>, @@ -2885,7 +3317,7 @@ fn __action5< } #[allow(unused_variables)] -fn __action6< +fn __action7< 'err, 'input, 'v, @@ -2896,7 +3328,7 @@ fn __action6< (_, left, _): (usize, usize, usize), (_, _, _): (usize, Token<'input>, usize), (_, _, _): (usize, Token<'input>, usize), - (_, iterable, _): (usize, IterableValue<'input>, usize), + (_, iterable, _): (usize, IterableScalarValue<'input>, usize), (_, iterator, _): (usize, &'input str, usize), (_, i, _): (usize, Box>, usize), (_, _, _): (usize, Token<'input>, usize), @@ -2905,16 +3337,45 @@ fn __action6< { { let instruction = Rc::new(*i); - let fold = Fold { iterable, iterator, instruction }; + let fold = FoldScalar { iterable, iterator, instruction }; let span = Span { left, right }; - validator.met_fold(&fold, span); + validator.met_fold_scalar(&fold, span); - Box::new(Instruction::Fold(fold)) + Box::new(Instruction::FoldScalar(fold)) } } #[allow(unused_variables)] -fn __action7< +fn __action8< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, left, _): (usize, usize, usize), + (_, _, _): (usize, Token<'input>, usize), + (_, _, _): (usize, Token<'input>, usize), + (_, stream, _): (usize, &'input str, usize), + (_, iterator, _): (usize, &'input str, usize), + (_, i, _): (usize, Box>, usize), + (_, _, _): (usize, Token<'input>, usize), + (_, right, _): (usize, usize, usize), +) -> Box> +{ + { + let instruction = Rc::new(*i); + let fold = FoldStream { stream_name: stream, iterator, instruction }; + let span = Span { left, right }; + validator.met_fold_stream(&fold, span); + + Box::new(Instruction::FoldStream(fold)) + } +} + +#[allow(unused_variables)] +fn __action9< 'err, 'input, 'v, @@ -2940,7 +3401,7 @@ fn __action7< } #[allow(unused_variables)] -fn __action8< +fn __action10< 'err, 'input, 'v, @@ -2959,7 +3420,7 @@ fn __action8< } #[allow(unused_variables)] -fn __action9< +fn __action11< 'err, 'input, 'v, @@ -2987,7 +3448,7 @@ fn __action9< } #[allow(unused_variables)] -fn __action10< +fn __action12< 'err, 'input, 'v, @@ -3015,7 +3476,7 @@ fn __action10< } #[allow(unused_variables)] -fn __action11< +fn __action13< 'err, 'input, 'v, @@ -3030,7 +3491,7 @@ fn __action11< } #[allow(unused_variables)] -fn __action12< +fn __action14< 'err, 'input, 'v, @@ -3047,7 +3508,7 @@ fn __action12< } #[allow(unused_variables)] -fn __action13< +fn __action15< 'err, 'input, 'v, @@ -3062,7 +3523,7 @@ fn __action13< } #[allow(unused_variables)] -fn __action14< +fn __action16< 'err, 'input, 'v, @@ -3080,7 +3541,7 @@ fn __action14< } #[allow(unused_variables)] -fn __action15< +fn __action17< 'err, 'input, 'v, @@ -3095,7 +3556,7 @@ fn __action15< } #[allow(unused_variables)] -fn __action16< +fn __action18< 'err, 'input, 'v, @@ -3112,36 +3573,6 @@ fn __action16< PeerPart::PeerPkWithServiceId(pid, sid) } -#[allow(unused_variables)] -fn __action17< - 'err, - 'input, - 'v, ->( - input: &'input str, - errors: &'err mut Vec, ParserError>>, - validator: &'v mut VariableValidator<'input>, - (_, a, _): (usize, &'input str, usize), -) -> CallOutputValue<'input> -{ - CallOutputValue::Variable(Variable::Scalar(a)) -} - -#[allow(unused_variables)] -fn __action18< - 'err, - 'input, - 'v, ->( - input: &'input str, - errors: &'err mut Vec, ParserError>>, - validator: &'v mut VariableValidator<'input>, - (_, s, _): (usize, &'input str, usize), -) -> CallOutputValue<'input> -{ - CallOutputValue::Variable(Variable::Stream(s)) -} - #[allow(unused_variables)] fn __action19< 'err, @@ -3151,10 +3582,10 @@ fn __action19< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, __0, _): (usize, CallInstrValue<'input>, usize), -) -> CallInstrValue<'input> + (_, a, _): (usize, &'input str, usize), +) -> AstVariable<'input> { - __0 + AstVariable::Scalar(a) } #[allow(unused_variables)] @@ -3166,10 +3597,10 @@ fn __action20< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, __0, _): (usize, CallInstrValue<'input>, usize), -) -> CallInstrValue<'input> + (_, s, _): (usize, &'input str, usize), +) -> AstVariable<'input> { - __0 + AstVariable::Stream(s) } #[allow(unused_variables)] @@ -3196,10 +3627,10 @@ fn __action22< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, l, _): (usize, &'input str, usize), + (_, __0, _): (usize, CallInstrValue<'input>, usize), ) -> CallInstrValue<'input> { - CallInstrValue::Literal(l) + __0 } #[allow(unused_variables)] @@ -3211,10 +3642,10 @@ fn __action23< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, a, _): (usize, &'input str, usize), + (_, __0, _): (usize, CallInstrValue<'input>, usize), ) -> CallInstrValue<'input> { - CallInstrValue::Variable(Variable::Scalar(a)) + __0 } #[allow(unused_variables)] @@ -3226,10 +3657,10 @@ fn __action24< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, s, _): (usize, &'input str, usize), + (_, l, _): (usize, &'input str, usize), ) -> CallInstrValue<'input> { - CallInstrValue::Variable(Variable::Stream(s)) + CallInstrValue::Literal(l) } #[allow(unused_variables)] @@ -3241,9 +3672,39 @@ fn __action25< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, l, _): (usize, usize, usize), - (_, j, _): (usize, (Variable<'input>, &'input str, bool), usize), - (_, r, _): (usize, usize, usize), + (_, a, _): (usize, &'input str, usize), +) -> CallInstrValue<'input> +{ + CallInstrValue::Variable(AstVariable::Scalar(a)) +} + +#[allow(unused_variables)] +fn __action26< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, s, _): (usize, &'input str, usize), +) -> CallInstrValue<'input> +{ + CallInstrValue::Variable(AstVariable::Stream(s)) +} + +#[allow(unused_variables)] +fn __action27< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, left, _): (usize, usize, usize), + (_, j, _): (usize, (AstVariable<'input>, &'input str, bool), usize), + (_, right, _): (usize, usize, usize), ) -> CallInstrValue<'input> { { @@ -3253,14 +3714,15 @@ fn __action25< // Due the json path constraints json path should be flattened in a call triplet. if !should_flatten { let token = Token::VariableWithJsonPath(variable.clone(), path, should_flatten); - errors.push(make_flattened_error(l, token, r)); + errors.push(make_flattened_error(left, token, right)); } - CallInstrValue::JsonPath { variable, path, should_flatten } + + CallInstrValue::JsonPath(JsonPath::new(variable, path, should_flatten)) } } #[allow(unused_variables)] -fn __action26< +fn __action28< 'err, 'input, 'v, @@ -3275,7 +3737,7 @@ fn __action26< } #[allow(unused_variables)] -fn __action27< +fn __action29< 'err, 'input, 'v, @@ -3290,7 +3752,7 @@ fn __action27< } #[allow(unused_variables)] -fn __action28< +fn __action30< 'err, 'input, 'v, @@ -3304,36 +3766,6 @@ fn __action28< CallInstrArgValue::Literal(s) } -#[allow(unused_variables)] -fn __action29< - 'err, - 'input, - 'v, ->( - input: &'input str, - errors: &'err mut Vec, ParserError>>, - validator: &'v mut VariableValidator<'input>, - (_, v, _): (usize, &'input str, usize), -) -> CallInstrArgValue<'input> -{ - CallInstrArgValue::Variable(Variable::Scalar(v)) -} - -#[allow(unused_variables)] -fn __action30< - 'err, - 'input, - 'v, ->( - input: &'input str, - errors: &'err mut Vec, ParserError>>, - validator: &'v mut VariableValidator<'input>, - (_, v, _): (usize, &'input str, usize), -) -> CallInstrArgValue<'input> -{ - CallInstrArgValue::Variable(Variable::Stream(v)) -} - #[allow(unused_variables)] fn __action31< 'err, @@ -3343,10 +3775,10 @@ fn __action31< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, j, _): (usize, (Variable<'input>, &'input str, bool), usize), + (_, v, _): (usize, &'input str, usize), ) -> CallInstrArgValue<'input> { - CallInstrArgValue::JsonPath { variable: j.0, path: j.1, should_flatten: j.2 } + CallInstrArgValue::Variable(AstVariable::Scalar(v)) } #[allow(unused_variables)] @@ -3354,6 +3786,36 @@ fn __action32< 'err, 'input, 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, v, _): (usize, &'input str, usize), +) -> CallInstrArgValue<'input> +{ + CallInstrArgValue::Variable(AstVariable::Stream(v)) +} + +#[allow(unused_variables)] +fn __action33< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, j, _): (usize, (AstVariable<'input>, &'input str, bool), usize), +) -> CallInstrArgValue<'input> +{ + CallInstrArgValue::JsonPath(JsonPath::new(j.0, j.1, j.2)) +} + +#[allow(unused_variables)] +fn __action34< + 'err, + 'input, + 'v, >( input: &'input str, errors: &'err mut Vec, ParserError>>, @@ -3365,7 +3827,7 @@ fn __action32< } #[allow(unused_variables)] -fn __action33< +fn __action35< 'err, 'input, 'v, @@ -3380,7 +3842,7 @@ fn __action33< } #[allow(unused_variables)] -fn __action34< +fn __action36< 'err, 'input, 'v, @@ -3395,7 +3857,22 @@ fn __action34< } #[allow(unused_variables)] -fn __action35< +fn __action37< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, __0, _): (usize, Token<'input>, usize), +) -> CallInstrArgValue<'input> +{ + CallInstrArgValue::EmptyArray +} + +#[allow(unused_variables)] +fn __action38< 'err, 'input, 'v, @@ -3409,56 +3886,164 @@ fn __action35< CallInstrArgValue::LastError(p) } -#[allow(unused_variables)] -fn __action36< - 'err, - 'input, - 'v, ->( - input: &'input str, - errors: &'err mut Vec, ParserError>>, - validator: &'v mut VariableValidator<'input>, - (_, v, _): (usize, &'input str, usize), -) -> IterableValue<'input> -{ - IterableValue::Variable(Variable::Scalar(v)) -} - -#[allow(unused_variables)] -fn __action37< - 'err, - 'input, - 'v, ->( - input: &'input str, - errors: &'err mut Vec, ParserError>>, - validator: &'v mut VariableValidator<'input>, - (_, v, _): (usize, &'input str, usize), -) -> IterableValue<'input> -{ - IterableValue::Variable(Variable::Stream(v)) -} - -#[allow(unused_variables)] -fn __action38< - 'err, - 'input, - 'v, ->( - input: &'input str, - errors: &'err mut Vec, ParserError>>, - validator: &'v mut VariableValidator<'input>, - (_, j, _): (usize, (Variable<'input>, &'input str, bool), usize), -) -> IterableValue<'input> -{ - IterableValue::JsonPath { variable: j.0, path: j.1, should_flatten: j.2 } -} - #[allow(unused_variables)] fn __action39< 'err, 'input, 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, a, _): (usize, &'input str, usize), +) -> ApArgument<'input> +{ + ApArgument::ScalarVariable(a) +} + +#[allow(unused_variables)] +fn __action40< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, j, _): (usize, (AstVariable<'input>, &'input str, bool), usize), +) -> ApArgument<'input> +{ + ApArgument::JsonPath(JsonPath::new(j.0, j.1, j.2)) +} + +#[allow(unused_variables)] +fn __action41< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, n, _): (usize, Number, usize), +) -> ApArgument<'input> +{ + ApArgument::Number(n) +} + +#[allow(unused_variables)] +fn __action42< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, b, _): (usize, bool, usize), +) -> ApArgument<'input> +{ + ApArgument::Boolean(b) +} + +#[allow(unused_variables)] +fn __action43< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, __0, _): (usize, Token<'input>, usize), +) -> ApArgument<'input> +{ + ApArgument::EmptyArray +} + +#[allow(unused_variables)] +fn __action44< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, s, _): (usize, &'input str, usize), +) -> ApArgument<'input> +{ + ApArgument::Literal(s) +} + +#[allow(unused_variables)] +fn __action45< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, p, _): (usize, LastErrorPath, usize), +) -> ApArgument<'input> +{ + ApArgument::LastError(p) +} + +#[allow(unused_variables)] +fn __action46< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, v, _): (usize, &'input str, usize), +) -> IterableScalarValue<'input> +{ + IterableScalarValue::ScalarVariable(v) +} + +#[allow(unused_variables)] +fn __action47< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, l, _): (usize, usize, usize), + (_, j, _): (usize, (AstVariable<'input>, &'input str, bool), usize), + (_, r, _): (usize, usize, usize), +) -> IterableScalarValue<'input> +{ + { + use crate::parser::air::AstVariable::*; + + let variable = j.0; + let path = j.1; + let should_flatten = j.2; + + let scalar_name = match variable { + Stream(name) => { + let token = Token::VariableWithJsonPath(variable, path, should_flatten); + errors.push(make_stream_iterable_error(l, token, r)); + name + } + Scalar(name) => name, + }; + IterableScalarValue::JsonPath { scalar_name, path, should_flatten } + } +} + +#[allow(unused_variables)] +fn __action48< + 'err, + 'input, + 'v, >( input: &'input str, errors: &'err mut Vec, ParserError>>, @@ -3470,7 +4055,7 @@ fn __action39< } #[allow(unused_variables)] -fn __action40< +fn __action49< 'err, 'input, 'v, @@ -3481,11 +4066,11 @@ fn __action40< (_, v, _): (usize, &'input str, usize), ) -> MatchableValue<'input> { - MatchableValue::Variable(Variable::Scalar(v)) + MatchableValue::Variable(AstVariable::Scalar(v)) } #[allow(unused_variables)] -fn __action41< +fn __action50< 'err, 'input, 'v, @@ -3496,11 +4081,11 @@ fn __action41< (_, v, _): (usize, &'input str, usize), ) -> MatchableValue<'input> { - MatchableValue::Variable(Variable::Stream(v)) + MatchableValue::Variable(AstVariable::Stream(v)) } #[allow(unused_variables)] -fn __action42< +fn __action51< 'err, 'input, 'v, @@ -3515,7 +4100,7 @@ fn __action42< } #[allow(unused_variables)] -fn __action43< +fn __action52< 'err, 'input, 'v, @@ -3530,7 +4115,7 @@ fn __action43< } #[allow(unused_variables)] -fn __action44< +fn __action53< 'err, 'input, 'v, @@ -3545,7 +4130,7 @@ fn __action44< } #[allow(unused_variables)] -fn __action45< +fn __action54< 'err, 'input, 'v, @@ -3553,14 +4138,29 @@ fn __action45< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, j, _): (usize, (Variable<'input>, &'input str, bool), usize), + (_, __0, _): (usize, Token<'input>, usize), ) -> MatchableValue<'input> { - MatchableValue::JsonPath { variable: j.0, path: j.1, should_flatten: j.2 } + MatchableValue::EmptyArray } #[allow(unused_variables)] -fn __action46< +fn __action55< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + (_, j, _): (usize, (AstVariable<'input>, &'input str, bool), usize), +) -> MatchableValue<'input> +{ + MatchableValue::JsonPath(JsonPath::new(j.0, j.1, j.2)) +} + +#[allow(unused_variables)] +fn __action56< 'err, 'input, 'v, @@ -3576,7 +4176,7 @@ fn __action46< } #[allow(unused_variables)] -fn __action47< +fn __action57< 'err, 'input, 'v, @@ -3591,7 +4191,7 @@ fn __action47< } #[allow(unused_variables)] -fn __action48< +fn __action58< 'err, 'input, 'v, @@ -3606,7 +4206,7 @@ fn __action48< } #[allow(unused_variables)] -fn __action49< +fn __action59< 'err, 'input, 'v, @@ -3622,7 +4222,7 @@ fn __action49< } #[allow(unused_variables)] -fn __action50< +fn __action60< 'err, 'input, 'v, @@ -3630,14 +4230,14 @@ fn __action50< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - (_, __0, _): (usize, CallOutputValue<'input>, usize), -) -> core::option::Option> + (_, __0, _): (usize, AstVariable<'input>, usize), +) -> core::option::Option> { Some(__0) } #[allow(unused_variables)] -fn __action51< +fn __action61< 'err, 'input, 'v, @@ -3647,13 +4247,13 @@ fn __action51< validator: &'v mut VariableValidator<'input>, __lookbehind: &usize, __lookahead: &usize, -) -> core::option::Option> +) -> core::option::Option> { None } #[allow(unused_variables)] -fn __action52< +fn __action62< 'err, 'input, 'v, @@ -3669,7 +4269,7 @@ fn __action52< } #[allow(unused_variables)] -fn __action53< +fn __action63< 'err, 'input, 'v, @@ -3684,7 +4284,7 @@ fn __action53< } #[allow(unused_variables)] -fn __action54< +fn __action64< 'err, 'input, 'v, @@ -3700,7 +4300,7 @@ fn __action54< } #[allow(unused_variables)] -fn __action55< +fn __action65< 'err, 'input, 'v, @@ -3713,14 +4313,14 @@ fn __action55< { let __start0 = __0.0.clone(); let __end0 = __0.2.clone(); - let __temp0 = __action48( + let __temp0 = __action58( input, errors, validator, __0, ); let __temp0 = (__start0, __temp0, __end0); - __action53( + __action63( input, errors, validator, @@ -3729,7 +4329,7 @@ fn __action55< } #[allow(unused_variables)] -fn __action56< +fn __action66< 'err, 'input, 'v, @@ -3743,14 +4343,14 @@ fn __action56< { let __start0 = __1.0.clone(); let __end0 = __1.2.clone(); - let __temp0 = __action48( + let __temp0 = __action58( input, errors, validator, __1, ); let __temp0 = (__start0, __temp0, __end0); - __action54( + __action64( input, errors, validator, @@ -3760,7 +4360,7 @@ fn __action56< } #[allow(unused_variables)] -fn __action57< +fn __action67< 'err, 'input, 'v, @@ -3774,7 +4374,7 @@ fn __action57< { let __start0 = __0.2.clone(); let __end0 = __1.0.clone(); - let __temp0 = __action46( + let __temp0 = __action56( input, errors, validator, @@ -3782,7 +4382,7 @@ fn __action57< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action12( + __action14( input, errors, validator, @@ -3793,7 +4393,7 @@ fn __action57< } #[allow(unused_variables)] -fn __action58< +fn __action68< 'err, 'input, 'v, @@ -3808,14 +4408,14 @@ fn __action58< { let __start0 = __1.0.clone(); let __end0 = __1.2.clone(); - let __temp0 = __action47( + let __temp0 = __action57( input, errors, validator, __1, ); let __temp0 = (__start0, __temp0, __end0); - __action12( + __action14( input, errors, validator, @@ -3826,7 +4426,7 @@ fn __action58< } #[allow(unused_variables)] -fn __action59< +fn __action69< 'err, 'input, 'v, @@ -3834,13 +4434,13 @@ fn __action59< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - __0: (usize, (Variable<'input>, &'input str, bool), usize), + __0: (usize, (AstVariable<'input>, &'input str, bool), usize), __1: (usize, usize, usize), ) -> CallInstrValue<'input> { let __start0 = __0.0.clone(); let __end0 = __0.0.clone(); - let __temp0 = __action52( + let __temp0 = __action62( input, errors, validator, @@ -3848,7 +4448,7 @@ fn __action59< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action25( + __action27( input, errors, validator, @@ -3859,7 +4459,7 @@ fn __action59< } #[allow(unused_variables)] -fn __action60< +fn __action70< 'err, 'input, 'v, @@ -3872,14 +4472,14 @@ fn __action60< __2: (usize, PeerPart<'input>, usize), __3: (usize, FunctionPart<'input>, usize), __4: (usize, Vec>, usize), - __5: (usize, core::option::Option>, usize), + __5: (usize, core::option::Option>, usize), __6: (usize, Token<'input>, usize), __7: (usize, usize, usize), ) -> Box> { let __start0 = __0.0.clone(); let __end0 = __0.0.clone(); - let __temp0 = __action52( + let __temp0 = __action62( input, errors, validator, @@ -3904,7 +4504,7 @@ fn __action60< } #[allow(unused_variables)] -fn __action61< +fn __action71< 'err, 'input, 'v, @@ -3914,16 +4514,15 @@ fn __action61< validator: &'v mut VariableValidator<'input>, __0: (usize, Token<'input>, usize), __1: (usize, Token<'input>, usize), - __2: (usize, IterableValue<'input>, usize), - __3: (usize, &'input str, usize), - __4: (usize, Box>, usize), - __5: (usize, Token<'input>, usize), - __6: (usize, usize, usize), + __2: (usize, ApArgument<'input>, usize), + __3: (usize, AstVariable<'input>, usize), + __4: (usize, Token<'input>, usize), + __5: (usize, usize, usize), ) -> Box> { let __start0 = __0.0.clone(); let __end0 = __0.0.clone(); - let __temp0 = __action52( + let __temp0 = __action62( input, errors, validator, @@ -3931,7 +4530,7 @@ fn __action61< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action6( + __action3( input, errors, validator, @@ -3942,12 +4541,11 @@ fn __action61< __3, __4, __5, - __6, ) } #[allow(unused_variables)] -fn __action62< +fn __action72< 'err, 'input, 'v, @@ -3957,14 +4555,16 @@ fn __action62< validator: &'v mut VariableValidator<'input>, __0: (usize, Token<'input>, usize), __1: (usize, Token<'input>, usize), - __2: (usize, &'input str, usize), - __3: (usize, Token<'input>, usize), - __4: (usize, usize, usize), + __2: (usize, IterableScalarValue<'input>, usize), + __3: (usize, &'input str, usize), + __4: (usize, Box>, usize), + __5: (usize, Token<'input>, usize), + __6: (usize, usize, usize), ) -> Box> { let __start0 = __0.0.clone(); let __end0 = __0.0.clone(); - let __temp0 = __action52( + let __temp0 = __action62( input, errors, validator, @@ -3982,11 +4582,13 @@ fn __action62< __2, __3, __4, + __5, + __6, ) } #[allow(unused_variables)] -fn __action63< +fn __action73< 'err, 'input, 'v, @@ -3996,8 +4598,8 @@ fn __action63< validator: &'v mut VariableValidator<'input>, __0: (usize, Token<'input>, usize), __1: (usize, Token<'input>, usize), - __2: (usize, MatchableValue<'input>, usize), - __3: (usize, MatchableValue<'input>, usize), + __2: (usize, &'input str, usize), + __3: (usize, &'input str, usize), __4: (usize, Box>, usize), __5: (usize, Token<'input>, usize), __6: (usize, usize, usize), @@ -4005,7 +4607,48 @@ fn __action63< { let __start0 = __0.0.clone(); let __end0 = __0.0.clone(); - let __temp0 = __action52( + let __temp0 = __action62( + input, + errors, + validator, + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action8( + input, + errors, + validator, + __temp0, + __0, + __1, + __2, + __3, + __4, + __5, + __6, + ) +} + +#[allow(unused_variables)] +fn __action74< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __0: (usize, Token<'input>, usize), + __1: (usize, Token<'input>, usize), + __2: (usize, &'input str, usize), + __3: (usize, Token<'input>, usize), + __4: (usize, usize, usize), +) -> Box> +{ + let __start0 = __0.0.clone(); + let __end0 = __0.0.clone(); + let __temp0 = __action62( input, errors, validator, @@ -4023,13 +4666,11 @@ fn __action63< __2, __3, __4, - __5, - __6, ) } #[allow(unused_variables)] -fn __action64< +fn __action75< 'err, 'input, 'v, @@ -4048,7 +4689,7 @@ fn __action64< { let __start0 = __0.0.clone(); let __end0 = __0.0.clone(); - let __temp0 = __action52( + let __temp0 = __action62( input, errors, validator, @@ -4056,7 +4697,7 @@ fn __action64< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action10( + __action11( input, errors, validator, @@ -4072,7 +4713,7 @@ fn __action64< } #[allow(unused_variables)] -fn __action65< +fn __action76< 'err, 'input, 'v, @@ -4080,12 +4721,18 @@ fn __action65< input: &'input str, errors: &'err mut Vec, ParserError>>, validator: &'v mut VariableValidator<'input>, - __0: (usize, (Variable<'input>, &'input str, bool), usize), -) -> CallInstrValue<'input> + __0: (usize, Token<'input>, usize), + __1: (usize, Token<'input>, usize), + __2: (usize, MatchableValue<'input>, usize), + __3: (usize, MatchableValue<'input>, usize), + __4: (usize, Box>, usize), + __5: (usize, Token<'input>, usize), + __6: (usize, usize, usize), +) -> Box> { - let __start0 = __0.2.clone(); - let __end0 = __0.2.clone(); - let __temp0 = __action49( + let __start0 = __0.0.clone(); + let __end0 = __0.0.clone(); + let __temp0 = __action62( input, errors, validator, @@ -4093,7 +4740,77 @@ fn __action65< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action59( + __action12( + input, + errors, + validator, + __temp0, + __0, + __1, + __2, + __3, + __4, + __5, + __6, + ) +} + +#[allow(unused_variables)] +fn __action77< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __0: (usize, (AstVariable<'input>, &'input str, bool), usize), + __1: (usize, usize, usize), +) -> IterableScalarValue<'input> +{ + let __start0 = __0.0.clone(); + let __end0 = __0.0.clone(); + let __temp0 = __action62( + input, + errors, + validator, + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action47( + input, + errors, + validator, + __temp0, + __0, + __1, + ) +} + +#[allow(unused_variables)] +fn __action78< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __0: (usize, (AstVariable<'input>, &'input str, bool), usize), +) -> CallInstrValue<'input> +{ + let __start0 = __0.2.clone(); + let __end0 = __0.2.clone(); + let __temp0 = __action59( + input, + errors, + validator, + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action69( input, errors, validator, @@ -4103,7 +4820,7 @@ fn __action65< } #[allow(unused_variables)] -fn __action66< +fn __action79< 'err, 'input, 'v, @@ -4116,13 +4833,13 @@ fn __action66< __2: (usize, PeerPart<'input>, usize), __3: (usize, FunctionPart<'input>, usize), __4: (usize, Vec>, usize), - __5: (usize, core::option::Option>, usize), + __5: (usize, core::option::Option>, usize), __6: (usize, Token<'input>, usize), ) -> Box> { let __start0 = __6.2.clone(); let __end0 = __6.2.clone(); - let __temp0 = __action49( + let __temp0 = __action59( input, errors, validator, @@ -4130,7 +4847,7 @@ fn __action66< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action60( + __action70( input, errors, validator, @@ -4146,7 +4863,7 @@ fn __action66< } #[allow(unused_variables)] -fn __action67< +fn __action80< 'err, 'input, 'v, @@ -4156,15 +4873,14 @@ fn __action67< validator: &'v mut VariableValidator<'input>, __0: (usize, Token<'input>, usize), __1: (usize, Token<'input>, usize), - __2: (usize, IterableValue<'input>, usize), - __3: (usize, &'input str, usize), - __4: (usize, Box>, usize), - __5: (usize, Token<'input>, usize), + __2: (usize, ApArgument<'input>, usize), + __3: (usize, AstVariable<'input>, usize), + __4: (usize, Token<'input>, usize), ) -> Box> { - let __start0 = __5.2.clone(); - let __end0 = __5.2.clone(); - let __temp0 = __action49( + let __start0 = __4.2.clone(); + let __end0 = __4.2.clone(); + let __temp0 = __action59( input, errors, validator, @@ -4172,7 +4888,47 @@ fn __action67< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action61( + __action71( + input, + errors, + validator, + __0, + __1, + __2, + __3, + __4, + __temp0, + ) +} + +#[allow(unused_variables)] +fn __action81< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __0: (usize, Token<'input>, usize), + __1: (usize, Token<'input>, usize), + __2: (usize, IterableScalarValue<'input>, usize), + __3: (usize, &'input str, usize), + __4: (usize, Box>, usize), + __5: (usize, Token<'input>, usize), +) -> Box> +{ + let __start0 = __5.2.clone(); + let __end0 = __5.2.clone(); + let __temp0 = __action59( + input, + errors, + validator, + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action72( input, errors, validator, @@ -4187,7 +4943,48 @@ fn __action67< } #[allow(unused_variables)] -fn __action68< +fn __action82< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __0: (usize, Token<'input>, usize), + __1: (usize, Token<'input>, usize), + __2: (usize, &'input str, usize), + __3: (usize, &'input str, usize), + __4: (usize, Box>, usize), + __5: (usize, Token<'input>, usize), +) -> Box> +{ + let __start0 = __5.2.clone(); + let __end0 = __5.2.clone(); + let __temp0 = __action59( + input, + errors, + validator, + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action73( + input, + errors, + validator, + __0, + __1, + __2, + __3, + __4, + __5, + __temp0, + ) +} + +#[allow(unused_variables)] +fn __action83< 'err, 'input, 'v, @@ -4203,7 +5000,7 @@ fn __action68< { let __start0 = __3.2.clone(); let __end0 = __3.2.clone(); - let __temp0 = __action49( + let __temp0 = __action59( input, errors, validator, @@ -4211,7 +5008,7 @@ fn __action68< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action62( + __action74( input, errors, validator, @@ -4224,7 +5021,7 @@ fn __action68< } #[allow(unused_variables)] -fn __action69< +fn __action84< 'err, 'input, 'v, @@ -4242,7 +5039,7 @@ fn __action69< { let __start0 = __5.2.clone(); let __end0 = __5.2.clone(); - let __temp0 = __action49( + let __temp0 = __action59( input, errors, validator, @@ -4250,7 +5047,7 @@ fn __action69< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action63( + __action75( input, errors, validator, @@ -4265,7 +5062,7 @@ fn __action69< } #[allow(unused_variables)] -fn __action70< +fn __action85< 'err, 'input, 'v, @@ -4283,7 +5080,7 @@ fn __action70< { let __start0 = __5.2.clone(); let __end0 = __5.2.clone(); - let __temp0 = __action49( + let __temp0 = __action59( input, errors, validator, @@ -4291,7 +5088,7 @@ fn __action70< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action64( + __action76( input, errors, validator, @@ -4306,7 +5103,38 @@ fn __action70< } #[allow(unused_variables)] -fn __action71< +fn __action86< + 'err, + 'input, + 'v, +>( + input: &'input str, + errors: &'err mut Vec, ParserError>>, + validator: &'v mut VariableValidator<'input>, + __0: (usize, (AstVariable<'input>, &'input str, bool), usize), +) -> IterableScalarValue<'input> +{ + let __start0 = __0.2.clone(); + let __end0 = __0.2.clone(); + let __temp0 = __action59( + input, + errors, + validator, + &__start0, + &__end0, + ); + let __temp0 = (__start0, __temp0, __end0); + __action77( + input, + errors, + validator, + __0, + __temp0, + ) +} + +#[allow(unused_variables)] +fn __action87< 'err, 'input, 'v, @@ -4319,20 +5147,20 @@ fn __action71< __2: (usize, PeerPart<'input>, usize), __3: (usize, FunctionPart<'input>, usize), __4: (usize, Vec>, usize), - __5: (usize, CallOutputValue<'input>, usize), + __5: (usize, AstVariable<'input>, usize), __6: (usize, Token<'input>, usize), ) -> Box> { let __start0 = __5.0.clone(); let __end0 = __5.2.clone(); - let __temp0 = __action50( + let __temp0 = __action60( input, errors, validator, __5, ); let __temp0 = (__start0, __temp0, __end0); - __action66( + __action79( input, errors, validator, @@ -4347,7 +5175,7 @@ fn __action71< } #[allow(unused_variables)] -fn __action72< +fn __action88< 'err, 'input, 'v, @@ -4365,7 +5193,7 @@ fn __action72< { let __start0 = __4.2.clone(); let __end0 = __5.0.clone(); - let __temp0 = __action51( + let __temp0 = __action61( input, errors, validator, @@ -4373,7 +5201,7 @@ fn __action72< &__end0, ); let __temp0 = (__start0, __temp0, __end0); - __action66( + __action79( input, errors, validator, diff --git a/crates/air-parser/src/parser/air_parser.rs b/crates/air-parser/src/parser/air_parser.rs index 8445e833..923f3483 100644 --- a/crates/air-parser/src/parser/air_parser.rs +++ b/crates/air-parser/src/parser/air_parser.rs @@ -127,6 +127,9 @@ fn parser_error_to_label(file_id: usize, error: ParserError) -> Label { CallArgsNotFlattened(start, end) => { Label::primary(file_id, start..end).with_message(error.to_string()) } + JsonPathAppliedToStream(start, end) => { + Label::primary(file_id, start..end).with_message(error.to_string()) + } UndefinedIterable(start, end, _) => { Label::primary(file_id, start..end).with_message(error.to_string()) } @@ -178,18 +181,32 @@ fn lexical_error_to_label(file_id: usize, error: LexerError) -> Label { } } +macro_rules! make_user_error( + ($error_type:ident, $start_pos: ident, $token:ident, $end_pos: ident) => { { + let error = ParserError::$error_type($start_pos, $end_pos); + let error = ParseError::User { error }; + + let dropped_tokens = vec![($start_pos, $token, $end_pos)]; + + ErrorRecovery { + error, + dropped_tokens, + } + }} +); + pub(super) fn make_flattened_error( start_pos: usize, token: Token<'_>, end_pos: usize, ) -> ErrorRecovery, ParserError> { - let error = ParserError::CallArgsNotFlattened(start_pos, end_pos); - let error = ParseError::User { error }; - - let dropped_tokens = vec![(start_pos, token, end_pos)]; - - ErrorRecovery { - error, - dropped_tokens, - } + make_user_error!(CallArgsNotFlattened, start_pos, token, end_pos) +} + +pub(super) fn make_stream_iterable_error( + start_pos: usize, + token: Token<'_>, + end_pos: usize, +) -> ErrorRecovery, ParserError> { + make_user_error!(JsonPathAppliedToStream, start_pos, token, end_pos) } diff --git a/crates/air-parser/src/parser/ast.rs b/crates/air-parser/src/parser/ast.rs index 68047826..b2890d9c 100644 --- a/crates/air-parser/src/parser/ast.rs +++ b/crates/air-parser/src/parser/ast.rs @@ -14,11 +14,12 @@ * limitations under the License. */ +mod impls; mod traits; +pub use crate::parser::lexer::AstVariable; pub use crate::parser::lexer::LastErrorPath; pub use crate::parser::lexer::Number; -pub use crate::parser::lexer::Variable; use serde::Deserialize; use serde::Serialize; @@ -30,12 +31,14 @@ use std::rc::Rc; pub enum Instruction<'i> { Null(Null), Call(Call<'i>), + Ap(Ap<'i>), Seq(Seq<'i>), Par(Par<'i>), Xor(Xor<'i>), Match(Match<'i>), MisMatch(MisMatch<'i>), - Fold(Fold<'i>), + FoldScalar(FoldScalar<'i>), + FoldStream(FoldStream<'i>), Next(Next<'i>), Error, } @@ -60,16 +63,29 @@ pub struct Call<'i> { pub output: CallOutputValue<'i>, } +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub enum ApArgument<'i> { + ScalarVariable(&'i str), + JsonPath(JsonPath<'i>), + Number(Number), + Boolean(bool), + Literal(&'i str), + EmptyArray, + LastError(LastErrorPath), +} + +#[derive(Serialize, Debug, PartialEq)] +pub struct Ap<'i> { + pub argument: ApArgument<'i>, + pub result: AstVariable<'i>, +} + #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum CallInstrValue<'i> { InitPeerId, Literal(&'i str), - Variable(Variable<'i>), - JsonPath { - variable: Variable<'i>, - path: &'i str, - should_flatten: bool, - }, + Variable(AstVariable<'i>), + JsonPath(JsonPath<'i>), } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] @@ -79,19 +95,16 @@ pub enum CallInstrArgValue<'i> { Literal(&'i str), Number(Number), Boolean(bool), - Variable(Variable<'i>), - JsonPath { - variable: Variable<'i>, - path: &'i str, - should_flatten: bool, - }, + EmptyArray, // only empty arrays are allowed now + Variable(AstVariable<'i>), + JsonPath(JsonPath<'i>), } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -pub enum IterableValue<'i> { - Variable(Variable<'i>), +pub enum IterableScalarValue<'i> { + ScalarVariable(&'i str), JsonPath { - variable: Variable<'i>, + scalar_name: &'i str, path: &'i str, should_flatten: bool, }, @@ -103,17 +116,14 @@ pub enum MatchableValue<'i> { Literal(&'i str), Number(Number), Boolean(bool), - Variable(Variable<'i>), - JsonPath { - variable: Variable<'i>, - path: &'i str, - should_flatten: bool, - }, + EmptyArray, + Variable(AstVariable<'i>), + JsonPath(JsonPath<'i>), } #[derive(Serialize, Debug, PartialEq, Clone)] pub enum CallOutputValue<'i> { - Variable(Variable<'i>), + Variable(AstVariable<'i>), None, } @@ -141,8 +151,15 @@ pub struct MisMatch<'i> { } #[derive(Serialize, Debug, PartialEq)] -pub struct Fold<'i> { - pub iterable: IterableValue<'i>, +pub struct FoldScalar<'i> { + pub iterable: IterableScalarValue<'i>, + pub iterator: &'i str, + pub instruction: Rc>, +} + +#[derive(Serialize, Debug, PartialEq)] +pub struct FoldStream<'i> { + pub stream_name: &'i str, pub iterator: &'i str, pub instruction: Rc>, } @@ -152,3 +169,10 @@ pub struct Next<'i>(pub &'i str); #[derive(Serialize, Debug, PartialEq)] pub struct Null; + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub struct JsonPath<'i> { + pub variable: AstVariable<'i>, + pub path: &'i str, + pub should_flatten: bool, +} diff --git a/crates/air-parser/src/parser/ast/impls.rs b/crates/air-parser/src/parser/ast/impls.rs new file mode 100644 index 00000000..ea6b73a0 --- /dev/null +++ b/crates/air-parser/src/parser/ast/impls.rs @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +impl<'i> Ap<'i> { + pub fn new(argument: ApArgument<'i>, result: AstVariable<'i>) -> Self { + Self { argument, result } + } +} + +impl<'i> JsonPath<'i> { + pub fn new(variable: AstVariable<'i>, path: &'i str, should_flatten: bool) -> Self { + Self { + variable, + path, + should_flatten, + } + } +} diff --git a/crates/air-parser/src/parser/ast/traits.rs b/crates/air-parser/src/parser/ast/traits.rs index 373219fe..87d1fa1d 100644 --- a/crates/air-parser/src/parser/ast/traits.rs +++ b/crates/air-parser/src/parser/ast/traits.rs @@ -28,27 +28,13 @@ impl fmt::Display for CallInstrArgValue<'_> { Literal(str) => write!(f, r#""{}""#, str), Number(number) => write!(f, "{}", number), Boolean(bool) => write!(f, "{}", bool), + EmptyArray => write!(f, "[]"), Variable(str) => write!(f, "{}", str), - JsonPath { - variable, - path, - should_flatten, - } => print_json_path(variable, path, should_flatten, f), + JsonPath(json_path) => write!(f, "{}", json_path), } } } -fn print_json_path<'a>( - variable: &Variable<'a>, - path: &str, - should_flatten: &bool, - f: &mut fmt::Formatter, -) -> fmt::Result { - let maybe_flatten_char = if *should_flatten { "!" } else { "" }; - - write!(f, "{}.{}{}", variable, path, maybe_flatten_char) -} - impl fmt::Display for CallInstrValue<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use CallInstrValue::*; @@ -57,26 +43,28 @@ impl fmt::Display for CallInstrValue<'_> { InitPeerId => write!(f, "%init_peer_id%"), Literal(str) => write!(f, r#""{}""#, str), Variable(str) => write!(f, "{}", str), - JsonPath { - variable, - path, - should_flatten, - } => print_json_path(variable, path, should_flatten, f), + JsonPath(json_path) => write!(f, "{}", json_path), } } } -impl fmt::Display for IterableValue<'_> { +impl fmt::Display for IterableScalarValue<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use IterableValue::*; + use IterableScalarValue::*; match self { - Variable(str) => write!(f, "{}", str), + ScalarVariable(str) => write!(f, "{}", str), JsonPath { - variable, + scalar_name, path, should_flatten, - } => print_json_path(variable, path, should_flatten, f), + } => write!( + f, + "{}.{}{}", + scalar_name, + path, + maybe_flatten_char(*should_flatten) + ), } } } @@ -90,12 +78,9 @@ impl fmt::Display for MatchableValue<'_> { Literal(str) => write!(f, r#""{}""#, str), Number(number) => write!(f, "{}", number), Boolean(bool) => write!(f, "{}", bool), + EmptyArray => write!(f, "[]"), Variable(str) => write!(f, "{}", str), - JsonPath { - variable, - path, - should_flatten, - } => print_json_path(variable, path, should_flatten, f), + JsonPath(json_path) => write!(f, "{}", json_path), } } } @@ -142,12 +127,14 @@ impl fmt::Display for Instruction<'_> { match self { Null(null) => write!(f, "{}", null), Call(call) => write!(f, "{}", call), + Ap(ap) => write!(f, "{}", ap), Seq(seq) => write!(f, "{}", seq), Par(par) => write!(f, "{}", par), Xor(xor) => write!(f, "{}", xor), Match(match_) => write!(f, "{}", match_), MisMatch(mismatch) => write!(f, "{}", mismatch), - Fold(fold) => write!(f, "{}", fold), + FoldScalar(fold) => write!(f, "{}", fold), + FoldStream(fold) => write!(f, "{}", fold), Next(next) => write!(f, "{}", next), Error => Ok(()), } @@ -167,12 +154,38 @@ impl fmt::Display for Call<'_> { } } -impl fmt::Display for Fold<'_> { +impl fmt::Display for Ap<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ap {} {}", self.argument, self.result) + } +} + +impl fmt::Display for ApArgument<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ApArgument::ScalarVariable(name) => write!(f, "{}", name), + ApArgument::JsonPath(json_path) => write!(f, "{}", json_path), + ApArgument::LastError(error_path) => write!(f, "{}", error_path), + ApArgument::Number(value) => write!(f, "{}", value), + ApArgument::Boolean(value) => write!(f, "{}", value), + ApArgument::Literal(value) => write!(f, "{}", value), + ApArgument::EmptyArray => write!(f, "[]"), + } + } +} + +impl fmt::Display for FoldScalar<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "fold {} {}", self.iterable, self.iterator) } } +impl fmt::Display for FoldStream<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "fold {} {}", self.stream_name, self.iterator) + } +} + impl fmt::Display for Seq<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "seq") @@ -214,3 +227,23 @@ impl fmt::Display for Next<'_> { write!(f, "next") } } + +impl fmt::Display for JsonPath<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}.{}{}", + self.variable, + self.path, + maybe_flatten_char(self.should_flatten) + ) + } +} + +fn maybe_flatten_char(should_flatten: bool) -> &'static str { + if should_flatten { + "!" + } else { + "" + } +} diff --git a/crates/air-parser/src/parser/errors.rs b/crates/air-parser/src/parser/errors.rs index a776870c..5952a150 100644 --- a/crates/air-parser/src/parser/errors.rs +++ b/crates/air-parser/src/parser/errors.rs @@ -22,9 +22,14 @@ pub enum ParserError { #[error("{0}")] LexerError(#[from] LexerError), - #[error("while using json path in call triplet, result should be flattened, add ! at the end")] + #[error( + "while using json path in this position, result should be flattened, add ! at the end" + )] CallArgsNotFlattened(usize, usize), + #[error("json path can't be applied to streams in this position")] + JsonPathAppliedToStream(usize, usize), + #[error("variable '{2}' wasn't defined")] UndefinedVariable(usize, usize, String), diff --git a/crates/air-parser/src/parser/lexer/air_lexer.rs b/crates/air-parser/src/parser/lexer/air_lexer.rs index 13416d89..d26ccb08 100644 --- a/crates/air-parser/src/parser/lexer/air_lexer.rs +++ b/crates/air-parser/src/parser/lexer/air_lexer.rs @@ -26,6 +26,7 @@ pub type Spanned = Result<(Loc, Token, Loc), Error>; pub struct AIRLexer<'input> { input: &'input str, + open_square_bracket_met: bool, chars: Peekable>, } @@ -41,6 +42,7 @@ impl<'input> AIRLexer<'input> { pub fn new(input: &'input str) -> Self { Self { input, + open_square_bracket_met: false, chars: input.char_indices().peekable(), } } @@ -51,8 +53,18 @@ impl<'input> AIRLexer<'input> { '(' => return Some(Ok((start_pos, Token::OpenRoundBracket, start_pos + 1))), ')' => return Some(Ok((start_pos, Token::CloseRoundBracket, start_pos + 1))), - '[' => return Some(Ok((start_pos, Token::OpenSquareBracket, start_pos + 1))), - ']' => return Some(Ok((start_pos, Token::CloseSquareBracket, start_pos + 1))), + '[' => { + return if !self.open_square_bracket_met { + self.open_square_bracket_met = true; + Some(Ok((start_pos, Token::OpenSquareBracket, start_pos + 1))) + } else { + self.tokenize_string(start_pos, true) + } + } + ']' => { + self.open_square_bracket_met = false; + return Some(Ok((start_pos, Token::CloseSquareBracket, start_pos + 1))); + } ';' => self.skip_comment(), @@ -60,7 +72,7 @@ impl<'input> AIRLexer<'input> { '"' => return self.tokenize_string_literal(start_pos), - _ => return self.tokenize_string(start_pos), + _ => return self.tokenize_string(start_pos, false), } } @@ -70,7 +82,7 @@ impl<'input> AIRLexer<'input> { fn skip_comment(&mut self) { const NEW_LINE: char = '\n'; // TODO: consider '\n\r' - while let Some((_, ch)) = self.chars.next() { + for (_, ch) in &mut self.chars { if ch == NEW_LINE { break; } @@ -82,7 +94,7 @@ impl<'input> AIRLexer<'input> { &mut self, start_pos: usize, ) -> Option, usize, LexerError>> { - while let Some((pos, ch)) = self.chars.next() { + for (pos, ch) in &mut self.chars { if ch == '"' { // + 1 to count an open double quote let string_size = pos - start_pos + 1; @@ -102,8 +114,9 @@ impl<'input> AIRLexer<'input> { fn tokenize_string( &mut self, start_pos: usize, + open_square_bracket_met: bool, ) -> Option, usize, LexerError>> { - let end_pos = self.advance_to_token_end(start_pos); + let end_pos = self.advance_to_token_end(start_pos, open_square_bracket_met); // this slicing is safe here because borders come from the chars iterator let token_str = &self.input[start_pos..end_pos]; @@ -117,10 +130,10 @@ impl<'input> AIRLexer<'input> { Some(Ok((start_pos, token, start_pos + token_str_len))) } - fn advance_to_token_end(&mut self, start_pos: usize) -> usize { + fn advance_to_token_end(&mut self, start_pos: usize, square_met: bool) -> usize { let mut end_pos = start_pos; let mut round_brackets_balance: i64 = 0; - let mut square_brackets_balance: i64 = 0; + let mut square_brackets_balance: i64 = if square_met { 1 } else { 0 }; while let Some((pos, ch)) = self.chars.peek() { end_pos = *pos; @@ -176,6 +189,7 @@ fn string_to_token(input: &str, start_pos: usize) -> LexerResult { "" => Err(LexerError::EmptyString(start_pos, start_pos)), CALL_INSTR => Ok(Token::Call), + AP_INSTR => Ok(Token::Ap), SEQ_INSTR => Ok(Token::Seq), PAR_INSTR => Ok(Token::Par), NULL_INSTR => Ok(Token::Null), @@ -185,6 +199,8 @@ fn string_to_token(input: &str, start_pos: usize) -> LexerResult { MATCH_INSTR => Ok(Token::Match), MISMATCH_INSTR => Ok(Token::MisMatch), + SQUARE_BRACKETS => Ok(Token::SquareBrackets), + INIT_PEER_ID => Ok(Token::InitPeerId), _ if input.starts_with(LAST_ERROR) => parse_last_error(input, start_pos), @@ -218,6 +234,7 @@ fn parse_last_error(input: &str, start_pos: usize) -> LexerResult> { } const CALL_INSTR: &str = "call"; +const AP_INSTR: &str = "ap"; const SEQ_INSTR: &str = "seq"; const PAR_INSTR: &str = "par"; const NULL_INSTR: &str = "null"; @@ -230,5 +247,7 @@ const MISMATCH_INSTR: &str = "mismatch"; const INIT_PEER_ID: &str = "%init_peer_id%"; const LAST_ERROR: &str = "%last_error%"; +const SQUARE_BRACKETS: &str = "[]"; + const TRUE_VALUE: &str = "true"; const FALSE_VALUE: &str = "false"; diff --git a/crates/air-parser/src/parser/lexer/call_variable_parser.rs b/crates/air-parser/src/parser/lexer/call_variable_parser.rs index 19449fa6..0bf05b5d 100644 --- a/crates/air-parser/src/parser/lexer/call_variable_parser.rs +++ b/crates/air-parser/src/parser/lexer/call_variable_parser.rs @@ -14,10 +14,10 @@ * limitations under the License. */ +use super::AstVariable; use super::LexerError; use super::LexerResult; use super::Token; -use super::Variable; use std::convert::TryInto; use std::iter::Peekable; @@ -272,12 +272,12 @@ impl<'input> CallVariableParser<'input> { self.current_pos() == self.string_to_parse.len() - 1 } - fn to_variable<'v>(&self, variable_name: &'v str) -> Variable<'v> { + fn to_variable<'v>(&self, variable_name: &'v str) -> AstVariable<'v> { if self.state.is_first_stream_tag { // TODO: cut the stream tag after the refactoring. - Variable::Stream(variable_name) + AstVariable::Stream(variable_name) } else { - Variable::Scalar(variable_name) + AstVariable::Scalar(variable_name) } } @@ -297,19 +297,16 @@ impl<'input> CallVariableParser<'input> { } (false, false) => { if self.state.is_first_stream_tag { - Ok(Token::Stream(&self.string_to_parse)) + Ok(Token::Stream(self.string_to_parse)) } else { - Ok(Token::Alphanumeric(&self.string_to_parse)) + Ok(Token::Alphanumeric(self.string_to_parse)) } } (false, true) => { let json_path_start_pos = self.state.first_dot_met_pos.unwrap(); let should_flatten = self.state.flattening_met; - let (variable, json_path) = to_variable_and_path( - &self.string_to_parse, - json_path_start_pos, - should_flatten, - ); + let (variable, json_path) = + to_variable_and_path(self.string_to_parse, json_path_start_pos, should_flatten); let variable = self.to_variable(variable); Ok(Token::VariableWithJsonPath( diff --git a/crates/air-parser/src/parser/lexer/mod.rs b/crates/air-parser/src/parser/lexer/mod.rs index 84f42760..d7ff3e7b 100644 --- a/crates/air-parser/src/parser/lexer/mod.rs +++ b/crates/air-parser/src/parser/lexer/mod.rs @@ -25,10 +25,10 @@ mod tests; pub use air_lexer::AIRLexer; pub use errors::LexerError; +pub use token::AstVariable; pub use token::LastErrorPath; pub use token::Number; pub use token::Token; -pub use token::Variable; pub(super) type LexerResult = std::result::Result; diff --git a/crates/air-parser/src/parser/lexer/tests.rs b/crates/air-parser/src/parser/lexer/tests.rs index 151fdb6d..495ca145 100644 --- a/crates/air-parser/src/parser/lexer/tests.rs +++ b/crates/air-parser/src/parser/lexer/tests.rs @@ -16,11 +16,11 @@ use super::air_lexer::Spanned; use super::AIRLexer; +use super::AstVariable; use super::LastErrorPath; use super::LexerError; use super::Number; use super::Token; -use super::Variable; fn run_lexer(input: &str) -> Vec, usize, LexerError>> { let lexer = AIRLexer::new(input); @@ -265,7 +265,7 @@ fn too_big_float_number() { fn json_path() { // this json path contains all allowed in json path characters const JSON_PATH: &str = r#"value.$[$@[]():?.*,"]"#; - let variable = Variable::Scalar("value"); + let variable = AstVariable::Scalar("value"); lexer_test( JSON_PATH, diff --git a/crates/air-parser/src/parser/lexer/token.rs b/crates/air-parser/src/parser/lexer/token.rs index ad196b95..26d16caa 100644 --- a/crates/air-parser/src/parser/lexer/token.rs +++ b/crates/air-parser/src/parser/lexer/token.rs @@ -28,11 +28,12 @@ pub enum Token<'input> { CloseRoundBracket, OpenSquareBracket, CloseSquareBracket, + SquareBrackets, // [] symbolize empty array, it's possible to have it only in an argument position StringLiteral(&'input str), Alphanumeric(&'input str), Stream(&'input str), - VariableWithJsonPath(Variable<'input>, &'input str, bool), + VariableWithJsonPath(AstVariable<'input>, &'input str, bool), Number(Number), Boolean(bool), @@ -40,6 +41,7 @@ pub enum Token<'input> { LastError(LastErrorPath), Call, + Ap, Seq, Par, Null, @@ -51,7 +53,7 @@ pub enum Token<'input> { } #[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)] -pub enum Variable<'input> { +pub enum AstVariable<'input> { Scalar(&'input str), Stream(&'input str), } diff --git a/crates/air-parser/src/parser/lexer/token/traits.rs b/crates/air-parser/src/parser/lexer/token/traits.rs index 1131d135..3b2b4434 100644 --- a/crates/air-parser/src/parser/lexer/token/traits.rs +++ b/crates/air-parser/src/parser/lexer/token/traits.rs @@ -43,9 +43,9 @@ impl fmt::Display for Number { } } -impl fmt::Display for Variable<'_> { +impl fmt::Display for AstVariable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use Variable::*; + use AstVariable::*; match self { Scalar(name) => write!(f, "{}", name), diff --git a/crates/air-parser/src/parser/tests.rs b/crates/air-parser/src/parser/tests.rs index cfb7ec27..b5c59d4c 100644 --- a/crates/air-parser/src/parser/tests.rs +++ b/crates/air-parser/src/parser/tests.rs @@ -18,12 +18,12 @@ use crate::ast; use crate::parser::lexer::LastErrorPath; use crate::parser::AIRParser; use crate::parser::ParserError; +use ast::AstVariable::Scalar; +use ast::AstVariable::Stream; use ast::Call; use ast::CallInstrArgValue; use ast::CallInstrValue; use ast::Instruction; -use ast::Variable::Scalar; -use ast::Variable::Stream; use fstrings::f; use lalrpop_util::ParseError; @@ -50,8 +50,8 @@ fn parse_seq() { let source_code = r#" (seq - (call peerid function [] output) - (call "id" "f" ["hello" name]) + (call peerid function [[] []] output) + (call "id" "f" ["hello" [] name]) ) "#; let instruction = parse(source_code); @@ -59,7 +59,10 @@ fn parse_seq() { Instruction::Call(Call { peer_part: PeerPk(CallInstrValue::Variable(Scalar("peerid"))), function_part: FuncName(CallInstrValue::Variable(Scalar("function"))), - args: Rc::new(vec![]), + args: Rc::new(vec![ + CallInstrArgValue::EmptyArray, + CallInstrArgValue::EmptyArray, + ]), output: Variable(Scalar("output")), }), Instruction::Call(Call { @@ -67,6 +70,7 @@ fn parse_seq() { function_part: FuncName(CallInstrValue::Literal("f")), args: Rc::new(vec![ CallInstrArgValue::Literal("hello"), + CallInstrArgValue::EmptyArray, CallInstrArgValue::Variable(Scalar("name")), ]), output: None, @@ -136,11 +140,11 @@ fn parse_json_path() { "#; let instruction = parse(source_code); let expected = Instruction::Call(Call { - peer_part: PeerPk(CallInstrValue::JsonPath { - variable: Scalar("id"), - path: "$.a", - should_flatten: true, - }), + peer_part: PeerPk(CallInstrValue::JsonPath(ast::JsonPath::new( + Scalar("id"), + "$.a", + true, + ))), function_part: FuncName(CallInstrValue::Literal("f")), args: Rc::new(vec![ CallInstrArgValue::Literal("hello"), @@ -151,6 +155,54 @@ fn parse_json_path() { assert_eq!(instruction, expected); } +#[test] +fn parse_empty_array() { + use ast::CallOutputValue::*; + use ast::FunctionPart::*; + use ast::PeerPart::*; + + let source_code = r#" + (call id "f" ["" [] arg]) + "#; + let actual = parse(source_code); + let expected = Instruction::Call(Call { + peer_part: PeerPk(CallInstrValue::Variable(ast::AstVariable::Scalar("id"))), + function_part: FuncName(CallInstrValue::Literal("f")), + args: Rc::new(vec![ + CallInstrArgValue::Literal(""), + CallInstrArgValue::EmptyArray, + CallInstrArgValue::Variable(Scalar("arg")), + ]), + output: None, + }); + + assert_eq!(actual, expected); +} + +#[test] +fn parse_empty_array_2() { + use ast::CallOutputValue::*; + use ast::FunctionPart::*; + use ast::PeerPart::*; + + let source_code = r#" + (call id "f" [k [] []]) + "#; + let actual = parse(source_code); + let expected = Instruction::Call(Call { + peer_part: PeerPk(CallInstrValue::Variable(ast::AstVariable::Scalar("id"))), + function_part: FuncName(CallInstrValue::Literal("f")), + args: Rc::new(vec![ + CallInstrArgValue::Variable(ast::AstVariable::Scalar("k")), + CallInstrArgValue::EmptyArray, + CallInstrArgValue::EmptyArray, + ]), + output: None, + }); + + assert_eq!(actual, expected); +} + #[test] fn parse_undefined_variable() { let source_code = r#" @@ -278,21 +330,21 @@ fn parse_json_path_complex() { let instruction = parse(source_code); let expected = seq( Instruction::Call(Call { - peer_part: PeerPk(CallInstrValue::JsonPath { - variable: Scalar("m"), - path: "$.[1]", - should_flatten: true, - }), + peer_part: PeerPk(CallInstrValue::JsonPath(ast::JsonPath::new( + Scalar("m"), + "$.[1]", + true, + ))), function_part: FuncName(CallInstrValue::Literal("f")), args: Rc::new(vec![]), output: Variable(Scalar("void")), }), Instruction::Call(Call { - peer_part: PeerPk(CallInstrValue::JsonPath { - variable: Scalar("m"), - path: r#"$.abc["c"].cde[a][0].cde["bcd"]"#, - should_flatten: true, - }), + peer_part: PeerPk(CallInstrValue::JsonPath(ast::JsonPath::new( + Scalar("m"), + r#"$.abc["c"].cde[a][0].cde["bcd"]"#, + true, + ))), function_part: FuncName(CallInstrValue::Literal("f")), args: Rc::new(vec![]), output: Variable(Scalar("void")), @@ -312,26 +364,22 @@ fn json_path_square_braces() { "#; let instruction = parse(source_code); let expected = Instruction::Call(Call { - peer_part: PeerPk(CallInstrValue::JsonPath { - variable: Scalar("u"), - path: r#"$["peer_id"]"#, - should_flatten: true, - }), + peer_part: PeerPk(CallInstrValue::JsonPath(ast::JsonPath::new( + Scalar("u"), + r#"$["peer_id"]"#, + true, + ))), function_part: ServiceIdWithFuncName( CallInstrValue::Literal("return"), CallInstrValue::Literal(""), ), args: Rc::new(vec![ - CallInstrArgValue::JsonPath { - variable: Scalar("u"), - path: r#"$["peer_id"].cde[0]["abc"].abc"#, - should_flatten: false, - }, - CallInstrArgValue::JsonPath { - variable: Scalar("u"), - path: r#"$["name"]"#, - should_flatten: false, - }, + CallInstrArgValue::JsonPath(ast::JsonPath::new( + Scalar("u"), + r#"$["peer_id"].cde[0]["abc"].abc"#, + false, + )), + CallInstrArgValue::JsonPath(ast::JsonPath::new(Scalar("u"), r#"$["name"]"#, false)), ]), output: Variable(Stream("$void")), }); @@ -384,8 +432,8 @@ fn parse_fold() { ) "#; let instruction = parse(&source_code); - let expected = fold( - ast::IterableValue::Variable(Scalar("iterable")), + let expected = fold_scalar( + ast::IterableScalarValue::ScalarVariable("iterable"), "i", null(), ); @@ -446,8 +494,8 @@ fn parse_fold_with_xor_par_seq() { let source_code = source_fold_with(name); let instruction = parse(&source_code); let instr = binary_instruction(*name); - let expected = fold( - ast::IterableValue::Variable(Scalar("iterable")), + let expected = fold_scalar( + ast::IterableScalarValue::ScalarVariable("iterable"), "i", instr(null(), null()), ); @@ -749,9 +797,10 @@ fn no_output() { use ast::PeerPart::*; let source_code = r#" - (call peer (service fname) []) + (call peer (service fname) []) "#; - let instruction = parse(source_code); + let actual = parse(source_code); + let expected = Instruction::Call(Call { peer_part: PeerPk(CallInstrValue::Variable(Scalar("peer"))), function_part: ServiceIdWithFuncName( @@ -761,13 +810,83 @@ fn no_output() { args: Rc::new(vec![]), output: None, }); - assert_eq!(instruction, expected); + assert_eq!(actual, expected); +} + +#[test] +fn ap_with_literal() { + use ast::Ap; + + let source_code = r#" + (ap "some_string" $stream) + "#; + + let actual = parse(source_code); + let expected = Instruction::Ap(Ap { + argument: ast::ApArgument::Literal("some_string"), + result: ast::AstVariable::Stream("$stream"), + }); + + assert_eq!(actual, expected); +} + +#[test] +fn ap_with_number() { + use ast::Ap; + use ast::Number; + + let source_code = r#" + (ap -100 $stream) + "#; + + let actual = parse(source_code); + let expected = Instruction::Ap(Ap { + argument: ast::ApArgument::Number(Number::Int(-100)), + result: ast::AstVariable::Stream("$stream"), + }); + + assert_eq!(actual, expected); +} + +#[test] +fn ap_with_bool() { + use ast::Ap; + + let source_code = r#" + (ap true $stream) + "#; + + let actual = parse(source_code); + let expected = Instruction::Ap(Ap { + argument: ast::ApArgument::Boolean(true), + result: ast::AstVariable::Stream("$stream"), + }); + + assert_eq!(actual, expected); +} + +#[test] +fn ap_with_last_error() { + use ast::Ap; + use ast::LastErrorPath; + + let source_code = r#" + (ap %last_error%.$.msg! $stream) + "#; + + let actual = parse(source_code); + let expected = Instruction::Ap(Ap { + argument: ast::ApArgument::LastError(LastErrorPath::Message), + result: ast::AstVariable::Stream("$stream"), + }); + + assert_eq!(actual, expected); } #[test] fn fold_json_path() { - use ast::Fold; - use ast::IterableValue::*; + use ast::FoldScalar; + use ast::IterableScalarValue::*; let source_code = r#" ; comment @@ -775,9 +894,9 @@ fn fold_json_path() { ;;; comment "#; let instruction = parse(source_code); - let expected = Instruction::Fold(Fold { + let expected = Instruction::FoldScalar(FoldScalar { iterable: JsonPath { - variable: Scalar("members"), + scalar_name: "members", path: "$.[\"users\"]", should_flatten: false, }, @@ -787,10 +906,26 @@ fn fold_json_path() { assert_eq!(instruction, expected); } +#[test] +fn fold_on_stream() { + use ast::FoldStream; + + let source_code = r#" + (fold $stream iterator (null)) + "#; + let instruction = parse(source_code); + let expected = Instruction::FoldStream(FoldStream { + stream_name: "$stream", + iterator: "iterator", + instruction: Rc::new(null()), + }); + assert_eq!(instruction, expected); +} + #[test] fn comments() { - use ast::Fold; - use ast::IterableValue::*; + use ast::FoldScalar; + use ast::IterableScalarValue::*; let source_code = r#" ; comment @@ -798,9 +933,9 @@ fn comments() { ;;; comme;?!.$. nt[][][][()()()null;$::! "#; let instruction = parse(source_code); - let expected = Instruction::Fold(Fold { + let expected = Instruction::FoldScalar(FoldScalar { iterable: JsonPath { - variable: Scalar("members"), + scalar_name: "members", path: "$.[\"users\"]", should_flatten: false, }, @@ -832,12 +967,12 @@ fn null() -> Instruction<'static> { Instruction::Null(ast::Null) } -fn fold<'a>( - iterable: ast::IterableValue<'a>, +fn fold_scalar<'a>( + iterable: ast::IterableScalarValue<'a>, iterator: &'a str, instruction: Instruction<'a>, ) -> Instruction<'a> { - Instruction::Fold(ast::Fold { + Instruction::FoldScalar(ast::FoldScalar { iterable, iterator, instruction: std::rc::Rc::new(instruction), diff --git a/crates/air-parser/src/parser/validator.rs b/crates/air-parser/src/parser/validator.rs index 9d4343e3..ed598172 100644 --- a/crates/air-parser/src/parser/validator.rs +++ b/crates/air-parser/src/parser/validator.rs @@ -58,7 +58,11 @@ impl<'i> VariableValidator<'i> { self.met_peer_part(&call.peer_part, span); self.met_function_part(&call.function_part, span); self.met_args(&call.args, span); - self.met_call_output_definition(&call.output, span) + + match &call.output { + CallOutputValue::Variable(variable) => self.met_variable_definition(variable, span), + CallOutputValue::None => {} + }; } pub(super) fn met_match(&mut self, match_: &Match<'i>, span: Span) { @@ -71,9 +75,14 @@ impl<'i> VariableValidator<'i> { self.met_matchable(&mismatch.right_value, span); } - pub(super) fn met_fold(&mut self, fold: &Fold<'i>, span: Span) { + pub(super) fn met_fold_scalar(&mut self, fold: &FoldScalar<'i>, span: Span) { self.met_iterable_value(&fold.iterable, span); - self.met_iterator_definition(&fold.iterator, span); + self.met_iterator_definition(fold.iterator, span); + } + + pub(super) fn met_fold_stream(&mut self, fold: &FoldStream<'i>, span: Span) { + self.met_variable(&AstVariable::Stream(fold.stream_name), span); + self.met_iterator_definition(fold.iterator, span); } pub(super) fn met_next(&mut self, next: &Next<'i>, span: Span) { @@ -84,6 +93,19 @@ impl<'i> VariableValidator<'i> { self.unresolved_iterables.insert(iterable_name, span); } + pub(super) fn met_ap(&mut self, ap: &Ap<'i>, span: Span) { + match &ap.argument { + ApArgument::ScalarVariable(name) => self.met_variable(&AstVariable::Scalar(name), span), + ApArgument::JsonPath(json_path) => self.met_variable(&json_path.variable, span), + ApArgument::Number(_) + | ApArgument::Boolean(_) + | ApArgument::Literal(_) + | ApArgument::EmptyArray + | ApArgument::LastError(_) => {} + } + self.met_variable_definition(&ap.result, span); + } + pub(super) fn finalize(&self) -> Vec, ParserError>> { let mut errors = Vec::new(); for (name, span) in self.unresolved_variables.iter() { @@ -129,7 +151,7 @@ impl<'i> VariableValidator<'i> { fn met_instr_value(&mut self, instr_value: &CallInstrValue<'i>, span: Span) { match instr_value { - CallInstrValue::JsonPath { variable, .. } => self.met_variable(variable, span), + CallInstrValue::JsonPath(json_path) => self.met_variable(&json_path.variable, span), CallInstrValue::Variable(variable) => self.met_variable(variable, span), _ => {} } @@ -137,10 +159,10 @@ impl<'i> VariableValidator<'i> { fn met_instr_arg_value(&mut self, instr_arg_value: &CallInstrArgValue<'i>, span: Span) { match instr_arg_value { - CallInstrArgValue::JsonPath { variable, .. } => self.met_variable(variable, span), + CallInstrArgValue::JsonPath(json_path) => self.met_variable(&json_path.variable, span), CallInstrArgValue::Variable(variable) => { // skipping streams here allows treating non-defined streams as empty arrays - if let Variable::Scalar(_) = variable { + if let AstVariable::Scalar(_) = variable { self.met_variable(variable, span) } } @@ -148,10 +170,10 @@ impl<'i> VariableValidator<'i> { } } - fn met_variable(&mut self, variable: &Variable<'i>, span: Span) { + fn met_variable(&mut self, variable: &AstVariable<'i>, span: Span) { let name = match variable { - Variable::Scalar(name) => name, - Variable::Stream(name) => name, + AstVariable::Scalar(name) => name, + AstVariable::Stream(name) => name, }; if !self.contains_variable(name, span) { @@ -174,13 +196,12 @@ impl<'i> VariableValidator<'i> { found_spans.iter().any(|s| s < &key_span) } - fn met_call_output_definition(&mut self, call_output: &CallOutputValue<'i>, span: Span) { + fn met_variable_definition(&mut self, variable: &AstVariable<'i>, span: Span) { use std::collections::hash_map::Entry; - let variable_name = match call_output { - CallOutputValue::Variable(Variable::Scalar(name)) => name, - CallOutputValue::Variable(Variable::Stream(name)) => name, - CallOutputValue::None => return, + let variable_name = match variable { + AstVariable::Scalar(name) => name, + AstVariable::Stream(name) => name, }; match self.met_variables.entry(variable_name) { @@ -200,9 +221,10 @@ impl<'i> VariableValidator<'i> { MatchableValue::InitPeerId | MatchableValue::Number(_) | MatchableValue::Boolean(_) - | MatchableValue::Literal(_) => {} + | MatchableValue::Literal(_) + | MatchableValue::EmptyArray => {} MatchableValue::Variable(variable) => self.met_variable(variable, span), - MatchableValue::JsonPath { variable, .. } => self.met_variable(variable, span), + MatchableValue::JsonPath(json_path) => self.met_variable(&json_path.variable, span), } } @@ -218,10 +240,14 @@ impl<'i> VariableValidator<'i> { .any(|s| s.left < key_span.left && s.right > key_span.right) } - fn met_iterable_value(&mut self, iterable_value: &IterableValue<'i>, span: Span) { + fn met_iterable_value(&mut self, iterable_value: &IterableScalarValue<'i>, span: Span) { match iterable_value { - IterableValue::JsonPath { variable, .. } => self.met_variable(variable, span), - IterableValue::Variable(variable) => self.met_variable(variable, span), + IterableScalarValue::JsonPath { scalar_name, .. } => { + self.met_variable(&AstVariable::Scalar(scalar_name), span) + } + IterableScalarValue::ScalarVariable(name) => { + self.met_variable(&AstVariable::Scalar(name), span) + } } } diff --git a/crates/interpreter-data/Cargo.toml b/crates/interpreter-data/Cargo.toml new file mode 100644 index 00000000..5972a222 --- /dev/null +++ b/crates/interpreter-data/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "air-interpreter-data" +description = "Data format of the AIR interpreter" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" +license = "Apache-2.0" +repository = "https://github.com/fluencelabs/air" +keywords = ["fluence", "air", "webassembly", "programming-language"] +categories = ["wasm"] + +[lib] +name = "air_interpreter_data" +path = "src/lib.rs" + +[dependencies] +serde = {version = "=1.0.118", features = ["derive", "rc"]} +serde_json = "=1.0.61" +semver = { version = "1.0.3", features = ["serde"] } +once_cell = "1.8.0" diff --git a/crates/interpreter-data/src/executed_state.rs b/crates/interpreter-data/src/executed_state.rs new file mode 100644 index 00000000..d8d4a41d --- /dev/null +++ b/crates/interpreter-data/src/executed_state.rs @@ -0,0 +1,125 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod impls; +mod se_de; + +use se_de::par_serializer; +use serde::Deserialize; +use serde::Serialize; +use serde_json::Value as JValue; +use std::fmt::Formatter; +use std::rc::Rc; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct ParResult { + pub left_size: u32, + pub right_size: u32, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum CallResult { + /// Request was sent to a target node by node with such public key and it shouldn't be called again. + #[serde(rename = "sent_by")] + RequestSentBy(Rc), + + /// A corresponding call's been already executed with such value as a result. + Executed(Value), + + /// call_service ended with a service error. + #[serde(rename = "failed")] + CallServiceFailed(i32, Rc), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Value { + Scalar(Rc), + Stream { value: Rc, generation: u32 }, +} + +/// Let's consider an example of trace that could be produces by the following fold: +/// (fold $stream v +/// (call 1) +/// (call 2) +/// (next v) +/// (call 3) +/// (call 4) +/// ) +/// +/// Having started with stream with two elements {v1, v2} the resulted trace would looks like +/// [(1) (2)] [(1) (2)] [(3) (4)] [(3) (4)] <--- the sequence of call states +/// v1 v2 v2 v1 <---- corresponding values from $stream that +/// the iterable v had at the moment of call +/// +/// From this example, it could be seen that each instruction sequence inside fold is divided into +/// two intervals (left and right), each of these intervals has borders [begin, end). +/// So, this struct describes position inside overall execution_step trace belongs to one fold iteration. +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct FoldSubTraceLore { + /// Position of current value in a trace. + #[serde(rename = "pos")] + pub value_pos: u32, + + /// Descriptors of a subtrace that are corresponded to the current value. Technically, now + /// it always contains two values, and Vec here is used to have a possibility to handle more + /// than one next inside fold in future. + #[serde(rename = "desc")] + pub subtraces_desc: Vec, +} + +/// Descriptor of a subtrace inside execution trace. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct SubTraceDesc { + /// Start position in a trace of this subtrace. + #[serde(rename = "pos")] + pub begin_pos: u32, + + /// Length of the subtrace. + #[serde(rename = "len")] + pub subtrace_len: u32, +} + +/// This type represents all information in an execution trace about states executed during +/// a fold execution. +pub type FoldLore = Vec; + +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct FoldResult { + pub lore: FoldLore, +} + +/// Describes result of applying functor `apply` to streams. +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct ApResult { + #[serde(rename = "gens")] + pub res_generations: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ExecutedState { + #[serde(with = "par_serializer")] + Par(ParResult), + Call(CallResult), + Fold(FoldResult), + Ap(ApResult), +} diff --git a/crates/interpreter-data/src/executed_state/impls.rs b/crates/interpreter-data/src/executed_state/impls.rs new file mode 100644 index 00000000..5a8c9862 --- /dev/null +++ b/crates/interpreter-data/src/executed_state/impls.rs @@ -0,0 +1,133 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; + +impl ParResult { + pub fn new(left_size: u32, right_size: u32) -> Self { + Self { + left_size, + right_size, + } + } + + /// Returns a size of subtrace that this par describes in execution_step trace. + pub fn size(&self) -> Option { + self.left_size + .checked_add(self.right_size) + .map(|v| v as usize) + } +} + +impl CallResult { + pub fn sent(sender: impl Into) -> CallResult { + CallResult::RequestSentBy(Rc::new(sender.into())) + } + + pub fn executed_scalar(value: Rc) -> CallResult { + let value = Value::Scalar(value); + + CallResult::Executed(value) + } + + pub fn executed_stream(value: Rc, generation: u32) -> CallResult { + let value = Value::Stream { value, generation }; + + CallResult::Executed(value) + } + + pub fn failed(ret_code: i32, error_msg: impl Into) -> CallResult { + CallResult::CallServiceFailed(ret_code, Rc::new(error_msg.into())) + } +} + +impl SubTraceDesc { + pub fn new(begin_pos: usize, subtrace_len: usize) -> Self { + Self { + begin_pos: begin_pos as _, + subtrace_len: subtrace_len as _, + } + } +} + +impl ExecutedState { + pub fn par(left_subtree_size: usize, right_subtree_size: usize) -> Self { + let par_result = ParResult { + left_size: left_subtree_size as _, + right_size: right_subtree_size as _, + }; + + Self::Par(par_result) + } +} + +impl ApResult { + pub fn new(res_gens: Vec) -> Self { + Self { + res_generations: res_gens, + } + } +} + +impl std::fmt::Display for ExecutedState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use CallResult::*; + use ExecutedState::*; + + match self { + Par(ParResult { + left_size: left_subtree_size, + right_size: right_subtree_size, + }) => write!(f, "par({}, {})", left_subtree_size, right_subtree_size), + Call(RequestSentBy(peer_id)) => write!(f, r#"request_sent_by("{}")"#, peer_id), + Call(Executed(value)) => { + write!(f, "executed({})", value) + } + Call(CallServiceFailed(ret_code, err_msg)) => { + write!(f, r#"call_service_failed({}, "{}")"#, ret_code, err_msg) + } + Fold(FoldResult { lore }) => { + writeln!(f, "fold(",)?; + for sublore in lore { + writeln!( + f, + " {} - [{}, {}], [{}, {}]", + sublore.value_pos, + sublore.subtraces_desc[0].begin_pos, + sublore.subtraces_desc[0].subtrace_len, + sublore.subtraces_desc[1].begin_pos, + sublore.subtraces_desc[1].subtrace_len + )?; + } + write!(f, " )") + } + Ap(ap) => { + write!(f, "ap: _ -> {:?}", ap.res_generations) + } + } + } +} + +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Value::Scalar(value) => write!(f, "scalar: {}", value), + Value::Stream { value, generation } => { + write!(f, "stream: {} generation: {}", value, generation) + } + } + } +} diff --git a/crates/interpreter-data/src/executed_state/se_de.rs b/crates/interpreter-data/src/executed_state/se_de.rs new file mode 100644 index 00000000..5fe95d67 --- /dev/null +++ b/crates/interpreter-data/src/executed_state/se_de.rs @@ -0,0 +1,68 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use serde::de::SeqAccess; +use serde::de::Visitor; +use serde::ser::SerializeSeq; +use serde::Deserializer; +use serde::Serializer; +use std::fmt; + +pub mod par_serializer { + use super::*; + + pub fn serialize(value: &ParResult, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(2))?; + seq.serialize_element(&value.left_size)?; + seq.serialize_element(&value.right_size)?; + seq.end() + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct VecVisitor; + impl<'de> Visitor<'de> for VecVisitor { + type Value = ParResult; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("[left_size, right_size]") + } + + fn visit_seq>(self, mut seq: A) -> Result { + let left_size = seq.next_element::()?; + let right_size = seq.next_element::()?; + + let (left_size, right_size) = match (left_size, right_size) { + (Some(left_size), Some(right_size)) => (left_size, right_size), + _ => return Err(serde::de::Error::custom( + "failed to deserialize ParResult, not enough elements in serialized array", + )), + }; + let par_result = ParResult::new(left_size, right_size); + + Ok(par_result) + } + } + + deserializer.deserialize_seq(VecVisitor {}) + } +} diff --git a/crates/interpreter-data/src/interpreter_data.rs b/crates/interpreter-data/src/interpreter_data.rs new file mode 100644 index 00000000..39d78dd4 --- /dev/null +++ b/crates/interpreter-data/src/interpreter_data.rs @@ -0,0 +1,77 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::ExecutedState; +use super::DATA_FORMAT_VERSION; +use serde::Deserialize; +use serde::Serialize; +use std::collections::HashMap; +use std::ops::Deref; + +pub type StreamGenerations = HashMap; +pub type ExecutionTrace = Vec; + +/// The AIR interpreter could be considered as a function +/// f(prev_data: InterpreterData, current_data: InterpreterData, ... ) -> (result_data: InterpreterData, ...). +/// This function receives prev and current data and produces a result data. All these data +/// have the following format. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InterpreterData { + /// Trace of AIR execution, which contains executed call, par and fold states. + pub trace: ExecutionTrace, + + /// Contains maximum generation for each stream. This info will be used while merging + /// values in streams. + pub streams: StreamGenerations, + + /// Version of this data format. + pub version: semver::Version, +} + +impl InterpreterData { + pub fn new() -> Self { + Self { + trace: <_>::default(), + streams: <_>::default(), + version: DATA_FORMAT_VERSION.deref().clone(), + } + } + + pub fn from_execution_result(trace: ExecutionTrace, streams: StreamGenerations) -> Self { + Self { + trace, + streams, + version: DATA_FORMAT_VERSION.deref().clone(), + } + } + + /// Tries to de InterpreterData from slice according to the data version. + pub fn try_from_slice(slice: &[u8]) -> Result { + // treat empty slice as an empty interpreter data allows abstracting from + // the internal format for empty data. + if slice.is_empty() { + return Ok(Self::default()); + } + + serde_json::from_slice(slice) + } +} + +impl Default for InterpreterData { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/interpreter-data/src/lib.rs b/crates/interpreter-data/src/lib.rs new file mode 100644 index 00000000..44fd7e0e --- /dev/null +++ b/crates/interpreter-data/src/lib.rs @@ -0,0 +1,28 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +mod executed_state; +mod interpreter_data; + +pub use executed_state::*; +pub use interpreter_data::*; + +use once_cell::sync::Lazy; +use std::str::FromStr; + +pub static DATA_FORMAT_VERSION: Lazy = Lazy::new(|| { + semver::Version::from_str("0.2.0").expect("invalid data format version specified") +}); diff --git a/crates/interpreter-interface/src/lib.rs b/crates/interpreter-interface/src/lib.rs index 20223f1e..948054bf 100644 --- a/crates/interpreter-interface/src/lib.rs +++ b/crates/interpreter-interface/src/lib.rs @@ -22,7 +22,7 @@ use serde::Serialize; pub const INTERPRETER_SUCCESS: i32 = 0; -/// Describes a result returned at the end of the interpreter execution. +/// Describes a result returned at the end of the interpreter execution_step. #[marine] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct InterpreterOutcome { diff --git a/crates/polyplets/src/tetraplet.rs b/crates/polyplets/src/tetraplet.rs index 9c806705..087f7de8 100644 --- a/crates/polyplets/src/tetraplet.rs +++ b/crates/polyplets/src/tetraplet.rs @@ -18,14 +18,13 @@ use crate::ResolvedTriplet; use serde::Deserialize; use serde::Serialize; -use std::rc::Rc; /// Describes an origin returned corresponding value. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct SecurityTetraplet { /// Describes origin of the value in the network. #[serde(flatten)] - pub triplet: Rc, + pub triplet: ResolvedTriplet, /// Value was produced by applying this `json_path` to the output from `call_service`. pub json_path: String, @@ -41,7 +40,6 @@ impl SecurityTetraplet { service_id: String::new(), function_name: String::new(), }; - let triplet = Rc::new(triplet); Self { triplet, @@ -50,10 +48,14 @@ impl SecurityTetraplet { } } - pub fn from_triplet(triplet: Rc) -> Self { + pub fn from_triplet(triplet: ResolvedTriplet) -> Self { Self { triplet, json_path: String::new(), } } + + pub fn add_json_path(&mut self, json_path: &str) { + self.json_path.push_str(json_path) + } } diff --git a/crates/polyplets/src/triplet.rs b/crates/polyplets/src/triplet.rs index 32f914a2..53aeba6b 100644 --- a/crates/polyplets/src/triplet.rs +++ b/crates/polyplets/src/triplet.rs @@ -19,7 +19,7 @@ use serde::Serialize; /// ResolvedTriplet represents peer network location with all /// variables, literals and etc resolved into final string. -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct ResolvedTriplet { pub peer_pk: String, pub service_id: String, diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 90832050..4dd5f148 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -15,4 +15,4 @@ marine-rs-sdk = "0.6.11" avm-server = { path = "../../avm/server", features = ["raw-avm-api"] } air = { path = "../../air" } -serde_json = "1.0.56" +serde_json = "=1.0.61" diff --git a/crates/test-utils/src/call_services.rs b/crates/test-utils/src/call_services.rs new file mode 100644 index 00000000..93f451b4 --- /dev/null +++ b/crates/test-utils/src/call_services.rs @@ -0,0 +1,153 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use super::*; +use std::collections::HashMap; + +pub fn unit_call_service() -> CallServiceClosure { + Box::new(|_| -> Option { + Some(IValue::Record( + NEVec::new(vec![ + IValue::S32(0), + IValue::String(String::from("\"test\"")), + ]) + .unwrap(), + )) + }) +} + +pub fn echo_call_service() -> CallServiceClosure { + Box::new(|args| -> Option { + let arg = match &args.function_args[2] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let arg: Vec = serde_json::from_str(arg).unwrap(); + + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(arg[0].to_string())]).unwrap(), + )) + }) +} + +pub fn echo_string_call_service() -> CallServiceClosure { + Box::new(|args| -> Option { + let arg = match &args.function_args[2] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let arg: Vec = serde_json::from_str(arg).unwrap(); + let arg = serde_json::to_string(&arg[0]).unwrap(); + + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(arg)]).unwrap(), + )) + }) +} + +pub fn echo_number_call_service() -> CallServiceClosure { + Box::new(|args| -> Option { + let arg = match &args.function_args[2] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + let arg: Vec = serde_json::from_str(arg).unwrap(); + + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(arg[0].clone())]).unwrap(), + )) + }) +} + +pub fn set_variable_call_service(json: impl Into) -> CallServiceClosure { + let json = json.into(); + Box::new(move |_| -> Option { + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(json.clone())]).unwrap(), + )) + }) +} + +pub fn set_variables_call_service(ret_mapping: HashMap) -> CallServiceClosure { + Box::new(move |args| -> Option { + let arg_name = match &args.function_args[2] { + IValue::String(json_str) => { + let json = serde_json::from_str(json_str).expect("a valid json"); + match json { + JValue::Array(array) => match array.first() { + Some(JValue::String(str)) => str.to_string(), + _ => String::from("default"), + }, + _ => String::from("default"), + } + } + _ => String::from("default"), + }; + + let result = ret_mapping + .get(&arg_name) + .cloned() + .unwrap_or_else(|| String::from(r#""test""#)); + + Some(IValue::Record( + NEVec::new(vec![IValue::S32(0), IValue::String(result)]).unwrap(), + )) + }) +} + +pub fn return_string_call_service(ret_str: impl Into) -> CallServiceClosure { + let ret_str = ret_str.into(); + + Box::new(move |_| -> Option { + Some(IValue::Record( + NEVec::new(vec![ + IValue::S32(0), + IValue::String(format!(r#""{}""#, ret_str)), + ]) + .unwrap(), + )) + }) +} + +pub fn fallible_call_service(fallible_service_id: impl Into) -> CallServiceClosure { + let fallible_service_id = fallible_service_id.into(); + + Box::new(move |args| -> Option { + let builtin_service = match &args.function_args[0] { + IValue::String(str) => str, + _ => unreachable!(), + }; + + // return a error for service with such id + if builtin_service == &fallible_service_id { + Some(IValue::Record( + NEVec::new(vec![IValue::S32(1), IValue::String(String::from("error"))]).unwrap(), + )) + } else { + // return success for services with other ids + Some(IValue::Record( + NEVec::new(vec![ + IValue::S32(0), + IValue::String(String::from(r#""res""#)), + ]) + .unwrap(), + )) + } + }) +} diff --git a/crates/test-utils/src/executed_state.rs b/crates/test-utils/src/executed_state.rs index e8a1023f..2687b4b2 100644 --- a/crates/test-utils/src/executed_state.rs +++ b/crates/test-utils/src/executed_state.rs @@ -14,23 +14,33 @@ * limitations under the License. */ +use super::ApResult; +use super::CallResult; +use super::ExecutedState; use super::JValue; -use air::execution_trace::CallResult; -use air::execution_trace::ExecutedState; -use air::execution_trace::ParResult; +use super::ParResult; +use super::Value; +use crate::FoldLore; +use crate::FoldResult; +use crate::FoldSubTraceLore; +use crate::SubTraceDesc; use std::rc::Rc; -pub fn scalar_jvalue(result: JValue) -> ExecutedState { - ExecutedState::Call(CallResult::Executed(Rc::new(result))) +pub fn scalar(result: JValue) -> ExecutedState { + let value = Value::Scalar(Rc::new(result)); + ExecutedState::Call(CallResult::Executed(value)) } -pub fn stream_jvalue(result: JValue, _stream_name: impl Into) -> ExecutedState { - ExecutedState::Call(CallResult::Executed(Rc::new(result))) +pub fn stream_jvalue(result: JValue, generation: u32) -> ExecutedState { + let call_result = CallResult::executed_stream(Rc::new(result), generation); + ExecutedState::Call(call_result) } pub fn scalar_string(result: impl Into) -> ExecutedState { - ExecutedState::Call(CallResult::Executed(Rc::new(JValue::String(result.into())))) + let result = JValue::String(result.into()); + let value = Rc::new(result); + ExecutedState::Call(CallResult::executed_scalar(value)) } pub fn scalar_string_array(result: Vec>) -> ExecutedState { @@ -38,39 +48,46 @@ pub fn scalar_string_array(result: Vec>) -> ExecutedState { .into_iter() .map(|s| JValue::String(s.into())) .collect::>(); + let value = Rc::new(JValue::Array(result)); - ExecutedState::Call(CallResult::Executed(Rc::new(JValue::Array(result)))) + ExecutedState::Call(CallResult::executed_scalar(value)) } -pub fn stream_string(result: impl Into, _stream_name: impl Into) -> ExecutedState { - ExecutedState::Call(CallResult::Executed(Rc::new(JValue::String(result.into())))) +pub fn stream_string(result: impl Into, generation: u32) -> ExecutedState { + let result = JValue::String(result.into()); + let value = Rc::new(result); + + ExecutedState::Call(CallResult::executed_stream(value, generation)) } -pub fn stream_number( - result: impl Into, - _stream_name: impl Into, -) -> ExecutedState { - ExecutedState::Call(CallResult::Executed(Rc::new(JValue::Number(result.into())))) +pub fn stream_number(result: impl Into, generation: u32) -> ExecutedState { + let result = JValue::Number(result.into()); + let value = Rc::new(result); + + ExecutedState::Call(CallResult::executed_stream(value, generation)) } -pub fn stream_string_array( - result: Vec>, - _stream_name: impl Into, -) -> ExecutedState { +pub fn stream_string_array(result: Vec>, generation: u32) -> ExecutedState { let result = result .into_iter() .map(|s| JValue::String(s.into())) .collect::>(); + let value = Rc::new(JValue::Array(result)); - ExecutedState::Call(CallResult::Executed(Rc::new(JValue::Array(result)))) + ExecutedState::Call(CallResult::executed_stream(value, generation)) } pub fn request_sent_by(sender: impl Into) -> ExecutedState { - ExecutedState::Call(CallResult::RequestSentBy(sender.into())) + ExecutedState::Call(CallResult::RequestSentBy(Rc::new(sender.into()))) } pub fn par(left: usize, right: usize) -> ExecutedState { - ExecutedState::Par(ParResult(left, right)) + let par_result = ParResult { + left_size: left as _, + right_size: right as _, + }; + + ExecutedState::Par(par_result) } pub fn service_failed(ret_code: i32, error_message: impl Into) -> ExecutedState { @@ -79,3 +96,33 @@ pub fn service_failed(ret_code: i32, error_message: impl Into) -> Execut Rc::new(error_message.into()), )) } + +pub fn fold(lore: FoldLore) -> ExecutedState { + let result = FoldResult { lore }; + ExecutedState::Fold(result) +} + +pub fn subtrace_lore( + value_pos: u32, + before: SubTraceDesc, + after: SubTraceDesc, +) -> FoldSubTraceLore { + FoldSubTraceLore { + value_pos, + subtraces_desc: vec![before, after], + } +} + +pub fn ap(dst: Option) -> ExecutedState { + let res_generations = option_to_vec(dst); + let ap_result = ApResult::new(res_generations); + + ExecutedState::Ap(ap_result) +} + +fn option_to_vec(maybe_value: Option) -> Vec { + match maybe_value { + Some(value) => vec![value], + None => vec![], + } +} diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 4ef48725..0ceb6ce0 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -25,6 +25,7 @@ unreachable_patterns )] +mod call_services; pub mod executed_state; pub use avm_server::ne_vec::NEVec; @@ -36,10 +37,10 @@ pub use avm_server::IValue; pub use avm_server::InterpreterOutcome; pub use avm_server::ParticleParameters; pub use avm_server::AVM; +pub use call_services::*; -pub use air::execution_trace::ExecutionTrace; +pub use air::interpreter_data::*; -use std::collections::HashMap; use std::path::PathBuf; pub(self) type JValue = serde_json::Value; @@ -59,110 +60,17 @@ pub fn create_avm(call_service: CallServiceClosure, current_peer_id: impl Into CallServiceClosure { - Box::new(|_| -> Option { - Some(IValue::Record( - NEVec::new(vec![ - IValue::S32(0), - IValue::String(String::from("\"test\"")), - ]) - .unwrap(), - )) - }) -} - -pub fn echo_string_call_service() -> CallServiceClosure { - Box::new(|args| -> Option { - let arg = match &args.function_args[2] { - IValue::String(str) => str, - _ => unreachable!(), - }; - - let arg: Vec = serde_json::from_str(arg).unwrap(); - let arg = serde_json::to_string(&arg[0]).unwrap(); - - Some(IValue::Record( - NEVec::new(vec![IValue::S32(0), IValue::String(arg)]).unwrap(), - )) - }) -} - -pub fn echo_number_call_service() -> CallServiceClosure { - Box::new(|args| -> Option { - let arg = match &args.function_args[2] { - IValue::String(str) => str, - _ => unreachable!(), - }; - - let arg: Vec = serde_json::from_str(arg).unwrap(); - - Some(IValue::Record( - NEVec::new(vec![IValue::S32(0), IValue::String(arg[0].clone())]).unwrap(), - )) - }) -} - -pub fn set_variable_call_service(json: impl Into) -> CallServiceClosure { - let json = json.into(); - Box::new(move |_| -> Option { - Some(IValue::Record( - NEVec::new(vec![IValue::S32(0), IValue::String(json.clone())]).unwrap(), - )) - }) -} - -pub fn set_variables_call_service(ret_mapping: HashMap) -> CallServiceClosure { - Box::new(move |args| -> Option { - let arg_name = match &args.function_args[2] { - IValue::String(json_str) => { - let json = serde_json::from_str(json_str).expect("a valid json"); - match json { - JValue::Array(array) => match array.first() { - Some(JValue::String(str)) => str.to_string(), - _ => String::from("default"), - }, - _ => String::from("default"), - } +#[macro_export] +macro_rules! checked_call_vm { + ($vm:expr, $init_peer_id:expr, $script:expr, $prev_data:expr, $data:expr) => {{ + match $vm.call_with_prev_data($init_peer_id, $script, $prev_data, $data) { + Ok(v) if v.ret_code != 0 => { + panic!("VM returns a error: {} {}", v.ret_code, v.error_message) } - _ => String::from("default"), - }; - - let result = ret_mapping - .get(&arg_name) - .cloned() - .unwrap_or_else(|| String::from(r#""test""#)); - - Some(IValue::Record( - NEVec::new(vec![IValue::S32(0), IValue::String(result)]).unwrap(), - )) - }) -} - -pub fn fallible_call_service(fallible_service_id: impl Into) -> CallServiceClosure { - let fallible_service_id = fallible_service_id.into(); - - Box::new(move |args| -> Option { - let builtin_service = match &args.function_args[0] { - IValue::String(str) => str, - _ => unreachable!(), - }; - - // return a error for service with such id - if builtin_service == &fallible_service_id { - Some(IValue::Record( - NEVec::new(vec![IValue::S32(1), IValue::String(String::from("error"))]).unwrap(), - )) - } else { - // return success for services with other ids - Some(IValue::Record( - NEVec::new(vec![ - IValue::S32(0), - IValue::String(String::from(r#""res""#)), - ]) - .unwrap(), - )) + Ok(v) => v, + Err(err) => panic!("VM call failed: {}", err), } - }) + }}; } #[macro_export] @@ -174,3 +82,38 @@ macro_rules! call_vm { } }; } + +pub fn trace_from_result(result: &InterpreterOutcome) -> ExecutionTrace { + let data = data_from_result(result); + data.trace +} + +pub fn data_from_result(result: &InterpreterOutcome) -> InterpreterData { + serde_json::from_slice(&result.data).expect("default serializer shouldn't fail") +} + +pub fn raw_data_from_trace(trace: ExecutionTrace) -> Vec { + let data = InterpreterData::from_execution_result(trace, <_>::default()); + serde_json::to_vec(&data).expect("default serializer shouldn't fail") +} + +#[macro_export] +macro_rules! assert_next_pks { + ($expected:expr, $actual:expr) => { + let expected: std::collections::HashSet<_> = + $expected.into_iter().map(|s| s.as_str()).collect(); + let actual: std::collections::HashSet<_> = $actual.into_iter().map(|s| *s).collect(); + + assert_eq!(expected, actual) + }; +} + +pub fn print_trace(result: &InterpreterOutcome, trace_name: &str) { + let trace = trace_from_result(result); + + println!("trace {} (states_count: {}): [", trace_name, trace.len()); + for (id, state) in trace.iter().enumerate() { + println!(" {}: {}", id, state); + } + println!("]"); +}