2015-03-28 01:12:07 +00:00
package line
import (
"bytes"
"encoding/binary"
2017-01-09 23:21:54 +00:00
"errors"
2015-03-28 01:12:07 +00:00
"fmt"
2018-07-30 09:18:41 +00:00
"io"
2015-03-28 01:12:07 +00:00
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/dwarf/util"
2015-03-28 01:12:07 +00:00
)
type Location struct {
File string
Line int
Address uint64
Delta int
}
type StateMachine struct {
2018-03-25 08:04:32 +00:00
dbl * DebugLineInfo
file string
line int
address uint64
column uint
isStmt bool
2019-08-27 21:27:15 +00:00
isa uint64 // instruction set architecture register (DWARFv4)
2018-03-25 08:04:32 +00:00
basicBlock bool
endSeq bool
lastDelta int
prologueEnd bool
epilogueBegin bool
2017-02-07 21:07:18 +00:00
// valid is true if the current value of the state machine is the address of
// an instruction (using the terminology used by DWARF spec the current
// value of the state machine should be appended to the matrix representing
// the compilation unit)
valid bool
2017-08-18 17:49:29 +00:00
started bool
buf * bytes . Buffer // remaining instructions
opcodes [ ] opcodefn
2017-08-19 11:06:20 +00:00
definedFiles [ ] * FileEntry // files defined with DW_LINE_define_file
2017-08-18 17:49:29 +00:00
lastAddress uint64
lastFile string
lastLine int
2020-03-10 16:34:40 +00:00
ptrSize int
2015-03-28 01:12:07 +00:00
}
2018-03-25 08:04:32 +00:00
type opcodeKind uint8
const (
specialOpcode opcodeKind = iota
standardOpcode
extendedOpcode
)
2015-03-28 01:12:07 +00:00
type opcodefn func ( * StateMachine , * bytes . Buffer )
// Special opcodes
const (
DW_LNS_copy = 1
DW_LNS_advance_pc = 2
DW_LNS_advance_line = 3
DW_LNS_set_file = 4
DW_LNS_set_column = 5
DW_LNS_negate_stmt = 6
DW_LNS_set_basic_block = 7
DW_LNS_const_add_pc = 8
DW_LNS_fixed_advance_pc = 9
2018-03-25 08:04:32 +00:00
DW_LNS_prologue_end = 10
DW_LNS_epilogue_begin = 11
2019-08-27 21:27:15 +00:00
DW_LNS_set_isa = 12
2015-03-28 01:12:07 +00:00
)
// Extended opcodes
const (
2020-04-09 20:57:44 +00:00
DW_LINE_end_sequence = 1
DW_LINE_set_address = 2
DW_LINE_define_file = 3
DW_LINE_set_discriminator = 4
2015-03-28 01:12:07 +00:00
)
var standardopcodes = map [ byte ] opcodefn {
DW_LNS_copy : copyfn ,
DW_LNS_advance_pc : advancepc ,
DW_LNS_advance_line : advanceline ,
DW_LNS_set_file : setfile ,
DW_LNS_set_column : setcolumn ,
DW_LNS_negate_stmt : negatestmt ,
DW_LNS_set_basic_block : setbasicblock ,
DW_LNS_const_add_pc : constaddpc ,
DW_LNS_fixed_advance_pc : fixedadvancepc ,
2018-03-25 08:04:32 +00:00
DW_LNS_prologue_end : prologueend ,
DW_LNS_epilogue_begin : epiloguebegin ,
2019-08-27 21:27:15 +00:00
DW_LNS_set_isa : setisa ,
2015-03-28 01:12:07 +00:00
}
var extendedopcodes = map [ byte ] opcodefn {
2020-04-09 20:57:44 +00:00
DW_LINE_end_sequence : endsequence ,
DW_LINE_set_address : setaddress ,
DW_LINE_define_file : definefile ,
DW_LINE_set_discriminator : setdiscriminator ,
2015-03-28 01:12:07 +00:00
}
2020-03-10 16:34:40 +00:00
func newStateMachine ( dbl * DebugLineInfo , instructions [ ] byte , ptrSize int ) * StateMachine {
2017-08-18 17:49:29 +00:00
opcodes := make ( [ ] opcodefn , len ( standardopcodes ) + 1 )
opcodes [ 0 ] = execExtendedOpcode
for op := range standardopcodes {
opcodes [ op ] = standardopcodes [ op ]
}
2020-03-10 16:34:40 +00:00
sm := & StateMachine {
dbl : dbl ,
file : dbl . FileNames [ 0 ] . Path ,
line : 1 ,
buf : bytes . NewBuffer ( instructions ) ,
opcodes : opcodes ,
isStmt : dbl . Prologue . InitialIsStmt == uint8 ( 1 ) ,
address : dbl . staticBase ,
lastAddress : ^ uint64 ( 0 ) ,
ptrSize : ptrSize ,
}
2018-03-25 08:04:32 +00:00
return sm
2015-03-28 01:12:07 +00:00
}
2019-06-28 02:39:15 +00:00
// AllPCsForFileLines Adds all PCs for a given file and set (domain of map) of lines
// to the map value corresponding to each line.
func ( lineInfo * DebugLineInfo ) AllPCsForFileLines ( f string , m map [ int ] [ ] uint64 ) {
if lineInfo == nil {
return
}
var (
lastAddr uint64
2020-03-10 16:34:40 +00:00
sm = newStateMachine ( lineInfo , lineInfo . Instructions , lineInfo . ptrSize )
2019-06-28 02:39:15 +00:00
)
for {
if err := sm . next ( ) ; err != nil {
if lineInfo . Logf != nil {
lineInfo . Logf ( "AllPCsForFileLine error: %v" , err )
}
break
}
if sm . address != lastAddr && sm . isStmt && sm . valid && sm . file == f {
if pcs , ok := m [ sm . line ] ; ok {
pcs = append ( pcs , sm . address )
m [ sm . line ] = pcs
lastAddr = sm . address
}
}
}
return
}
2017-01-09 23:21:54 +00:00
var NoSourceError = errors . New ( "no source available" )
2018-03-25 08:04:32 +00:00
// AllPCsBetween returns all PC addresses between begin and end (including both begin and end) that have the is_stmt flag set and do not belong to excludeFile:excludeLine
func ( lineInfo * DebugLineInfo ) AllPCsBetween ( begin , end uint64 , excludeFile string , excludeLine int ) ( [ ] uint64 , error ) {
2017-01-09 23:21:54 +00:00
if lineInfo == nil {
2017-08-22 14:40:01 +00:00
return nil , NoSourceError
2017-01-09 23:21:54 +00:00
}
2017-09-01 13:30:45 +00:00
2015-04-20 18:03:22 +00:00
var (
2015-10-22 17:07:24 +00:00
pcs [ ] uint64
lastaddr uint64
2020-03-10 16:34:40 +00:00
sm = newStateMachine ( lineInfo , lineInfo . Instructions , lineInfo . ptrSize )
2015-04-20 18:03:22 +00:00
)
2017-08-18 17:49:29 +00:00
for {
if err := sm . next ( ) ; err != nil {
2018-06-14 18:12:11 +00:00
if lineInfo . Logf != nil {
lineInfo . Logf ( "AllPCsBetween error: %v" , err )
2018-03-25 08:04:32 +00:00
}
2017-08-18 17:49:29 +00:00
break
}
2017-02-07 21:07:18 +00:00
if ! sm . valid {
continue
}
2019-10-07 16:54:32 +00:00
if ( sm . address > end ) && ( end >= sm . lastAddress ) {
2015-04-20 18:03:22 +00:00
break
}
2019-10-07 16:54:32 +00:00
if sm . address >= begin && sm . address <= end && sm . address > lastaddr && sm . isStmt && ( ( sm . file != excludeFile ) || ( sm . line != excludeLine ) ) {
2015-10-22 17:07:24 +00:00
lastaddr = sm . address
2015-04-20 18:03:22 +00:00
pcs = append ( pcs , sm . address )
}
}
2017-01-09 23:21:54 +00:00
return pcs , nil
2015-04-20 18:03:22 +00:00
}
2017-08-18 17:49:29 +00:00
// copy returns a copy of this state machine, running the returned state
// machine will not affect sm.
func ( sm * StateMachine ) copy ( ) * StateMachine {
var r StateMachine
r = * sm
r . buf = bytes . NewBuffer ( sm . buf . Bytes ( ) )
return & r
}
2018-03-25 08:04:32 +00:00
func ( lineInfo * DebugLineInfo ) stateMachineForEntry ( basePC uint64 ) ( sm * StateMachine ) {
sm = lineInfo . stateMachineCache [ basePC ]
if sm == nil {
2020-03-10 16:34:40 +00:00
sm = newStateMachine ( lineInfo , lineInfo . Instructions , lineInfo . ptrSize )
2018-03-25 08:04:32 +00:00
sm . PCToLine ( basePC )
lineInfo . stateMachineCache [ basePC ] = sm
}
sm = sm . copy ( )
return
}
2017-09-01 13:30:45 +00:00
// PCToLine returns the filename and line number associated with pc.
// If pc isn't found inside lineInfo's table it will return the filename and
// line number associated with the closest PC address preceding pc.
2017-08-18 17:49:29 +00:00
// basePC will be used for caching, it's normally the entry point for the
// function containing pc.
func ( lineInfo * DebugLineInfo ) PCToLine ( basePC , pc uint64 ) ( string , int ) {
2017-09-01 13:30:45 +00:00
if lineInfo == nil {
return "" , 0
}
2017-08-18 17:49:29 +00:00
if basePC > pc {
panic ( fmt . Errorf ( "basePC after pc %#x %#x" , basePC , pc ) )
}
2017-09-01 13:30:45 +00:00
2019-11-01 19:41:06 +00:00
sm := lineInfo . stateMachineFor ( basePC , pc )
file , line , _ := sm . PCToLine ( pc )
return file , line
}
func ( lineInfo * DebugLineInfo ) stateMachineFor ( basePC , pc uint64 ) * StateMachine {
2017-08-18 17:49:29 +00:00
var sm * StateMachine
if basePC == 0 {
2020-03-10 16:34:40 +00:00
sm = newStateMachine ( lineInfo , lineInfo . Instructions , lineInfo . ptrSize )
2017-08-18 17:49:29 +00:00
} else {
// Try to use the last state machine that we used for this function, if
// there isn't one or it's already past pc try to clone the cached state
// machine stopped at the entry point of the function.
// As a last resort start from the start of the debug_line section.
sm = lineInfo . lastMachineCache [ basePC ]
2019-11-01 19:41:06 +00:00
if sm == nil || sm . lastAddress >= pc {
2018-03-25 08:04:32 +00:00
sm = lineInfo . stateMachineForEntry ( basePC )
2017-08-18 17:49:29 +00:00
lineInfo . lastMachineCache [ basePC ] = sm
2017-09-01 13:30:45 +00:00
}
2017-08-18 17:49:29 +00:00
}
2019-11-01 19:41:06 +00:00
return sm
2017-08-18 17:49:29 +00:00
}
func ( sm * StateMachine ) PCToLine ( pc uint64 ) ( string , int , bool ) {
if ! sm . started {
if err := sm . next ( ) ; err != nil {
2018-06-14 18:12:11 +00:00
if sm . dbl . Logf != nil {
sm . dbl . Logf ( "PCToLine error: %v" , err )
2018-03-25 08:04:32 +00:00
}
2017-08-18 17:49:29 +00:00
return "" , 0 , false
2017-09-01 13:30:45 +00:00
}
2017-08-18 17:49:29 +00:00
}
2019-10-07 16:54:32 +00:00
if sm . lastAddress > pc && sm . lastAddress != ^ uint64 ( 0 ) {
2017-08-18 17:49:29 +00:00
return "" , 0 , false
}
for {
if sm . valid {
2019-10-07 16:54:32 +00:00
if ( sm . address > pc ) && ( pc >= sm . lastAddress ) {
2017-08-18 17:49:29 +00:00
return sm . lastFile , sm . lastLine , true
}
if sm . address == pc {
return sm . file , sm . line , true
}
}
if err := sm . next ( ) ; err != nil {
2018-06-14 18:12:11 +00:00
if sm . dbl . Logf != nil {
sm . dbl . Logf ( "PCToLine error: %v" , err )
2018-03-25 08:04:32 +00:00
}
2017-08-18 17:49:29 +00:00
break
2017-09-01 13:30:45 +00:00
}
}
2017-11-28 14:42:50 +00:00
if sm . valid {
return sm . file , sm . line , true
}
2017-08-18 17:49:29 +00:00
return "" , 0 , false
2017-09-01 13:30:45 +00:00
}
// LineToPC returns the first PC address associated with filename:lineno.
func ( lineInfo * DebugLineInfo ) LineToPC ( filename string , lineno int ) uint64 {
if lineInfo == nil {
return 0
}
2020-03-10 16:34:40 +00:00
sm := newStateMachine ( lineInfo , lineInfo . Instructions , lineInfo . ptrSize )
2017-09-01 13:30:45 +00:00
2018-03-25 08:04:32 +00:00
// if no instruction marked is_stmt is found fallback to the first
// instruction assigned to the filename:line.
var fallbackPC uint64
2017-08-18 17:49:29 +00:00
for {
if err := sm . next ( ) ; err != nil {
2018-07-30 09:18:41 +00:00
if lineInfo . Logf != nil && err != io . EOF {
2018-06-14 18:12:11 +00:00
lineInfo . Logf ( "LineToPC error: %v" , err )
2018-03-25 08:04:32 +00:00
}
2017-08-18 17:49:29 +00:00
break
}
2018-07-23 15:54:50 +00:00
if sm . line == lineno && sm . file == filename && sm . valid {
if sm . isStmt {
return sm . address
} else if fallbackPC == 0 {
fallbackPC = sm . address
2017-09-01 13:30:45 +00:00
}
}
}
2018-03-25 08:04:32 +00:00
return fallbackPC
}
2019-11-01 19:41:06 +00:00
// LineToPCIn returns the first PC for filename:lineno in the interval [startPC, endPC).
// This function is used to find the instruction corresponding to
// filename:lineno for a function that has been inlined.
// basePC will be used for caching, it's normally the entry point for the
// function containing pc.
func ( lineInfo * DebugLineInfo ) LineToPCIn ( filename string , lineno int , basePC , startPC , endPC uint64 ) uint64 {
if lineInfo == nil {
return 0
}
if basePC > startPC {
panic ( fmt . Errorf ( "basePC after startPC %#x %#x" , basePC , startPC ) )
}
sm := lineInfo . stateMachineFor ( basePC , startPC )
2020-01-09 11:10:02 +00:00
var fallbackPC uint64
2019-11-01 19:41:06 +00:00
for {
if sm . valid && sm . started {
if sm . address >= endPC {
2020-01-09 11:10:02 +00:00
break
2019-11-01 19:41:06 +00:00
}
2020-01-09 11:10:02 +00:00
if sm . line == lineno && sm . file == filename && sm . address >= startPC {
if sm . isStmt {
return sm . address
} else {
fallbackPC = sm . address
}
2019-11-01 19:41:06 +00:00
}
}
if err := sm . next ( ) ; err != nil {
if lineInfo . Logf != nil && err != io . EOF {
lineInfo . Logf ( "LineToPC error: %v" , err )
}
break
}
}
2020-01-09 11:10:02 +00:00
return fallbackPC
2019-11-01 19:41:06 +00:00
}
2018-03-25 08:04:32 +00:00
// PrologueEndPC returns the first PC address marked as prologue_end in the half open interval [start, end)
func ( lineInfo * DebugLineInfo ) PrologueEndPC ( start , end uint64 ) ( pc uint64 , file string , line int , ok bool ) {
2019-10-21 17:43:03 +00:00
if lineInfo == nil {
return 0 , "" , 0 , false
}
2018-03-25 08:04:32 +00:00
sm := lineInfo . stateMachineForEntry ( start )
for {
if sm . valid {
if sm . address >= end {
return 0 , "" , 0 , false
}
if sm . prologueEnd {
return sm . address , sm . file , sm . line , true
}
}
if err := sm . next ( ) ; err != nil {
2018-06-14 18:12:11 +00:00
if lineInfo . Logf != nil {
lineInfo . Logf ( "PrologueEnd error: %v" , err )
2018-03-25 08:04:32 +00:00
}
return 0 , "" , 0 , false
}
}
2017-09-01 13:30:45 +00:00
}
2019-11-01 19:41:06 +00:00
// FirstStmtForLine looks in the half open interval [start, end) for the
// first PC address marked as stmt for the line at address 'start'.
func ( lineInfo * DebugLineInfo ) FirstStmtForLine ( start , end uint64 ) ( pc uint64 , file string , line int , ok bool ) {
first := true
sm := lineInfo . stateMachineForEntry ( start )
for {
if sm . valid {
if sm . address >= end {
return 0 , "" , 0 , false
}
if first {
first = false
file , line = sm . file , sm . line
}
if sm . isStmt && sm . file == file && sm . line == line {
return sm . address , sm . file , sm . line , true
}
}
if err := sm . next ( ) ; err != nil {
if lineInfo . Logf != nil {
2019-12-03 13:00:30 +00:00
lineInfo . Logf ( "FirstStmtForLine error: %v" , err )
2019-11-01 19:41:06 +00:00
}
return 0 , "" , 0 , false
}
}
}
2019-12-03 13:00:30 +00:00
func ( lineInfo * DebugLineInfo ) FirstFile ( ) string {
2020-03-10 16:34:40 +00:00
sm := newStateMachine ( lineInfo , lineInfo . Instructions , lineInfo . ptrSize )
2019-12-03 13:00:30 +00:00
for {
if sm . valid {
return sm . file
}
if err := sm . next ( ) ; err != nil {
if lineInfo . Logf != nil {
lineInfo . Logf ( "FirstFile error: %v" , err )
}
return ""
}
}
}
2017-08-18 17:49:29 +00:00
func ( sm * StateMachine ) next ( ) error {
sm . started = true
if sm . valid {
sm . lastAddress , sm . lastFile , sm . lastLine = sm . address , sm . file , sm . line
2018-07-30 09:18:41 +00:00
// valid is set by either a special opcode or a DW_LNS_copy, in both cases
// we need to reset basic_block, prologue_end and epilogue_begin
sm . basicBlock = false
sm . prologueEnd = false
sm . epilogueBegin = false
2017-08-18 17:49:29 +00:00
}
2017-12-04 10:03:17 +00:00
if sm . endSeq {
sm . endSeq = false
sm . file = sm . dbl . FileNames [ 0 ] . Path
sm . line = 1
sm . column = 0
2019-08-27 21:27:15 +00:00
sm . isa = 0
2018-07-30 09:18:41 +00:00
sm . isStmt = sm . dbl . Prologue . InitialIsStmt == uint8 ( 1 )
2017-12-04 10:03:17 +00:00
sm . basicBlock = false
2019-10-07 16:54:32 +00:00
sm . lastAddress = ^ uint64 ( 0 )
2017-12-04 10:03:17 +00:00
}
2017-08-18 17:49:29 +00:00
b , err := sm . buf . ReadByte ( )
if err != nil {
return err
}
2018-07-30 09:18:41 +00:00
if b < sm . dbl . Prologue . OpcodeBase {
if int ( b ) < len ( sm . opcodes ) {
sm . valid = false
sm . opcodes [ b ] ( sm , sm . buf )
2018-03-25 08:04:32 +00:00
} else {
2018-07-30 09:18:41 +00:00
// unimplemented standard opcode, read the number of arguments specified
// in the prologue and do nothing with them
opnum := sm . dbl . Prologue . StdOpLengths [ b - 1 ]
for i := 0 ; i < int ( opnum ) ; i ++ {
util . DecodeSLEB128 ( sm . buf )
}
2019-08-12 22:10:01 +00:00
fmt . Printf ( "unknown opcode %d(0x%x), %d arguments, file %s, line %d, address 0x%x\n" , b , b , opnum , sm . file , sm . line , sm . address )
2017-08-18 17:49:29 +00:00
}
} else {
2015-03-28 01:12:07 +00:00
execSpecialOpcode ( sm , b )
}
2017-08-18 17:49:29 +00:00
return nil
2015-03-28 01:12:07 +00:00
}
func execSpecialOpcode ( sm * StateMachine , instr byte ) {
var (
opcode = uint8 ( instr )
decoded = opcode - sm . dbl . Prologue . OpcodeBase
)
sm . lastDelta = int ( sm . dbl . Prologue . LineBase + int8 ( decoded % sm . dbl . Prologue . LineRange ) )
sm . line += sm . lastDelta
2017-02-07 21:07:18 +00:00
sm . address += uint64 ( decoded / sm . dbl . Prologue . LineRange ) * uint64 ( sm . dbl . Prologue . MinInstrLength )
sm . valid = true
2015-03-28 01:12:07 +00:00
}
2017-08-18 17:49:29 +00:00
func execExtendedOpcode ( sm * StateMachine , buf * bytes . Buffer ) {
2015-03-28 01:12:07 +00:00
_ , _ = util . DecodeULEB128 ( buf )
b , _ := buf . ReadByte ( )
2017-08-18 17:49:29 +00:00
if fn , ok := extendedopcodes [ b ] ; ok {
fn ( sm , buf )
2015-03-28 01:12:07 +00:00
}
}
func copyfn ( sm * StateMachine , buf * bytes . Buffer ) {
2017-02-07 21:07:18 +00:00
sm . valid = true
2015-03-28 01:12:07 +00:00
}
func advancepc ( sm * StateMachine , buf * bytes . Buffer ) {
addr , _ := util . DecodeULEB128 ( buf )
sm . address += addr * uint64 ( sm . dbl . Prologue . MinInstrLength )
}
func advanceline ( sm * StateMachine , buf * bytes . Buffer ) {
line , _ := util . DecodeSLEB128 ( buf )
sm . line += int ( line )
sm . lastDelta = int ( line )
}
func setfile ( sm * StateMachine , buf * bytes . Buffer ) {
i , _ := util . DecodeULEB128 ( buf )
2017-08-19 11:06:20 +00:00
if i - 1 < uint64 ( len ( sm . dbl . FileNames ) ) {
sm . file = sm . dbl . FileNames [ i - 1 ] . Path
} else {
j := ( i - 1 ) - uint64 ( len ( sm . dbl . FileNames ) )
if j < uint64 ( len ( sm . definedFiles ) ) {
sm . file = sm . definedFiles [ j ] . Path
} else {
sm . file = ""
}
}
2015-03-28 01:12:07 +00:00
}
func setcolumn ( sm * StateMachine , buf * bytes . Buffer ) {
c , _ := util . DecodeULEB128 ( buf )
sm . column = uint ( c )
}
func negatestmt ( sm * StateMachine , buf * bytes . Buffer ) {
sm . isStmt = ! sm . isStmt
}
func setbasicblock ( sm * StateMachine , buf * bytes . Buffer ) {
sm . basicBlock = true
}
func constaddpc ( sm * StateMachine , buf * bytes . Buffer ) {
2017-02-07 21:07:18 +00:00
sm . address += uint64 ( ( 255 - sm . dbl . Prologue . OpcodeBase ) / sm . dbl . Prologue . LineRange ) * uint64 ( sm . dbl . Prologue . MinInstrLength )
2015-03-28 01:12:07 +00:00
}
func fixedadvancepc ( sm * StateMachine , buf * bytes . Buffer ) {
var operand uint16
binary . Read ( buf , binary . LittleEndian , & operand )
sm . address += uint64 ( operand )
}
func endsequence ( sm * StateMachine , buf * bytes . Buffer ) {
sm . endSeq = true
2017-02-07 21:07:18 +00:00
sm . valid = true
2015-03-28 01:12:07 +00:00
}
func setaddress ( sm * StateMachine , buf * bytes . Buffer ) {
2020-03-10 16:34:40 +00:00
addr , err := util . ReadUintRaw ( buf , binary . LittleEndian , sm . ptrSize )
if err != nil {
panic ( err )
}
2018-05-29 15:01:51 +00:00
sm . address = addr + sm . dbl . staticBase
2015-03-28 01:12:07 +00:00
}
2020-04-09 20:57:44 +00:00
func setdiscriminator ( sm * StateMachine , buf * bytes . Buffer ) {
_ , _ = util . DecodeULEB128 ( buf )
}
2015-03-28 01:12:07 +00:00
func definefile ( sm * StateMachine , buf * bytes . Buffer ) {
2017-08-19 11:06:20 +00:00
entry := readFileEntry ( sm . dbl , sm . buf , false )
sm . definedFiles = append ( sm . definedFiles , entry )
2015-03-28 01:12:07 +00:00
}
2018-03-25 08:04:32 +00:00
func prologueend ( sm * StateMachine , buf * bytes . Buffer ) {
sm . prologueEnd = true
}
func epiloguebegin ( sm * StateMachine , buf * bytes . Buffer ) {
sm . epilogueBegin = true
}
2019-08-27 21:27:15 +00:00
func setisa ( sm * StateMachine , buf * bytes . Buffer ) {
c , _ := util . DecodeULEB128 ( buf )
sm . isa = c
}