freetype/truetype: if/else opcodes.

R=gri
CC=golang-dev
http://codereview.appspot.com/6354067
This commit is contained in:
Nigel Tao 2012-07-04 21:28:44 +10:00
parent 8ed9e9345e
commit 523d04e0a7
3 changed files with 230 additions and 9 deletions

View File

@ -18,6 +18,9 @@ type hinter struct {
}
func (h *hinter) run(program []byte) error {
if len(program) > 50000 {
return errors.New("truetype: hinting: too many instructions")
}
var (
steps, pc, top int
opcode uint8
@ -25,7 +28,7 @@ func (h *hinter) run(program []byte) error {
for int(pc) < len(program) {
steps++
if steps == 100000 {
return errors.New("truetype: hinting: too many instructions")
return errors.New("truetype: hinting: too many steps")
}
opcode = program[pc]
if popCount[opcode] == q {
@ -36,6 +39,10 @@ func (h *hinter) run(program []byte) error {
}
switch opcode {
case opELSE:
opcode = 1
goto ifelse
case opDUP:
if int(top) >= len(h.stack) {
return errors.New("truetype: hinting: stack overflow")
@ -78,6 +85,9 @@ func (h *hinter) run(program []byte) error {
opcode = 0x80
goto push
case opDEBUG:
// No-op.
case opLT:
h.stack[top-2] = bool2int32(h.stack[top-2] < h.stack[top-1])
top--
@ -113,6 +123,16 @@ func (h *hinter) run(program []byte) error {
case opNOT:
h.stack[top-1] = bool2int32(h.stack[top-1] == 0)
case opIF:
top--
if h.stack[top] == 0 {
opcode = 0
goto ifelse
}
case opEIF:
// No-op.
case opADD:
h.stack[top-2] += h.stack[top-1]
top--
@ -162,8 +182,55 @@ func (h *hinter) run(program []byte) error {
pc++
continue
ifelse:
// Skip past bytecode until the next ELSE (if opcode == 0) or the
// next EIF (for all opcodes). Opcode == 0 means that we have come
// from an IF. Opcode == 1 means that we have come from an ELSE.
{
ifelseloop:
for depth := 0; ; {
pc++
if pc >= len(program) {
return errors.New("truetype: hinting: unbalanced IF or ELSE")
}
switch program[pc] {
case opIF:
depth++
case opELSE:
if depth == 0 && opcode == 0 {
break ifelseloop
}
case opEIF:
depth--
if depth < 0 {
break ifelseloop
}
case opNPUSHB:
pc++
if pc >= len(program) {
return errors.New("truetype: hinting: unbalanced IF or ELSE")
}
pc += int(program[pc])
case opNPUSHW:
pc++
if pc >= len(program) {
return errors.New("truetype: hinting: unbalanced IF or ELSE")
}
pc += 2 * int(program[pc])
case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011, opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111:
pc += int(program[pc] - (opPUSHB000 - 1))
case opPUSHW000, opPUSHW001, opPUSHW010, opPUSHW011, opPUSHW100, opPUSHW101, opPUSHW110, opPUSHW111:
pc += 2 * int(program[pc]-(opPUSHW000-1))
default:
// No-op.
}
}
pc++
continue
}
push:
// push n elements from the program to the stack, where n is the low 7 bits of
// Push n elements from the program to the stack, where n is the low 7 bits of
// opcode. If the low 7 bits are zero, then n is the next byte from the program.
// The high bit being 0 means that the elements are zero-extended bytes.
// The high bit being 1 means that the elements are sign-extended words.

View File

@ -26,6 +26,16 @@ func TestBytecode(t *testing.T) {
nil,
"underflow",
},
{
"unbalanced if/else",
[]byte{
opPUSHB000, // [0]
0,
opIF,
},
nil,
"unbalanced",
},
{
"stack ops",
[]byte{
@ -102,6 +112,150 @@ func TestBytecode(t *testing.T) {
[]int32{1, 0},
"",
},
{
"if true",
[]byte{
opPUSHB001, // [255, 1]
255,
1,
opIF,
opPUSHB000, // [255, 2]
2,
opEIF,
opPUSHB000, // [255, 2, 254]
254,
},
[]int32{255, 2, 254},
"",
},
{
"if false",
[]byte{
opPUSHB001, // [255, 0]
255,
0,
opIF,
opPUSHB000, // [255]
2,
opEIF,
opPUSHB000, // [255, 254]
254,
},
[]int32{255, 254},
"",
},
{
"if/else true",
[]byte{
opPUSHB000, // [1]
1,
opIF,
opPUSHB000, // [2]
2,
opELSE,
opPUSHB000, // not executed
3,
opEIF,
},
[]int32{2},
"",
},
{
"if/else false",
[]byte{
opPUSHB000, // [0]
0,
opIF,
opPUSHB000, // not executed
2,
opELSE,
opPUSHB000, // [3]
3,
opEIF,
},
[]int32{3},
"",
},
{
"if/else true if/else false",
// 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
[]byte{
opPUSHB010, // [255, 0, 1]
255,
0,
1,
opIF,
opIF,
opPUSHB001, // not executed
0x58,
0x58,
opELSE,
opPUSHW000, // [255, 0x5858]
0x58,
0x58,
opEIF,
opELSE,
opIF,
opNPUSHB, // not executed
3,
0x58,
0x58,
0x58,
opELSE,
opNPUSHW, // not executed
2,
0x58,
0x58,
0x58,
0x58,
opEIF,
opEIF,
opPUSHB000, // [255, 0x5858, 254]
254,
},
[]int32{255, 0x5858, 254},
"",
},
{
"if/else false if/else true",
// 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
[]byte{
opPUSHB010, // [255, 1, 0]
255,
1,
0,
opIF,
opIF,
opPUSHB001, // not executed
0x58,
0x58,
opELSE,
opPUSHW000, // not executed
0x58,
0x58,
opEIF,
opELSE,
opIF,
opNPUSHB, // [255, 0x58, 0x58, 0x58]
3,
0x58,
0x58,
0x58,
opELSE,
opNPUSHW, // not executed
2,
0x58,
0x58,
0x58,
0x58,
opEIF,
opEIF,
opPUSHB000, // [255, 0x58, 0x58, 0x58, 254]
254,
},
[]int32{255, 0x58, 0x58, 0x58, 254},
"",
},
{
"logical ops",
[]byte{

View File

@ -37,7 +37,7 @@ const (
opRTG = 0x18
opRTHG = 0x19
opSMD = 0x1a
opELSE = 0x1b
opELSE = 0x1b // ELSE clause
opJMPR = 0x1c
opSCVTCI = 0x1d
opSSWCI = 0x1e
@ -89,7 +89,7 @@ const (
opMPS = 0x4c
opFLIPON = 0x4d
opFLIPOFF = 0x4e
opDEBUG = 0x4f
opDEBUG = 0x4f // DEBUG call
opLT = 0x50 // Less Than
opLTEQ = 0x51 // Less Than or EQual
opGT = 0x52 // Greater Than
@ -98,8 +98,8 @@ const (
opNEQ = 0x55 // Not EQual
opODD = 0x56
opEVEN = 0x57
opIF = 0x58
opEIF = 0x59
opIF = 0x58 // IF test
opEIF = 0x59 // End IF
opAND = 0x5a // logical AND
opOR = 0x5b // logical OR
opNOT = 0x5c // logical NOT
@ -272,11 +272,11 @@ 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, q, q, q, q, q, // 0x10 - 0x1f
q, q, q, q, q, q, q, q, q, q, q, 0, q, 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, q, // 0x40 - 0x4f
2, 2, 2, 2, 2, 2, q, q, q, q, 2, 2, 1, q, q, q, // 0x50 - 0x5f
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, q, q, q, q, q, q, q, q, // 0x80 - 0x8f