-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathcode_load.go
911 lines (812 loc) · 21.7 KB
/
code_load.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
package graphite
import "fmt"
/** Used for looking up slot attributes. Most are already available in other functions **/
type attrCode uint8
const (
/// adjusted glyph advance in x direction in design units
acAdvX attrCode = iota
/// adjusted glyph advance in y direction (usually 0) in design units
acAdvY
/// returns 0. Deprecated.
acAttTo
/// This slot attaches to its parent at the given design units in the x direction
acAttX
/// This slot attaches to its parent at the given design units in the y direction
acAttY
/// This slot attaches to its parent at the given glyph point (not implemented)
_
/// x-direction adjustment from the given glyph point (not implemented)
acAttXOff
/// y-direction adjustment from the given glyph point (not implemented)
acAttYOff
/// Where on this glyph should align with the attachment point on the parent glyph in the x-direction.
acAttWithX
/// Where on this glyph should align with the attachment point on the parent glyph in the y-direction
acAttWithY
/// Which glyph point on this glyph should align with the attachment point on the parent glyph (not implemented).
_
/// Adjustment to acWithGpt in x-direction (not implemented)
acAttWithXOff
/// Adjustment to acWithGpt in y-direction (not implemented)
acAttWithYOff
/// Attach at given nesting level (not implemented)
acAttLevel
/// Line break breakweight for this glyph
acBreak
/// Ligature component reference (not implemented)
acCompRef
/// bidi directionality of this glyph (not implemented)
acDir
/// Whether insertion is allowed before this glyph
acInsert
/// Final positioned position of this glyph relative to its parent in x-direction in pixels
acPosX
/// Final positioned position of this glyph relative to its parent in y-direction in pixels
acPosY
/// Amount to shift glyph by in x-direction design units
acShiftX
/// Amount to shift glyph by in y-direction design units
acShiftY
/// attribute user1
acUserDefnV1
/// not implemented
acMeasureSol
/// not implemented
acMeasureEol
/// Amount this slot can stretch (not implemented)
acJStretch
/// Amount this slot can shrink (not implemented)
_
/// Granularity by which this slot can stretch or shrink (not implemented)
_
/// Justification weight for this glyph (not implemented)
_
/// Amount this slot mush shrink or stretch in design units
acJWidth
/// SubSegment split point
acSegSplit = acJStretch + 29
/// User defined attribute, see subattr for user attr number
acUserDefn = 55
/// Bidi level
acBidiLevel = 24 + iota
/// Collision flags
acColFlags
/// Collision constraint rectangle left (bl.x)
acColLimitblx
/// Collision constraint rectangle lower (bl.y)
acColLimitbly
/// Collision constraint rectangle right (tr.x)
acColLimittrx
/// Collision constraint rectangle upper (tr.y)
acColLimittry
/// Collision shift x
acColShiftx
/// Collision shift y
acColShifty
/// Collision margin
acColMargin
/// Margin cost weight
acColMarginWt
// Additional glyph that excludes movement near this one:
acColExclGlyph
acColExclOffx
acColExclOffy
// Collision sequence enforcing attributes:
acSeqClass
acSeqProxClass
acSeqOrder
acSeqAboveXoff
acSeqAboveWt
acSeqBelowXlim
acSeqBelowWt
acSeqValignHt
acSeqValignWt
/// not implemented
acMax
/// not implemented
_ = acMax + 1
)
type opcode uint8
// opcodes
const (
ocNOP opcode = iota
ocPUSH_BYTE
ocPUSH_BYTEU
ocPUSH_SHORT
ocPUSH_SHORTU
ocPUSH_LONG
ocADD
ocSUB
ocMUL
ocDIV
ocMIN
ocMAX
ocNEG
ocTRUNC8
ocTRUNC16
ocCOND
ocAND
ocOR
ocNOT
ocEQUAL
ocNOT_EQ
ocLESS
ocGTR
ocLESS_EQ
ocGTR_EQ
ocNEXT
ocNEXT_N
ocCOPY_NEXT
ocPUT_GLYPH_8BIT_OBS
ocPUT_SUBS_8BIT_OBS
ocPUT_COPY
ocINSERT
ocDELETE
ocASSOC
ocCNTXT_ITEM
ocATTR_SET
ocATTR_ADD
ocATTR_SUB
ocATTR_SET_SLOT
ocIATTR_SET_SLOT
ocPUSH_SLOT_ATTR
ocPUSH_GLYPH_ATTR_OBS
ocPUSH_GLYPH_METRIC
ocPUSH_FEAT
ocPUSH_ATT_TO_GATTR_OBS
ocPUSH_ATT_TO_GLYPH_METRIC
ocPUSH_ISLOT_ATTR
ocPUSH_IGLYPH_ATTR // not implemented
ocPOP_RET
ocRET_ZERO
ocRET_TRUE
ocIATTR_SET
ocIATTR_ADD
ocIATTR_SUB
ocPUSH_PROC_STATE
ocPUSH_VERSION
ocPUT_SUBS
ocPUT_SUBS2
ocPUT_SUBS3
ocPUT_GLYPH
ocPUSH_GLYPH_ATTR
ocPUSH_ATT_TO_GLYPH_ATTR
ocBITOR
ocBITAND
ocBITNOT
ocBITSET
ocSET_FEAT
ocMAX_OPCODE
// private opcodes for internal use only, comes after all other on disk opcodes
ocTEMP_COPY = ocMAX_OPCODE
)
func (opc opcode) String() string {
if int(opc) < len(opcodeTable) {
return opcodeTable[opc].name
}
return fmt.Sprintf("<unknown opcode: %d>", opc)
}
func (opc opcode) isReturn() bool {
return opc == ocPOP_RET || opc == ocRET_ZERO || opc == ocRET_TRUE
}
type errorStatusCode uint8
const (
invalidOpCode errorStatusCode = iota
unimplementedOpCodeUsed
outOfRangeData
jumpPastEnd
argumentsExhausted
missingReturn
nestedContextItem
underfullStack
)
func (c errorStatusCode) Error() string {
switch c {
case invalidOpCode:
return "invalid opcode"
case unimplementedOpCodeUsed:
return "unimplemented opcode used"
case outOfRangeData:
return "out of range data"
case jumpPastEnd:
return "jump past end"
case argumentsExhausted:
return "arguments exhausted"
case missingReturn:
return "missing return"
case nestedContextItem:
return "nested context item"
case underfullStack:
return "underfull stack"
}
return "<unknown error code>"
}
// instr is an op code with the selected implementation
type instr struct {
fn instrImpl
code opcode
}
// represents loaded graphite stack machine code
type code struct {
instrs []instr
args []byte // concatenated arguments for `instrs`
// instr * _code;
// byte * _data;
// size_t _data_size,
// instrCount int
maxRef int // maximum index of slot encountered in the instructions
// status codeStatus
constraint, delete, modify bool
// mutable bool _own;
}
// settings from the font needed to load a bytecode sequence
type codeContext struct {
NumClasses, NumAttributes, NumFeatures uint16
NumUserAttributes uint8
Pt passtype
}
// newCode decodes an input and returns the loaded instructions
// the errors returns are of type errorStatusCode
// If skipAnalysis is true, the required TEMP_COPY opcodes are not inserted.
// This is only useful to get a direct representation of the font file content required in tests.
func newCode(isConstraint bool, bytecode []byte,
preContext uint8, ruleLength uint16, context codeContext, skipAnalysis bool,
) (code, error) {
if len(bytecode) == 0 {
return code{}, nil
}
lims := decoderLimits{
preContext: uint16(preContext),
ruleLength: ruleLength,
classes: context.NumClasses,
glyfAttrs: context.NumAttributes,
features: context.NumFeatures,
attrid: [acMax]byte{
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 255,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, context.NumUserAttributes, // 0, 0, etc...
},
}
dec := newDecoder(isConstraint, lims, context.Pt)
// parse the bytecodes
lastOpcode, err := dec.load(bytecode)
if err != nil {
return code{}, err
}
// Is this an empty program?
if len(dec.code.instrs) == 0 {
return dec.code, nil
}
// When we reach the end check we've terminated it correctly
if !lastOpcode.isReturn() {
return dec.code, missingReturn
}
if !skipAnalysis {
dec.code.instrs = dec.applyAnalysis(dec.code.instrs)
}
return dec.code, nil
}
const decoderNUMCONTEXTS = 256
type decoderLimits struct {
preContext uint16
ruleLength uint16
classes, glyfAttrs, features uint16
attrid [acMax]byte
}
type context struct {
codeRef uint8
changed, referenced bool
}
// decoder is responsible for reading a sequence of instructions.
// This is the first step of the use of the code sequence from a font:
// - at font creation (see computeRules), a first analyze of the sequence if done,
// checking for some errors like out of bounds access
// - at runtime, the code is run against an input
type decoder struct {
code code // resulting loaded code
stackDepth int // the number of element needed in stack
outIndex, outLength int
slotRef int
inCtxtItem bool
passtype passtype
contexts [decoderNUMCONTEXTS]context
max decoderLimits
}
func newDecoder(isConstraint bool, max decoderLimits, pt passtype) *decoder {
out := decoder{code: code{constraint: isConstraint}, max: max, passtype: pt}
out.outLength = 1
if !out.code.constraint {
out.outIndex = int(max.preContext)
out.outLength = int(max.ruleLength)
}
return &out
}
// parses the input byte code and checks that operations
// and arguments are valid
// note that the instructions are NOT executed
// the last opcode is returned
func (dec *decoder) load(bytecode []byte) (opcode, error) {
var lastOpcode opcode
for len(bytecode) != 0 {
opc, err := dec.fetchOpcode(bytecode)
if err != nil {
return 0, err
}
bytecode = bytecode[1:]
dec.analyseOpcode(opc, bytecode)
bytecode, err = dec.emitOpcode(opc, bytecode)
if err != nil {
return 0, err
}
lastOpcode = opc
}
return lastOpcode, nil
}
func (dec *decoder) validateOpcode(opc opcode, bc []byte) error {
if opc >= ocMAX_OPCODE {
return invalidOpCode
}
op := opcodeTable[opc]
if op.impl[boolToInt(dec.code.constraint)] == nil {
return unimplementedOpCodeUsed
}
if op.paramSize == varArgs && len(bc) == 0 {
return argumentsExhausted
}
paramSize := op.paramSize
if op.paramSize == varArgs { // read the number of additional args as first arg
paramSize = bc[0] + 1
}
if len(bc) < int(paramSize) {
return argumentsExhausted
}
return nil
}
// bc is not empty
func (dec *decoder) fetchOpcode(bc []byte) (opcode, error) {
opc := opcode(bc[0])
bc = bc[1:]
// Do some basic sanity checks based on what we know about the opcode
if err := dec.validateOpcode(opc, bc); err != nil {
return 0, err
}
// And check its arguments as far as possible
switch opc {
case ocNOP:
case ocPUSH_BYTE, ocPUSH_BYTEU, ocPUSH_SHORT, ocPUSH_SHORTU, ocPUSH_LONG:
dec.stackDepth++
case ocADD, ocSUB, ocMUL, ocDIV, ocMIN, ocMAX, ocAND, ocOR,
ocEQUAL, ocNOT_EQ, ocLESS, ocGTR, ocLESS_EQ, ocGTR_EQ, ocBITOR, ocBITAND:
dec.stackDepth--
if dec.stackDepth <= 0 {
return 0, underfullStack
}
case ocNEG, ocTRUNC8, ocTRUNC16, ocNOT, ocBITNOT, ocBITSET:
if dec.stackDepth <= 0 {
return 0, underfullStack
}
case ocCOND:
dec.stackDepth -= 2
if dec.stackDepth <= 0 {
return 0, underfullStack
}
case ocNEXT_N:
// runtime checked
case ocNEXT, ocCOPY_NEXT:
dec.outIndex++
if dec.outIndex < -1 || dec.outIndex > dec.outLength || dec.slotRef > int(dec.max.ruleLength) {
return 0, outOfRangeData
}
case ocPUT_GLYPH_8BIT_OBS:
if err := validUpto(dec.max.classes, uint16(bc[0])); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocPUT_SUBS_8BIT_OBS:
if err := dec.testRef(bc[0]); err != nil {
return 0, err
}
if err := validUpto(dec.max.classes, uint16(bc[1])); err != nil {
return 0, err
}
if err := validUpto(dec.max.classes, uint16(bc[2])); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocPUT_COPY:
if err := dec.testRef(bc[0]); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocINSERT:
if dec.passtype >= ptPOSITIONING {
return 0, invalidOpCode
}
dec.outLength++
if dec.outIndex < 0 {
dec.outIndex++
}
if dec.outIndex < -1 || dec.outIndex >= dec.outLength {
return 0, outOfRangeData
}
case ocDELETE:
if dec.passtype >= ptPOSITIONING {
return 0, invalidOpCode
}
if dec.outIndex < int(dec.max.preContext) {
return 0, outOfRangeData
}
dec.outIndex--
dec.outLength--
if dec.outIndex < -1 || dec.outIndex > dec.outLength {
return 0, outOfRangeData
}
case ocASSOC:
if bc[0] == 0 {
return 0, outOfRangeData
}
for num := bc[0]; num != 0; num-- {
if err := dec.testRef(bc[num]); err != nil {
return 0, err
}
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocCNTXT_ITEM:
err := validUpto(dec.max.ruleLength, uint16(int(dec.max.preContext)+int(int8(bc[0]))))
if err != nil {
return 0, err
}
if len(bc) < 2+int(bc[1]) {
return 0, jumpPastEnd
}
if dec.inCtxtItem {
return 0, nestedContextItem
}
case ocATTR_SET, ocATTR_ADD, ocATTR_SUB, ocATTR_SET_SLOT:
dec.stackDepth--
if dec.stackDepth < 0 {
return 0, underfullStack
}
err := validUpto(acMax, uint16(bc[0]))
if err != nil {
return 0, err
}
if attrCode(bc[0]) == acUserDefn { // use IATTR for user attributes
return 0, outOfRangeData
}
if err := dec.testAttr(attrCode(bc[0])); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocIATTR_SET_SLOT:
dec.stackDepth--
if dec.stackDepth < 0 {
return 0, underfullStack
}
if err := validUpto(acMax, uint16(bc[0])); err != nil {
return 0, err
}
if err := validUpto(uint16(dec.max.attrid[bc[0]]), uint16(bc[1])); err != nil {
return 0, err
}
if err := dec.testAttr(attrCode(bc[0])); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocPUSH_SLOT_ATTR:
dec.stackDepth++
if err := validUpto(acMax, uint16(bc[0])); err != nil {
return 0, err
}
if err := dec.testRef(bc[1]); err != nil {
return 0, err
}
if attrCode(bc[0]) == acUserDefn { // use IATTR for user attributes
return 0, outOfRangeData
}
if err := dec.testAttr(attrCode(bc[0])); err != nil {
return 0, err
}
case ocPUSH_GLYPH_ATTR_OBS, ocPUSH_ATT_TO_GATTR_OBS:
dec.stackDepth++
if err := validUpto(dec.max.glyfAttrs, uint16(bc[0])); err != nil {
return 0, err
}
if err := dec.testRef(bc[1]); err != nil {
return 0, err
}
case ocPUSH_ATT_TO_GLYPH_METRIC, ocPUSH_GLYPH_METRIC:
dec.stackDepth++
if err := validUpto(kgmetDescent, uint16(bc[0])); err != nil {
return 0, err
}
if err := dec.testRef(bc[1]); err != nil {
return 0, err
}
// level: dp[2] no check necessary
case ocPUSH_FEAT:
dec.stackDepth++
if err := validUpto(dec.max.features, uint16(bc[0])); err != nil {
return 0, err
}
if err := dec.testRef(bc[1]); err != nil {
return 0, err
}
case ocPUSH_ISLOT_ATTR:
dec.stackDepth++
if err := validUpto(acMax, uint16(bc[0])); err != nil {
return 0, err
}
if err := dec.testRef(bc[1]); err != nil {
return 0, err
}
if err := validUpto(uint16(dec.max.attrid[bc[0]]), uint16(bc[2])); err != nil {
return 0, err
}
if err := dec.testAttr(attrCode(bc[0])); err != nil {
return 0, err
}
case ocPUSH_IGLYPH_ATTR:
// not implemented
dec.stackDepth++
case ocPOP_RET:
dec.stackDepth--
if dec.stackDepth < 0 {
return 0, underfullStack
}
fallthrough
case ocRET_ZERO, ocRET_TRUE:
case ocIATTR_SET, ocIATTR_ADD, ocIATTR_SUB:
dec.stackDepth--
if dec.stackDepth < 0 {
return 0, underfullStack
}
if err := validUpto(acMax, uint16(bc[0])); err != nil {
return 0, err
}
if err := validUpto(uint16(dec.max.attrid[bc[0]]), uint16(bc[1])); err != nil {
return 0, err
}
if err := dec.testAttr(attrCode(bc[0])); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocPUSH_PROC_STATE, ocPUSH_VERSION:
dec.stackDepth++
case ocPUT_SUBS:
if err := dec.testRef(bc[0]); err != nil {
return 0, err
}
if err := validUpto(dec.max.classes, uint16(bc[1])<<8|uint16(bc[2])); err != nil {
return 0, err
}
if err := validUpto(dec.max.classes, uint16(bc[3])<<8|uint16(bc[4])); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocPUT_SUBS2, ocPUT_SUBS3:
// not implemented
case ocPUT_GLYPH:
if err := validUpto(dec.max.classes, uint16(bc[0])<<8|uint16(bc[1])); err != nil {
return 0, err
}
if err := dec.testContext(); err != nil {
return 0, err
}
case ocPUSH_GLYPH_ATTR, ocPUSH_ATT_TO_GLYPH_ATTR:
dec.stackDepth++
if err := validUpto(dec.max.glyfAttrs, uint16(bc[0])<<8|uint16(bc[1])); err != nil {
return 0, err
}
if err := dec.testRef(bc[2]); err != nil {
return 0, err
}
case ocSET_FEAT:
if err := validUpto(dec.max.features, uint16(bc[0])); err != nil {
return 0, err
}
if err := dec.testRef(bc[1]); err != nil {
return 0, err
}
default:
return 0, invalidOpCode
}
return opc, nil
}
func validUpto(limit, x uint16) error {
if (limit != 0) && (x < limit) {
return nil
}
return outOfRangeData
}
func (dec *decoder) testContext() error {
if dec.outIndex >= dec.outLength || dec.outIndex < 0 || dec.slotRef >= decoderNUMCONTEXTS-1 {
return outOfRangeData
}
return nil
}
func (dec *decoder) testRef(index_ byte) error {
index := int8(index_)
if dec.code.constraint && !dec.inCtxtItem {
if index > 0 || uint16(-index) > dec.max.preContext {
return outOfRangeData
}
} else {
if L := dec.slotRef + int(dec.max.preContext) + int(index); dec.max.ruleLength == 0 ||
L >= int(dec.max.ruleLength) || L < 0 {
return outOfRangeData
}
}
return nil
}
func (dec *decoder) testAttr(attr attrCode) error {
if dec.passtype < ptPOSITIONING {
if attr != acBreak && attr != acDir && attr != acUserDefn && attr != acCompRef {
return outOfRangeData
}
}
return nil
}
// the length of arg as been checked
func (dec *decoder) analyseOpcode(opc opcode, arg []byte) {
switch opc {
case ocDELETE:
dec.code.delete = true
case ocASSOC:
dec.setChanged(0)
// for (uint8 num = arg[0]; num; --num)
// _analysis.setNoref(num);
case ocPUT_GLYPH_8BIT_OBS, ocPUT_GLYPH:
dec.code.modify = true
dec.setChanged(0)
case ocATTR_SET, ocATTR_ADD, ocATTR_SUB, ocATTR_SET_SLOT, ocIATTR_SET_SLOT, ocIATTR_SET, ocIATTR_ADD, ocIATTR_SUB:
dec.setNoref(0)
case ocNEXT, ocCOPY_NEXT:
dec.slotRef++
dec.contexts[dec.slotRef] = context{codeRef: uint8(len(dec.code.instrs) + 1)}
// if (_analysis.slotRef > _analysis.max_ref) _analysis.max_ref = _analysis.slotRef;
case ocINSERT:
if dec.slotRef >= 0 {
dec.slotRef--
}
dec.code.modify = true
case ocPUT_SUBS_8BIT_OBS /* slotRef on 1st parameter */, ocPUT_SUBS:
dec.code.modify = true
dec.setChanged(0)
fallthrough
case ocPUT_COPY:
if arg[0] != 0 {
dec.setChanged(0)
dec.code.modify = true
}
dec.setRef(int8(arg[0]))
case ocPUSH_GLYPH_ATTR_OBS, ocPUSH_SLOT_ATTR, ocPUSH_GLYPH_METRIC, ocPUSH_ATT_TO_GATTR_OBS, ocPUSH_ATT_TO_GLYPH_METRIC, ocPUSH_ISLOT_ATTR, ocPUSH_FEAT, ocSET_FEAT:
dec.setRef(int8(arg[1]))
case ocPUSH_ATT_TO_GLYPH_ATTR, ocPUSH_GLYPH_ATTR:
dec.setRef(int8(arg[2]))
}
}
func (dec *decoder) setRef(arg int8) {
index := int(arg)
if index+dec.slotRef < 0 || index+dec.slotRef >= decoderNUMCONTEXTS {
return
}
dec.contexts[index+dec.slotRef].referenced = true
if index+dec.slotRef > dec.code.maxRef {
dec.code.maxRef = index + dec.slotRef
}
}
func (dec *decoder) setNoref(index int) {
if index+dec.slotRef < 0 || index+dec.slotRef >= decoderNUMCONTEXTS {
return
}
if index+dec.slotRef > dec.code.maxRef {
dec.code.maxRef = index + dec.slotRef
}
}
func (dec *decoder) setChanged(index int) {
if index+dec.slotRef < 0 || index+dec.slotRef >= decoderNUMCONTEXTS {
return
}
dec.contexts[index+dec.slotRef].changed = true
if index+dec.slotRef > dec.code.maxRef {
dec.code.maxRef = index + dec.slotRef
}
}
// implements one op code.
// `args` has already been checked for its length (see `fetchOpcode` for exceptions)
// the function returns the truncated `args` slice and `true` if no error occured
type instrImpl func(reg *regbank, st *stack, args []byte) ([]byte, bool)
// length of bc has been checked
// the `code` item will be updated, and the remaining bytecode
// input is returned
func (dec *decoder) emitOpcode(opc opcode, bc []byte) ([]byte, error) {
op := opcodeTable[opc]
fn := op.impl[boolToInt(dec.code.constraint)]
if fn == nil {
return nil, unimplementedOpCodeUsed
}
paramSize := op.paramSize
if op.paramSize == varArgs {
paramSize = bc[0] + 1
}
// Add this instruction
dec.code.instrs = append(dec.code.instrs, instr{
fn: fn,
code: opc,
})
// Grab the parameters
if paramSize != 0 {
dec.code.args = append(dec.code.args, bc[:paramSize]...)
bc = bc[paramSize:]
}
// recursively decode a context item so we can split the skip into
// instruction and data portions.
if opc == ocCNTXT_ITEM {
// assert(_out_index == 0);
dec.inCtxtItem = true
dec.slotRef = int(int8(dec.code.args[len(dec.code.args)-2]))
dec.outIndex = int(dec.max.preContext) + dec.slotRef
dec.outLength = int(dec.max.ruleLength)
// instrSkip takes into account the opcodes
instrSkipIndex := len(dec.code.args) - 1
instrSkip := dec.code.args[instrSkipIndex]
dec.code.args = append(dec.code.args, 0) // filled later
// save the current number of instructions
nbOpcodesStart := len(dec.code.instrs)
_, err := dec.load(bc[:instrSkip])
if err != nil {
return nil, err
}
nbOpcodesContext := byte(len(dec.code.instrs) - nbOpcodesStart)
// update the args slice (see the opcode implementation)
dec.code.args[instrSkipIndex] = nbOpcodesContext
dec.code.args[instrSkipIndex+1] = instrSkip - nbOpcodesContext // without op codes
bc = bc[instrSkip:]
dec.outLength = 1
dec.outIndex = 0
dec.slotRef = 0
dec.inCtxtItem = false
}
return bc, nil
}
// insert TEMP_COPY commands for slots that need them (that change and are referenced later)
func (dec *decoder) applyAnalysis(code []instr) []instr {
if dec.code.constraint {
return code
}
tempcount := 0
tempCopy := opcodeTable[ocTEMP_COPY].impl[0]
for _, c := range dec.contexts[:dec.slotRef] {
if !c.referenced || !c.changed {
continue
}
code = append(code, instr{})
tip := code[int(c.codeRef)+tempcount:]
copy(tip[1:], tip)
tip[0] = instr{fn: tempCopy, code: ocTEMP_COPY}
dec.code.delete = true
tempcount++
}
return code
}