From 88d1e3163ded3e490acc7463dbbcc0e343e3d116 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 24 Feb 2017 09:38:25 -0800 Subject: [PATCH] Analyze branch targets in TrueType instructions --- src/error.rs | 31 ++++++++++++- src/hinting/insns.rs | 36 ++++++--------- src/hinting/interp.rs | 102 ++++++++++++++++++++++++++++++++++++++++++ src/hinting/mod.rs | 4 ++ 4 files changed, 149 insertions(+), 24 deletions(-) create mode 100644 src/hinting/interp.rs diff --git a/src/error.rs b/src/error.rs index c8529501..8187721c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -125,9 +125,38 @@ pub enum GlyphStoreCreationError { } /// An error in hinting instruction evaluation. -#[derive(Debug)] +#[derive(Clone, Copy, PartialEq, Debug)] pub enum HintingError { /// A miscellaneous error occurred. Failed, + /// An error was encountered during hinting program analysis. + AnalysisError(HintingAnalysisError), +} + +/// An error encountered during parsing of the TrueType hinting bytecode. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum HintingParseError { + /// The instruction stream terminated normally. + Eof, + /// The instruction stream terminated abnormally. + UnexpectedEof, + /// An unexpected opcode was encountered. + UnknownOpcode, + /// An unexpected value was encountered for `DistanceType`. + InvalidDistanceType, +} + +/// An error encountered during semantic analysis of the TrueType hinting bytecode. +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum HintingAnalysisError { + /// An error occurred while parsing the instruction stream. + ParseError(HintingParseError), + /// A branch target (e.g. `Eif`) was found without a corresponding branch instruction. + BranchTargetMissingBranch, + /// A branch target was (e.g. `If`) was found without a corresponding branch target (e.g. + /// `Eif`). + BranchMissingBranchTarget, + /// A branch target was mismatched with its branch instruction (`Eif` vs. `If`, etc.) + MismatchedBranchInstruction, } diff --git a/src/hinting/insns.rs b/src/hinting/insns.rs index d3742868..d9cb499c 100644 --- a/src/hinting/insns.rs +++ b/src/hinting/insns.rs @@ -10,6 +10,8 @@ //! TrueType instructions. +use error::HintingParseError; + /// All TrueType instructions. #[derive(Clone, Copy, Debug)] pub enum Instruction<'a> { @@ -182,7 +184,7 @@ pub enum Instruction<'a> { /// Else (0x1b) (ttinst2.doc, 315) Else, /// End If (0x59) (ttinst2.doc, 316) - EIf, + Eif, /// Jump Relative on True (0x78) (ttinst2.doc, 317-318) Jrot, /// Jump (0x1c) (ttinst2.doc, 319) @@ -256,12 +258,12 @@ pub enum Instruction<'a> { impl<'a> Instruction<'a> { #[inline] pub fn parse<'b, 'c>(data: &'b [u8], pc: &'c mut usize) - -> Result, ParseError> { - let op = try!(get(data, pc).ok_or(ParseError::Eof)); + -> Result, HintingParseError> { + let op = try!(get(data, pc).ok_or(HintingParseError::Eof)); match op { 0x40 | 0xb0...0xb7 => { let count = if op == 0x40 { - try!(get(data, pc).ok_or(ParseError::UnexpectedEof)) as usize + try!(get(data, pc).ok_or(HintingParseError::UnexpectedEof)) as usize } else { (op as usize & 7) + 1 }; @@ -270,12 +272,12 @@ impl<'a> Instruction<'a> { *pc += count; Ok(insn) } else { - Err(ParseError::UnexpectedEof) + Err(HintingParseError::UnexpectedEof) } } 0x41 | 0xb8...0xbf => { let count = if op == 0x41 { - try!(get(data, pc).ok_or(ParseError::UnexpectedEof)) as usize * 2 + try!(get(data, pc).ok_or(HintingParseError::UnexpectedEof)) as usize * 2 } else { ((op as usize & 7) + 1) * 2 }; @@ -284,7 +286,7 @@ impl<'a> Instruction<'a> { *pc += count; Ok(insn) } else { - Err(ParseError::UnexpectedEof) + Err(HintingParseError::UnexpectedEof) } } 0x43 => Ok(Instruction::Rs), @@ -385,7 +387,7 @@ impl<'a> Instruction<'a> { 0x8a => Ok(Instruction::Roll), 0x58 => Ok(Instruction::If), 0x1b => Ok(Instruction::Else), - 0x59 => Ok(Instruction::EIf), + 0x59 => Ok(Instruction::Eif), 0x78 => Ok(Instruction::Jrot), 0x1c => Ok(Instruction::Jmpr), 0x79 => Ok(Instruction::Jrof), @@ -420,7 +422,7 @@ impl<'a> Instruction<'a> { 0x4f => Ok(Instruction::Debug), 0x88 => Ok(Instruction::Getinfo), 0x91 => Ok(Instruction::Getvariation), - _ => Err(ParseError::UnknownOpcode), + _ => Err(HintingParseError::UnknownOpcode), } } } @@ -484,25 +486,13 @@ pub enum DistanceType { } impl DistanceType { - fn parse(value: u8) -> Result { + fn parse(value: u8) -> Result { match value { 0 => Ok(DistanceType::Gray), 1 => Ok(DistanceType::Black), 2 => Ok(DistanceType::White), - _ => Err(ParseError::InvalidDistanceType), + _ => Err(HintingParseError::InvalidDistanceType), } } } -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum ParseError { - /// The instruction stream terminated normally. - Eof, - /// The instruction stream terminated abnormally. - UnexpectedEof, - /// An unexpected opcode was encountered. - UnknownOpcode, - /// An unexpected value was encountered for `DistanceType`. - InvalidDistanceType, -} - diff --git a/src/hinting/interp.rs b/src/hinting/interp.rs new file mode 100644 index 00000000..dfa61f3d --- /dev/null +++ b/src/hinting/interp.rs @@ -0,0 +1,102 @@ +// Copyright 2017 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The TrueType interpreter. + +use error::{HintingAnalysisError, HintingParseError}; +use hinting::insns::Instruction; + +pub struct ScriptInterpreter<'a> { + bytecode: &'a [u8], + branch_targets: Vec, +} + +impl<'a> ScriptInterpreter<'a> { + pub fn new<'b>(bytecode: &'b [u8]) -> Result, HintingAnalysisError> { + let mut interpreter = ScriptInterpreter { + bytecode: bytecode, + branch_targets: vec![], + }; + try!(interpreter.populate_branch_targets()); + println!("{:#?}", interpreter.branch_targets); + Ok(interpreter) + } + + fn populate_branch_targets(&mut self) -> Result<(), HintingAnalysisError> { + let (mut pc, mut pending_branch_targets) = (0, vec![]); + loop { + let location = pc; + let instruction = match Instruction::parse(self.bytecode, &mut pc) { + Ok(instruction) => instruction, + Err(HintingParseError::Eof) => break, + Err(err) => return Err(HintingAnalysisError::ParseError(err)), + }; + + match instruction { + Instruction::If | Instruction::Fdef | Instruction::Idef => { + pending_branch_targets.push((location, instruction)) + } + Instruction::Endf => { + match pending_branch_targets.pop() { + Some((branch_location, Instruction::Fdef)) | + Some((branch_location, Instruction::Idef)) => { + self.branch_targets.push(BranchTarget { + branch_location: branch_location, + target_location: location, + }) + } + Some(_) => return Err(HintingAnalysisError::MismatchedBranchInstruction), + None => return Err(HintingAnalysisError::BranchTargetMissingBranch), + } + } + Instruction::Eif => { + match pending_branch_targets.pop() { + Some((branch_location, Instruction::If)) | + Some((branch_location, Instruction::Else)) => { + self.branch_targets.push(BranchTarget { + branch_location: branch_location, + target_location: location, + }) + } + Some(_) => return Err(HintingAnalysisError::MismatchedBranchInstruction), + None => return Err(HintingAnalysisError::BranchTargetMissingBranch), + } + } + Instruction::Else => { + match pending_branch_targets.pop() { + Some((branch_location, Instruction::If)) => { + self.branch_targets.push(BranchTarget { + branch_location: branch_location, + target_location: location, + }); + pending_branch_targets.push((location, instruction)) + } + Some(_) => return Err(HintingAnalysisError::MismatchedBranchInstruction), + None => return Err(HintingAnalysisError::BranchTargetMissingBranch), + } + } + _ => {} + } + } + + if pending_branch_targets.is_empty() { + Ok(()) + } else { + Err(HintingAnalysisError::BranchMissingBranchTarget) + } + } +} + +#[derive(Clone, Copy, Debug)] +struct BranchTarget { + branch_location: usize, + target_location: usize, +} + diff --git a/src/hinting/mod.rs b/src/hinting/mod.rs index 7b6ba556..eb87f056 100644 --- a/src/hinting/mod.rs +++ b/src/hinting/mod.rs @@ -18,8 +18,10 @@ use byteorder::{BigEndian, ByteOrder}; use error::HintingError; use euclid::Point2D; use font::Font; +use hinting::interp::ScriptInterpreter; mod insns; +mod interp; /// A TrueType hinting virtual machine. pub struct Hinter { @@ -94,6 +96,8 @@ impl Hinter { graphics_state_flags: AUTO_FLIP, }; + try!(ScriptInterpreter::new(font.font_program()).map_err(HintingError::AnalysisError)); + Ok(hinter) } }