cfsolve 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +10 -9
- data/UPSTREAM.md +6 -6
- data/lib/cfsolve/profiles/safari-26-ios/answers.json +2562 -1887
- data/lib/cfsolve/profiles/safari-26-ios/challenge-page-answers.json +1873 -1198
- data/lib/cfsolve/profiles/safari-26-ios.json +0 -8
- data/lib/cfsolve/version.rb +2 -2
- data/vendor/cfsolve/README.md +0 -35
- data/vendor/cfsolve/examples/build_profile.rs +17 -15
- data/vendor/cfsolve/examples/capture_session.rs +37 -0
- data/vendor/cfsolve/examples/check_answers.rs +66 -0
- data/vendor/cfsolve/examples/compare_session.rs +190 -0
- data/vendor/cfsolve/examples/stage2_snapshot.rs +4 -1
- data/vendor/cfsolve/profiles/safari-26-ios/answers.json +2562 -1887
- data/vendor/cfsolve/profiles/safari-26-ios/challenge-page-answers.json +1873 -1198
- data/vendor/cfsolve/profiles/safari-26-ios.json +0 -8
- data/vendor/cfsolve/scripts/capture_cssom.mjs +42 -0
- data/vendor/cfsolve/scripts/serve_cssom.py +111 -0
- data/vendor/cfsolve/src/capture/profile.rs +7 -18
- data/vendor/cfsolve/src/capture/recorded.rs +42 -7
- data/vendor/cfsolve/src/challenge/installed/audit.rs +131 -0
- data/vendor/cfsolve/src/challenge/installed.rs +122 -271
- data/vendor/cfsolve/src/challenge/payload.rs +107 -5
- data/vendor/cfsolve/src/challenge/state.rs +98 -16
- data/vendor/cfsolve/src/deobfuscate/encoded_strings.rs +3 -2
- data/vendor/cfsolve/src/deobfuscate/helper_objects.rs +17 -32
- data/vendor/cfsolve/src/deobfuscate/register_arrays.rs +18 -5
- data/vendor/cfsolve/src/embedding_page/extra_params.rs +0 -14
- data/vendor/cfsolve/src/embedding_page/page.rs +28 -2
- data/vendor/cfsolve/src/http/client.rs +11 -0
- data/vendor/cfsolve/src/js/ast.rs +12 -3
- data/vendor/cfsolve/src/js/objects.rs +9 -2
- data/vendor/cfsolve/src/listing/fold.rs +0 -83
- data/vendor/cfsolve/src/listing/query.rs +25 -244
- data/vendor/cfsolve/src/listing.rs +0 -1
- data/vendor/cfsolve/src/session/initial.rs +14 -5
- data/vendor/cfsolve/src/session/probes.rs +160 -125
- data/vendor/cfsolve/src/session/timing.rs +58 -53
- data/vendor/cfsolve/src/session/widget.rs +5 -6
- data/vendor/cfsolve/src/stage2/analysis/analyze.rs +50 -33
- data/vendor/cfsolve/src/stage2/analysis/discovery.rs +61 -66
- data/vendor/cfsolve/src/stage2/analysis/envelope.rs +31 -21
- data/vendor/cfsolve/src/stage2/analysis/recognize.rs +3 -5
- data/vendor/cfsolve/src/stage2/analysis.rs +0 -1
- data/vendor/cfsolve/src/stage2/audit_globals.rs +310 -0
- data/vendor/cfsolve/src/stage2/benchmark.rs +5 -8
- data/vendor/cfsolve/src/stage2/call_stack.rs +72 -16
- data/vendor/cfsolve/src/stage2/interaction/native.rs +2 -1
- data/vendor/cfsolve/src/stage2/interaction/plan.rs +0 -3
- data/vendor/cfsolve/src/stage2/payload/recover.rs +54 -23
- data/vendor/cfsolve/src/stage2/probes/ice.rs +11 -10
- data/vendor/cfsolve/src/stage2/probes/media.rs +47 -33
- data/vendor/cfsolve/src/stage2/probes/performance.rs +1 -0
- data/vendor/cfsolve/src/stage2/probes/rects.rs +12 -17
- data/vendor/cfsolve/src/stage2/probes/stack.rs +50 -103
- data/vendor/cfsolve/src/stage2/probes/websocket_echo.rs +17 -86
- data/vendor/cfsolve/src/stage2/probes.rs +0 -1
- data/vendor/cfsolve/src/stage2/records.rs +4 -13
- data/vendor/cfsolve/src/stage2/reference.json +1 -1
- data/vendor/cfsolve/src/stage2/reference.rs +98 -100
- data/vendor/cfsolve/src/stage2/registry.rs +41 -34
- data/vendor/cfsolve/src/stage2/replay.rs +182 -254
- data/vendor/cfsolve/src/stage2/stylesheets.rs +176 -0
- data/vendor/cfsolve/src/stage2/trace.rs +18 -0
- data/vendor/cfsolve/src/stage2/vocabulary.rs +111 -401
- data/vendor/cfsolve/src/stage2.rs +2 -1
- data/vendor/cfsolve/src/vm/disassemble.rs +39 -10
- data/vendor/cfsolve/src/vm/interpret/access.rs +5 -21
- data/vendor/cfsolve/src/vm/interpret/builtin.rs +6 -53
- data/vendor/cfsolve/src/vm/interpret/call.rs +86 -9
- data/vendor/cfsolve/src/vm/interpret/compile.rs +626 -0
- data/vendor/cfsolve/src/vm/interpret/copies.rs +232 -120
- data/vendor/cfsolve/src/vm/interpret/evaluate/tests.rs +96 -8
- data/vendor/cfsolve/src/vm/interpret/evaluate.rs +219 -92
- data/vendor/cfsolve/src/vm/interpret/explore.rs +155 -39
- data/vendor/cfsolve/src/vm/interpret/frozen.rs +171 -0
- data/vendor/cfsolve/src/vm/interpret/intrinsic.rs +350 -0
- data/vendor/cfsolve/src/vm/interpret/plan.rs +71 -44
- data/vendor/cfsolve/src/vm/interpret/run.rs +202 -13
- data/vendor/cfsolve/src/vm/interpret.rs +7 -2
- data/vendor/cfsolve/src/vm/machine.rs +6 -2
- data/vendor/cfsolve/src/vm/packed_names.rs +307 -0
- data/vendor/cfsolve/src/vm/session.rs +36 -14
- data/vendor/cfsolve/src/vm/value.rs +13 -0
- data/vendor/cfsolve/src/vm.rs +1 -0
- data/vendor/cfsolve/vendor/btls-sys/src/lib.rs +3 -0
- metadata +13 -6
- data/vendor/cfsolve/src/listing/normalize.rs +0 -264
- data/vendor/cfsolve/src/stage2/analysis/outputs.rs +0 -327
- data/vendor/cfsolve/src/stage2/probes/stylesheets.rs +0 -249
- data/vendor/cfsolve/src/stage2/reference-expanded.json +0 -42
- data/vendor/cfsolve/src/stage2/session_names.rs +0 -470
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
//! layout, instance fields, and opcode handlers. [`Session::constructed`] adds the constructor
|
|
3
3
|
//! fields that depend on a program.
|
|
4
4
|
//!
|
|
5
|
-
//! Each handler run copies mutable values; see [`super::interpret`]. Only
|
|
6
|
-
//!
|
|
5
|
+
//! Each handler run copies mutable values; see [`super::interpret`]. Only lazily compiled plans
|
|
6
|
+
//! are cached between runs, besides the decrypted strings a loaded program caches.
|
|
7
7
|
|
|
8
8
|
use std::cell::RefCell;
|
|
9
9
|
use std::collections::BTreeSet;
|
|
@@ -17,7 +17,7 @@ use swc_core::ecma::ast::{
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
use super::extract::{self, RunMethod};
|
|
20
|
-
use super::interpret::{self, Plans, Run, Spare};
|
|
20
|
+
use super::interpret::{self, Branching, Frozen, Intrinsics, Plans, Run, Spare};
|
|
21
21
|
use super::output::{Constant, Position, Term};
|
|
22
22
|
use super::registers::Member;
|
|
23
23
|
use super::value::Value;
|
|
@@ -33,6 +33,10 @@ pub(crate) struct Session<'ast> {
|
|
|
33
33
|
/// Lazily prepared handlers and helpers, including unsupported functions.
|
|
34
34
|
pub compiled: Plans<'ast>,
|
|
35
35
|
pub spare: RefCell<Spare<'ast>>,
|
|
36
|
+
/// Session binding objects handler runs share instead of copying.
|
|
37
|
+
pub frozen: Frozen,
|
|
38
|
+
/// What exploration has learned about each handler's paths, by handler address.
|
|
39
|
+
pub branching: RefCell<HashMap<usize, Branching>>,
|
|
36
40
|
pub bindings: HashMap<Atom, Value<'ast>>,
|
|
37
41
|
/// Names interrupted setup statements may have written, with their errors. Reading them
|
|
38
42
|
/// stops interpretation.
|
|
@@ -43,9 +47,9 @@ pub(crate) struct Session<'ast> {
|
|
|
43
47
|
/// Initial register stacks created by the constructor for handler execution.
|
|
44
48
|
pub initial: Vec<(u8, Value<'ast>)>,
|
|
45
49
|
/// Function used to decrypt string records stored in registers.
|
|
46
|
-
pub record_decoder: Option
|
|
47
|
-
///
|
|
48
|
-
pub
|
|
50
|
+
pub record_decoder: Option<&'ast Function>,
|
|
51
|
+
/// Script functions run natively.
|
|
52
|
+
pub intrinsics: Intrinsics,
|
|
49
53
|
/// The symbol the instance keeps its program under, if any.
|
|
50
54
|
pub program_key: Option<Rc<()>>,
|
|
51
55
|
/// Instance objects stored under the remaining symbols.
|
|
@@ -81,16 +85,22 @@ impl<'ast> Session<'ast> {
|
|
|
81
85
|
let functions = extract::functions(&body.stmts);
|
|
82
86
|
let run = extract::run_method(&functions, run)?;
|
|
83
87
|
let handlers = extract::handlers(&functions, &run.dispatch)?;
|
|
88
|
+
let record_decoder =
|
|
89
|
+
extract::record_decoder(body).and_then(|name| functions.get(&name).copied());
|
|
84
90
|
let mut session = Self {
|
|
85
91
|
functions,
|
|
86
92
|
handlers,
|
|
87
93
|
entry: extract::entry(body)?,
|
|
88
|
-
record_decoder
|
|
94
|
+
record_decoder,
|
|
89
95
|
..Self::unevaluated(constructor)
|
|
90
96
|
};
|
|
91
97
|
(session.bindings, session.unavailable) = interpret::setup(&session, prelude, &body.stmts);
|
|
92
98
|
let constructed = construct(&mut Run::method(&session), constructor);
|
|
93
|
-
|
|
99
|
+
Recovered {
|
|
100
|
+
state: session.state,
|
|
101
|
+
layout: session.layout,
|
|
102
|
+
initial: session.initial,
|
|
103
|
+
} = state(&run, &constructed)?;
|
|
94
104
|
session.symbol_fields = constructed.objects;
|
|
95
105
|
session.program_key = match &constructed.programs[..] {
|
|
96
106
|
[] => None,
|
|
@@ -98,6 +108,8 @@ impl<'ast> Session<'ast> {
|
|
|
98
108
|
symbols => bail!("the VM constructor keys {} values by symbol", symbols.len()),
|
|
99
109
|
};
|
|
100
110
|
session.fields = concrete(constructed.fields);
|
|
111
|
+
session.intrinsics = interpret::intrinsics(&session);
|
|
112
|
+
session.frozen = Frozen::of(&session.bindings);
|
|
101
113
|
Ok(session)
|
|
102
114
|
}
|
|
103
115
|
|
|
@@ -107,6 +119,8 @@ impl<'ast> Session<'ast> {
|
|
|
107
119
|
function_values: RefCell::default(),
|
|
108
120
|
compiled: Default::default(),
|
|
109
121
|
spare: RefCell::default(),
|
|
122
|
+
frozen: Frozen::default(),
|
|
123
|
+
branching: RefCell::default(),
|
|
110
124
|
bindings: HashMap::default(),
|
|
111
125
|
unavailable: HashMap::default(),
|
|
112
126
|
layout: Layout::default(),
|
|
@@ -115,7 +129,7 @@ impl<'ast> Session<'ast> {
|
|
|
115
129
|
symbol_fields: Vec::new(),
|
|
116
130
|
initial: Vec::new(),
|
|
117
131
|
record_decoder: None,
|
|
118
|
-
|
|
132
|
+
intrinsics: Intrinsics::default(),
|
|
119
133
|
fields: HashMap::default(),
|
|
120
134
|
constructor,
|
|
121
135
|
handlers: HashMap::default(),
|
|
@@ -345,13 +359,17 @@ fn concrete<'a>(mut fields: HashMap<String, Value<'a>>) -> HashMap<String, Value
|
|
|
345
359
|
fields
|
|
346
360
|
}
|
|
347
361
|
|
|
362
|
+
/// The handler state, register layout and initial register stacks [`state`] recovers.
|
|
363
|
+
struct Recovered<'a> {
|
|
364
|
+
state: HashMap<String, Value<'a>>,
|
|
365
|
+
layout: Layout,
|
|
366
|
+
initial: Vec<(u8, Value<'a>)>,
|
|
367
|
+
}
|
|
368
|
+
|
|
348
369
|
/// Recovers state-field values and register layout. The constructor computes indices as
|
|
349
370
|
/// `this.<mask> ^ <register>` with a random mask per run. Uses zero for the mask during
|
|
350
371
|
/// analysis.
|
|
351
|
-
fn state<'a>(
|
|
352
|
-
names: &RunMethod<'_>,
|
|
353
|
-
constructed: &Constructed<'a>,
|
|
354
|
-
) -> Result<(HashMap<String, Value<'a>>, Layout, Vec<(u8, Value<'a>)>)> {
|
|
372
|
+
fn state<'a>(names: &RunMethod<'_>, constructed: &Constructed<'a>) -> Result<Recovered<'a>> {
|
|
355
373
|
let fields = &constructed.fields;
|
|
356
374
|
let pc = constructed
|
|
357
375
|
.pc
|
|
@@ -402,7 +420,11 @@ fn state<'a>(
|
|
|
402
420
|
.map(|write| (write.register, write.value.clone()))
|
|
403
421
|
.collect();
|
|
404
422
|
state.extend(mask.map(|mask| (mask, Value::Number(0.0))));
|
|
405
|
-
Ok(
|
|
423
|
+
Ok(Recovered {
|
|
424
|
+
state,
|
|
425
|
+
layout,
|
|
426
|
+
initial,
|
|
427
|
+
})
|
|
406
428
|
}
|
|
407
429
|
|
|
408
430
|
/// Extracts the register and optional mask field from `this.<mask> ^ <register>` or a bare
|
|
@@ -215,6 +215,19 @@ impl<'ast> Value<'ast> {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/// Whether nothing a run does can change the value or its [`Value::expr`]: it is not a
|
|
219
|
+
/// container or a function with captured state.
|
|
220
|
+
pub(super) fn is_immutable(&self) -> bool {
|
|
221
|
+
match self {
|
|
222
|
+
Self::Array(_) | Self::Object(_) | Self::Set(_) | Self::Map(_) => false,
|
|
223
|
+
Self::Function(callable) => matches!(
|
|
224
|
+
&**callable,
|
|
225
|
+
Callable::Builtin(_) | Callable::Standard(_) | Callable::Script { scope: None, .. }
|
|
226
|
+
),
|
|
227
|
+
_ => true,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
218
231
|
pub(super) fn is_symbolic(&self) -> bool {
|
|
219
232
|
matches!(self, Self::Symbolic(_))
|
|
220
233
|
}
|
data/vendor/cfsolve/src/vm.rs
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cfsolve
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oliver Morgan
|
|
@@ -684,6 +684,9 @@ files:
|
|
|
684
684
|
- vendor/cfsolve/LICENSE
|
|
685
685
|
- vendor/cfsolve/README.md
|
|
686
686
|
- vendor/cfsolve/examples/build_profile.rs
|
|
687
|
+
- vendor/cfsolve/examples/capture_session.rs
|
|
688
|
+
- vendor/cfsolve/examples/check_answers.rs
|
|
689
|
+
- vendor/cfsolve/examples/compare_session.rs
|
|
687
690
|
- vendor/cfsolve/examples/disassemble.rs
|
|
688
691
|
- vendor/cfsolve/examples/fill_profile.rs
|
|
689
692
|
- vendor/cfsolve/examples/probe_plan.rs
|
|
@@ -698,6 +701,8 @@ files:
|
|
|
698
701
|
- vendor/cfsolve/profiles/safari-26-ios/challenge-page-answers.json
|
|
699
702
|
- vendor/cfsolve/rust-toolchain.toml
|
|
700
703
|
- vendor/cfsolve/rustfmt.toml
|
|
704
|
+
- vendor/cfsolve/scripts/capture_cssom.mjs
|
|
705
|
+
- vendor/cfsolve/scripts/serve_cssom.py
|
|
701
706
|
- vendor/cfsolve/src/bin/capture-proxy/cli.rs
|
|
702
707
|
- vendor/cfsolve/src/bin/capture-proxy/io.rs
|
|
703
708
|
- vendor/cfsolve/src/bin/capture-proxy/main.rs
|
|
@@ -722,6 +727,7 @@ files:
|
|
|
722
727
|
- vendor/cfsolve/src/challenge/codec.rs
|
|
723
728
|
- vendor/cfsolve/src/challenge/expression.rs
|
|
724
729
|
- vendor/cfsolve/src/challenge/installed.rs
|
|
730
|
+
- vendor/cfsolve/src/challenge/installed/audit.rs
|
|
725
731
|
- vendor/cfsolve/src/challenge/merge.rs
|
|
726
732
|
- vendor/cfsolve/src/challenge/orchestrator.rs
|
|
727
733
|
- vendor/cfsolve/src/challenge/payload.rs
|
|
@@ -789,7 +795,6 @@ files:
|
|
|
789
795
|
- vendor/cfsolve/src/listing.rs
|
|
790
796
|
- vendor/cfsolve/src/listing/fold.rs
|
|
791
797
|
- vendor/cfsolve/src/listing/index.rs
|
|
792
|
-
- vendor/cfsolve/src/listing/normalize.rs
|
|
793
798
|
- vendor/cfsolve/src/listing/query.rs
|
|
794
799
|
- vendor/cfsolve/src/listing/strings.rs
|
|
795
800
|
- vendor/cfsolve/src/main.rs
|
|
@@ -816,8 +821,8 @@ files:
|
|
|
816
821
|
- vendor/cfsolve/src/stage2/analysis/discovery.rs
|
|
817
822
|
- vendor/cfsolve/src/stage2/analysis/envelope.rs
|
|
818
823
|
- vendor/cfsolve/src/stage2/analysis/outcome.rs
|
|
819
|
-
- vendor/cfsolve/src/stage2/analysis/outputs.rs
|
|
820
824
|
- vendor/cfsolve/src/stage2/analysis/recognize.rs
|
|
825
|
+
- vendor/cfsolve/src/stage2/audit_globals.rs
|
|
821
826
|
- vendor/cfsolve/src/stage2/benchmark.rs
|
|
822
827
|
- vendor/cfsolve/src/stage2/call_stack.rs
|
|
823
828
|
- vendor/cfsolve/src/stage2/challenge_page.rs
|
|
@@ -851,18 +856,16 @@ files:
|
|
|
851
856
|
- vendor/cfsolve/src/stage2/probes/private_token.rs
|
|
852
857
|
- vendor/cfsolve/src/stage2/probes/rects.rs
|
|
853
858
|
- vendor/cfsolve/src/stage2/probes/stack.rs
|
|
854
|
-
- vendor/cfsolve/src/stage2/probes/stylesheets.rs
|
|
855
859
|
- vendor/cfsolve/src/stage2/probes/websocket_echo.rs
|
|
856
860
|
- vendor/cfsolve/src/stage2/probes/worker_fetch.rs
|
|
857
861
|
- vendor/cfsolve/src/stage2/probes/worker_navigator.rs
|
|
858
862
|
- vendor/cfsolve/src/stage2/proof.rs
|
|
859
863
|
- vendor/cfsolve/src/stage2/records.rs
|
|
860
|
-
- vendor/cfsolve/src/stage2/reference-expanded.json
|
|
861
864
|
- vendor/cfsolve/src/stage2/reference.json
|
|
862
865
|
- vendor/cfsolve/src/stage2/reference.rs
|
|
863
866
|
- vendor/cfsolve/src/stage2/registry.rs
|
|
864
867
|
- vendor/cfsolve/src/stage2/replay.rs
|
|
865
|
-
- vendor/cfsolve/src/stage2/
|
|
868
|
+
- vendor/cfsolve/src/stage2/stylesheets.rs
|
|
866
869
|
- vendor/cfsolve/src/stage2/trace.rs
|
|
867
870
|
- vendor/cfsolve/src/stage2/vocabulary.rs
|
|
868
871
|
- vendor/cfsolve/src/stage2/worker_timing.rs
|
|
@@ -876,16 +879,20 @@ files:
|
|
|
876
879
|
- vendor/cfsolve/src/vm/interpret/access.rs
|
|
877
880
|
- vendor/cfsolve/src/vm/interpret/builtin.rs
|
|
878
881
|
- vendor/cfsolve/src/vm/interpret/call.rs
|
|
882
|
+
- vendor/cfsolve/src/vm/interpret/compile.rs
|
|
879
883
|
- vendor/cfsolve/src/vm/interpret/copies.rs
|
|
880
884
|
- vendor/cfsolve/src/vm/interpret/evaluate.rs
|
|
881
885
|
- vendor/cfsolve/src/vm/interpret/evaluate/tests.rs
|
|
882
886
|
- vendor/cfsolve/src/vm/interpret/explore.rs
|
|
887
|
+
- vendor/cfsolve/src/vm/interpret/frozen.rs
|
|
888
|
+
- vendor/cfsolve/src/vm/interpret/intrinsic.rs
|
|
883
889
|
- vendor/cfsolve/src/vm/interpret/plan.rs
|
|
884
890
|
- vendor/cfsolve/src/vm/interpret/run.rs
|
|
885
891
|
- vendor/cfsolve/src/vm/interpret/semantics.rs
|
|
886
892
|
- vendor/cfsolve/src/vm/interpret/setup.rs
|
|
887
893
|
- vendor/cfsolve/src/vm/machine.rs
|
|
888
894
|
- vendor/cfsolve/src/vm/output.rs
|
|
895
|
+
- vendor/cfsolve/src/vm/packed_names.rs
|
|
889
896
|
- vendor/cfsolve/src/vm/registers.rs
|
|
890
897
|
- vendor/cfsolve/src/vm/session.rs
|
|
891
898
|
- vendor/cfsolve/src/vm/value.rs
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
//! Exposes constant expressions to structural readers without executing calls.
|
|
2
|
-
|
|
3
|
-
use std::collections::BTreeSet;
|
|
4
|
-
use std::convert::Infallible;
|
|
5
|
-
use std::ops::ControlFlow;
|
|
6
|
-
|
|
7
|
-
use super::Index;
|
|
8
|
-
use super::fold::{self, Frame, Known};
|
|
9
|
-
use crate::js::JsValue;
|
|
10
|
-
use crate::vm::{Constant, Effect, Listing, Term, dataflow};
|
|
11
|
-
|
|
12
|
-
fn evaluate(frame: &Frame, term: &Term) -> Option<Known> {
|
|
13
|
-
if let Some(known) = frame.resolve(term) {
|
|
14
|
-
return Some(known);
|
|
15
|
-
}
|
|
16
|
-
let value = fold::constant(frame, term);
|
|
17
|
-
if matches!(term, Term::Binary(_, _, _)) && matches!(value, Some(JsValue::String(_))) {
|
|
18
|
-
return None;
|
|
19
|
-
}
|
|
20
|
-
value.map(|value| {
|
|
21
|
-
if matches!(value, JsValue::String(_)) {
|
|
22
|
-
Known::Decoded(value)
|
|
23
|
-
} else {
|
|
24
|
-
Known::Literal(value)
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/// Fold only values agreed on by every incoming path. Function and dispatcher
|
|
30
|
-
/// entries start with common header facts, never another function's registers.
|
|
31
|
-
pub(crate) fn constants(listing: &mut Listing) {
|
|
32
|
-
let setup = Frame::seeded(Index::new(listing).header());
|
|
33
|
-
let mut entries: BTreeSet<_> = listing
|
|
34
|
-
.functions
|
|
35
|
-
.iter()
|
|
36
|
-
.map(|entry| entry.pc)
|
|
37
|
-
.chain(listing.hidden.iter().map(|branch| branch.target.pc))
|
|
38
|
-
.collect();
|
|
39
|
-
for instruction in listing.instructions.values() {
|
|
40
|
-
for effect in instruction.effects() {
|
|
41
|
-
effect.visit_terms(&mut |term| {
|
|
42
|
-
if let Term::Array(frame) = term {
|
|
43
|
-
entries.extend(crate::vm::catch_frame(frame).map(|handler| handler.pc));
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
let entries = entries.into_iter().map(|pc| (pc, setup.clone()));
|
|
49
|
-
let ControlFlow::<Infallible, _>::Continue(incoming) =
|
|
50
|
-
dataflow::forward(listing, entries, |pc, path, frame| {
|
|
51
|
-
frame.begin();
|
|
52
|
-
if !fold::feasible(path, |term| evaluate(frame, term)) {
|
|
53
|
-
return ControlFlow::Continue(false);
|
|
54
|
-
}
|
|
55
|
-
frame.follow(pc, &path.effects, evaluate, |_, _| {});
|
|
56
|
-
ControlFlow::Continue(true)
|
|
57
|
-
});
|
|
58
|
-
for (&pc, instruction) in &mut listing.instructions {
|
|
59
|
-
let Some(entry) = incoming.get(&pc) else {
|
|
60
|
-
continue;
|
|
61
|
-
};
|
|
62
|
-
for path in &mut instruction.paths {
|
|
63
|
-
let mut frame = entry.clone();
|
|
64
|
-
frame.begin();
|
|
65
|
-
for effect in &mut path.effects {
|
|
66
|
-
if let Effect::SetProperty { key, .. } = effect
|
|
67
|
-
&& let Some(Known::Decoded(JsValue::String(text))) = frame.resolve(key)
|
|
68
|
-
{
|
|
69
|
-
**key = Term::Const(Constant::String(text));
|
|
70
|
-
}
|
|
71
|
-
let folded = match &*effect {
|
|
72
|
-
Effect::Let(_, value) | Effect::Set(_, value) => evaluate(&frame, value),
|
|
73
|
-
_ => None,
|
|
74
|
-
};
|
|
75
|
-
frame.follow(pc, std::slice::from_ref(effect), evaluate, |_, _| {});
|
|
76
|
-
if let Effect::Let(_, value) | Effect::Set(_, value) = effect {
|
|
77
|
-
match folded {
|
|
78
|
-
Some(Known::Decoded(JsValue::String(text))) => {
|
|
79
|
-
*value = Term::Const(Constant::String(text))
|
|
80
|
-
}
|
|
81
|
-
Some(Known::Literal(JsValue::Number(number)))
|
|
82
|
-
if matches!(value, Term::Call { .. } | Term::Binary(_, _, _)) =>
|
|
83
|
-
{
|
|
84
|
-
*value = Term::Const(Constant::Number(number))
|
|
85
|
-
}
|
|
86
|
-
_ => {}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
#[cfg(test)]
|
|
95
|
-
mod tests {
|
|
96
|
-
use super::*;
|
|
97
|
-
use crate::vm::{Exit, Instruction, Path, Position};
|
|
98
|
-
|
|
99
|
-
fn text(value: &str) -> Term {
|
|
100
|
-
Term::Const(Constant::String(value.into()))
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
fn slice(register: u8) -> Term {
|
|
104
|
-
Term::Call {
|
|
105
|
-
callee: Box::new(Term::Get(
|
|
106
|
-
Box::new(Term::Reg(register)),
|
|
107
|
-
Box::new(text("slice")),
|
|
108
|
-
)),
|
|
109
|
-
this: Some(Box::new(Term::Reg(register))),
|
|
110
|
-
arguments: vec![
|
|
111
|
-
Term::Const(Constant::Number(2.0)),
|
|
112
|
-
Term::Const(Constant::Number(7.0)),
|
|
113
|
-
],
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
fn path(effects: Vec<Effect>, exit: Exit) -> Path {
|
|
118
|
-
Path {
|
|
119
|
-
conditions: vec![],
|
|
120
|
-
effects,
|
|
121
|
-
exit,
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
fn program(paths: Vec<Vec<Path>>) -> Listing {
|
|
126
|
-
let mut listing = Listing::default();
|
|
127
|
-
listing.functions.push(Position::new(0, 0));
|
|
128
|
-
for (pc, paths) in (0..).zip(paths) {
|
|
129
|
-
listing.instructions.insert(
|
|
130
|
-
pc,
|
|
131
|
-
Instruction {
|
|
132
|
-
pc,
|
|
133
|
-
key: 0,
|
|
134
|
-
opcode: 0,
|
|
135
|
-
paths,
|
|
136
|
-
},
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
// End the common header before any branch-specific constants.
|
|
140
|
-
listing.instructions.get_mut(&0).unwrap().paths[0]
|
|
141
|
-
.effects
|
|
142
|
-
.insert(
|
|
143
|
-
0,
|
|
144
|
-
Effect::Set(
|
|
145
|
-
255,
|
|
146
|
-
Term::Closure {
|
|
147
|
-
pc: 100,
|
|
148
|
-
key: 0,
|
|
149
|
-
parameters: vec![],
|
|
150
|
-
arity: 0,
|
|
151
|
-
},
|
|
152
|
-
),
|
|
153
|
-
);
|
|
154
|
-
listing
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
#[test]
|
|
158
|
-
fn decoded_strings_survive_temporaries_and_instruction_boundaries() {
|
|
159
|
-
let mut listing = program(vec![
|
|
160
|
-
vec![path(
|
|
161
|
-
vec![Effect::Set(1, text("\0\u{5}freshpadding"))],
|
|
162
|
-
Exit::Next { pc: 1, key: 0 },
|
|
163
|
-
)],
|
|
164
|
-
vec![path(
|
|
165
|
-
vec![Effect::Let(0, slice(1)), Effect::Set(2, Term::Temp(0))],
|
|
166
|
-
Exit::Next { pc: 2, key: 0 },
|
|
167
|
-
)],
|
|
168
|
-
vec![path(vec![Effect::Set(3, Term::Reg(2))], Exit::Return)],
|
|
169
|
-
]);
|
|
170
|
-
constants(&mut listing);
|
|
171
|
-
assert_eq!(
|
|
172
|
-
listing.instructions[&1].paths[0].effects[1],
|
|
173
|
-
Effect::Set(2, text("fresh"))
|
|
174
|
-
);
|
|
175
|
-
assert_eq!(
|
|
176
|
-
listing.instructions[&2].paths[0].effects[0],
|
|
177
|
-
Effect::Set(3, text("fresh"))
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
#[test]
|
|
182
|
-
fn disagreement_and_unknown_writes_leave_calls_unfolded() {
|
|
183
|
-
let mut listing = program(vec![
|
|
184
|
-
vec![path(vec![], Exit::Next { pc: 1, key: 0 })],
|
|
185
|
-
vec![
|
|
186
|
-
path(
|
|
187
|
-
vec![Effect::Set(1, text("\0\u{5}first"))],
|
|
188
|
-
Exit::Next { pc: 2, key: 0 },
|
|
189
|
-
),
|
|
190
|
-
path(
|
|
191
|
-
vec![Effect::Set(1, text("\0\u{5}other"))],
|
|
192
|
-
Exit::Next { pc: 2, key: 0 },
|
|
193
|
-
),
|
|
194
|
-
],
|
|
195
|
-
vec![path(
|
|
196
|
-
vec![
|
|
197
|
-
Effect::Set(2, slice(1)),
|
|
198
|
-
Effect::Set(1, Term::Global("unknown".into())),
|
|
199
|
-
Effect::Set(3, slice(1)),
|
|
200
|
-
],
|
|
201
|
-
Exit::Return,
|
|
202
|
-
)],
|
|
203
|
-
]);
|
|
204
|
-
let expected = listing.instructions[&2].paths[0].effects.clone();
|
|
205
|
-
constants(&mut listing);
|
|
206
|
-
assert_eq!(listing.instructions[&2].paths[0].effects, expected);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
#[test]
|
|
210
|
-
fn ordinary_register_copies_and_string_assembly_keep_their_shape() {
|
|
211
|
-
use swc_core::ecma::ast::BinaryOp;
|
|
212
|
-
let mut listing = program(vec![vec![path(
|
|
213
|
-
vec![
|
|
214
|
-
Effect::Set(1, text("window")),
|
|
215
|
-
Effect::Set(2, Term::Reg(1)),
|
|
216
|
-
Effect::Set(
|
|
217
|
-
3,
|
|
218
|
-
Term::Binary(
|
|
219
|
-
BinaryOp::Add,
|
|
220
|
-
Box::new(text("prefix")),
|
|
221
|
-
Box::new(text("suffix")),
|
|
222
|
-
),
|
|
223
|
-
),
|
|
224
|
-
],
|
|
225
|
-
Exit::Return,
|
|
226
|
-
)]]);
|
|
227
|
-
let expected = listing.instructions[&0].paths[0].effects.clone();
|
|
228
|
-
constants(&mut listing);
|
|
229
|
-
assert_eq!(listing.instructions[&0].paths[0].effects, expected);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
#[test]
|
|
233
|
-
fn exception_handlers_start_with_header_facts_and_decode_their_own_strings() {
|
|
234
|
-
let frame = Term::Array(vec![
|
|
235
|
-
Term::Const(Constant::Number(1.0)),
|
|
236
|
-
Term::Const(Constant::Number(0.0)),
|
|
237
|
-
Term::Cells,
|
|
238
|
-
Term::Const(Constant::Number(0.0)),
|
|
239
|
-
]);
|
|
240
|
-
let mut listing = program(vec![
|
|
241
|
-
vec![path(
|
|
242
|
-
vec![Effect::Set(2, text("\0\u{5}prior")), Effect::Let(0, frame)],
|
|
243
|
-
Exit::Return,
|
|
244
|
-
)],
|
|
245
|
-
vec![path(
|
|
246
|
-
vec![
|
|
247
|
-
Effect::Set(3, slice(2)),
|
|
248
|
-
Effect::Set(1, text("\0\u{5}catch")),
|
|
249
|
-
],
|
|
250
|
-
Exit::Next { pc: 2, key: 0 },
|
|
251
|
-
)],
|
|
252
|
-
vec![path(vec![Effect::Set(4, slice(1))], Exit::Return)],
|
|
253
|
-
]);
|
|
254
|
-
constants(&mut listing);
|
|
255
|
-
assert_eq!(
|
|
256
|
-
listing.instructions[&1].paths[0].effects[0],
|
|
257
|
-
Effect::Set(3, slice(2))
|
|
258
|
-
);
|
|
259
|
-
assert_eq!(
|
|
260
|
-
listing.instructions[&2].paths[0].effects[0],
|
|
261
|
-
Effect::Set(4, text("catch"))
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
}
|