quickjs_oxide/engine/compiler/
options.rs1use crate::engine::code::function::metadata::{
4 ClosureVariableKind, EvalCallerProfile, EvalCallerVariableTarget, EvalKind, EvalRootBinding,
5 EvalScopeKind,
6};
7use crate::engine::value::JsString;
8
9pub const DEFAULT_EVAL_FILENAME: &str = "<input>";
11
12#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct CompileOptions {
15 pub filename: String,
16 pub transient: bool,
19}
20
21impl CompileOptions {
22 #[must_use]
23 pub fn new(filename: impl Into<String>) -> Self {
24 Self {
25 filename: filename.into(),
26 transient: false,
27 }
28 }
29
30 #[must_use]
32 pub fn transient(mut self) -> Self {
33 self.transient = true;
34 self
35 }
36}
37
38impl Default for CompileOptions {
39 fn default() -> Self {
40 Self::new(DEFAULT_EVAL_FILENAME)
41 }
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct EvalCompileContext {
53 pub kind: EvalKind,
54 pub caller_strict: bool,
55 pub bindings: Box<[EvalRootBinding<JsString>]>,
56 pub caller_profile: EvalCallerProfile,
57 pub super_call_allowed: bool,
58 pub super_allowed: bool,
59 pub arguments_forbidden: bool,
60}
61
62impl EvalCompileContext {
63 #[cfg_attr(not(test), allow(dead_code))]
64 pub fn direct(caller_strict: bool, bindings: Vec<EvalRootBinding<JsString>>) -> Self {
65 let scope_count = bindings
66 .iter()
67 .map(|binding| usize::from(binding.scope) + 1)
68 .max()
69 .unwrap_or(0);
70 let mut scope_kinds = vec![EvalScopeKind::FunctionRoot; scope_count];
71 for binding in &bindings {
72 if binding.kind == ClosureVariableKind::WithObject {
73 scope_kinds[usize::from(binding.scope)] = EvalScopeKind::With;
74 } else if binding.is_catch_parameter {
75 scope_kinds[usize::from(binding.scope)] = EvalScopeKind::Catch;
76 }
77 }
78 for (scope, scope_kind) in scope_kinds.iter_mut().enumerate() {
79 let has_parameter_object = bindings.iter().any(|binding| {
80 usize::from(binding.scope) == scope
81 && binding.kind == ClosureVariableKind::ArgEvalVariableObject
82 });
83 let has_body_object = bindings.iter().any(|binding| {
84 usize::from(binding.scope) == scope
85 && binding.kind == ClosureVariableKind::EvalVariableObject
86 });
87 if has_parameter_object && !has_body_object {
88 *scope_kind = EvalScopeKind::Parameter;
89 }
90 }
91 let variable_target = if caller_strict {
92 EvalCallerVariableTarget::StrictLocal
93 } else {
94 bindings
95 .iter()
96 .position(|binding| {
97 matches!(
98 binding.kind,
99 ClosureVariableKind::EvalVariableObject
100 | ClosureVariableKind::ArgEvalVariableObject
101 )
102 })
103 .and_then(|index| u16::try_from(index).ok())
104 .map_or(
105 EvalCallerVariableTarget::Global,
106 EvalCallerVariableTarget::ExternalBinding,
107 )
108 };
109 Self::direct_with_profile(
110 caller_strict,
111 bindings,
112 EvalCallerProfile {
113 scope_kinds: scope_kinds.into_boxed_slice(),
114 variable_target,
115 },
116 false,
117 false,
118 )
119 }
120
121 pub fn direct_with_profile(
122 caller_strict: bool,
123 bindings: Vec<EvalRootBinding<JsString>>,
124 caller_profile: EvalCallerProfile,
125 super_call_allowed: bool,
126 super_allowed: bool,
127 ) -> Self {
128 Self {
129 kind: EvalKind::Direct,
130 caller_strict,
131 bindings: bindings.into_boxed_slice(),
132 caller_profile,
133 super_call_allowed,
134 super_allowed,
135 arguments_forbidden: false,
136 }
137 }
138
139 pub fn indirect() -> Self {
140 Self {
141 kind: EvalKind::Indirect,
142 caller_strict: false,
143 bindings: Box::new([]),
144 caller_profile: EvalCallerProfile {
145 scope_kinds: Box::new([]),
146 variable_target: EvalCallerVariableTarget::Global,
147 },
148 super_call_allowed: false,
149 super_allowed: false,
150 arguments_forbidden: false,
151 }
152 }
153}