1999-12-16 21:34:53 +00:00
|
|
|
"""ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
|
2024-02-06 15:47:35 +02:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
from __future__ import annotations
|
1999-12-16 21:34:53 +00:00
|
|
|
|
2021-08-20 00:45:43 +02:00
|
|
|
from fontTools.misc.textTools import num2binary, binary2num, readHex, strjoin
|
1999-12-16 21:34:53 +00:00
|
|
|
import array
|
2021-03-29 11:45:58 +02:00
|
|
|
from io import StringIO
|
2023-03-14 14:51:16 +01:00
|
|
|
from typing import List
|
2013-11-27 05:47:34 -05:00
|
|
|
import re
|
2016-01-24 14:45:05 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
1999-12-16 21:34:53 +00:00
|
|
|
|
2023-03-14 16:48:21 +01:00
|
|
|
# fmt: off
|
|
|
|
|
1999-12-16 21:34:53 +00:00
|
|
|
# first, the list of instructions that eat bytes or words from the instruction stream
|
|
|
|
|
|
|
|
streamInstructions = [
|
2023-03-14 16:48:21 +01:00
|
|
|
#
|
|
|
|
# opcode mnemonic argBits descriptive name pops pushes eats from instruction stream pushes
|
|
|
|
#
|
|
|
|
(0x40, 'NPUSHB', 0, 'PushNBytes', 0, -1), # n, b1, b2,...bn b1,b2...bn
|
|
|
|
(0x41, 'NPUSHW', 0, 'PushNWords', 0, -1), # n, w1, w2,...w w1,w2...wn
|
|
|
|
(0xb0, 'PUSHB', 3, 'PushBytes', 0, -1), # b0, b1,..bn b0, b1, ...,bn
|
|
|
|
(0xb8, 'PUSHW', 3, 'PushWords', 0, -1), # w0,w1,..wn w0 ,w1, ...wn
|
1999-12-16 21:34:53 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2023-03-14 16:48:21 +01:00
|
|
|
# next, the list of "normal" instructions
|
1999-12-16 21:34:53 +00:00
|
|
|
|
|
|
|
instructions = [
|
2023-03-14 16:48:21 +01:00
|
|
|
#
|
|
|
|
# opcode mnemonic argBits descriptive name pops pushes eats from instruction stream pushes
|
|
|
|
#
|
|
|
|
(0x7f, 'AA', 0, 'AdjustAngle', 1, 0), # p -
|
|
|
|
(0x64, 'ABS', 0, 'Absolute', 1, 1), # n |n|
|
|
|
|
(0x60, 'ADD', 0, 'Add', 2, 1), # n2, n1 (n1 + n2)
|
|
|
|
(0x27, 'ALIGNPTS', 0, 'AlignPts', 2, 0), # p2, p1 -
|
|
|
|
(0x3c, 'ALIGNRP', 0, 'AlignRelativePt', -1, 0), # p1, p2, ... , ploopvalue -
|
|
|
|
(0x5a, 'AND', 0, 'LogicalAnd', 2, 1), # e2, e1 b
|
|
|
|
(0x2b, 'CALL', 0, 'CallFunction', 1, 0), # f -
|
|
|
|
(0x67, 'CEILING', 0, 'Ceiling', 1, 1), # n ceil(n)
|
|
|
|
(0x25, 'CINDEX', 0, 'CopyXToTopStack', 1, 1), # k ek
|
|
|
|
(0x22, 'CLEAR', 0, 'ClearStack', -1, 0), # all items on the stack -
|
|
|
|
(0x4f, 'DEBUG', 0, 'DebugCall', 1, 0), # n -
|
|
|
|
(0x73, 'DELTAC1', 0, 'DeltaExceptionC1', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
|
|
|
|
(0x74, 'DELTAC2', 0, 'DeltaExceptionC2', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
|
|
|
|
(0x75, 'DELTAC3', 0, 'DeltaExceptionC3', -1, 0), # argn, cn, argn-1,cn-1, , arg1, c1 -
|
|
|
|
(0x5d, 'DELTAP1', 0, 'DeltaExceptionP1', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
|
|
|
|
(0x71, 'DELTAP2', 0, 'DeltaExceptionP2', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
|
|
|
|
(0x72, 'DELTAP3', 0, 'DeltaExceptionP3', -1, 0), # argn, pn, argn-1, pn-1, , arg1, p1 -
|
|
|
|
(0x24, 'DEPTH', 0, 'GetDepthStack', 0, 1), # - n
|
|
|
|
(0x62, 'DIV', 0, 'Divide', 2, 1), # n2, n1 (n1 * 64)/ n2
|
|
|
|
(0x20, 'DUP', 0, 'DuplicateTopStack', 1, 2), # e e, e
|
|
|
|
(0x59, 'EIF', 0, 'EndIf', 0, 0), # - -
|
|
|
|
(0x1b, 'ELSE', 0, 'Else', 0, 0), # - -
|
|
|
|
(0x2d, 'ENDF', 0, 'EndFunctionDefinition', 0, 0), # - -
|
|
|
|
(0x54, 'EQ', 0, 'Equal', 2, 1), # e2, e1 b
|
|
|
|
(0x57, 'EVEN', 0, 'Even', 1, 1), # e b
|
|
|
|
(0x2c, 'FDEF', 0, 'FunctionDefinition', 1, 0), # f -
|
|
|
|
(0x4e, 'FLIPOFF', 0, 'SetAutoFlipOff', 0, 0), # - -
|
|
|
|
(0x4d, 'FLIPON', 0, 'SetAutoFlipOn', 0, 0), # - -
|
|
|
|
(0x80, 'FLIPPT', 0, 'FlipPoint', -1, 0), # p1, p2, ..., ploopvalue -
|
|
|
|
(0x82, 'FLIPRGOFF', 0, 'FlipRangeOff', 2, 0), # h, l -
|
|
|
|
(0x81, 'FLIPRGON', 0, 'FlipRangeOn', 2, 0), # h, l -
|
|
|
|
(0x66, 'FLOOR', 0, 'Floor', 1, 1), # n floor(n)
|
|
|
|
(0x46, 'GC', 1, 'GetCoordOnPVector', 1, 1), # p c
|
|
|
|
(0x88, 'GETINFO', 0, 'GetInfo', 1, 1), # selector result
|
|
|
|
(0x91, 'GETVARIATION', 0, 'GetVariation', 0, -1), # - a1,..,an
|
|
|
|
(0x0d, 'GFV', 0, 'GetFVector', 0, 2), # - px, py
|
|
|
|
(0x0c, 'GPV', 0, 'GetPVector', 0, 2), # - px, py
|
|
|
|
(0x52, 'GT', 0, 'GreaterThan', 2, 1), # e2, e1 b
|
|
|
|
(0x53, 'GTEQ', 0, 'GreaterThanOrEqual', 2, 1), # e2, e1 b
|
|
|
|
(0x89, 'IDEF', 0, 'InstructionDefinition', 1, 0), # f -
|
|
|
|
(0x58, 'IF', 0, 'If', 1, 0), # e -
|
|
|
|
(0x8e, 'INSTCTRL', 0, 'SetInstrExecControl', 2, 0), # s, v -
|
|
|
|
(0x39, 'IP', 0, 'InterpolatePts', -1, 0), # p1, p2, ... , ploopvalue -
|
|
|
|
(0x0f, 'ISECT', 0, 'MovePtToIntersect', 5, 0), # a1, a0, b1, b0, p -
|
|
|
|
(0x30, 'IUP', 1, 'InterpolateUntPts', 0, 0), # - -
|
|
|
|
(0x1c, 'JMPR', 0, 'Jump', 1, 0), # offset -
|
|
|
|
(0x79, 'JROF', 0, 'JumpRelativeOnFalse', 2, 0), # e, offset -
|
|
|
|
(0x78, 'JROT', 0, 'JumpRelativeOnTrue', 2, 0), # e, offset -
|
|
|
|
(0x2a, 'LOOPCALL', 0, 'LoopAndCallFunction', 2, 0), # f, count -
|
|
|
|
(0x50, 'LT', 0, 'LessThan', 2, 1), # e2, e1 b
|
|
|
|
(0x51, 'LTEQ', 0, 'LessThenOrEqual', 2, 1), # e2, e1 b
|
|
|
|
(0x8b, 'MAX', 0, 'Maximum', 2, 1), # e2, e1 max(e1, e2)
|
|
|
|
(0x49, 'MD', 1, 'MeasureDistance', 2, 1), # p2,p1 d
|
|
|
|
(0x2e, 'MDAP', 1, 'MoveDirectAbsPt', 1, 0), # p -
|
|
|
|
(0xc0, 'MDRP', 5, 'MoveDirectRelPt', 1, 0), # p -
|
|
|
|
(0x3e, 'MIAP', 1, 'MoveIndirectAbsPt', 2, 0), # n, p -
|
|
|
|
(0x8c, 'MIN', 0, 'Minimum', 2, 1), # e2, e1 min(e1, e2)
|
|
|
|
(0x26, 'MINDEX', 0, 'MoveXToTopStack', 1, 1), # k ek
|
|
|
|
(0xe0, 'MIRP', 5, 'MoveIndirectRelPt', 2, 0), # n, p -
|
|
|
|
(0x4b, 'MPPEM', 0, 'MeasurePixelPerEm', 0, 1), # - ppem
|
|
|
|
(0x4c, 'MPS', 0, 'MeasurePointSize', 0, 1), # - pointSize
|
|
|
|
(0x3a, 'MSIRP', 1, 'MoveStackIndirRelPt', 2, 0), # d, p -
|
|
|
|
(0x63, 'MUL', 0, 'Multiply', 2, 1), # n2, n1 (n1 * n2)/64
|
|
|
|
(0x65, 'NEG', 0, 'Negate', 1, 1), # n -n
|
|
|
|
(0x55, 'NEQ', 0, 'NotEqual', 2, 1), # e2, e1 b
|
|
|
|
(0x5c, 'NOT', 0, 'LogicalNot', 1, 1), # e ( not e )
|
|
|
|
(0x6c, 'NROUND', 2, 'NoRound', 1, 1), # n1 n2
|
|
|
|
(0x56, 'ODD', 0, 'Odd', 1, 1), # e b
|
|
|
|
(0x5b, 'OR', 0, 'LogicalOr', 2, 1), # e2, e1 b
|
|
|
|
(0x21, 'POP', 0, 'PopTopStack', 1, 0), # e -
|
|
|
|
(0x45, 'RCVT', 0, 'ReadCVT', 1, 1), # location value
|
|
|
|
(0x7d, 'RDTG', 0, 'RoundDownToGrid', 0, 0), # - -
|
|
|
|
(0x7a, 'ROFF', 0, 'RoundOff', 0, 0), # - -
|
|
|
|
(0x8a, 'ROLL', 0, 'RollTopThreeStack', 3, 3), # a,b,c b,a,c
|
|
|
|
(0x68, 'ROUND', 2, 'Round', 1, 1), # n1 n2
|
|
|
|
(0x43, 'RS', 0, 'ReadStore', 1, 1), # n v
|
|
|
|
(0x3d, 'RTDG', 0, 'RoundToDoubleGrid', 0, 0), # - -
|
|
|
|
(0x18, 'RTG', 0, 'RoundToGrid', 0, 0), # - -
|
|
|
|
(0x19, 'RTHG', 0, 'RoundToHalfGrid', 0, 0), # - -
|
|
|
|
(0x7c, 'RUTG', 0, 'RoundUpToGrid', 0, 0), # - -
|
|
|
|
(0x77, 'S45ROUND', 0, 'SuperRound45Degrees', 1, 0), # n -
|
|
|
|
(0x7e, 'SANGW', 0, 'SetAngleWeight', 1, 0), # weight -
|
|
|
|
(0x85, 'SCANCTRL', 0, 'ScanConversionControl', 1, 0), # n -
|
|
|
|
(0x8d, 'SCANTYPE', 0, 'ScanType', 1, 0), # n -
|
|
|
|
(0x48, 'SCFS', 0, 'SetCoordFromStackFP', 2, 0), # c, p -
|
|
|
|
(0x1d, 'SCVTCI', 0, 'SetCVTCutIn', 1, 0), # n -
|
|
|
|
(0x5e, 'SDB', 0, 'SetDeltaBaseInGState', 1, 0), # n -
|
|
|
|
(0x86, 'SDPVTL', 1, 'SetDualPVectorToLine', 2, 0), # p2, p1 -
|
|
|
|
(0x5f, 'SDS', 0, 'SetDeltaShiftInGState', 1, 0), # n -
|
|
|
|
(0x0b, 'SFVFS', 0, 'SetFVectorFromStack', 2, 0), # y, x -
|
|
|
|
(0x04, 'SFVTCA', 1, 'SetFVectorToAxis', 0, 0), # - -
|
|
|
|
(0x08, 'SFVTL', 1, 'SetFVectorToLine', 2, 0), # p2, p1 -
|
|
|
|
(0x0e, 'SFVTPV', 0, 'SetFVectorToPVector', 0, 0), # - -
|
|
|
|
(0x34, 'SHC', 1, 'ShiftContourByLastPt', 1, 0), # c -
|
|
|
|
(0x32, 'SHP', 1, 'ShiftPointByLastPoint', -1, 0), # p1, p2, ..., ploopvalue -
|
|
|
|
(0x38, 'SHPIX', 0, 'ShiftZoneByPixel', -1, 0), # d, p1, p2, ..., ploopvalue -
|
|
|
|
(0x36, 'SHZ', 1, 'ShiftZoneByLastPoint', 1, 0), # e -
|
|
|
|
(0x17, 'SLOOP', 0, 'SetLoopVariable', 1, 0), # n -
|
|
|
|
(0x1a, 'SMD', 0, 'SetMinimumDistance', 1, 0), # distance -
|
|
|
|
(0x0a, 'SPVFS', 0, 'SetPVectorFromStack', 2, 0), # y, x -
|
|
|
|
(0x02, 'SPVTCA', 1, 'SetPVectorToAxis', 0, 0), # - -
|
|
|
|
(0x06, 'SPVTL', 1, 'SetPVectorToLine', 2, 0), # p2, p1 -
|
|
|
|
(0x76, 'SROUND', 0, 'SuperRound', 1, 0), # n -
|
|
|
|
(0x10, 'SRP0', 0, 'SetRefPoint0', 1, 0), # p -
|
|
|
|
(0x11, 'SRP1', 0, 'SetRefPoint1', 1, 0), # p -
|
|
|
|
(0x12, 'SRP2', 0, 'SetRefPoint2', 1, 0), # p -
|
|
|
|
(0x1f, 'SSW', 0, 'SetSingleWidth', 1, 0), # n -
|
|
|
|
(0x1e, 'SSWCI', 0, 'SetSingleWidthCutIn', 1, 0), # n -
|
|
|
|
(0x61, 'SUB', 0, 'Subtract', 2, 1), # n2, n1 (n1 - n2)
|
|
|
|
(0x00, 'SVTCA', 1, 'SetFPVectorToAxis', 0, 0), # - -
|
|
|
|
(0x23, 'SWAP', 0, 'SwapTopStack', 2, 2), # e2, e1 e1, e2
|
|
|
|
(0x13, 'SZP0', 0, 'SetZonePointer0', 1, 0), # n -
|
|
|
|
(0x14, 'SZP1', 0, 'SetZonePointer1', 1, 0), # n -
|
|
|
|
(0x15, 'SZP2', 0, 'SetZonePointer2', 1, 0), # n -
|
|
|
|
(0x16, 'SZPS', 0, 'SetZonePointerS', 1, 0), # n -
|
|
|
|
(0x29, 'UTP', 0, 'UnTouchPt', 1, 0), # p -
|
|
|
|
(0x70, 'WCVTF', 0, 'WriteCVTInFUnits', 2, 0), # n, l -
|
|
|
|
(0x44, 'WCVTP', 0, 'WriteCVTInPixels', 2, 0), # v, l -
|
|
|
|
(0x42, 'WS', 0, 'WriteStore', 2, 0), # v, l -
|
1999-12-16 21:34:53 +00:00
|
|
|
]
|
|
|
|
|
2023-03-14 16:48:21 +01:00
|
|
|
# fmt: on
|
|
|
|
|
1999-12-16 21:34:53 +00:00
|
|
|
|
|
|
|
def bitRepr(value, bits):
|
|
|
|
s = ""
|
|
|
|
for i in range(bits):
|
|
|
|
s = "01"[value & 0x1] + s
|
|
|
|
value = value >> 1
|
|
|
|
return s
|
|
|
|
|
2000-02-01 15:30:15 +00:00
|
|
|
|
2018-12-05 12:46:36 +01:00
|
|
|
_mnemonicPat = re.compile(r"[A-Z][A-Z0-9]*$")
|
2000-02-01 15:30:15 +00:00
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2000-02-01 15:30:15 +00:00
|
|
|
def _makeDict(instructionList):
|
1999-12-16 21:34:53 +00:00
|
|
|
opcodeDict = {}
|
2000-02-01 15:30:15 +00:00
|
|
|
mnemonicDict = {}
|
|
|
|
for op, mnemonic, argBits, name, pops, pushes in instructionList:
|
|
|
|
assert _mnemonicPat.match(mnemonic)
|
2015-01-06 02:38:13 +01:00
|
|
|
mnemonicDict[mnemonic] = op, argBits, name
|
2000-02-01 15:30:15 +00:00
|
|
|
if argBits:
|
1999-12-16 21:34:53 +00:00
|
|
|
argoffset = op
|
2000-02-01 15:30:15 +00:00
|
|
|
for i in range(1 << argBits):
|
2015-01-06 02:38:13 +01:00
|
|
|
opcodeDict[op + i] = mnemonic, argBits, argoffset, name
|
1999-12-16 21:34:53 +00:00
|
|
|
else:
|
2015-01-06 02:38:13 +01:00
|
|
|
opcodeDict[op] = mnemonic, 0, 0, name
|
2000-02-01 15:30:15 +00:00
|
|
|
return opcodeDict, mnemonicDict
|
2022-12-13 11:26:36 +00:00
|
|
|
|
1999-12-16 21:34:53 +00:00
|
|
|
|
2000-02-01 15:30:15 +00:00
|
|
|
streamOpcodeDict, streamMnemonicDict = _makeDict(streamInstructions)
|
|
|
|
opcodeDict, mnemonicDict = _makeDict(instructions)
|
1999-12-16 21:34:53 +00:00
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2009-11-08 06:39:37 +00:00
|
|
|
class tt_instructions_error(Exception):
|
|
|
|
def __init__(self, error):
|
|
|
|
self.error = error
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2009-11-08 06:39:37 +00:00
|
|
|
def __str__(self):
|
|
|
|
return "TT instructions error: %s" % repr(self.error)
|
1999-12-16 21:34:53 +00:00
|
|
|
|
|
|
|
|
2000-02-01 15:30:15 +00:00
|
|
|
_comment = r"/\*.*?\*/"
|
|
|
|
_instruction = r"([A-Z][A-Z0-9]*)\s*\[(.*?)\]"
|
|
|
|
_number = r"-?[0-9]+"
|
|
|
|
_token = "(%s)|(%s)|(%s)" % (_instruction, _number, _comment)
|
|
|
|
|
|
|
|
_tokenRE = re.compile(_token)
|
|
|
|
_whiteRE = re.compile(r"\s*")
|
|
|
|
|
2015-01-06 02:38:13 +01:00
|
|
|
_pushCountPat = re.compile(r"[A-Z][A-Z0-9]*\s*\[.*?\]\s*/\* ([0-9]+).*?\*/")
|
2000-02-01 15:54:37 +00:00
|
|
|
|
2018-12-05 12:46:36 +01:00
|
|
|
_indentRE = re.compile(r"^FDEF|IF|ELSE\[ \]\t.+")
|
|
|
|
_unindentRE = re.compile(r"^ELSE|ENDF|EIF\[ \]\t.+")
|
2000-02-01 15:54:37 +00:00
|
|
|
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2013-12-04 01:15:46 -05:00
|
|
|
def _skipWhite(data, pos):
|
2000-02-01 15:30:15 +00:00
|
|
|
m = _whiteRE.match(data, pos)
|
|
|
|
newPos = m.regs[0][1]
|
|
|
|
assert newPos >= pos
|
|
|
|
return newPos
|
|
|
|
|
|
|
|
|
2013-11-28 14:26:58 -05:00
|
|
|
class Program(object):
|
2023-03-14 14:51:16 +01:00
|
|
|
def __init__(self) -> None:
|
2022-12-13 11:26:36 +00:00
|
|
|
pass
|
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def fromBytecode(self, bytecode: bytes) -> None:
|
2000-02-01 15:30:15 +00:00
|
|
|
self.bytecode = array.array("B", bytecode)
|
1999-12-16 21:34:53 +00:00
|
|
|
if hasattr(self, "assembly"):
|
2000-02-01 15:30:15 +00:00
|
|
|
del self.assembly
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def fromAssembly(self, assembly: List[str] | str) -> None:
|
|
|
|
if isinstance(assembly, list):
|
|
|
|
self.assembly = assembly
|
|
|
|
elif isinstance(assembly, str):
|
|
|
|
self.assembly = assembly.splitlines()
|
|
|
|
else:
|
|
|
|
raise TypeError(f"expected str or List[str], got {type(assembly).__name__}")
|
1999-12-16 21:34:53 +00:00
|
|
|
if hasattr(self, "bytecode"):
|
2000-02-01 15:30:15 +00:00
|
|
|
del self.bytecode
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def getBytecode(self) -> bytes:
|
1999-12-16 21:34:53 +00:00
|
|
|
if not hasattr(self, "bytecode"):
|
2000-02-01 15:30:15 +00:00
|
|
|
self._assemble()
|
2019-08-29 23:21:22 -04:00
|
|
|
return self.bytecode.tobytes()
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def getAssembly(self, preserve=True) -> List[str]:
|
1999-12-16 21:34:53 +00:00
|
|
|
if not hasattr(self, "assembly"):
|
2015-04-14 11:51:03 -07:00
|
|
|
self._disassemble(preserve=preserve)
|
1999-12-16 21:34:53 +00:00
|
|
|
return self.assembly
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def toXML(self, writer, ttFont) -> None:
|
2022-12-13 11:26:36 +00:00
|
|
|
if (
|
2013-12-07 16:06:10 -05:00
|
|
|
not hasattr(ttFont, "disassembleInstructions")
|
|
|
|
or ttFont.disassembleInstructions
|
2022-12-13 11:26:36 +00:00
|
|
|
):
|
|
|
|
try:
|
2015-09-24 09:32:49 +01:00
|
|
|
assembly = self.getAssembly()
|
2022-12-13 11:26:36 +00:00
|
|
|
except:
|
2015-09-24 09:32:49 +01:00
|
|
|
import traceback
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-09-24 09:32:49 +01:00
|
|
|
tmp = StringIO()
|
|
|
|
traceback.print_exc(file=tmp)
|
|
|
|
msg = "An exception occurred during the decompilation of glyph program:\n\n"
|
|
|
|
msg += tmp.getvalue()
|
2016-01-24 14:45:05 +00:00
|
|
|
log.error(msg)
|
2000-02-01 15:30:15 +00:00
|
|
|
writer.begintag("bytecode")
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2015-09-24 09:32:49 +01:00
|
|
|
writer.comment(msg.strip())
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2015-09-24 09:32:49 +01:00
|
|
|
writer.dumphex(self.getBytecode())
|
2000-02-01 15:30:15 +00:00
|
|
|
writer.endtag("bytecode")
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2022-12-13 11:26:36 +00:00
|
|
|
else:
|
2017-01-24 14:03:51 +00:00
|
|
|
if not assembly:
|
2022-12-13 11:26:36 +00:00
|
|
|
return
|
2015-09-24 09:32:49 +01:00
|
|
|
writer.begintag("assembly")
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2022-12-13 11:26:36 +00:00
|
|
|
i = 0
|
2017-01-23 16:10:48 +01:00
|
|
|
indent = 0
|
2015-09-24 09:32:49 +01:00
|
|
|
nInstr = len(assembly)
|
|
|
|
while i < nInstr:
|
|
|
|
instr = assembly[i]
|
2017-01-23 16:10:48 +01:00
|
|
|
if _unindentRE.match(instr):
|
|
|
|
indent -= 1
|
2017-01-24 13:07:20 +01:00
|
|
|
writer.write(writer.indentwhite * indent)
|
2015-09-24 09:32:49 +01:00
|
|
|
writer.write(instr)
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2015-09-24 09:32:49 +01:00
|
|
|
m = _pushCountPat.match(instr)
|
1999-12-16 21:34:53 +00:00
|
|
|
i = i + 1
|
2022-12-13 11:26:36 +00:00
|
|
|
if m:
|
2015-09-24 09:32:49 +01:00
|
|
|
nValues = int(m.group(1))
|
2023-03-14 14:51:16 +01:00
|
|
|
line: List[str] = []
|
2022-12-13 11:26:36 +00:00
|
|
|
j = 0
|
2013-12-17 04:19:14 -05:00
|
|
|
for j in range(nValues):
|
2015-09-24 09:32:49 +01:00
|
|
|
if j and not (j % 25):
|
2017-01-24 13:07:20 +01:00
|
|
|
writer.write(writer.indentwhite * indent)
|
2015-09-24 09:32:49 +01:00
|
|
|
writer.write(" ".join(line))
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2015-09-24 09:32:49 +01:00
|
|
|
line = []
|
|
|
|
line.append(assembly[i + j])
|
2017-01-24 13:07:20 +01:00
|
|
|
writer.write(writer.indentwhite * indent)
|
2015-09-24 09:32:49 +01:00
|
|
|
writer.write(" ".join(line))
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2015-09-24 09:32:49 +01:00
|
|
|
i = i + j + 1
|
2017-01-23 16:10:48 +01:00
|
|
|
if _indentRE.match(instr):
|
|
|
|
indent += 1
|
2015-09-24 09:32:49 +01:00
|
|
|
writer.endtag("assembly")
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2022-12-13 11:26:36 +00:00
|
|
|
else:
|
2017-01-24 14:03:51 +00:00
|
|
|
bytecode = self.getBytecode()
|
|
|
|
if not bytecode:
|
2022-12-13 11:26:36 +00:00
|
|
|
return
|
2000-02-01 15:30:15 +00:00
|
|
|
writer.begintag("bytecode")
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2017-01-24 14:03:51 +00:00
|
|
|
writer.dumphex(bytecode)
|
2000-02-01 15:30:15 +00:00
|
|
|
writer.endtag("bytecode")
|
2017-01-24 13:58:34 +00:00
|
|
|
writer.newline()
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def fromXML(self, name, attrs, content, ttFont) -> None:
|
2000-02-01 15:30:15 +00:00
|
|
|
if name == "assembly":
|
2013-11-27 21:17:35 -05:00
|
|
|
self.fromAssembly(strjoin(content))
|
2000-02-01 15:30:15 +00:00
|
|
|
self._assemble()
|
|
|
|
del self.assembly
|
2022-12-13 11:26:36 +00:00
|
|
|
else:
|
2000-02-01 15:30:15 +00:00
|
|
|
assert name == "bytecode"
|
|
|
|
self.fromBytecode(readHex(content))
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def _assemble(self) -> None:
|
|
|
|
assembly = " ".join(getattr(self, "assembly", []))
|
|
|
|
bytecode: List[int] = []
|
2000-02-01 15:30:15 +00:00
|
|
|
push = bytecode.append
|
|
|
|
lenAssembly = len(assembly)
|
2013-12-04 01:15:46 -05:00
|
|
|
pos = _skipWhite(assembly, 0)
|
2000-02-01 15:30:15 +00:00
|
|
|
while pos < lenAssembly:
|
|
|
|
m = _tokenRE.match(assembly, pos)
|
|
|
|
if m is None:
|
2013-11-27 02:42:28 -05:00
|
|
|
raise tt_instructions_error(
|
|
|
|
"Syntax error in TT program (%s)" % assembly[pos - 5 : pos + 15]
|
2022-12-13 11:26:36 +00:00
|
|
|
)
|
2000-02-01 15:30:15 +00:00
|
|
|
dummy, mnemonic, arg, number, comment = m.groups()
|
|
|
|
pos = m.regs[0][1]
|
|
|
|
if comment:
|
2013-12-04 01:15:46 -05:00
|
|
|
pos = _skipWhite(assembly, pos)
|
2022-12-13 11:26:36 +00:00
|
|
|
continue
|
|
|
|
|
2013-11-27 05:47:34 -05:00
|
|
|
arg = arg.strip()
|
2013-11-14 20:24:16 -05:00
|
|
|
if mnemonic.startswith("INSTR"):
|
|
|
|
# Unknown instruction
|
|
|
|
op = int(mnemonic[5:])
|
2022-12-13 11:26:36 +00:00
|
|
|
push(op)
|
When writing PUSH instructions, obey requested opcode
Previously fonttools was choosing between the optimal opcode
from PUSHB, NPUSHB, PUSHW, and NPUSHW. We now respect
whatever was requested and err if the format doesn't support
the data.
Morevoer, if the number of numbers to push is more than 255,
we add multiple push instructions to push all the numbers.
Finally, add a new pseudo-opcode "PUSH" that automatically
chooses the best format.
This, by itself, reduces roundtrip noise by not optimizing
the bytecode as it was before. In a followup commit I'll
change the bytecode disassembler to always produce PUSH
in the textual instructions instead of the four variants.
That way, we get both the optimization during assembling,
and reduced noise in XML.
Part of https://github.com/behdad/fonttools/issues/73
2013-12-17 03:47:44 -05:00
|
|
|
elif mnemonic not in ("PUSH", "NPUSHB", "NPUSHW", "PUSHB", "PUSHW"):
|
2015-01-06 02:38:13 +01:00
|
|
|
op, argBits, name = mnemonicDict[mnemonic]
|
2013-11-27 02:40:30 -05:00
|
|
|
if len(arg) != argBits:
|
2013-11-27 02:42:28 -05:00
|
|
|
raise tt_instructions_error(
|
|
|
|
"Incorrect number of argument bits (%s[%s])" % (mnemonic, arg)
|
2022-12-13 11:26:36 +00:00
|
|
|
)
|
|
|
|
if arg:
|
2000-02-01 15:30:15 +00:00
|
|
|
arg = binary2num(arg)
|
|
|
|
push(op + arg)
|
2022-12-13 11:26:36 +00:00
|
|
|
else:
|
|
|
|
push(op)
|
|
|
|
else:
|
2000-02-01 15:30:15 +00:00
|
|
|
args = []
|
2013-12-04 01:15:46 -05:00
|
|
|
pos = _skipWhite(assembly, pos)
|
2000-02-01 15:30:15 +00:00
|
|
|
while pos < lenAssembly:
|
|
|
|
m = _tokenRE.match(assembly, pos)
|
|
|
|
if m is None:
|
2013-11-27 02:42:28 -05:00
|
|
|
raise tt_instructions_error(
|
|
|
|
"Syntax error in TT program (%s)" % assembly[pos : pos + 15]
|
2022-12-13 11:26:36 +00:00
|
|
|
)
|
When writing PUSH instructions, obey requested opcode
Previously fonttools was choosing between the optimal opcode
from PUSHB, NPUSHB, PUSHW, and NPUSHW. We now respect
whatever was requested and err if the format doesn't support
the data.
Morevoer, if the number of numbers to push is more than 255,
we add multiple push instructions to push all the numbers.
Finally, add a new pseudo-opcode "PUSH" that automatically
chooses the best format.
This, by itself, reduces roundtrip noise by not optimizing
the bytecode as it was before. In a followup commit I'll
change the bytecode disassembler to always produce PUSH
in the textual instructions instead of the four variants.
That way, we get both the optimization during assembling,
and reduced noise in XML.
Part of https://github.com/behdad/fonttools/issues/73
2013-12-17 03:47:44 -05:00
|
|
|
dummy, _mnemonic, arg, number, comment = m.groups()
|
2000-02-01 15:30:15 +00:00
|
|
|
if number is None and comment is None:
|
2022-12-13 11:26:36 +00:00
|
|
|
break
|
2000-02-01 15:30:15 +00:00
|
|
|
pos = m.regs[0][1]
|
2013-12-04 01:15:46 -05:00
|
|
|
pos = _skipWhite(assembly, pos)
|
2000-02-01 15:30:15 +00:00
|
|
|
if comment is not None:
|
2022-12-13 11:26:36 +00:00
|
|
|
continue
|
2000-02-01 15:30:15 +00:00
|
|
|
args.append(int(number))
|
|
|
|
nArgs = len(args)
|
When writing PUSH instructions, obey requested opcode
Previously fonttools was choosing between the optimal opcode
from PUSHB, NPUSHB, PUSHW, and NPUSHW. We now respect
whatever was requested and err if the format doesn't support
the data.
Morevoer, if the number of numbers to push is more than 255,
we add multiple push instructions to push all the numbers.
Finally, add a new pseudo-opcode "PUSH" that automatically
chooses the best format.
This, by itself, reduces roundtrip noise by not optimizing
the bytecode as it was before. In a followup commit I'll
change the bytecode disassembler to always produce PUSH
in the textual instructions instead of the four variants.
That way, we get both the optimization during assembling,
and reduced noise in XML.
Part of https://github.com/behdad/fonttools/issues/73
2013-12-17 03:47:44 -05:00
|
|
|
if mnemonic == "PUSH":
|
2013-12-17 05:06:37 -05:00
|
|
|
# Automatically choose the most compact representation
|
|
|
|
nWords = 0
|
|
|
|
while nArgs:
|
2022-12-13 11:26:36 +00:00
|
|
|
while (
|
2013-12-17 05:10:30 -05:00
|
|
|
nWords < nArgs
|
|
|
|
and nWords < 255
|
2013-12-17 05:06:37 -05:00
|
|
|
and not (0 <= args[nWords] <= 255)
|
2022-12-13 11:26:36 +00:00
|
|
|
):
|
2013-12-17 05:06:37 -05:00
|
|
|
nWords += 1
|
|
|
|
nBytes = 0
|
2022-12-13 11:26:36 +00:00
|
|
|
while (
|
2013-12-17 05:10:30 -05:00
|
|
|
nWords + nBytes < nArgs
|
|
|
|
and nBytes < 255
|
2013-12-17 05:06:37 -05:00
|
|
|
and 0 <= args[nWords + nBytes] <= 255
|
2022-12-13 11:26:36 +00:00
|
|
|
):
|
2013-12-17 05:06:37 -05:00
|
|
|
nBytes += 1
|
2022-12-13 11:26:36 +00:00
|
|
|
if (
|
2013-12-17 05:10:30 -05:00
|
|
|
nBytes < 2
|
|
|
|
and nWords + nBytes < 255
|
|
|
|
and nWords + nBytes != nArgs
|
2022-12-13 11:26:36 +00:00
|
|
|
):
|
2013-12-17 05:06:37 -05:00
|
|
|
# Will write bytes as words
|
|
|
|
nWords += nBytes
|
2022-12-13 11:26:36 +00:00
|
|
|
continue
|
2015-04-26 02:01:01 -04:00
|
|
|
|
1999-12-16 21:34:53 +00:00
|
|
|
# Write words
|
|
|
|
if nWords:
|
|
|
|
if nWords <= 8:
|
2000-02-01 15:30:15 +00:00
|
|
|
op, argBits, name = streamMnemonicDict["PUSHW"]
|
2013-12-17 05:06:37 -05:00
|
|
|
op = op + nWords - 1
|
2000-02-01 15:30:15 +00:00
|
|
|
push(op)
|
1999-12-16 21:34:53 +00:00
|
|
|
else:
|
|
|
|
op, argBits, name = streamMnemonicDict["NPUSHW"]
|
|
|
|
push(op)
|
2019-08-29 23:21:22 -04:00
|
|
|
push(nWords)
|
2013-12-07 16:06:10 -05:00
|
|
|
for value in args[:nWords]:
|
|
|
|
assert -32768 <= value < 32768, (
|
2015-09-24 09:32:49 +01:00
|
|
|
"PUSH value out of range %d" % value
|
2022-12-13 11:26:36 +00:00
|
|
|
)
|
2015-09-24 09:32:49 +01:00
|
|
|
push((value >> 8) & 0xFF)
|
|
|
|
push(value & 0xFF)
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-09-24 09:32:49 +01:00
|
|
|
# Write bytes
|
|
|
|
if nBytes:
|
2022-12-13 11:26:36 +00:00
|
|
|
pass
|
2015-09-24 09:32:49 +01:00
|
|
|
if nBytes <= 8:
|
|
|
|
op, argBits, name = streamMnemonicDict["PUSHB"]
|
|
|
|
op = op + nBytes - 1
|
2017-01-24 13:58:34 +00:00
|
|
|
push(op)
|
2015-09-24 09:32:49 +01:00
|
|
|
else:
|
|
|
|
op, argBits, name = streamMnemonicDict["NPUSHB"]
|
|
|
|
push(op)
|
|
|
|
push(nBytes)
|
|
|
|
for value in args[nWords : nWords + nBytes]:
|
2013-12-17 05:06:37 -05:00
|
|
|
push(value)
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-09-24 09:32:49 +01:00
|
|
|
nTotal = nWords + nBytes
|
|
|
|
args = args[nTotal:]
|
2013-12-17 05:06:37 -05:00
|
|
|
nArgs -= nTotal
|
|
|
|
nWords = 0
|
2015-09-24 09:32:49 +01:00
|
|
|
else:
|
2017-01-24 13:07:20 +01:00
|
|
|
# Write exactly what we've been asked to
|
2015-09-24 09:32:49 +01:00
|
|
|
words = mnemonic[-1] == "W"
|
|
|
|
op, argBits, name = streamMnemonicDict[mnemonic]
|
|
|
|
if mnemonic[0] != "N":
|
2013-12-17 05:06:37 -05:00
|
|
|
assert nArgs <= 8, nArgs
|
2015-09-24 09:32:49 +01:00
|
|
|
op = op + nArgs - 1
|
2017-01-24 13:58:34 +00:00
|
|
|
push(op)
|
2000-02-01 15:30:15 +00:00
|
|
|
else:
|
|
|
|
assert nArgs < 256
|
2022-12-13 11:26:36 +00:00
|
|
|
push(op)
|
2013-12-17 05:06:37 -05:00
|
|
|
push(nArgs)
|
2000-02-01 15:30:15 +00:00
|
|
|
if words:
|
2013-11-27 21:17:35 -05:00
|
|
|
for value in args:
|
2000-02-01 15:30:15 +00:00
|
|
|
assert -32768 <= value < 32768, (
|
|
|
|
"PUSHW value out of range %d" % value
|
|
|
|
)
|
2017-01-24 14:03:51 +00:00
|
|
|
push((value >> 8) & 0xFF)
|
2000-02-01 15:30:15 +00:00
|
|
|
push(value & 0xFF)
|
|
|
|
else:
|
2013-11-27 02:42:28 -05:00
|
|
|
for value in args:
|
|
|
|
assert 0 <= value < 256, (
|
|
|
|
"PUSHB value out of range %d" % value
|
2022-12-13 11:26:36 +00:00
|
|
|
)
|
2013-11-27 02:42:28 -05:00
|
|
|
push(value)
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-01-06 02:38:13 +01:00
|
|
|
pos = _skipWhite(assembly, pos)
|
2022-12-13 11:26:36 +00:00
|
|
|
|
When writing PUSH instructions, obey requested opcode
Previously fonttools was choosing between the optimal opcode
from PUSHB, NPUSHB, PUSHW, and NPUSHW. We now respect
whatever was requested and err if the format doesn't support
the data.
Morevoer, if the number of numbers to push is more than 255,
we add multiple push instructions to push all the numbers.
Finally, add a new pseudo-opcode "PUSH" that automatically
chooses the best format.
This, by itself, reduces roundtrip noise by not optimizing
the bytecode as it was before. In a followup commit I'll
change the bytecode disassembler to always produce PUSH
in the textual instructions instead of the four variants.
That way, we get both the optimization during assembling,
and reduced noise in XML.
Part of https://github.com/behdad/fonttools/issues/73
2013-12-17 03:47:44 -05:00
|
|
|
if bytecode:
|
2013-11-27 02:40:30 -05:00
|
|
|
assert max(bytecode) < 256 and min(bytecode) >= 0
|
2013-11-27 02:42:28 -05:00
|
|
|
self.bytecode = array.array("B", bytecode)
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def _disassemble(self, preserve=False) -> None:
|
2000-02-01 15:30:15 +00:00
|
|
|
assembly = []
|
2022-12-13 11:26:36 +00:00
|
|
|
i = 0
|
2017-01-24 14:03:51 +00:00
|
|
|
bytecode = getattr(self, "bytecode", [])
|
2000-02-01 15:30:15 +00:00
|
|
|
numBytecode = len(bytecode)
|
|
|
|
while i < numBytecode:
|
|
|
|
op = bytecode[i]
|
2013-11-27 02:42:28 -05:00
|
|
|
try:
|
When writing PUSH instructions, obey requested opcode
Previously fonttools was choosing between the optimal opcode
from PUSHB, NPUSHB, PUSHW, and NPUSHW. We now respect
whatever was requested and err if the format doesn't support
the data.
Morevoer, if the number of numbers to push is more than 255,
we add multiple push instructions to push all the numbers.
Finally, add a new pseudo-opcode "PUSH" that automatically
chooses the best format.
This, by itself, reduces roundtrip noise by not optimizing
the bytecode as it was before. In a followup commit I'll
change the bytecode disassembler to always produce PUSH
in the textual instructions instead of the four variants.
That way, we get both the optimization during assembling,
and reduced noise in XML.
Part of https://github.com/behdad/fonttools/issues/73
2013-12-17 03:47:44 -05:00
|
|
|
mnemonic, argBits, argoffset, name = opcodeDict[op]
|
2000-02-01 15:30:15 +00:00
|
|
|
except KeyError:
|
When writing PUSH instructions, obey requested opcode
Previously fonttools was choosing between the optimal opcode
from PUSHB, NPUSHB, PUSHW, and NPUSHW. We now respect
whatever was requested and err if the format doesn't support
the data.
Morevoer, if the number of numbers to push is more than 255,
we add multiple push instructions to push all the numbers.
Finally, add a new pseudo-opcode "PUSH" that automatically
chooses the best format.
This, by itself, reduces roundtrip noise by not optimizing
the bytecode as it was before. In a followup commit I'll
change the bytecode disassembler to always produce PUSH
in the textual instructions instead of the four variants.
That way, we get both the optimization during assembling,
and reduced noise in XML.
Part of https://github.com/behdad/fonttools/issues/73
2013-12-17 03:47:44 -05:00
|
|
|
if op in streamOpcodeDict:
|
2013-12-17 05:06:37 -05:00
|
|
|
values = []
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2013-12-17 05:06:37 -05:00
|
|
|
# Merge consecutive PUSH operations
|
|
|
|
while bytecode[i] in streamOpcodeDict:
|
2013-12-17 05:10:30 -05:00
|
|
|
op = bytecode[i]
|
2015-01-08 14:45:51 +01:00
|
|
|
mnemonic, argBits, argoffset, name = streamOpcodeDict[op]
|
2013-12-17 05:06:37 -05:00
|
|
|
words = mnemonic[-1] == "W"
|
2000-02-01 15:30:15 +00:00
|
|
|
if argBits:
|
2013-12-17 05:06:37 -05:00
|
|
|
nValues = op - argoffset + 1
|
|
|
|
else:
|
2015-01-08 14:45:51 +01:00
|
|
|
i = i + 1
|
2013-12-17 05:06:37 -05:00
|
|
|
nValues = bytecode[i]
|
1999-12-16 21:34:53 +00:00
|
|
|
i = i + 1
|
2013-12-17 05:06:37 -05:00
|
|
|
assert nValues > 0
|
|
|
|
if not words:
|
|
|
|
for j in range(nValues):
|
|
|
|
value = bytecode[i]
|
|
|
|
values.append(repr(value))
|
|
|
|
i = i + 1
|
|
|
|
else:
|
|
|
|
for j in range(nValues):
|
|
|
|
# cast to signed int16
|
|
|
|
value = (bytecode[i] << 8) | bytecode[i + 1]
|
2013-12-17 04:19:14 -05:00
|
|
|
if value >= 0x8000:
|
|
|
|
value = value - 0x10000
|
|
|
|
values.append(repr(value))
|
|
|
|
i = i + 2
|
2014-01-19 17:30:19 +08:00
|
|
|
if preserve:
|
2022-12-13 11:26:36 +00:00
|
|
|
break
|
|
|
|
|
2014-01-19 17:30:19 +08:00
|
|
|
if not preserve:
|
2013-12-17 05:06:37 -05:00
|
|
|
mnemonic = "PUSH"
|
When writing PUSH instructions, obey requested opcode
Previously fonttools was choosing between the optimal opcode
from PUSHB, NPUSHB, PUSHW, and NPUSHW. We now respect
whatever was requested and err if the format doesn't support
the data.
Morevoer, if the number of numbers to push is more than 255,
we add multiple push instructions to push all the numbers.
Finally, add a new pseudo-opcode "PUSH" that automatically
chooses the best format.
This, by itself, reduces roundtrip noise by not optimizing
the bytecode as it was before. In a followup commit I'll
change the bytecode disassembler to always produce PUSH
in the textual instructions instead of the four variants.
That way, we get both the optimization during assembling,
and reduced noise in XML.
Part of https://github.com/behdad/fonttools/issues/73
2013-12-17 03:47:44 -05:00
|
|
|
nValues = len(values)
|
|
|
|
if nValues == 1:
|
|
|
|
assembly.append("%s[ ] /* 1 value pushed */" % mnemonic)
|
|
|
|
else:
|
|
|
|
assembly.append(
|
|
|
|
"%s[ ] /* %s values pushed */" % (mnemonic, nValues)
|
|
|
|
)
|
2013-12-04 01:15:46 -05:00
|
|
|
assembly.extend(values)
|
2014-01-19 17:30:19 +08:00
|
|
|
else:
|
1999-12-16 21:34:53 +00:00
|
|
|
assembly.append("INSTR%d[ ]" % op)
|
|
|
|
i = i + 1
|
|
|
|
else:
|
2015-01-06 02:38:13 +01:00
|
|
|
if argBits:
|
|
|
|
assembly.append(
|
|
|
|
mnemonic
|
|
|
|
+ "[%s] /* %s */" % (num2binary(op - argoffset, argBits), name)
|
2022-12-13 11:26:36 +00:00
|
|
|
)
|
|
|
|
else:
|
2013-12-17 04:19:14 -05:00
|
|
|
assembly.append(mnemonic + "[ ] /* %s */" % name)
|
|
|
|
i = i + 1
|
|
|
|
self.assembly = assembly
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def __bool__(self) -> bool:
|
2022-12-13 11:26:36 +00:00
|
|
|
"""
|
2013-12-17 04:19:14 -05:00
|
|
|
>>> p = Program()
|
2015-06-25 19:22:42 +01:00
|
|
|
>>> bool(p)
|
2013-12-17 04:19:14 -05:00
|
|
|
False
|
|
|
|
>>> bc = array.array("B", [0])
|
|
|
|
>>> p.fromBytecode(bc)
|
|
|
|
>>> bool(p)
|
|
|
|
True
|
2013-11-27 04:57:33 -05:00
|
|
|
>>> p.bytecode.pop()
|
2013-12-17 04:19:14 -05:00
|
|
|
0
|
2015-01-05 17:49:38 -08:00
|
|
|
>>> bool(p)
|
|
|
|
False
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-06-25 19:22:42 +01:00
|
|
|
>>> p = Program()
|
|
|
|
>>> asm = ['SVTCA[0]']
|
|
|
|
>>> p.fromAssembly(asm)
|
|
|
|
>>> bool(p)
|
|
|
|
True
|
|
|
|
>>> p.assembly.pop()
|
|
|
|
'SVTCA[0]'
|
|
|
|
>>> bool(p)
|
|
|
|
False
|
2022-12-13 11:26:36 +00:00
|
|
|
"""
|
2015-06-25 19:22:42 +01:00
|
|
|
return (hasattr(self, "assembly") and len(self.assembly) > 0) or (
|
|
|
|
hasattr(self, "bytecode") and len(self.bytecode) > 0
|
|
|
|
)
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-06-25 18:04:07 +01:00
|
|
|
__nonzero__ = __bool__
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def __eq__(self, other) -> bool:
|
2016-03-11 13:31:16 +00:00
|
|
|
if type(self) != type(other):
|
|
|
|
return NotImplemented
|
|
|
|
return self.__dict__ == other.__dict__
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2023-03-14 14:51:16 +01:00
|
|
|
def __ne__(self, other) -> bool:
|
2016-03-11 13:31:16 +00:00
|
|
|
result = self.__eq__(other)
|
|
|
|
return result if result is NotImplemented else not result
|
|
|
|
|
1999-12-16 21:34:53 +00:00
|
|
|
|
2015-04-14 11:51:03 -07:00
|
|
|
def _test():
|
|
|
|
"""
|
|
|
|
>>> _test()
|
|
|
|
True
|
|
|
|
"""
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-04-14 13:25:19 -07:00
|
|
|
bc = b"""@;:9876543210/.-,+*)(\'&%$#"! \037\036\035\034\033\032\031\030\027\026\025\024\023\022\021\020\017\016\015\014\013\012\011\010\007\006\005\004\003\002\001\000,\001\260\030CXEj\260\031C`\260F#D#\020 \260FN\360M/\260\000\022\033!#\0213Y-,\001\260\030CX\260\005+\260\000\023K\260\024PX\261\000@8Y\260\006+\033!#\0213Y-,\001\260\030CXN\260\003%\020\362!\260\000\022M\033 E\260\004%\260\004%#Jad\260(RX!#\020\326\033\260\003%\020\362!\260\000\022YY-,\260\032CX!!\033\260\002%\260\002%I\260\003%\260\003%Ja d\260\020PX!!!\033\260\003%\260\003%I\260\000PX\260\000PX\270\377\3428!\033\260\0208!Y\033\260\000RX\260\0368!\033\270\377\3608!YYYY-,\001\260\030CX\260\005+\260\000\023K\260\024PX\271\000\000\377\3008Y\260\006+\033!#\0213Y-,N\001\212\020\261F\031CD\260\000\024\261\000F\342\260\000\025\271\000\000\377\3608\000\260\000<\260(+\260\002%\020\260\000<-,\001\030\260\000/\260\001\024\362\260\001\023\260\001\025M\260\000\022-,\001\260\030CX\260\005+\260\000\023\271\000\000\377\3408\260\006+\033!#\0213Y-,\001\260\030CXEdj#Edi\260\031Cd``\260F#D#\020 \260F\360/\260\000\022\033!! \212 \212RX\0213\033!!YY-,\001\261\013\012C#Ce\012-,\000\261\012\013C#C\013-,\000\260F#p\261\001F>\001\260F#p\261\002FE:\261\002\000\010\015-,\260\022+\260\002%E\260\002%Ej\260@\213`\260\002%#D!!!-,\260\023+\260\002%E\260\002%Ej\270\377\300\214`\260\002%#D!!!-,\260\000\260\022+!!!-,\260\000\260\023+!!!-,\001\260\006C\260\007Ce\012-, i\260@a\260\000\213 \261,\300\212\214\270\020\000b`+\014d#da\\X\260\003aY-,\261\000\003%EhT\260\034KPZX\260\003%E\260\003%E`h \260\004%#D\260\004%#D\033\260\003% Eh \212#D\260\003%Eh`\260\003%#DY-,\260\003% Eh \212#D\260\003%Edhe`\260\004%\260\001`#D-,\260\011CX\207!\300\033\260\022CX\207E\260\021+\260G#D\260Gz\344\033\003\212E\030i \260G#D\212\212\207 \260\240QX\260\021+\260G#D\260Gz\344\033!\260Gz\344YYY\030-, \212E#Eh`D-,EjB-,\001\030/-,\001\260\030CX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260\031C`\260F#D!\212\020\260F\366!\033!!!!Y-,\001\260\030CX\260\002%E\260\002%Ed`j\260\003%Eja \260\004%Ej \212\213e\260\004%#D\214\260\003%#D!!\033 EjD EjDY-,\001 E\260\000U\260\030CZXEh#Ei\260@\213a \260\200bj \212#a \260\003%\213e\260\004%#D\214\260\003%#D!!\033!!\260\031+Y-,\001\212\212Ed#EdadB-,\260\004%\260\004%\260\031+\260\030CX\260\004%\260\004%\260\003%\260\033+\001\260\002%C\260@T\260\002%C\260\000TZX\260\003% E\260@aDY\260\002%C\260\000T\260\002%C\260@TZX\260\004% E\260@`DYY!!!!-,\001KRXC\260\002%E#aD\033!!Y-,\001KRXC\260\002%E#`D\033!!Y-,KRXED\033!!Y-,\001 \260\003%#I\260@`\260 c \260\000RX#\260\002%8#\260\002%e8\000\212c8\033!!!!!Y\001-,KPXED\033!!Y-,\001\260\005%\020# \212\365\000\260\001`#\355\354-,\001\260\005%\020# \212\365\000\260\001a#\355\354-,\001\260\006%\020\365\000\355\354-,F#F`\212\212F# F\212`\212a\270\377\200b# \020#\212\261KK\212pE` \260\000PX\260\001a\270\377\272\213\033\260F\214Y\260\020`h\001:-, E\260\003%FRX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-, E\260\003%FPX\260\002%F ha\260\003%\260\003%?#!8\033!\021Y-,\000\260\007C\260\006C\013-,\212\020\354-,\260\014CX!\033 F\260\000RX\270\377\3608\033\260\0208YY-, \260\000UX\270\020\000c\260\003%Ed\260\003%Eda\260\000SX\260\002\033\260@a\260\003Y%EiSXED\033!!Y\033!\260\002%E\260\002%Ead\260(QXED\033!!YY-,!!\014d#d\213\270@\000b-,!\260\200QX\014d#d\213\270 \000b\033\262\000@/+Y\260\002`-,!\260\300QX\014d#d\213\270\025Ub\033\262\000\200/+Y\260\002`-,\014d#d\213\270@\000b`#!-,KSX\260\004%\260\004%Id#Edi\260@\213a \260\200bj\260\002%\260\002%a\214\260F#D!\212\020\260F\366!\033!\212\021#\022 9/Y-,\260\002%\260\002%Id\260\300TX\270\377\3708\260\0108\033!!Y-,\260\023CX\003\033\002Y-,\260\023CX\002\033\003Y-,\260\012+#\020 <\260\027+-,\260\002%\270\377\3608\260(+\212\020# \320#\260\020+\260\005CX\300\033<Y \020\021\260\000\022\001-,KS#KQZX8\033!!Y-,\001\260\002%\020\320#\311\001\260\001\023\260\000\024\020\260\001<\260\001\026-,\001\260\000\023\260\001\260\003%I\260\003\0278\260\001\023-,KS#KQZX E\212`D\033!!Y-, 9/-"""
|
2015-04-14 11:51:03 -07:00
|
|
|
|
2000-02-01 15:30:15 +00:00
|
|
|
p = Program()
|
2015-04-14 13:25:19 -07:00
|
|
|
p.fromBytecode(bc)
|
2015-04-14 11:51:03 -07:00
|
|
|
asm = p.getAssembly(preserve=True)
|
2008-09-16 14:14:44 +00:00
|
|
|
p.fromAssembly(asm)
|
2015-04-14 13:25:19 -07:00
|
|
|
print(bc == p.getBytecode())
|
2015-04-26 02:01:01 -04:00
|
|
|
|
1999-12-16 21:34:53 +00:00
|
|
|
|
2015-04-14 11:51:03 -07:00
|
|
|
if __name__ == "__main__":
|
2015-04-26 00:54:30 -04:00
|
|
|
import sys
|
|
|
|
import doctest
|
2022-12-13 11:26:36 +00:00
|
|
|
|
2015-04-14 11:51:03 -07:00
|
|
|
sys.exit(doctest.testmod().failed)
|