Skip to main content

shape_vm/bytecode/
program_impl.rs

1use super::*;
2
3impl BytecodeProgram {
4    fn trait_method_symbol_key(
5        trait_name: &str,
6        type_name: &str,
7        impl_name: Option<&str>,
8        method_name: &str,
9    ) -> String {
10        format!(
11            "{}::{}::{}::{}",
12            trait_name,
13            type_name,
14            impl_name.unwrap_or(DEFAULT_TRAIT_IMPL_SELECTOR),
15            method_name
16        )
17    }
18
19    /// Create a new empty program
20    pub fn new() -> Self {
21        Self::default()
22    }
23
24    /// Add a constant to the pool and return its index
25    pub fn add_constant(&mut self, constant: Constant) -> u16 {
26        // Check if constant already exists
27        if let Some(idx) = self.constants.iter().position(|c| c == &constant) {
28            return idx as u16;
29        }
30
31        let idx = self.constants.len() as u16;
32        self.constants.push(constant);
33        idx
34    }
35
36    /// Add a string to the pool and return its index.
37    /// Uses the HashMap index for O(1) dedup.
38    pub fn add_string(&mut self, string: String) -> u16 {
39        self.ensure_string_index();
40        if let Some(&idx) = self.string_index.get(&string) {
41            return idx as u16;
42        }
43        let idx = self.strings.len() as u32;
44        self.string_index.insert(string.clone(), idx);
45        self.strings.push(string);
46        idx as u16
47    }
48
49    /// Intern a string and return a `StringId` for use in `Operand::Name` /
50    /// `Operand::MethodCall`. Uses the HashMap index for O(1) dedup.
51    pub fn intern_string(&mut self, s: &str) -> StringId {
52        self.ensure_string_index();
53        if let Some(&idx) = self.string_index.get(s) {
54            return StringId(idx);
55        }
56        let idx = self.strings.len() as u32;
57        self.string_index.insert(s.to_owned(), idx);
58        self.strings.push(s.to_owned());
59        StringId(idx)
60    }
61
62    /// Ensure the string_index HashMap is populated.
63    /// After deserialization, string_index is empty — rebuild it from the strings Vec.
64    pub fn ensure_string_index(&mut self) {
65        if self.string_index.is_empty() && !self.strings.is_empty() {
66            for (i, s) in self.strings.iter().enumerate() {
67                self.string_index.insert(s.clone(), i as u32);
68            }
69        }
70    }
71
72    /// Resolve a `StringId` back to a `&str`.
73    ///
74    /// Panics if the id is out of bounds (indicates a compiler bug).
75    pub fn resolve_string(&self, id: StringId) -> &str {
76        &self.strings[id.0 as usize]
77    }
78
79    /// Add an instruction to the program
80    pub fn emit(&mut self, instruction: Instruction) -> usize {
81        let idx = self.instructions.len();
82        self.instructions.push(instruction);
83        idx
84    }
85
86    /// Get the current instruction pointer
87    pub fn current_offset(&self) -> usize {
88        self.instructions.len()
89    }
90
91    /// Register a trait-method dispatch symbol.
92    pub fn register_trait_method_symbol(
93        &mut self,
94        trait_name: &str,
95        type_name: &str,
96        impl_name: Option<&str>,
97        method_name: &str,
98        function_name: &str,
99    ) {
100        let key = Self::trait_method_symbol_key(trait_name, type_name, impl_name, method_name);
101        self.trait_method_symbols
102            .insert(key, function_name.to_string());
103    }
104
105    /// Resolve a trait-method dispatch symbol name.
106    pub fn lookup_trait_method_symbol(
107        &self,
108        trait_name: &str,
109        type_name: &str,
110        impl_name: Option<&str>,
111        method_name: &str,
112    ) -> Option<&str> {
113        let key = Self::trait_method_symbol_key(trait_name, type_name, impl_name, method_name);
114        self.trait_method_symbols.get(&key).map(|s| s.as_str())
115    }
116
117    /// List named impl selectors for a trait method on a specific type.
118    ///
119    /// Excludes the default selector (`__default__`) and returns stable sorted names.
120    pub fn named_trait_impls_for_method(
121        &self,
122        trait_name: &str,
123        type_name: &str,
124        method_name: &str,
125    ) -> Vec<String> {
126        let prefix = format!("{}::{}::", trait_name, type_name);
127        let suffix = format!("::{}", method_name);
128        let mut names = std::collections::BTreeSet::new();
129
130        for key in self.trait_method_symbols.keys() {
131            if !key.starts_with(&prefix) || !key.ends_with(&suffix) {
132                continue;
133            }
134            let middle = &key[prefix.len()..key.len() - suffix.len()];
135            if middle != DEFAULT_TRAIT_IMPL_SELECTOR && !middle.is_empty() {
136                names.insert(middle.to_string());
137            }
138        }
139
140        names.into_iter().collect()
141    }
142
143    /// Find the default trait impl function for a given type and method (any trait).
144    ///
145    /// Searches `trait_method_symbols` for any entry whose type and method match,
146    /// preferring the default selector (`__default__`). Returns the compiled function
147    /// symbol name if found.
148    ///
149    /// This is used by method call dispatch to detect when a trait impl method exists
150    /// for the receiver type, so that builtin functions with the same name don't shadow it.
151    pub fn find_default_trait_impl_for_type_method(
152        &self,
153        type_name: &str,
154        method_name: &str,
155    ) -> Option<&str> {
156        // Pattern: "Trait::Type::Selector::method"
157        // We look for any key ending with "::Type::__default__::method"
158        let default_suffix = format!(
159            "::{}::{}::{}",
160            type_name, DEFAULT_TRAIT_IMPL_SELECTOR, method_name
161        );
162        for (key, func_name) in &self.trait_method_symbols {
163            if key.ends_with(&default_suffix) {
164                return Some(func_name.as_str());
165            }
166        }
167        // Fall back to any named impl (first match)
168        let suffix = format!("::{}", method_name);
169        let type_segment = format!("::{}::", type_name);
170        for (key, func_name) in &self.trait_method_symbols {
171            if key.contains(&type_segment) && key.ends_with(&suffix) {
172                return Some(func_name.as_str());
173            }
174        }
175        None
176    }
177
178    /// Append another program's bytecode to this one.
179    ///
180    /// Used internally for stdlib compilation where each module is compiled
181    /// separately and then combined. Unlike the old `merge_prepend`, this
182    /// appends `other` after `self`, so only `other`'s indices are adjusted.
183    pub(crate) fn merge_append(&mut self, other: BytecodeProgram) {
184        let mut other = other;
185
186        // Remove trailing Halt from self (we'll have the appended one or add our own)
187        if self.instructions.last().map(|i| i.opcode) == Some(OpCode::Halt) {
188            self.instructions.pop();
189        }
190
191        // Offsets for adjusting other's references
192        let const_offset = self.constants.len() as u16;
193        let string_offset = self.strings.len() as u16;
194        let func_offset = self.functions.len() as u16;
195        let foreign_offset = self.foreign_functions.len() as u16;
196        let instr_offset = self.instructions.len();
197
198        // Adjust other's constants
199        for constant in &mut other.constants {
200            if let Constant::Function(id) = constant {
201                *id += func_offset;
202            }
203        }
204
205        // Adjust other's function entry points
206        for func in &mut other.functions {
207            func.entry_point += instr_offset;
208        }
209
210        // Adjust other's instruction operands
211        for instr in &mut other.instructions {
212            if let Some(ref mut operand) = instr.operand {
213                match operand {
214                    Operand::Const(idx) => *idx += const_offset,
215                    Operand::Property(idx) => *idx += string_offset,
216                    Operand::Function(idx) => idx.0 += func_offset,
217                    Operand::Name(id) => id.0 += string_offset as u32,
218                    Operand::MethodCall { name, .. } => name.0 += string_offset as u32,
219                    Operand::TypedMethodCall { string_id, .. } => {
220                        *string_id += string_offset;
221                    }
222                    Operand::ForeignFunction(idx) => *idx += foreign_offset,
223                    Operand::Offset(_)
224                    | Operand::Local(_)
225                    | Operand::ModuleBinding(_)
226                    | Operand::Count(_)
227                    | Operand::Builtin(_)
228                    | Operand::ColumnIndex(_)
229                    | Operand::TypedField { .. }
230                    | Operand::TypedObjectAlloc { .. }
231                    | Operand::TypedMerge { .. }
232                    | Operand::ColumnAccess { .. }
233                    | Operand::MatrixDims { .. }
234                    | Operand::Width(_)
235                    | Operand::TypedLocal(_, _)
236                    | Operand::TypedModuleBinding(_, _) => {}
237                }
238            }
239        }
240
241        // Append constants, strings, functions, instructions
242        self.constants.append(&mut other.constants);
243        self.strings.append(&mut other.strings);
244        // Rebuild string index after merge
245        self.string_index.clear();
246        self.functions.append(&mut other.functions);
247        self.instructions.append(&mut other.instructions);
248
249        // Merge function local storage hints
250        self.function_local_storage_hints
251            .append(&mut other.function_local_storage_hints);
252
253        // Merge module binding names (dedup by name)
254        for name in other.module_binding_names {
255            if !self.module_binding_names.contains(&name) {
256                self.module_binding_names.push(name);
257            }
258        }
259
260        // Merge type schema registry
261        self.type_schema_registry.merge(other.type_schema_registry);
262
263        // Merge trait method symbols (self wins on collision)
264        for (key, value) in other.trait_method_symbols {
265            self.trait_method_symbols.entry(key).or_insert(value);
266        }
267
268        // Merge expanded function definitions (self wins on collision)
269        for (key, value) in other.expanded_function_defs {
270            self.expanded_function_defs.entry(key).or_insert(value);
271        }
272
273        // Merge foreign functions
274        self.foreign_functions.append(&mut other.foreign_functions);
275
276        // Merge native struct layouts (dedup by name, self wins)
277        for layout in other.native_struct_layouts {
278            if !self
279                .native_struct_layouts
280                .iter()
281                .any(|l| l.name == layout.name)
282            {
283                self.native_struct_layouts.push(layout);
284            }
285        }
286    }
287}