//! Defining and using functions and instructions. //! //! Implements 5 instructions. //! //! See use read_fonts::tables::glyf::bytecode::Opcode; use super::{ super::{definition::Definition, program::Program}, Engine, HintErrorKind, OpResult, }; /// [Functions|Instructions] may not exceed 64K in size. /// See /// See const MAX_DEFINITION_SIZE: usize = u16::MAX as usize; impl Engine<'_> { /// Function definition. /// /// FDEF[] (0x2C) /// /// Pops: f: function identifier number /// /// Marks the start of a function definition. The argument f is a number /// that uniquely identifies this function. A function definition can /// appear only in the Font Program or the CVT program; attempts to invoke /// the FDEF instruction within a glyph program will result in an error. /// Functions may not exceed 64K in size. /// /// See /// and pub(super) fn op_fdef(&mut self) -> OpResult { let f = self.value_stack.pop()?; self.do_def(DefKind::Function, f) } /// End function definition. /// /// ENDF[] (0x2D) /// /// Marks the end of a function definition or an instruction definition. /// /// See /// and pub(super) fn op_endf(&mut self) -> OpResult { self.program.leave() } /// Call function. /// /// CALL[] (0x2B) /// /// Pops: f: function identifier number /// /// Calls the function identified by the number f. /// /// See /// and pub(super) fn op_call(&mut self) -> OpResult { let f = self.value_stack.pop()?; self.do_call(DefKind::Function, 1, f) } /// Loop and call function. /// /// LOOPCALL[] (0x2a) /// /// Pops: f: function identifier number /// count: number of times to call the function /// /// Calls the function f, count number of times. /// /// See /// and pub(super) fn op_loopcall(&mut self) -> OpResult { let f = self.value_stack.pop()?; let count = self.value_stack.pop()?; if count > 0 { self.loop_budget.doing_loop_call(count as usize)?; self.do_call(DefKind::Function, count as u32, f) } else { Ok(()) } } /// Instruction definition. /// /// IDEF[] (0x89) /// /// Pops: opcode /// /// Begins the definition of an instruction. The instruction definition /// terminates when at ENDF, which is encountered in the instruction /// stream. Subsequent executions of the opcode popped will be directed /// to the contents of this instruction definition (IDEF). IDEFs must be /// defined in the Font Program or the CVT Program; attempts to invoke the /// IDEF instruction within a glyph program will result in an error. An /// IDEF affects only undefined opcodes. If the opcode in question is /// already defined, the interpreter will ignore the IDEF. This is to be /// used as a patching mechanism for future instructions. Instructions /// may not exceed 64K in size. /// /// See /// and pub(super) fn op_idef(&mut self) -> OpResult { let opcode = self.value_stack.pop()?; self.do_def(DefKind::Instruction, opcode) } /// Catch all for unhandled opcodes which will attempt to dispatch to a /// user defined instruction. pub(super) fn op_unknown(&mut self, opcode: u8) -> OpResult { self.do_call(DefKind::Instruction, 1, opcode as i32) } /// Common code for FDEF and IDEF. fn do_def(&mut self, kind: DefKind, key: i32) -> OpResult { if self.program.initial == Program::Glyph { return Err(HintErrorKind::DefinitionInGlyphProgram); } let defs = match kind { DefKind::Function => &mut self.definitions.functions, DefKind::Instruction => &mut self.definitions.instructions, }; let def = defs.allocate(key)?; let start = self.program.decoder.pc; while let Some(ins) = self.program.decoder.decode() { let ins = ins?; match ins.opcode { Opcode::FDEF | Opcode::IDEF => return Err(HintErrorKind::NestedDefinition), Opcode::ENDF => { let range = start..ins.pc + 1; if self.graphics.is_pedantic && range.len() > MAX_DEFINITION_SIZE { *def = Default::default(); return Err(HintErrorKind::DefinitionTooLarge); } *def = Definition::new(self.program.current, range, key); return Ok(()); } _ => {} } } Err(HintErrorKind::UnexpectedEndOfBytecode) } /// Common code for CALL, LOOPCALL and unknown opcode handling. fn do_call(&mut self, kind: DefKind, count: u32, key: i32) -> OpResult { if count == 0 { return Ok(()); } let def = match kind { DefKind::Function => self.definitions.functions.get(key), DefKind::Instruction => match self.definitions.instructions.get(key) { // Remap an invalid definition error to unhandled opcode Err(HintErrorKind::InvalidDefinition(opcode)) => Err( HintErrorKind::UnhandledOpcode(Opcode::from_byte(opcode as u8)), ), result => result, }, }; self.program.enter(*def?, count) } } enum DefKind { Function, Instruction, } #[cfg(test)] mod tests { use super::{ super::{ super::program::{Program, ProgramState}, Engine, MockEngine, }, HintErrorKind, Opcode, MAX_DEFINITION_SIZE, }; /// Define two functions, one of which calls the other with /// both CALL and LOOPCALL. #[test] fn define_function_call_loopcall() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(PUSHB001), 1, 0, // FDEF 0: adds 2 to top stack value op(FDEF), op(PUSHB000), 2, op(ADD), op(ENDF), // FDEF 1: calls FDEF 0 once, loop calls 5 times, then // negates the result op(FDEF), op(PUSHB000), 0, op(CALL), op(PUSHB001), 5, 0, op(LOOPCALL), op(NEG), op(ENDF), ]; // Execute this code to define our functions engine.set_font_code(&font_code); engine.run().unwrap(); // Call FDEF 1 with value of 10 on the stack: // * calls FDEF 0 which adds 2 // * loop calls FDEF 0 an additional 5 times which adds a total of 10 // * then negates the result // leaving -22 on the stack engine.value_stack.push(10).unwrap(); engine.value_stack.push(1).unwrap(); engine.op_call().unwrap(); engine.run().unwrap(); assert_eq!(engine.value_stack.pop().ok(), Some(-22)); } /// Control value programs can override functions defined in the font /// program based on instance state. #[test] fn override_function() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(PUSHB001), 0, 0, // FDEF 0: adds 2 to top stack value op(FDEF), op(PUSHB000), 2, op(ADD), op(ENDF), // Redefine FDEF 0: subtract 2 instead op(FDEF), op(PUSHB000), 2, op(SUB), op(ENDF), ]; // Execute this code to define our functions engine.set_font_code(&font_code); engine.run().unwrap(); // Call FDEF 0 with value of 10 on the stack: // * should subtract 2 rather than add // leaving 8 on the stack engine.value_stack.push(10).unwrap(); engine.value_stack.push(0).unwrap(); engine.op_call().unwrap(); engine.run().unwrap(); assert_eq!(engine.value_stack.pop().ok(), Some(8)); } /// Executes a call from a CV program into a font program. /// /// Tests ProgramState bytecode/decoder management. #[test] fn call_different_program() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(PUSHB000), 0, // FDEF 0: adds 2 to top stack value op(FDEF), op(PUSHB000), 2, op(ADD), op(ENDF), ]; #[rustfmt::skip] let cv_code = [ // Call function defined in font program and negate result op(PUSHB001), 40, 0, op(CALL), op(NEG) ]; let glyph_code = &[]; // Run font program first to define the function engine.program = ProgramState::new(&font_code, &cv_code, glyph_code, Program::Font); engine.run().unwrap(); // Now run CV program which calls into the font program engine.program = ProgramState::new(&font_code, &cv_code, glyph_code, Program::ControlValue); engine.run().unwrap(); // Executing CV program: // * pushes 40 to the stack // * calls FDEF 0 in font program which adds 2 // * returns to CV program // * negates the value // leaving -42 on the stack assert_eq!(engine.value_stack.pop().ok(), Some(-42)); } /// Fail when we exceed loop call budget. #[test] fn loopcall_budget() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); let limit = engine.loop_budget.limit; #[rustfmt::skip] let font_code = [ op(PUSHB001), 1, 0, // FDEF 0: does nothing op(FDEF), op(ENDF), // FDEF 1: loop calls FDEF 0 twice, exceeding the budget on the // second attempt op(FDEF), op(PUSHB001), limit as u8, 0, op(LOOPCALL), op(PUSHB001), 1, 0, op(LOOPCALL), // pc = 13 op(ENDF), ]; // Execute this code to define our functions engine.set_font_code(&font_code); engine.run().unwrap(); // Call FDEF 1 which attempts to loop call FDEF 0 (limit + 1) times engine.value_stack.push(10).unwrap(); engine.value_stack.push(1).unwrap(); engine.op_call().unwrap(); let err = engine.run().unwrap_err(); assert!(matches!(err.kind, HintErrorKind::ExceededExecutionBudget)); assert_eq!(err.pc, 13); } /// Defines an instruction using an available opcode and executes it. #[test] fn define_instruction_and_use() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ // IDEF 0x93: adds 2 to top stack value op(PUSHB000), op(INS93), op(IDEF), op(PUSHB000), 2, op(ADD), op(ENDF), // FDEF 0: uses defined instruction 0x93 and negates the result op(PUSHB000), 0, op(FDEF), op(INS93), op(NEG), op(ENDF), ]; // Execute this code to define our functions engine.set_font_code(&font_code); engine.run().unwrap(); // Call FDEF 0 with value of 10 on the stack: // * executes defined instruction 0x93 // * then negates the result // leaving -12 on the stack engine.value_stack.push(10).unwrap(); engine.value_stack.push(0).unwrap(); engine.op_call().unwrap(); engine.run().unwrap(); assert_eq!(engine.value_stack.pop().ok(), Some(-12)); } // Invalid to nest definitions. #[test] fn nested_definition() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(PUSHB001), 1, 0, op(FDEF), // pc = 3 op(FDEF), op(ENDF), op(ENDF), ]; // Execute this code to define our functions engine.set_font_code(&font_code); let err = engine.run().unwrap_err(); assert!(matches!(err.kind, HintErrorKind::NestedDefinition)); assert_eq!(err.pc, 3); } // Invalid to modify definitions from the glyph program. #[test] fn definition_in_glyph_program() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(PUSHB000), 0, op(FDEF), // pc = 2 op(ENDF), ]; engine.set_font_code(&font_code); engine.program.initial = Program::Glyph; let err = engine.run().unwrap_err(); assert!(matches!(err.kind, HintErrorKind::DefinitionInGlyphProgram)); assert_eq!(err.pc, 2); } #[test] fn undefined_function() { let mut mock = MockEngine::new(); let mut engine = mock.engine(); engine.value_stack.push(111).unwrap(); assert!(matches!( engine.op_call(), Err(HintErrorKind::InvalidDefinition(111)) )); } /// Fun function that just calls itself :) #[test] fn infinite_recursion() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ // FDEF 0: call FDEF 0 op(PUSHB000), 0, op(FDEF), op(PUSHB000), 0, op(CALL), // pc = 5 op(ENDF), ]; engine.set_font_code(&font_code); engine.run().unwrap(); // Call stack overflow engine.value_stack.push(0).unwrap(); engine.op_call().unwrap(); let err = engine.run().unwrap_err(); assert!(matches!(err.kind, HintErrorKind::CallStackOverflow)); assert_eq!(err.pc, 5); } #[test] fn call_stack_underflow() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(ENDF) ]; engine.set_font_code(&font_code); let err = engine.run().unwrap_err(); assert!(matches!(err.kind, HintErrorKind::CallStackUnderflow)); assert_eq!(err.pc, 0); } #[test] fn unhandled_opcode() { let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(Opcode::INS28), ]; engine.set_font_code(&font_code); let err = engine.run().unwrap_err(); assert!(matches!( err.kind, HintErrorKind::UnhandledOpcode(Opcode::INS28) )); assert_eq!(err.pc, 0); } #[test] fn too_many_definitions() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); #[rustfmt::skip] let font_code = [ op(PUSHB101), 0, 1, 2, 3, 4, 5, op(FDEF), op(ENDF), op(FDEF), op(ENDF), op(FDEF), op(ENDF), op(FDEF), op(ENDF), op(FDEF), op(ENDF), op(FDEF), op(ENDF), ]; engine.set_font_code(&font_code); let err = engine.run().unwrap_err(); assert!(matches!(err.kind, HintErrorKind::TooManyDefinitions)); assert_eq!(err.pc, 17); } #[test] fn big_definition() { use Opcode::*; let mut mock = MockEngine::new(); let mut engine = mock.engine(); let mut font_code = vec![]; font_code.extend_from_slice(&[op(PUSHB000), 0, op(FDEF)]); font_code.extend(std::iter::repeat_n(op(NEG), MAX_DEFINITION_SIZE + 1)); font_code.push(op(ENDF)); engine.set_font_code(&font_code); engine.graphics.is_pedantic = true; engine.value_stack.push(1).unwrap(); let err = engine.run().unwrap_err(); assert!(matches!(err.kind, HintErrorKind::DefinitionTooLarge)); assert_eq!(err.pc, 2); } fn op(opcode: Opcode) -> u8 { opcode as u8 } impl<'a> Engine<'a> { fn set_font_code(&mut self, code: &'a [u8]) { self.program.bytecode[0] = code; self.program.decoder.bytecode = code; self.program.current = Program::Font; } } }