mirror of https://github.com/tinygo-org/tinygo.git
wasmstm32webassemblymicrocontrollerarmavrspiwasiadafruitarduinocircuitplayground-expressgpioi2cllvmmicrobitnrf51nrf52nrf52840samd21tinygo
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
2.8 KiB
95 lines
2.8 KiB
package interp
|
|
|
|
import (
|
|
"os"
|
|
"sort"
|
|
"testing"
|
|
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
var scanTestTable = []struct {
|
|
name string
|
|
severity sideEffectSeverity
|
|
mentionsGlobals []string
|
|
}{
|
|
{"returnsConst", sideEffectNone, nil},
|
|
{"returnsArg", sideEffectNone, nil},
|
|
{"externalCallOnly", sideEffectNone, nil},
|
|
{"externalCallAndReturn", sideEffectLimited, nil},
|
|
{"externalCallBranch", sideEffectLimited, nil},
|
|
{"readCleanGlobal", sideEffectNone, []string{"cleanGlobalInt"}},
|
|
{"readDirtyGlobal", sideEffectLimited, []string{"dirtyGlobalInt"}},
|
|
{"callFunctionPointer", sideEffectAll, []string{"functionPointer"}},
|
|
{"getDirtyPointer", sideEffectLimited, nil},
|
|
{"storeToPointer", sideEffectLimited, nil},
|
|
{"callTypeAssert", sideEffectNone, nil},
|
|
{"callInterfaceImplements", sideEffectNone, nil},
|
|
}
|
|
|
|
func TestScan(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Read the input IR.
|
|
path := "testdata/scan.ll"
|
|
ctx := llvm.NewContext()
|
|
buf, err := llvm.NewMemoryBufferFromFile(path)
|
|
os.Stat(path) // make sure this file is tracked by `go test` caching
|
|
if err != nil {
|
|
t.Fatalf("could not read file %s: %v", path, err)
|
|
}
|
|
mod, err := ctx.ParseIR(buf)
|
|
if err != nil {
|
|
t.Fatalf("could not load module:\n%v", err)
|
|
}
|
|
|
|
// Check all to-be-tested functions.
|
|
for _, tc := range scanTestTable {
|
|
// Create an eval object, for testing.
|
|
e := &Eval{
|
|
Mod: mod,
|
|
TargetData: llvm.NewTargetData(mod.DataLayout()),
|
|
dirtyGlobals: map[llvm.Value]struct{}{},
|
|
}
|
|
|
|
// Mark some globals dirty, for testing.
|
|
e.markDirty(mod.NamedGlobal("dirtyGlobalInt"))
|
|
|
|
// Scan for side effects.
|
|
fn := mod.NamedFunction(tc.name)
|
|
if fn.IsNil() {
|
|
t.Errorf("scan test: could not find tested function %s in the IR", tc.name)
|
|
continue
|
|
}
|
|
evalPkg := &evalPackage{e, "testdata"}
|
|
result, err := evalPkg.hasSideEffects(fn)
|
|
if err != nil {
|
|
t.Errorf("scan test: failed to scan %s for side effects: %v", fn.Name(), err)
|
|
}
|
|
|
|
// Check whether the result is what we expect.
|
|
if result.severity != tc.severity {
|
|
t.Errorf("scan test: function %s should have severity %s but it has %s", tc.name, tc.severity, result.severity)
|
|
}
|
|
|
|
// Check whether the mentioned globals match with what we'd expect.
|
|
mentionsGlobalNames := make([]string, 0, len(result.mentionsGlobals))
|
|
for global := range result.mentionsGlobals {
|
|
mentionsGlobalNames = append(mentionsGlobalNames, global.Name())
|
|
}
|
|
sort.Strings(mentionsGlobalNames)
|
|
globalsMismatch := false
|
|
if len(result.mentionsGlobals) != len(tc.mentionsGlobals) {
|
|
globalsMismatch = true
|
|
} else {
|
|
for i, globalName := range mentionsGlobalNames {
|
|
if tc.mentionsGlobals[i] != globalName {
|
|
globalsMismatch = true
|
|
}
|
|
}
|
|
}
|
|
if globalsMismatch {
|
|
t.Errorf("scan test: expected %s to mention globals %v, but it mentions globals %v", tc.name, tc.mentionsGlobals, mentionsGlobalNames)
|
|
}
|
|
}
|
|
}
|
|
|