skrifa/outline/glyf/hint/engine/
definition.rs

1//! Defining and using functions and instructions.
2//!
3//! Implements 5 instructions.
4//!
5//! See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#defining-and-using-functions-and-instructions>
6
7use read_fonts::tables::glyf::bytecode::Opcode;
8
9use super::{
10    super::{definition::Definition, program::Program},
11    Engine, HintErrorKind, OpResult,
12};
13
14/// [Functions|Instructions] may not exceed 64K in size.
15/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#function-definition>
16/// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#instruction-definition>
17const MAX_DEFINITION_SIZE: usize = u16::MAX as usize;
18
19impl Engine<'_> {
20    /// Function definition.
21    ///
22    /// FDEF[] (0x2C)
23    ///
24    /// Pops: f: function identifier number
25    ///
26    /// Marks the start of a function definition. The argument f is a number
27    /// that uniquely identifies this function. A function definition can
28    /// appear only in the Font Program or the CVT program; attempts to invoke
29    /// the FDEF instruction within a glyph program will result in an error.
30    /// Functions may not exceed 64K in size.
31    ///
32    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#function-definition>
33    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3496>
34    pub(super) fn op_fdef(&mut self) -> OpResult {
35        let f = self.value_stack.pop()?;
36        self.do_def(DefKind::Function, f)
37    }
38
39    /// End function definition.
40    ///
41    /// ENDF[] (0x2D)
42    ///
43    /// Marks the end of a function definition or an instruction definition.
44    ///
45    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#end-function-definition>
46    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3578>
47    pub(super) fn op_endf(&mut self) -> OpResult {
48        self.program.leave()
49    }
50
51    /// Call function.
52    ///
53    /// CALL[] (0x2B)
54    ///
55    /// Pops: f: function identifier number
56    ///
57    /// Calls the function identified by the number f.
58    ///
59    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#call-function>
60    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3623>
61    pub(super) fn op_call(&mut self) -> OpResult {
62        let f = self.value_stack.pop()?;
63        self.do_call(DefKind::Function, 1, f)
64    }
65
66    /// Loop and call function.
67    ///
68    /// LOOPCALL[] (0x2a)
69    ///
70    /// Pops: f: function identifier number
71    ///       count: number of times to call the function
72    ///
73    /// Calls the function f, count number of times.
74    ///
75    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#loop-and-call-function>
76    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3704>
77    pub(super) fn op_loopcall(&mut self) -> OpResult {
78        let f = self.value_stack.pop()?;
79        let count = self.value_stack.pop()?;
80        if count > 0 {
81            self.loop_budget.doing_loop_call(count as usize)?;
82            self.do_call(DefKind::Function, count as u32, f)
83        } else {
84            Ok(())
85        }
86    }
87
88    /// Instruction definition.
89    ///
90    /// IDEF[] (0x89)
91    ///
92    /// Pops: opcode
93    ///
94    /// Begins the definition of an instruction. The instruction definition
95    /// terminates when at ENDF, which is encountered in the instruction
96    /// stream. Subsequent executions of the opcode popped will be directed
97    /// to the contents of this instruction definition (IDEF). IDEFs must be
98    /// defined in the Font Program or the CVT Program; attempts to invoke the
99    /// IDEF instruction within a glyph program will result in an error. An
100    /// IDEF affects only undefined opcodes. If the opcode in question is
101    /// already defined, the interpreter will ignore the IDEF. This is to be
102    /// used as a patching mechanism for future instructions. Instructions
103    /// may not exceed 64K in size.
104    ///
105    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructions#instruction-definition>
106    /// and <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttinterp.c#L3788>
107    pub(super) fn op_idef(&mut self) -> OpResult {
108        let opcode = self.value_stack.pop()?;
109        self.do_def(DefKind::Instruction, opcode)
110    }
111
112    /// Catch all for unhandled opcodes which will attempt to dispatch to a
113    /// user defined instruction.
114    pub(super) fn op_unknown(&mut self, opcode: u8) -> OpResult {
115        self.do_call(DefKind::Instruction, 1, opcode as i32)
116    }
117
118    /// Common code for FDEF and IDEF.
119    fn do_def(&mut self, kind: DefKind, key: i32) -> OpResult {
120        if self.program.initial == Program::Glyph {
121            return Err(HintErrorKind::DefinitionInGlyphProgram);
122        }
123        let defs = match kind {
124            DefKind::Function => &mut self.definitions.functions,
125            DefKind::Instruction => &mut self.definitions.instructions,
126        };
127        let def = defs.allocate(key)?;
128        let start = self.program.decoder.pc;
129        while let Some(ins) = self.program.decoder.decode() {
130            let ins = ins?;
131            match ins.opcode {
132                Opcode::FDEF | Opcode::IDEF => return Err(HintErrorKind::NestedDefinition),
133                Opcode::ENDF => {
134                    let range = start..ins.pc + 1;
135                    if self.graphics.is_pedantic && range.len() > MAX_DEFINITION_SIZE {
136                        *def = Default::default();
137                        return Err(HintErrorKind::DefinitionTooLarge);
138                    }
139                    *def = Definition::new(self.program.current, range, key);
140                    return Ok(());
141                }
142                _ => {}
143            }
144        }
145        Err(HintErrorKind::UnexpectedEndOfBytecode)
146    }
147
148    /// Common code for CALL, LOOPCALL and unknown opcode handling.
149    fn do_call(&mut self, kind: DefKind, count: u32, key: i32) -> OpResult {
150        if count == 0 {
151            return Ok(());
152        }
153        let def = match kind {
154            DefKind::Function => self.definitions.functions.get(key),
155            DefKind::Instruction => match self.definitions.instructions.get(key) {
156                // Remap an invalid definition error to unhandled opcode
157                Err(HintErrorKind::InvalidDefinition(opcode)) => Err(
158                    HintErrorKind::UnhandledOpcode(Opcode::from_byte(opcode as u8)),
159                ),
160                result => result,
161            },
162        };
163        self.program.enter(*def?, count)
164    }
165}
166
167enum DefKind {
168    Function,
169    Instruction,
170}
171
172#[cfg(test)]
173mod tests {
174    use super::{
175        super::{
176            super::program::{Program, ProgramState},
177            Engine, MockEngine,
178        },
179        HintErrorKind, Opcode, MAX_DEFINITION_SIZE,
180    };
181
182    /// Define two functions, one of which calls the other with
183    /// both CALL and LOOPCALL.
184    #[test]
185    fn define_function_call_loopcall() {
186        use Opcode::*;
187        let mut mock = MockEngine::new();
188        let mut engine = mock.engine();
189        #[rustfmt::skip]
190        let font_code = [
191            op(PUSHB001), 1, 0,
192            // FDEF 0: adds 2 to top stack value
193            op(FDEF),
194                op(PUSHB000), 2,
195                op(ADD),
196            op(ENDF),
197            // FDEF 1: calls FDEF 0 once, loop calls 5 times, then
198            // negates the result
199            op(FDEF),
200                op(PUSHB000), 0,
201                op(CALL),
202                op(PUSHB001), 5, 0,
203                op(LOOPCALL),
204                op(NEG),
205            op(ENDF),
206        ];
207        // Execute this code to define our functions
208        engine.set_font_code(&font_code);
209        engine.run().unwrap();
210        // Call FDEF 1 with value of 10 on the stack:
211        // * calls FDEF 0 which adds 2
212        // * loop calls FDEF 0 an additional 5 times which adds a total of 10
213        // * then negates the result
214        // leaving -22 on the stack
215        engine.value_stack.push(10).unwrap();
216        engine.value_stack.push(1).unwrap();
217        engine.op_call().unwrap();
218        engine.run().unwrap();
219        assert_eq!(engine.value_stack.pop().ok(), Some(-22));
220    }
221
222    /// Control value programs can override functions defined in the font
223    /// program based on instance state.
224    #[test]
225    fn override_function() {
226        use Opcode::*;
227        let mut mock = MockEngine::new();
228        let mut engine = mock.engine();
229        #[rustfmt::skip]
230        let font_code = [
231            op(PUSHB001), 0, 0,
232            // FDEF 0: adds 2 to top stack value
233            op(FDEF),
234                op(PUSHB000), 2,
235                op(ADD),
236            op(ENDF),
237            // Redefine FDEF 0: subtract 2 instead
238            op(FDEF),
239                op(PUSHB000), 2,
240                op(SUB),                
241            op(ENDF),
242        ];
243        // Execute this code to define our functions
244        engine.set_font_code(&font_code);
245        engine.run().unwrap();
246        // Call FDEF 0 with value of 10 on the stack:
247        // * should subtract 2 rather than add
248        // leaving 8 on the stack
249        engine.value_stack.push(10).unwrap();
250        engine.value_stack.push(0).unwrap();
251        engine.op_call().unwrap();
252        engine.run().unwrap();
253        assert_eq!(engine.value_stack.pop().ok(), Some(8));
254    }
255
256    /// Executes a call from a CV program into a font program.
257    ///
258    /// Tests ProgramState bytecode/decoder management.
259    #[test]
260    fn call_different_program() {
261        use Opcode::*;
262        let mut mock = MockEngine::new();
263        let mut engine = mock.engine();
264        #[rustfmt::skip]
265        let font_code = [
266            op(PUSHB000), 0,
267            // FDEF 0: adds 2 to top stack value
268            op(FDEF),
269                op(PUSHB000), 2,
270                op(ADD),
271            op(ENDF),
272        ];
273        #[rustfmt::skip]
274        let cv_code = [
275            // Call function defined in font program and negate result
276            op(PUSHB001), 40, 0,
277            op(CALL),
278            op(NEG)
279        ];
280        let glyph_code = &[];
281        // Run font program first to define the function
282        engine.program = ProgramState::new(&font_code, &cv_code, glyph_code, Program::Font);
283        engine.run().unwrap();
284        // Now run CV program which calls into the font program
285        engine.program = ProgramState::new(&font_code, &cv_code, glyph_code, Program::ControlValue);
286        engine.run().unwrap();
287        // Executing CV program:
288        // * pushes 40 to the stack
289        // * calls FDEF 0 in font program which adds 2
290        // * returns to CV program
291        // * negates the value
292        // leaving -42 on the stack
293        assert_eq!(engine.value_stack.pop().ok(), Some(-42));
294    }
295
296    /// Fail when we exceed loop call budget.
297    #[test]
298    fn loopcall_budget() {
299        use Opcode::*;
300        let mut mock = MockEngine::new();
301        let mut engine = mock.engine();
302        let limit = engine.loop_budget.limit;
303        #[rustfmt::skip]
304        let font_code = [
305            op(PUSHB001), 1, 0,
306            // FDEF 0: does nothing
307            op(FDEF),
308            op(ENDF),
309            // FDEF 1: loop calls FDEF 0 twice, exceeding the budget on the
310            // second attempt
311            op(FDEF),
312                op(PUSHB001), limit as u8, 0,
313                op(LOOPCALL),
314                op(PUSHB001), 1, 0,
315                op(LOOPCALL), // pc = 13
316            op(ENDF),
317        ];
318        // Execute this code to define our functions
319        engine.set_font_code(&font_code);
320        engine.run().unwrap();
321        // Call FDEF 1 which attempts to loop call FDEF 0 (limit + 1) times
322        engine.value_stack.push(10).unwrap();
323        engine.value_stack.push(1).unwrap();
324        engine.op_call().unwrap();
325        let err = engine.run().unwrap_err();
326        assert!(matches!(err.kind, HintErrorKind::ExceededExecutionBudget));
327        assert_eq!(err.pc, 13);
328    }
329
330    /// Defines an instruction using an available opcode and executes it.
331    #[test]
332    fn define_instruction_and_use() {
333        use Opcode::*;
334        let mut mock = MockEngine::new();
335        let mut engine = mock.engine();
336        #[rustfmt::skip]
337        let font_code = [
338            // IDEF 0x93: adds 2 to top stack value
339            op(PUSHB000), op(INS93),
340            op(IDEF),
341                op(PUSHB000), 2,
342                op(ADD),
343            op(ENDF),
344            // FDEF 0: uses defined instruction 0x93 and negates the result 
345            op(PUSHB000), 0,
346            op(FDEF),
347                op(INS93),
348                op(NEG),
349            op(ENDF),
350        ];
351        // Execute this code to define our functions
352        engine.set_font_code(&font_code);
353        engine.run().unwrap();
354        // Call FDEF 0 with value of 10 on the stack:
355        // * executes defined instruction 0x93
356        // * then negates the result
357        // leaving -12 on the stack
358        engine.value_stack.push(10).unwrap();
359        engine.value_stack.push(0).unwrap();
360        engine.op_call().unwrap();
361        engine.run().unwrap();
362        assert_eq!(engine.value_stack.pop().ok(), Some(-12));
363    }
364
365    // Invalid to nest definitions.
366    #[test]
367    fn nested_definition() {
368        use Opcode::*;
369        let mut mock = MockEngine::new();
370        let mut engine = mock.engine();
371        #[rustfmt::skip]
372        let font_code = [
373            op(PUSHB001), 1, 0,
374            op(FDEF), // pc = 3
375                op(FDEF),
376                op(ENDF),
377            op(ENDF),
378        ];
379        // Execute this code to define our functions
380        engine.set_font_code(&font_code);
381        let err = engine.run().unwrap_err();
382        assert!(matches!(err.kind, HintErrorKind::NestedDefinition));
383        assert_eq!(err.pc, 3);
384    }
385
386    // Invalid to modify definitions from the glyph program.
387    #[test]
388    fn definition_in_glyph_program() {
389        use Opcode::*;
390        let mut mock = MockEngine::new();
391        let mut engine = mock.engine();
392        #[rustfmt::skip]
393        let font_code = [
394            op(PUSHB000), 0,
395            op(FDEF), // pc = 2
396            op(ENDF),
397        ];
398        engine.set_font_code(&font_code);
399        engine.program.initial = Program::Glyph;
400        let err = engine.run().unwrap_err();
401        assert!(matches!(err.kind, HintErrorKind::DefinitionInGlyphProgram));
402        assert_eq!(err.pc, 2);
403    }
404
405    #[test]
406    fn undefined_function() {
407        let mut mock = MockEngine::new();
408        let mut engine = mock.engine();
409        engine.value_stack.push(111).unwrap();
410        assert!(matches!(
411            engine.op_call(),
412            Err(HintErrorKind::InvalidDefinition(111))
413        ));
414    }
415
416    /// Fun function that just calls itself :)
417    #[test]
418    fn infinite_recursion() {
419        use Opcode::*;
420        let mut mock = MockEngine::new();
421        let mut engine = mock.engine();
422        #[rustfmt::skip]
423        let font_code = [
424            // FDEF 0: call FDEF 0
425            op(PUSHB000), 0,
426            op(FDEF),
427                op(PUSHB000), 0,
428                op(CALL), // pc = 5
429            op(ENDF),
430        ];
431        engine.set_font_code(&font_code);
432        engine.run().unwrap();
433        // Call stack overflow
434        engine.value_stack.push(0).unwrap();
435        engine.op_call().unwrap();
436        let err = engine.run().unwrap_err();
437        assert!(matches!(err.kind, HintErrorKind::CallStackOverflow));
438        assert_eq!(err.pc, 5);
439    }
440
441    #[test]
442    fn call_stack_underflow() {
443        use Opcode::*;
444        let mut mock = MockEngine::new();
445        let mut engine = mock.engine();
446        #[rustfmt::skip]
447        let font_code = [
448            op(ENDF)
449        ];
450        engine.set_font_code(&font_code);
451        let err = engine.run().unwrap_err();
452        assert!(matches!(err.kind, HintErrorKind::CallStackUnderflow));
453        assert_eq!(err.pc, 0);
454    }
455
456    #[test]
457    fn unhandled_opcode() {
458        let mut mock = MockEngine::new();
459        let mut engine = mock.engine();
460        #[rustfmt::skip]
461        let font_code = [
462            op(Opcode::INS28),
463        ];
464        engine.set_font_code(&font_code);
465        let err = engine.run().unwrap_err();
466        assert!(matches!(
467            err.kind,
468            HintErrorKind::UnhandledOpcode(Opcode::INS28)
469        ));
470        assert_eq!(err.pc, 0);
471    }
472
473    #[test]
474    fn too_many_definitions() {
475        use Opcode::*;
476        let mut mock = MockEngine::new();
477        let mut engine = mock.engine();
478        #[rustfmt::skip]
479        let font_code = [
480            op(PUSHB101), 0, 1, 2, 3, 4, 5,
481            op(FDEF), op(ENDF),
482            op(FDEF), op(ENDF),
483            op(FDEF), op(ENDF),
484            op(FDEF), op(ENDF),
485            op(FDEF), op(ENDF),
486            op(FDEF), op(ENDF),
487        ];
488        engine.set_font_code(&font_code);
489        let err = engine.run().unwrap_err();
490        assert!(matches!(err.kind, HintErrorKind::TooManyDefinitions));
491        assert_eq!(err.pc, 17);
492    }
493
494    #[test]
495    fn big_definition() {
496        use Opcode::*;
497        let mut mock = MockEngine::new();
498        let mut engine = mock.engine();
499        let mut font_code = vec![];
500        font_code.extend_from_slice(&[op(PUSHB000), 0, op(FDEF)]);
501        font_code.extend(core::iter::repeat_n(op(NEG), MAX_DEFINITION_SIZE + 1));
502        font_code.push(op(ENDF));
503        engine.set_font_code(&font_code);
504        engine.graphics.is_pedantic = true;
505        engine.value_stack.push(1).unwrap();
506        let err = engine.run().unwrap_err();
507        assert!(matches!(err.kind, HintErrorKind::DefinitionTooLarge));
508        assert_eq!(err.pc, 2);
509    }
510
511    fn op(opcode: Opcode) -> u8 {
512        opcode as u8
513    }
514
515    impl<'a> Engine<'a> {
516        fn set_font_code(&mut self, code: &'a [u8]) {
517            self.program.bytecode[0] = code;
518            self.program.decoder.bytecode = code;
519            self.program.current = Program::Font;
520        }
521    }
522}