mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-08 21:13:39 +00:00
interp: replace some panics with error messages
A number of functions now return errors instead of panicking, which should help greatly when investigating interp errors. It at least shows the package responsible for it.
This commit is contained in:
committed by
Ron Evans
parent
d1ac0138e6
commit
ccb803e35d
+30
-8
@@ -95,7 +95,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
|
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
|
||||||
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
||||||
} else {
|
} else {
|
||||||
value = operand.Load()
|
var err error
|
||||||
|
value, err = operand.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if value.Type() != inst.Type() {
|
if value.Type() != inst.Type() {
|
||||||
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
||||||
@@ -308,7 +312,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
}
|
}
|
||||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||||
// into separate pointer and length parameters.
|
// into separate pointer and length parameters.
|
||||||
m.PutString(keyBuf, keyLen, valPtr)
|
err := m.PutString(keyBuf, keyLen, valPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
case callee.Name() == "runtime.hashmapBinarySet":
|
case callee.Name() == "runtime.hashmapBinarySet":
|
||||||
// set a binary (int etc.) key in the map
|
// set a binary (int etc.) key in the map
|
||||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||||
@@ -329,15 +336,24 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
fr.builder.CreateCall(callee, llvmParams, "")
|
fr.builder.CreateCall(callee, llvmParams, "")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.PutBinary(keyBuf, valPtr)
|
err := m.PutBinary(keyBuf, valPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
case callee.Name() == "runtime.stringConcat":
|
case callee.Name() == "runtime.stringConcat":
|
||||||
// adding two strings together
|
// adding two strings together
|
||||||
buf1Ptr := fr.getLocal(inst.Operand(0))
|
buf1Ptr := fr.getLocal(inst.Operand(0))
|
||||||
buf1Len := fr.getLocal(inst.Operand(1))
|
buf1Len := fr.getLocal(inst.Operand(1))
|
||||||
buf2Ptr := fr.getLocal(inst.Operand(2))
|
buf2Ptr := fr.getLocal(inst.Operand(2))
|
||||||
buf2Len := fr.getLocal(inst.Operand(3))
|
buf2Len := fr.getLocal(inst.Operand(3))
|
||||||
buf1 := getStringBytes(buf1Ptr, buf1Len.Value())
|
buf1, err := getStringBytes(buf1Ptr, buf1Len.Value())
|
||||||
buf2 := getStringBytes(buf2Ptr, buf2Len.Value())
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
buf2, err := getStringBytes(buf2Ptr, buf2Len.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
result := []byte(string(buf1) + string(buf2))
|
result := []byte(string(buf1) + string(buf2))
|
||||||
vals := make([]llvm.Value, len(result))
|
vals := make([]llvm.Value, len(result))
|
||||||
for i := range vals {
|
for i := range vals {
|
||||||
@@ -401,9 +417,12 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
|
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
|
||||||
}
|
}
|
||||||
for i := int64(0); i < length; i++ {
|
for i := int64(0); i < length; i++ {
|
||||||
var err error
|
|
||||||
// *dst = *src
|
// *dst = *src
|
||||||
dstArray.Store(srcArray.Load())
|
val, err := srcArray.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
dstArray.Store(val)
|
||||||
// dst++
|
// dst++
|
||||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -421,7 +440,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
// convert a string to a []byte
|
// convert a string to a []byte
|
||||||
bufPtr := fr.getLocal(inst.Operand(0))
|
bufPtr := fr.getLocal(inst.Operand(0))
|
||||||
bufLen := fr.getLocal(inst.Operand(1))
|
bufLen := fr.getLocal(inst.Operand(1))
|
||||||
result := getStringBytes(bufPtr, bufLen.Value())
|
result, err := getStringBytes(bufPtr, bufLen.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
vals := make([]llvm.Value, len(result))
|
vals := make([]llvm.Value, len(result))
|
||||||
for i := range vals {
|
for i := range vals {
|
||||||
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
||||||
|
|||||||
+10
-5
@@ -1,6 +1,8 @@
|
|||||||
package interp
|
package interp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,20 +20,23 @@ func getUses(value llvm.Value) []llvm.Value {
|
|||||||
|
|
||||||
// getStringBytes loads the byte slice of a Go string represented as a
|
// getStringBytes loads the byte slice of a Go string represented as a
|
||||||
// {ptr, len} pair.
|
// {ptr, len} pair.
|
||||||
func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
|
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
|
||||||
if !strLen.IsConstant() {
|
if !strLen.IsConstant() {
|
||||||
panic("getStringBytes with a non-constant length")
|
return nil, errors.New("getStringBytes with a non-constant length")
|
||||||
}
|
}
|
||||||
buf := make([]byte, strLen.ZExtValue())
|
buf := make([]byte, strLen.ZExtValue())
|
||||||
for i := range buf {
|
for i := range buf {
|
||||||
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := gep.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
c := gep.Load()
|
|
||||||
buf[i] = byte(c.ZExtValue())
|
buf[i] = byte(c.ZExtValue())
|
||||||
}
|
}
|
||||||
return buf
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
||||||
|
|||||||
+47
-24
@@ -15,7 +15,7 @@ type Value interface {
|
|||||||
Value() llvm.Value // returns a LLVM value
|
Value() llvm.Value // returns a LLVM value
|
||||||
Type() llvm.Type // equal to Value().Type()
|
Type() llvm.Type // equal to Value().Type()
|
||||||
IsConstant() bool // returns true if this value is a constant value
|
IsConstant() bool // returns true if this value is a constant value
|
||||||
Load() llvm.Value // dereference a pointer
|
Load() (llvm.Value, error) // dereference a pointer
|
||||||
Store(llvm.Value) // store to a pointer
|
Store(llvm.Value) // store to a pointer
|
||||||
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
||||||
String() string // string representation, for debugging
|
String() string // string representation, for debugging
|
||||||
@@ -44,23 +44,26 @@ func (v *LocalValue) IsConstant() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load loads a constant value if this is a constant pointer.
|
// Load loads a constant value if this is a constant pointer.
|
||||||
func (v *LocalValue) Load() llvm.Value {
|
func (v *LocalValue) Load() (llvm.Value, error) {
|
||||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||||
return v.Underlying.Initializer()
|
return v.Underlying.Initializer(), nil
|
||||||
}
|
}
|
||||||
switch v.Underlying.Opcode() {
|
switch v.Underlying.Opcode() {
|
||||||
case llvm.GetElementPtr:
|
case llvm.GetElementPtr:
|
||||||
indices := v.getConstGEPIndices()
|
indices := v.getConstGEPIndices()
|
||||||
if indices[0] != 0 {
|
if indices[0] != 0 {
|
||||||
panic("invalid GEP")
|
return llvm.Value{}, errors.New("invalid GEP")
|
||||||
}
|
}
|
||||||
global := v.Eval.getValue(v.Underlying.Operand(0))
|
global := v.Eval.getValue(v.Underlying.Operand(0))
|
||||||
agg := global.Load()
|
agg, err := global.Load()
|
||||||
return llvm.ConstExtractValue(agg, indices[1:])
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
return llvm.ConstExtractValue(agg, indices[1:]), nil
|
||||||
case llvm.BitCast:
|
case llvm.BitCast:
|
||||||
panic("interp: load from a bitcast")
|
return llvm.Value{}, errors.New("interp: load from a bitcast")
|
||||||
default:
|
default:
|
||||||
panic("interp: load from a constant")
|
return llvm.Value{}, errors.New("interp: load from a constant")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +91,10 @@ func (v *LocalValue) Store(value llvm.Value) {
|
|||||||
panic("invalid GEP")
|
panic("invalid GEP")
|
||||||
}
|
}
|
||||||
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
|
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
|
||||||
agg := global.Load()
|
agg, err := global.Load()
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO
|
||||||
|
}
|
||||||
agg = llvm.ConstInsertValue(agg, value, indices[1:])
|
agg = llvm.ConstInsertValue(agg, value, indices[1:])
|
||||||
global.Store(agg)
|
global.Store(agg)
|
||||||
return
|
return
|
||||||
@@ -225,7 +231,11 @@ func (v *MapValue) Value() llvm.Value {
|
|||||||
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
|
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
|
||||||
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
|
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
|
||||||
keyPtrVal := v.Eval.getValue(keyPtr)
|
keyPtrVal := v.Eval.getValue(keyPtr)
|
||||||
keyBuf = getStringBytes(keyPtrVal, keyLen)
|
var err error
|
||||||
|
keyBuf, err = getStringBytes(keyPtrVal, keyLen)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO
|
||||||
|
}
|
||||||
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
|
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
||||||
n := key.Value().ZExtValue()
|
n := key.Value().ZExtValue()
|
||||||
@@ -299,7 +309,7 @@ func (v *MapValue) IsConstant() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load panics: maps are of reference type so cannot be dereferenced.
|
// Load panics: maps are of reference type so cannot be dereferenced.
|
||||||
func (v *MapValue) Load() llvm.Value {
|
func (v *MapValue) Load() (llvm.Value, error) {
|
||||||
panic("interp: load from a map")
|
panic("interp: load from a map")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,23 +326,26 @@ func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
|
|||||||
|
|
||||||
// PutString does a map assign operation, assuming that the map is of type
|
// PutString does a map assign operation, assuming that the map is of type
|
||||||
// map[string]T.
|
// map[string]T.
|
||||||
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
|
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) error {
|
||||||
if !v.Underlying.IsNil() {
|
if !v.Underlying.IsNil() {
|
||||||
panic("map already created")
|
return errors.New("map already created")
|
||||||
}
|
}
|
||||||
|
|
||||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
value := valPtr.Load()
|
value, err := valPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.ValueType.IsNil() {
|
if v.ValueType.IsNil() {
|
||||||
v.ValueType = value.Type()
|
v.ValueType = value.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||||
panic("interp: map store value type has the wrong size")
|
return errors.New("interp: map store value type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if value.Type() != v.ValueType {
|
if value.Type() != v.ValueType {
|
||||||
panic("interp: map store value type is inconsistent")
|
return errors.New("interp: map store value type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,26 +358,31 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
|
|||||||
// TODO: avoid duplicate keys
|
// TODO: avoid duplicate keys
|
||||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutBinary does a map assign operation.
|
// PutBinary does a map assign operation.
|
||||||
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) error {
|
||||||
if !v.Underlying.IsNil() {
|
if !v.Underlying.IsNil() {
|
||||||
panic("map already created")
|
return errors.New("map already created")
|
||||||
}
|
}
|
||||||
|
|
||||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
value := valPtr.Load()
|
value, err := valPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.ValueType.IsNil() {
|
if v.ValueType.IsNil() {
|
||||||
v.ValueType = value.Type()
|
v.ValueType = value.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||||
panic("interp: map store value type has the wrong size")
|
return errors.New("interp: map store value type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if value.Type() != v.ValueType {
|
if value.Type() != v.ValueType {
|
||||||
panic("interp: map store value type is inconsistent")
|
return errors.New("interp: map store value type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,21 +393,26 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
|||||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key := keyPtr.Load()
|
key, err := keyPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.KeyType.IsNil() {
|
if v.KeyType.IsNil() {
|
||||||
v.KeyType = key.Type()
|
v.KeyType = key.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
|
||||||
panic("interp: map store key type has the wrong size")
|
return errors.New("interp: map store key type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if key.Type() != v.KeyType {
|
if key.Type() != v.KeyType {
|
||||||
panic("interp: map store key type is inconsistent")
|
return errors.New("interp: map store key type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: avoid duplicate keys
|
// TODO: avoid duplicate keys
|
||||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get FNV-1a hash of this string.
|
// Get FNV-1a hash of this string.
|
||||||
|
|||||||
Reference in New Issue
Block a user