Skip to content

Commit ccb803e

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent d1ac013 commit ccb803e

File tree

3 files changed

+87
-37
lines changed

3 files changed

+87
-37
lines changed

interp/frame.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
9595
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
9696
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
9797
} else {
98-
value = operand.Load()
98+
var err error
99+
value, err = operand.Load()
100+
if err != nil {
101+
return nil, nil, fr.errorAt(inst, err)
102+
}
99103
}
100104
if value.Type() != inst.Type() {
101105
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
308312
}
309313
// "key" is a Go string value, which in the TinyGo calling convention is split up
310314
// into separate pointer and length parameters.
311-
m.PutString(keyBuf, keyLen, valPtr)
315+
err := m.PutString(keyBuf, keyLen, valPtr)
316+
if err != nil {
317+
return nil, nil, fr.errorAt(inst, err)
318+
}
312319
case callee.Name() == "runtime.hashmapBinarySet":
313320
// set a binary (int etc.) key in the map
314321
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
@@ -329,15 +336,24 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
329336
fr.builder.CreateCall(callee, llvmParams, "")
330337
continue
331338
}
332-
m.PutBinary(keyBuf, valPtr)
339+
err := m.PutBinary(keyBuf, valPtr)
340+
if err != nil {
341+
return nil, nil, fr.errorAt(inst, err)
342+
}
333343
case callee.Name() == "runtime.stringConcat":
334344
// adding two strings together
335345
buf1Ptr := fr.getLocal(inst.Operand(0))
336346
buf1Len := fr.getLocal(inst.Operand(1))
337347
buf2Ptr := fr.getLocal(inst.Operand(2))
338348
buf2Len := fr.getLocal(inst.Operand(3))
339-
buf1 := getStringBytes(buf1Ptr, buf1Len.Value())
340-
buf2 := getStringBytes(buf2Ptr, buf2Len.Value())
349+
buf1, err := getStringBytes(buf1Ptr, buf1Len.Value())
350+
if err != nil {
351+
return nil, nil, fr.errorAt(inst, err)
352+
}
353+
buf2, err := getStringBytes(buf2Ptr, buf2Len.Value())
354+
if err != nil {
355+
return nil, nil, fr.errorAt(inst, err)
356+
}
341357
result := []byte(string(buf1) + string(buf2))
342358
vals := make([]llvm.Value, len(result))
343359
for i := range vals {
@@ -401,9 +417,12 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
401417
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
402418
}
403419
for i := int64(0); i < length; i++ {
404-
var err error
405420
// *dst = *src
406-
dstArray.Store(srcArray.Load())
421+
val, err := srcArray.Load()
422+
if err != nil {
423+
return nil, nil, fr.errorAt(inst, err)
424+
}
425+
dstArray.Store(val)
407426
// dst++
408427
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
409428
if err != nil {
@@ -421,7 +440,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
421440
// convert a string to a []byte
422441
bufPtr := fr.getLocal(inst.Operand(0))
423442
bufLen := fr.getLocal(inst.Operand(1))
424-
result := getStringBytes(bufPtr, bufLen.Value())
443+
result, err := getStringBytes(bufPtr, bufLen.Value())
444+
if err != nil {
445+
return nil, nil, fr.errorAt(inst, err)
446+
}
425447
vals := make([]llvm.Value, len(result))
426448
for i := range vals {
427449
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)

interp/utils.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package interp
22

33
import (
4+
"errors"
5+
46
"tinygo.org/x/go-llvm"
57
)
68

@@ -18,20 +20,23 @@ func getUses(value llvm.Value) []llvm.Value {
1820

1921
// getStringBytes loads the byte slice of a Go string represented as a
2022
// {ptr, len} pair.
21-
func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
23+
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
2224
if !strLen.IsConstant() {
23-
panic("getStringBytes with a non-constant length")
25+
return nil, errors.New("getStringBytes with a non-constant length")
2426
}
2527
buf := make([]byte, strLen.ZExtValue())
2628
for i := range buf {
2729
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
2830
if err != nil {
29-
panic(err) // TODO
31+
return nil, err
32+
}
33+
c, err := gep.Load()
34+
if err != nil {
35+
return nil, err
3036
}
31-
c := gep.Load()
3237
buf[i] = byte(c.ZExtValue())
3338
}
34-
return buf
39+
return buf, nil
3540
}
3641

3742
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in

interp/values.go

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type Value interface {
1515
Value() llvm.Value // returns a LLVM value
1616
Type() llvm.Type // equal to Value().Type()
1717
IsConstant() bool // returns true if this value is a constant value
18-
Load() llvm.Value // dereference a pointer
18+
Load() (llvm.Value, error) // dereference a pointer
1919
Store(llvm.Value) // store to a pointer
2020
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
2121
String() string // string representation, for debugging
@@ -44,23 +44,26 @@ func (v *LocalValue) IsConstant() bool {
4444
}
4545

4646
// Load loads a constant value if this is a constant pointer.
47-
func (v *LocalValue) Load() llvm.Value {
47+
func (v *LocalValue) Load() (llvm.Value, error) {
4848
if !v.Underlying.IsAGlobalVariable().IsNil() {
49-
return v.Underlying.Initializer()
49+
return v.Underlying.Initializer(), nil
5050
}
5151
switch v.Underlying.Opcode() {
5252
case llvm.GetElementPtr:
5353
indices := v.getConstGEPIndices()
5454
if indices[0] != 0 {
55-
panic("invalid GEP")
55+
return llvm.Value{}, errors.New("invalid GEP")
5656
}
5757
global := v.Eval.getValue(v.Underlying.Operand(0))
58-
agg := global.Load()
59-
return llvm.ConstExtractValue(agg, indices[1:])
58+
agg, err := global.Load()
59+
if err != nil {
60+
return llvm.Value{}, err
61+
}
62+
return llvm.ConstExtractValue(agg, indices[1:]), nil
6063
case llvm.BitCast:
61-
panic("interp: load from a bitcast")
64+
return llvm.Value{}, errors.New("interp: load from a bitcast")
6265
default:
63-
panic("interp: load from a constant")
66+
return llvm.Value{}, errors.New("interp: load from a constant")
6467
}
6568
}
6669

@@ -88,7 +91,10 @@ func (v *LocalValue) Store(value llvm.Value) {
8891
panic("invalid GEP")
8992
}
9093
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
91-
agg := global.Load()
94+
agg, err := global.Load()
95+
if err != nil {
96+
panic(err) // TODO
97+
}
9298
agg = llvm.ConstInsertValue(agg, value, indices[1:])
9399
global.Store(agg)
94100
return
@@ -225,7 +231,11 @@ func (v *MapValue) Value() llvm.Value {
225231
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
226232
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
227233
keyPtrVal := v.Eval.getValue(keyPtr)
228-
keyBuf = getStringBytes(keyPtrVal, keyLen)
234+
var err error
235+
keyBuf, err = getStringBytes(keyPtrVal, keyLen)
236+
if err != nil {
237+
panic(err) // TODO
238+
}
229239
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
230240
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
231241
n := key.Value().ZExtValue()
@@ -299,7 +309,7 @@ func (v *MapValue) IsConstant() bool {
299309
}
300310

301311
// Load panics: maps are of reference type so cannot be dereferenced.
302-
func (v *MapValue) Load() llvm.Value {
312+
func (v *MapValue) Load() (llvm.Value, error) {
303313
panic("interp: load from a map")
304314
}
305315

@@ -316,23 +326,26 @@ func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
316326

317327
// PutString does a map assign operation, assuming that the map is of type
318328
// map[string]T.
319-
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
329+
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) error {
320330
if !v.Underlying.IsNil() {
321-
panic("map already created")
331+
return errors.New("map already created")
322332
}
323333

324334
if valPtr.Underlying.Opcode() == llvm.BitCast {
325335
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
326336
}
327-
value := valPtr.Load()
337+
value, err := valPtr.Load()
338+
if err != nil {
339+
return err
340+
}
328341
if v.ValueType.IsNil() {
329342
v.ValueType = value.Type()
330343
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
331-
panic("interp: map store value type has the wrong size")
344+
return errors.New("interp: map store value type has the wrong size")
332345
}
333346
} else {
334347
if value.Type() != v.ValueType {
335-
panic("interp: map store value type is inconsistent")
348+
return errors.New("interp: map store value type is inconsistent")
336349
}
337350
}
338351

@@ -345,26 +358,31 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
345358
// TODO: avoid duplicate keys
346359
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
347360
v.Values = append(v.Values, &LocalValue{v.Eval, value})
361+
362+
return nil
348363
}
349364

350365
// PutBinary does a map assign operation.
351-
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
366+
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) error {
352367
if !v.Underlying.IsNil() {
353-
panic("map already created")
368+
return errors.New("map already created")
354369
}
355370

356371
if valPtr.Underlying.Opcode() == llvm.BitCast {
357372
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
358373
}
359-
value := valPtr.Load()
374+
value, err := valPtr.Load()
375+
if err != nil {
376+
return err
377+
}
360378
if v.ValueType.IsNil() {
361379
v.ValueType = value.Type()
362380
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
363-
panic("interp: map store value type has the wrong size")
381+
return errors.New("interp: map store value type has the wrong size")
364382
}
365383
} else {
366384
if value.Type() != v.ValueType {
367-
panic("interp: map store value type is inconsistent")
385+
return errors.New("interp: map store value type is inconsistent")
368386
}
369387
}
370388

@@ -375,21 +393,26 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
375393
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
376394
}
377395
}
378-
key := keyPtr.Load()
396+
key, err := keyPtr.Load()
397+
if err != nil {
398+
return err
399+
}
379400
if v.KeyType.IsNil() {
380401
v.KeyType = key.Type()
381402
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
382-
panic("interp: map store key type has the wrong size")
403+
return errors.New("interp: map store key type has the wrong size")
383404
}
384405
} else {
385406
if key.Type() != v.KeyType {
386-
panic("interp: map store key type is inconsistent")
407+
return errors.New("interp: map store key type is inconsistent")
387408
}
388409
}
389410

390411
// TODO: avoid duplicate keys
391412
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
392413
v.Values = append(v.Values, &LocalValue{v.Eval, value})
414+
415+
return nil
393416
}
394417

395418
// Get FNV-1a hash of this string.

0 commit comments

Comments
 (0)