1use xee_xpath_ast::ast;
2
3use xee_interpreter::interpreter::instruction::{
4 encode_instruction, instruction_size, Instruction,
5};
6use xee_interpreter::{context, function, interpreter, sequence, span, xml};
7
8use crate::ir;
9
10#[must_use]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub(crate) struct ForwardJumpRef(usize);
13
14#[must_use]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub(crate) struct BackwardJumpRef(usize);
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub(crate) enum JumpCondition {
20 Always,
21 True,
22 False,
23}
24
25pub struct FunctionBuilder<'a> {
26 program: &'a mut interpreter::Program,
27 compiled: Vec<u8>,
28 spans: Vec<span::SourceSpan>,
29 constants: Vec<sequence::Sequence>,
30 steps: Vec<xml::Step>,
31 cast_types: Vec<function::CastType>,
32 sequence_types: Vec<ast::SequenceType>,
33 closure_names: Vec<ir::Name>,
34}
35
36impl<'a> FunctionBuilder<'a> {
37 pub fn new(program: &'a mut interpreter::Program) -> Self {
38 FunctionBuilder {
39 program,
40 compiled: Vec::new(),
41 spans: Vec::new(),
42 constants: Vec::new(),
43 steps: Vec::new(),
44 cast_types: Vec::new(),
45 sequence_types: Vec::new(),
46 closure_names: Vec::new(),
47 }
48 }
49
50 pub(crate) fn static_context(&self) -> &context::StaticContext {
51 self.program.static_context()
52 }
53
54 pub(crate) fn emit(&mut self, instruction: Instruction, span: span::SourceSpan) {
55 for _ in 0..instruction_size(&instruction) {
56 self.spans.push(span);
57 }
58 encode_instruction(instruction, &mut self.compiled);
59 }
60
61 pub(crate) fn emit_constant(&mut self, constant: sequence::Sequence, span: span::SourceSpan) {
62 let constant_id = self.constants.len();
63 self.constants.push(constant);
64 if constant_id > (u16::MAX as usize) {
65 panic!("too many constants");
66 }
67 self.emit(Instruction::Const(constant_id as u16), span);
68 }
69
70 pub(crate) fn add_closure_name(&mut self, name: &ir::Name) -> usize {
71 let found = self.closure_names.iter().position(|n| n == name);
72 if let Some(index) = found {
73 return index;
74 }
75 let index = self.closure_names.len();
76 self.closure_names.push(name.clone());
77 if index > (u16::MAX as usize) {
78 panic!("too many closure names");
79 }
80 index
81 }
82
83 pub(crate) fn add_step(&mut self, step: xml::Step) -> usize {
84 let step_id = self.steps.len();
85 self.steps.push(step);
86 if step_id > (u16::MAX as usize) {
87 panic!("too many steps");
88 }
89 step_id
90 }
91
92 pub(crate) fn add_cast_type(&mut self, cast_type: function::CastType) -> usize {
93 let cast_type_id = self.cast_types.len();
94 self.cast_types.push(cast_type);
95 if cast_type_id > (u16::MAX as usize) {
96 panic!("too many cast types");
97 }
98 cast_type_id
99 }
100
101 pub(crate) fn add_sequence_type(&mut self, sequence_type: ast::SequenceType) -> usize {
102 let sequence_type_id = self.sequence_types.len();
103 self.sequence_types.push(sequence_type);
104 if sequence_type_id > (u16::MAX as usize) {
105 panic!("too many sequence types");
106 }
107 sequence_type_id
108 }
109
110 pub(crate) fn loop_start(&self) -> BackwardJumpRef {
111 BackwardJumpRef(self.compiled.len())
112 }
113
114 pub(crate) fn emit_jump_backward(
115 &mut self,
116 jump_ref: BackwardJumpRef,
117 condition: JumpCondition,
118 span: span::SourceSpan,
119 ) {
120 let current = self.compiled.len() + 3;
121 let offset = current - jump_ref.0;
122 if jump_ref.0 > current {
123 panic!("cannot jump forward");
124 }
125 if offset > (u16::MAX as usize) {
126 panic!("jump too far");
127 }
128
129 match condition {
130 JumpCondition::True => self.emit(Instruction::JumpIfTrue(-(offset as i16)), span),
131 JumpCondition::False => self.emit(Instruction::JumpIfFalse(-(offset as i16)), span),
132 JumpCondition::Always => self.emit(Instruction::Jump(-(offset as i16)), span),
133 }
134 }
135
136 pub(crate) fn emit_jump_forward(
137 &mut self,
138 condition: JumpCondition,
139 span: span::SourceSpan,
140 ) -> ForwardJumpRef {
141 let index = self.compiled.len();
142 match condition {
143 JumpCondition::True => self.emit(Instruction::JumpIfTrue(0), span),
144 JumpCondition::False => self.emit(Instruction::JumpIfFalse(0), span),
145 JumpCondition::Always => self.emit(Instruction::Jump(0), span),
146 }
147 ForwardJumpRef(index)
148 }
149
150 pub(crate) fn patch_jump(&mut self, jump_ref: ForwardJumpRef) {
151 let current = self.compiled.len();
152 if jump_ref.0 > current {
153 panic!("can only patch forward jumps");
154 }
155 let offset = current - jump_ref.0 - 3; if offset > (u16::MAX as usize) {
157 panic!("jump too far");
158 }
159 let offset_bytes = offset.to_le_bytes();
160 self.compiled[jump_ref.0 + 1] = offset_bytes[0];
161 self.compiled[jump_ref.0 + 2] = offset_bytes[1];
162 }
163
164 pub(crate) fn finish(
165 mut self,
166 name: String,
167 function_definition: &ir::FunctionDefinition,
168 span: span::SourceSpan,
169 ) -> function::InlineFunction {
170 if let Some(return_type) = &function_definition.return_type {
171 let sequence_type_id = self.add_sequence_type(return_type.clone());
172 if sequence_type_id > (u16::MAX as usize) {
173 panic!("too many sequence types");
174 }
175 self.emit(Instruction::ReturnConvert(sequence_type_id as u16), span);
176 }
177 self.emit(Instruction::Return, span);
178 function::InlineFunction {
179 name,
180 signature: function_definition.signature(),
181 chunk: self.compiled,
182 spans: self.spans,
183 closure_names: self.closure_names,
184 constants: self.constants,
185 steps: self.steps,
186 cast_types: self.cast_types,
187 sequence_types: self.sequence_types,
188 }
189 }
190
191 pub(crate) fn builder(&mut self) -> FunctionBuilder {
192 FunctionBuilder::new(self.program)
193 }
194
195 pub(crate) fn add_function(
196 &mut self,
197 function: function::InlineFunction,
198 ) -> function::InlineFunctionId {
199 self.program.add_function(function)
200 }
201}