diff --git a/freetype/truetype/hint.go b/freetype/truetype/hint.go index ea90692..697a2b1 100644 --- a/freetype/truetype/hint.go +++ b/freetype/truetype/hint.go @@ -25,7 +25,7 @@ func (h *hinter) run(program []byte) error { steps, pc, top int opcode uint8 ) - for int(pc) < len(program) { + for 0 <= pc && int(pc) < len(program) { steps++ if steps == 100000 { return errors.New("truetype: hinting: too many steps") @@ -43,6 +43,11 @@ func (h *hinter) run(program []byte) error { opcode = 1 goto ifelse + case opJMPR: + top-- + pc += int(h.stack[top]) + continue + case opDUP: if int(top) >= len(h.stack) { return errors.New("truetype: hinting: stack overflow") @@ -89,36 +94,36 @@ func (h *hinter) run(program []byte) error { // No-op. case opLT: - h.stack[top-2] = bool2int32(h.stack[top-2] < h.stack[top-1]) top-- + h.stack[top-1] = bool2int32(h.stack[top-1] < h.stack[top]) case opLTEQ: - h.stack[top-2] = bool2int32(h.stack[top-2] <= h.stack[top-1]) top-- + h.stack[top-1] = bool2int32(h.stack[top-1] <= h.stack[top]) case opGT: - h.stack[top-2] = bool2int32(h.stack[top-2] > h.stack[top-1]) top-- + h.stack[top-1] = bool2int32(h.stack[top-1] > h.stack[top]) case opGTEQ: - h.stack[top-2] = bool2int32(h.stack[top-2] >= h.stack[top-1]) top-- + h.stack[top-1] = bool2int32(h.stack[top-1] >= h.stack[top]) case opEQ: - h.stack[top-2] = bool2int32(h.stack[top-2] == h.stack[top-1]) top-- + h.stack[top-1] = bool2int32(h.stack[top-1] == h.stack[top]) case opNEQ: - h.stack[top-2] = bool2int32(h.stack[top-2] != h.stack[top-1]) top-- + h.stack[top-1] = bool2int32(h.stack[top-1] != h.stack[top]) case opAND: - h.stack[top-2] = bool2int32(h.stack[top-2] != 0 && h.stack[top-1] != 0) top-- + h.stack[top-1] = bool2int32(h.stack[top-1] != 0 && h.stack[top] != 0) case opOR: - h.stack[top-2] = bool2int32(h.stack[top-2]|h.stack[top-1] != 0) top-- + h.stack[top-1] = bool2int32(h.stack[top-1]|h.stack[top] != 0) case opNOT: h.stack[top-1] = bool2int32(h.stack[top-1] == 0) @@ -134,23 +139,23 @@ func (h *hinter) run(program []byte) error { // No-op. case opADD: - h.stack[top-2] += h.stack[top-1] top-- + h.stack[top-1] += h.stack[top] case opSUB: - h.stack[top-2] -= h.stack[top-1] top-- + h.stack[top-1] -= h.stack[top] case opDIV: - if h.stack[top-1] == 0 { + top-- + if h.stack[top] == 0 { return errors.New("truetype: hinting: division by zero") } - h.stack[top-2] = int32((int64(h.stack[top-2]) << 6) / int64(h.stack[top-1])) - top-- + h.stack[top-1] = int32((int64(h.stack[top-1]) << 6) / int64(h.stack[top])) case opMUL: - h.stack[top-2] = int32((int64(h.stack[top-2]) * int64(h.stack[top-1])) >> 6) top-- + h.stack[top-1] = int32((int64(h.stack[top-1]) * int64(h.stack[top])) >> 6) case opABS: if h.stack[top-1] < 0 { @@ -167,6 +172,20 @@ func (h *hinter) run(program []byte) error { h.stack[top-1] += 63 h.stack[top-1] &^= 63 + case opJROT: + top -= 2 + if h.stack[top+1] != 0 { + pc += int(h.stack[top]) + continue + } + + case opJROF: + top -= 2 + if h.stack[top+1] == 0 { + pc += int(h.stack[top]) + continue + } + case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011, opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111: opcode -= opPUSHB000 - 1 goto push diff --git a/freetype/truetype/hint_test.go b/freetype/truetype/hint_test.go index 61f4300..4fa585d 100644 --- a/freetype/truetype/hint_test.go +++ b/freetype/truetype/hint_test.go @@ -26,6 +26,18 @@ func TestBytecode(t *testing.T) { nil, "underflow", }, + { + "infinite loop", + []byte{ + opPUSHW000, // [-1] + 0xff, + 0xff, + opDUP, // [-1, -1] + opJMPR, // [-1] + }, + nil, + "too many steps", + }, { "unbalanced if/else", []byte{ @@ -36,6 +48,33 @@ func TestBytecode(t *testing.T) { nil, "unbalanced", }, + { + "jumps", + []byte{ + opPUSHB001, // [10, 2] + 10, + 2, + opJMPR, // [10] + opDUP, // not executed + opDUP, // [10, 10] + opPUSHB010, // [10, 10, 20, 2, 1] + 20, + 2, + 1, + opJROT, // [10, 10, 20] + opDUP, // not executed + opDUP, // [10, 10, 20, 20] + opPUSHB010, // [10, 10, 20, 20, 30, 2, 1] + 30, + 2, + 1, + opJROF, // [10, 10, 20, 20, 30] + opDUP, // [10, 10, 20, 20, 30, 30] + opDUP, // [10, 10, 20, 20, 30, 30, 30] + }, + []int32{10, 10, 20, 20, 30, 30, 30}, + "", + }, { "stack ops", []byte{ diff --git a/freetype/truetype/opcodes.go b/freetype/truetype/opcodes.go index e8956fd..aa1dd54 100644 --- a/freetype/truetype/opcodes.go +++ b/freetype/truetype/opcodes.go @@ -38,7 +38,7 @@ const ( opRTHG = 0x19 opSMD = 0x1a opELSE = 0x1b // ELSE clause - opJMPR = 0x1c + opJMPR = 0x1c // JuMP Relative opSCVTCI = 0x1d opSSWCI = 0x1e opSSW = 0x1f @@ -130,8 +130,8 @@ const ( opDELTAC3 = 0x75 opSROUND = 0x76 opS45ROUND = 0x77 - opJROT = 0x78 - opJROF = 0x79 + opJROT = 0x78 // Jump Relative On True + opJROF = 0x79 // Jump Relative On False opROFF = 0x7a op_0x7b = 0x7b opRUTG = 0x7c @@ -272,13 +272,13 @@ const ( var popCount = [256]uint8{ // 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x00 - 0x0f - q, q, q, q, q, q, q, q, q, q, q, 0, q, q, q, q, // 0x10 - 0x1f + q, q, q, q, q, q, q, q, q, q, q, 0, 1, q, q, q, // 0x10 - 0x1f 1, 1, 0, 2, 0, 1, 1, q, q, q, q, q, q, q, q, q, // 0x20 - 0x2f q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x30 - 0x3f 0, 0, q, q, q, q, q, q, q, q, q, q, q, q, q, 0, // 0x40 - 0x4f 2, 2, 2, 2, 2, 2, q, q, 1, 0, 2, 2, 1, q, q, q, // 0x50 - 0x5f 2, 2, 2, 2, 1, 1, 1, 1, q, q, q, q, q, q, q, q, // 0x60 - 0x6f - q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x70 - 0x7f + q, q, q, q, q, q, q, q, 2, 2, q, q, q, q, q, q, // 0x70 - 0x7f q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x80 - 0x8f q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x90 - 0x9f q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xa0 - 0xaf