userland: add archive tools and pinned GoAWK, esbuild, and QuickJS
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
// Disassembles compiled program to text assembly instructions
|
||||
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/benhoyt/goawk/internal/ast"
|
||||
"github.com/benhoyt/goawk/internal/resolver"
|
||||
"github.com/benhoyt/goawk/lexer"
|
||||
)
|
||||
|
||||
// Disassemble writes a human-readable form of the program's virtual machine
|
||||
// instructions to writer.
|
||||
func (p *Program) Disassemble(writer io.Writer) error {
|
||||
if p.Begin != nil {
|
||||
d := &disassembler{
|
||||
program: p,
|
||||
writer: writer,
|
||||
code: p.Begin,
|
||||
nativeFuncNames: p.nativeFuncNames,
|
||||
}
|
||||
err := d.disassemble("BEGIN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, action := range p.Actions {
|
||||
switch len(action.Pattern) {
|
||||
case 0:
|
||||
// Nothing to do here.
|
||||
case 1:
|
||||
d := &disassembler{
|
||||
program: p,
|
||||
writer: writer,
|
||||
code: action.Pattern[0],
|
||||
nativeFuncNames: p.nativeFuncNames,
|
||||
}
|
||||
err := d.disassemble("pattern")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case 2:
|
||||
d := &disassembler{
|
||||
program: p,
|
||||
writer: writer,
|
||||
code: action.Pattern[0],
|
||||
nativeFuncNames: p.nativeFuncNames,
|
||||
}
|
||||
err := d.disassemble("start")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d = &disassembler{
|
||||
program: p,
|
||||
writer: writer,
|
||||
code: action.Pattern[1],
|
||||
nativeFuncNames: p.nativeFuncNames,
|
||||
}
|
||||
err = d.disassemble("stop")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(action.Body) > 0 {
|
||||
d := &disassembler{
|
||||
program: p,
|
||||
writer: writer,
|
||||
code: action.Body,
|
||||
nativeFuncNames: p.nativeFuncNames,
|
||||
}
|
||||
err := d.disassemble("{ body }")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.End != nil {
|
||||
d := &disassembler{
|
||||
program: p,
|
||||
writer: writer,
|
||||
code: p.End,
|
||||
nativeFuncNames: p.nativeFuncNames,
|
||||
}
|
||||
err := d.disassemble("END")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for i, f := range p.Functions {
|
||||
d := &disassembler{
|
||||
program: p,
|
||||
writer: writer,
|
||||
code: f.Body,
|
||||
nativeFuncNames: p.nativeFuncNames,
|
||||
funcIndex: i,
|
||||
}
|
||||
err := d.disassemble("function " + f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disassembles a single block of opcodes.
|
||||
type disassembler struct {
|
||||
program *Program
|
||||
writer io.Writer
|
||||
code []Opcode
|
||||
nativeFuncNames []string
|
||||
funcIndex int
|
||||
ip int
|
||||
opAddr int
|
||||
err error
|
||||
}
|
||||
|
||||
func (d *disassembler) disassemble(prefix string) error {
|
||||
if prefix != "" {
|
||||
d.writef(" // %s\n", prefix)
|
||||
}
|
||||
|
||||
for d.ip < len(d.code) && d.err == nil {
|
||||
d.opAddr = d.ip
|
||||
op := d.fetch()
|
||||
|
||||
switch op {
|
||||
case Num:
|
||||
index := d.fetch()
|
||||
num := d.program.Nums[index]
|
||||
if num == float64(int64(num)) {
|
||||
d.writeOpf("Num %d (%d)", int64(num), index)
|
||||
} else {
|
||||
d.writeOpf("Num %.6g (%d)", num, index)
|
||||
}
|
||||
|
||||
case Str:
|
||||
index := d.fetch()
|
||||
d.writeOpf("Str %q (%d)", d.program.Strs[index], index)
|
||||
|
||||
case FieldInt:
|
||||
index := d.fetch()
|
||||
d.writeOpf("FieldInt %d", index)
|
||||
|
||||
case FieldByNameStr:
|
||||
index := d.fetch()
|
||||
d.writeOpf("FieldByNameStr %q (%d)", d.program.Strs[index], index)
|
||||
|
||||
case Global:
|
||||
index := d.fetch()
|
||||
d.writeOpf("Global %s", d.program.scalarNames[index])
|
||||
|
||||
case Local:
|
||||
index := int(d.fetch())
|
||||
d.writeOpf("Local %s", d.localName(index))
|
||||
|
||||
case Special:
|
||||
index := d.fetch()
|
||||
d.writeOpf("Special %s", ast.SpecialVarName(int(index)))
|
||||
|
||||
case ArrayGlobal:
|
||||
arrayIndex := d.fetch()
|
||||
d.writeOpf("ArrayGlobal %s", d.program.arrayNames[arrayIndex])
|
||||
|
||||
case ArrayLocal:
|
||||
arrayIndex := d.fetch()
|
||||
d.writeOpf("ArrayLocal %s", d.localArrayName(int(arrayIndex)))
|
||||
|
||||
case InGlobal:
|
||||
arrayIndex := d.fetch()
|
||||
d.writeOpf("InGlobal %s", d.program.arrayNames[arrayIndex])
|
||||
|
||||
case InLocal:
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("InLocal %s", d.localArrayName(arrayIndex))
|
||||
|
||||
case AssignGlobal:
|
||||
index := d.fetch()
|
||||
d.writeOpf("AssignGlobal %s", d.program.scalarNames[index])
|
||||
|
||||
case AssignLocal:
|
||||
index := int(d.fetch())
|
||||
d.writeOpf("AssignLocal %s", d.localName(index))
|
||||
|
||||
case AssignSpecial:
|
||||
index := d.fetch()
|
||||
d.writeOpf("AssignSpecial %s", ast.SpecialVarName(int(index)))
|
||||
|
||||
case AssignArrayGlobal:
|
||||
arrayIndex := d.fetch()
|
||||
d.writeOpf("AssignArrayGlobal %s", d.program.arrayNames[arrayIndex])
|
||||
|
||||
case AssignArrayLocal:
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("AssignArrayLocal %s", d.localArrayName(arrayIndex))
|
||||
|
||||
case Delete:
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("Delete %s", d.arrayName(arrayScope, arrayIndex))
|
||||
|
||||
case DeleteAll:
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("DeleteAll %s", d.arrayName(arrayScope, arrayIndex))
|
||||
|
||||
case IncrField:
|
||||
amount := d.fetch()
|
||||
d.writeOpf("IncrField %d", amount)
|
||||
|
||||
case IncrGlobal:
|
||||
amount := d.fetch()
|
||||
index := d.fetch()
|
||||
d.writeOpf("IncrGlobal %d %s", amount, d.program.scalarNames[index])
|
||||
|
||||
case IncrLocal:
|
||||
amount := d.fetch()
|
||||
index := int(d.fetch())
|
||||
d.writeOpf("IncrLocal %d %s", amount, d.localName(index))
|
||||
|
||||
case IncrSpecial:
|
||||
amount := d.fetch()
|
||||
index := d.fetch()
|
||||
d.writeOpf("IncrSpecial %d %s", amount, ast.SpecialVarName(int(index)))
|
||||
|
||||
case IncrArrayGlobal:
|
||||
amount := d.fetch()
|
||||
arrayIndex := d.fetch()
|
||||
d.writeOpf("IncrArrayGlobal %d %s", amount, d.program.arrayNames[arrayIndex])
|
||||
|
||||
case IncrArrayLocal:
|
||||
amount := d.fetch()
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("IncrArrayLocal %d %s", amount, d.localArrayName(arrayIndex))
|
||||
|
||||
case AugAssignField:
|
||||
operation := AugOp(d.fetch())
|
||||
d.writeOpf("AugAssignField %s", operation)
|
||||
|
||||
case AugAssignGlobal:
|
||||
operation := AugOp(d.fetch())
|
||||
index := d.fetch()
|
||||
d.writeOpf("AugAssignGlobal %s %s", operation, d.program.scalarNames[index])
|
||||
|
||||
case AugAssignLocal:
|
||||
operation := AugOp(d.fetch())
|
||||
index := int(d.fetch())
|
||||
d.writeOpf("AugAssignLocal %s %s", operation, d.localName(index))
|
||||
|
||||
case AugAssignSpecial:
|
||||
operation := AugOp(d.fetch())
|
||||
index := d.fetch()
|
||||
d.writeOpf("AugAssignSpecial %s %d", operation, ast.SpecialVarName(int(index)))
|
||||
|
||||
case AugAssignArrayGlobal:
|
||||
operation := AugOp(d.fetch())
|
||||
arrayIndex := d.fetch()
|
||||
d.writeOpf("AugAssignArrayGlobal %s %s", operation, d.program.arrayNames[arrayIndex])
|
||||
|
||||
case AugAssignArrayLocal:
|
||||
operation := AugOp(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("AugAssignArrayLocal %s %s", operation, d.localArrayName(arrayIndex))
|
||||
|
||||
case Regex:
|
||||
regexIndex := d.fetch()
|
||||
d.writeOpf("Regex %q (%d)", d.program.Regexes[regexIndex], regexIndex)
|
||||
|
||||
case IndexMulti:
|
||||
num := d.fetch()
|
||||
d.writeOpf("IndexMulti %d", num)
|
||||
|
||||
case ConcatMulti:
|
||||
num := d.fetch()
|
||||
d.writeOpf("ConcatMulti %d", num)
|
||||
|
||||
case Jump:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("Jump 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpFalse:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpFalse 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpTrue:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpTrue 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpEquals:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpEquals 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpNotEquals:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpNotEquals 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpLess:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpLess 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpGreater:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpGreater 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpLessOrEqual:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpLessOrEqual 0x%04x", d.ip+int(offset))
|
||||
|
||||
case JumpGreaterOrEqual:
|
||||
offset := d.fetch()
|
||||
d.writeOpf("JumpGreaterOrEqual 0x%04x", d.ip+int(offset))
|
||||
|
||||
case ForIn:
|
||||
varScope := resolver.Scope(d.fetch())
|
||||
varIndex := int(d.fetch())
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
offset := d.fetch()
|
||||
d.writeOpf("ForIn %s %s 0x%04x", d.varName(varScope, varIndex), d.arrayName(arrayScope, arrayIndex), d.ip+int(offset))
|
||||
|
||||
case CallBuiltin:
|
||||
builtinOp := BuiltinOp(d.fetch())
|
||||
d.writeOpf("CallBuiltin %s", builtinOp)
|
||||
|
||||
case CallLengthArray:
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("CallLengthArray %s", d.arrayName(arrayScope, arrayIndex))
|
||||
|
||||
case CallSplit:
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("CallSplit %s", d.arrayName(arrayScope, arrayIndex))
|
||||
|
||||
case CallSplitSep:
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("CallSplitSep %s", d.arrayName(arrayScope, arrayIndex))
|
||||
|
||||
case CallSprintf:
|
||||
numArgs := d.fetch()
|
||||
d.writeOpf("CallSprintf %d", numArgs)
|
||||
|
||||
case CallUser:
|
||||
funcIndex := d.fetch()
|
||||
numArrayArgs := int(d.fetch())
|
||||
var arrayArgs []string
|
||||
for i := 0; i < numArrayArgs; i++ {
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
arrayArgs = append(arrayArgs, d.arrayName(arrayScope, arrayIndex))
|
||||
}
|
||||
d.writeOpf("CallUser %s [%s]", d.program.Functions[funcIndex].Name, strings.Join(arrayArgs, ", "))
|
||||
|
||||
case CallNative:
|
||||
funcIndex := d.fetch()
|
||||
numArgs := d.fetch()
|
||||
d.writeOpf("CallNative %s %d", d.nativeFuncNames[funcIndex], numArgs)
|
||||
|
||||
case Nulls:
|
||||
numNulls := d.fetch()
|
||||
d.writeOpf("Nulls %d", numNulls)
|
||||
|
||||
case Print:
|
||||
numArgs := d.fetch()
|
||||
redirect := lexer.Token(d.fetch())
|
||||
if redirect == lexer.ILLEGAL {
|
||||
d.writeOpf("Print %d", numArgs)
|
||||
} else {
|
||||
d.writeOpf("Print %d %s", numArgs, redirect)
|
||||
}
|
||||
|
||||
case Printf:
|
||||
numArgs := d.fetch()
|
||||
redirect := lexer.Token(d.fetch())
|
||||
if redirect == lexer.ILLEGAL {
|
||||
d.writeOpf("Printf %d", numArgs)
|
||||
} else {
|
||||
d.writeOpf("Printf %d %s", numArgs, redirect)
|
||||
}
|
||||
|
||||
case Getline:
|
||||
redirect := lexer.Token(d.fetch())
|
||||
d.writeOpf("Getline %s", redirect)
|
||||
|
||||
case GetlineField:
|
||||
redirect := lexer.Token(d.fetch())
|
||||
d.writeOpf("GetlineField %s", redirect)
|
||||
|
||||
case GetlineGlobal:
|
||||
redirect := lexer.Token(d.fetch())
|
||||
index := d.fetch()
|
||||
d.writeOpf("GetlineGlobal %s %s", redirect, d.program.scalarNames[index])
|
||||
|
||||
case GetlineLocal:
|
||||
redirect := lexer.Token(d.fetch())
|
||||
index := int(d.fetch())
|
||||
d.writeOpf("GetlineLocal %s %s", redirect, d.localName(index))
|
||||
|
||||
case GetlineSpecial:
|
||||
redirect := lexer.Token(d.fetch())
|
||||
index := d.fetch()
|
||||
d.writeOpf("GetlineSpecial %s %s", redirect, ast.SpecialVarName(int(index)))
|
||||
|
||||
case GetlineArray:
|
||||
redirect := lexer.Token(d.fetch())
|
||||
arrayScope := resolver.Scope(d.fetch())
|
||||
arrayIndex := int(d.fetch())
|
||||
d.writeOpf("GetlineArray %s %s", redirect, d.arrayName(arrayScope, arrayIndex))
|
||||
|
||||
default:
|
||||
// Handles all other opcodes with no arguments
|
||||
d.writeOpf("%s", op)
|
||||
}
|
||||
}
|
||||
|
||||
d.writef("\n")
|
||||
return d.err
|
||||
}
|
||||
|
||||
// Fetch the next opcode and increment the "instruction pointer".
|
||||
func (d *disassembler) fetch() Opcode {
|
||||
op := d.code[d.ip]
|
||||
d.ip++
|
||||
return op
|
||||
}
|
||||
|
||||
// Write formatted string to the disassembly output.
|
||||
func (d *disassembler) writef(format string, args ...any) {
|
||||
if d.err != nil {
|
||||
return
|
||||
}
|
||||
_, d.err = fmt.Fprintf(d.writer, format, args...)
|
||||
}
|
||||
|
||||
// Write formatted opcode (with address and newline) to disassembly output.
|
||||
func (d *disassembler) writeOpf(format string, args ...any) {
|
||||
if d.err != nil {
|
||||
return
|
||||
}
|
||||
addrStr := fmt.Sprintf("%04x", d.opAddr)
|
||||
_, d.err = fmt.Fprintf(d.writer, addrStr+" "+format+"\n", args...)
|
||||
}
|
||||
|
||||
// Return the scalar variable name described by scope and index.
|
||||
func (d *disassembler) varName(scope resolver.Scope, index int) string {
|
||||
switch scope {
|
||||
case resolver.Global:
|
||||
return d.program.scalarNames[index]
|
||||
case resolver.Local:
|
||||
return d.localName(index)
|
||||
default: // resolver.Special
|
||||
return ast.SpecialVarName(index)
|
||||
}
|
||||
}
|
||||
|
||||
// Return the local variable name with the given index.
|
||||
func (d *disassembler) localName(index int) string {
|
||||
f := d.program.Functions[d.funcIndex]
|
||||
n := 0
|
||||
for i, p := range f.Params {
|
||||
if f.Arrays[i] {
|
||||
continue
|
||||
}
|
||||
if n == index {
|
||||
return p
|
||||
}
|
||||
n++
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected local variable index %d", index))
|
||||
}
|
||||
|
||||
// Return the array variable name describes by scope and index.
|
||||
func (d *disassembler) arrayName(scope resolver.Scope, index int) string {
|
||||
if scope == resolver.Local {
|
||||
return d.localArrayName(index)
|
||||
}
|
||||
return d.program.arrayNames[index]
|
||||
}
|
||||
|
||||
// Return the local array name with the given index.
|
||||
func (d *disassembler) localArrayName(index int) string {
|
||||
f := d.program.Functions[d.funcIndex]
|
||||
n := 0
|
||||
for i, p := range f.Params {
|
||||
if !f.Arrays[i] {
|
||||
continue
|
||||
}
|
||||
if n == index {
|
||||
return p
|
||||
}
|
||||
n++
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected local array index %d", index))
|
||||
}
|
||||
Reference in New Issue
Block a user