freetype/truetype: if/else opcodes.
R=gri CC=golang-dev http://codereview.appspot.com/6354067
This commit is contained in:
parent
8ed9e9345e
commit
523d04e0a7
|
@ -18,6 +18,9 @@ type hinter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *hinter) run(program []byte) error {
|
func (h *hinter) run(program []byte) error {
|
||||||
|
if len(program) > 50000 {
|
||||||
|
return errors.New("truetype: hinting: too many instructions")
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
steps, pc, top int
|
steps, pc, top int
|
||||||
opcode uint8
|
opcode uint8
|
||||||
|
@ -25,7 +28,7 @@ func (h *hinter) run(program []byte) error {
|
||||||
for int(pc) < len(program) {
|
for int(pc) < len(program) {
|
||||||
steps++
|
steps++
|
||||||
if steps == 100000 {
|
if steps == 100000 {
|
||||||
return errors.New("truetype: hinting: too many instructions")
|
return errors.New("truetype: hinting: too many steps")
|
||||||
}
|
}
|
||||||
opcode = program[pc]
|
opcode = program[pc]
|
||||||
if popCount[opcode] == q {
|
if popCount[opcode] == q {
|
||||||
|
@ -36,6 +39,10 @@ func (h *hinter) run(program []byte) error {
|
||||||
}
|
}
|
||||||
switch opcode {
|
switch opcode {
|
||||||
|
|
||||||
|
case opELSE:
|
||||||
|
opcode = 1
|
||||||
|
goto ifelse
|
||||||
|
|
||||||
case opDUP:
|
case opDUP:
|
||||||
if int(top) >= len(h.stack) {
|
if int(top) >= len(h.stack) {
|
||||||
return errors.New("truetype: hinting: stack overflow")
|
return errors.New("truetype: hinting: stack overflow")
|
||||||
|
@ -78,6 +85,9 @@ func (h *hinter) run(program []byte) error {
|
||||||
opcode = 0x80
|
opcode = 0x80
|
||||||
goto push
|
goto push
|
||||||
|
|
||||||
|
case opDEBUG:
|
||||||
|
// No-op.
|
||||||
|
|
||||||
case opLT:
|
case opLT:
|
||||||
h.stack[top-2] = bool2int32(h.stack[top-2] < h.stack[top-1])
|
h.stack[top-2] = bool2int32(h.stack[top-2] < h.stack[top-1])
|
||||||
top--
|
top--
|
||||||
|
@ -113,6 +123,16 @@ func (h *hinter) run(program []byte) error {
|
||||||
case opNOT:
|
case opNOT:
|
||||||
h.stack[top-1] = bool2int32(h.stack[top-1] == 0)
|
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:
|
case opADD:
|
||||||
h.stack[top-2] += h.stack[top-1]
|
h.stack[top-2] += h.stack[top-1]
|
||||||
top--
|
top--
|
||||||
|
@ -162,8 +182,55 @@ func (h *hinter) run(program []byte) error {
|
||||||
pc++
|
pc++
|
||||||
continue
|
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:
|
||||||
// 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.
|
// 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 0 means that the elements are zero-extended bytes.
|
||||||
// The high bit being 1 means that the elements are sign-extended words.
|
// The high bit being 1 means that the elements are sign-extended words.
|
||||||
|
|
|
@ -26,6 +26,16 @@ func TestBytecode(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
"underflow",
|
"underflow",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"unbalanced if/else",
|
||||||
|
[]byte{
|
||||||
|
opPUSHB000, // [0]
|
||||||
|
0,
|
||||||
|
opIF,
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
"unbalanced",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"stack ops",
|
"stack ops",
|
||||||
[]byte{
|
[]byte{
|
||||||
|
@ -102,6 +112,150 @@ func TestBytecode(t *testing.T) {
|
||||||
[]int32{1, 0},
|
[]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",
|
"logical ops",
|
||||||
[]byte{
|
[]byte{
|
||||||
|
|
|
@ -37,7 +37,7 @@ const (
|
||||||
opRTG = 0x18
|
opRTG = 0x18
|
||||||
opRTHG = 0x19
|
opRTHG = 0x19
|
||||||
opSMD = 0x1a
|
opSMD = 0x1a
|
||||||
opELSE = 0x1b
|
opELSE = 0x1b // ELSE clause
|
||||||
opJMPR = 0x1c
|
opJMPR = 0x1c
|
||||||
opSCVTCI = 0x1d
|
opSCVTCI = 0x1d
|
||||||
opSSWCI = 0x1e
|
opSSWCI = 0x1e
|
||||||
|
@ -89,7 +89,7 @@ const (
|
||||||
opMPS = 0x4c
|
opMPS = 0x4c
|
||||||
opFLIPON = 0x4d
|
opFLIPON = 0x4d
|
||||||
opFLIPOFF = 0x4e
|
opFLIPOFF = 0x4e
|
||||||
opDEBUG = 0x4f
|
opDEBUG = 0x4f // DEBUG call
|
||||||
opLT = 0x50 // Less Than
|
opLT = 0x50 // Less Than
|
||||||
opLTEQ = 0x51 // Less Than or EQual
|
opLTEQ = 0x51 // Less Than or EQual
|
||||||
opGT = 0x52 // Greater Than
|
opGT = 0x52 // Greater Than
|
||||||
|
@ -98,8 +98,8 @@ const (
|
||||||
opNEQ = 0x55 // Not EQual
|
opNEQ = 0x55 // Not EQual
|
||||||
opODD = 0x56
|
opODD = 0x56
|
||||||
opEVEN = 0x57
|
opEVEN = 0x57
|
||||||
opIF = 0x58
|
opIF = 0x58 // IF test
|
||||||
opEIF = 0x59
|
opEIF = 0x59 // End IF
|
||||||
opAND = 0x5a // logical AND
|
opAND = 0x5a // logical AND
|
||||||
opOR = 0x5b // logical OR
|
opOR = 0x5b // logical OR
|
||||||
opNOT = 0x5c // logical NOT
|
opNOT = 0x5c // logical NOT
|
||||||
|
@ -272,11 +272,11 @@ const (
|
||||||
var popCount = [256]uint8{
|
var popCount = [256]uint8{
|
||||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
|
// 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, // 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
|
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
|
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
|
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, q, q, 2, 2, 1, q, q, q, // 0x50 - 0x5f
|
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
|
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, // 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, // 0x80 - 0x8f
|
||||||
|
|
Loading…
Reference in New Issue