main.go raw

   1  package main
   2  
   3  import (
   4  	"fmt"
   5  	"os"
   6  	"path/filepath"
   7  	"runtime"
   8  	"unsafe"
   9  
  10  	"github.com/ebitengine/purego"
  11  )
  12  
  13  func main() {
  14  	if len(os.Args) < 2 {
  15  		fmt.Fprintf(os.Stderr, "usage: %s <path-to-typecheck.so>\n", os.Args[0])
  16  		os.Exit(1)
  17  	}
  18  	soPath := os.Args[1]
  19  	if !filepath.IsAbs(soPath) {
  20  		wd, _ := os.Getwd()
  21  		soPath = filepath.Join(wd, soPath)
  22  	}
  23  
  24  	lib, err := purego.Dlopen(soPath, purego.RTLD_NOW|purego.RTLD_GLOBAL)
  25  	if err != nil {
  26  		fmt.Fprintf(os.Stderr, "dlopen %s: %v\n", soPath, err)
  27  		os.Exit(1)
  28  	}
  29  
  30  	var universeOk func() int32
  31  	purego.RegisterLibFunc(&universeOk, lib, "moxie_tc_universe_ok")
  32  
  33  	var parseOnly func(uintptr, int32, uintptr, int32) int32
  34  	purego.RegisterLibFunc(&parseOnly, lib, "moxie_tc_parse_only")
  35  
  36  	var pass1Insert func(uintptr, int32, uintptr, int32) int32
  37  	purego.RegisterLibFunc(&pass1Insert, lib, "moxie_tc_pass1_insert")
  38  
  39  	var checkBytes func(uintptr, int32, uintptr, int32) int32
  40  	purego.RegisterLibFunc(&checkBytes, lib, "moxie_tc_check_bytes")
  41  
  42  	var scopeNameCount func(int32) int32
  43  	purego.RegisterLibFunc(&scopeNameCount, lib, "moxie_tc_scope_name_count")
  44  
  45  	var scopeTypeOf func(int32, uintptr, int32, uintptr, int32) int32
  46  	purego.RegisterLibFunc(&scopeTypeOf, lib, "moxie_tc_scope_type_of")
  47  
  48  	var pkgName func(int32, uintptr, int32) int32
  49  	purego.RegisterLibFunc(&pkgName, lib, "moxie_tc_pkg_name")
  50  
  51  	var pkgPath func(int32, uintptr, int32) int32
  52  	purego.RegisterLibFunc(&pkgPath, lib, "moxie_tc_pkg_path")
  53  
  54  	var tcFree func(int32)
  55  	purego.RegisterLibFunc(&tcFree, lib, "moxie_tc_free")
  56  
  57  	var scopeName func(int32, int32, uintptr, int32) int32
  58  	purego.RegisterLibFunc(&scopeName, lib, "moxie_tc_scope_name")
  59  
  60  	readStr := func(fn func(int32, uintptr, int32) int32, h int32) string {
  61  		buf := make([]byte, 256)
  62  		n := fn(h, uintptr(unsafe.Pointer(&buf[0])), 256)
  63  		return string(buf[:n])
  64  	}
  65  
  66  	pass := 0
  67  	fail := 0
  68  	assert := func(name string, cond bool) {
  69  		if cond {
  70  			pass++
  71  		} else {
  72  			fail++
  73  			fmt.Fprintf(os.Stderr, "FAIL: %s\n", name)
  74  		}
  75  	}
  76  
  77  	src1 := []byte(`package mypkg
  78  
  79  var x int32
  80  
  81  func add(a, b int32) int32 {
  82  	return a + b
  83  }
  84  
  85  type Point struct {
  86  	X int32
  87  	Y int32
  88  }
  89  `)
  90  	name1 := []byte("mypkg")
  91  
  92  	uLen := universeOk()
  93  	fmt.Printf("universe: %d names\n", uLen)
  94  	assert("universe initialized", uLen > 0)
  95  
  96  	h := checkBytes(
  97  		uintptr(unsafe.Pointer(&src1[0])), int32(len(src1)),
  98  		uintptr(unsafe.Pointer(&name1[0])), int32(len(name1)),
  99  	)
 100  	fmt.Printf("checkBytes: handle = %d\n", h)
 101  	assert("check returns valid handle", h >= 0)
 102  
 103  	if h < 0 {
 104  		fmt.Printf("%d/%d tests passed\n", pass, pass+fail)
 105  		os.Exit(1)
 106  	}
 107  
 108  	pName := readStr(pkgName, h)
 109  	assert("pkg name is mypkg, got "+pName, pName == "mypkg")
 110  
 111  	pPath := readStr(pkgPath, h)
 112  	assert("pkg path is mypkg, got "+pPath, pPath == "mypkg")
 113  
 114  	nameCount := scopeNameCount(h)
 115  	assert(fmt.Sprintf("scope has names (count=%d)", nameCount), nameCount == 3)
 116  
 117  	// Verify Names() iteration returns all 3 unique keys
 118  	expectedNames := map[string]bool{"x": false, "add": false, "Point": false}
 119  	for i := int32(0); i < nameCount; i++ {
 120  		nbuf := make([]byte, 256)
 121  		nn := scopeName(h, i, uintptr(unsafe.Pointer(&nbuf[0])), 256)
 122  		nm := string(nbuf[:nn])
 123  		if _, ok := expectedNames[nm]; ok {
 124  			expectedNames[nm] = true
 125  		} else {
 126  			fmt.Fprintf(os.Stderr, "UNEXPECTED name[%d] = %q\n", i, nm)
 127  		}
 128  	}
 129  	for nm, found := range expectedNames {
 130  		assert(fmt.Sprintf("name %q found via Names() iteration", nm), found)
 131  	}
 132  
 133  	xName := []byte("x")
 134  	buf := make([]byte, 256)
 135  	n := scopeTypeOf(h, uintptr(unsafe.Pointer(&xName[0])), int32(len(xName)),
 136  		uintptr(unsafe.Pointer(&buf[0])), 256)
 137  	xType := string(buf[:n])
 138  	assert("x type is int32, got "+xType, xType == "int32")
 139  
 140  	addName := []byte("add")
 141  	n = scopeTypeOf(h, uintptr(unsafe.Pointer(&addName[0])), int32(len(addName)),
 142  		uintptr(unsafe.Pointer(&buf[0])), 256)
 143  	addType := string(buf[:n])
 144  	assert("add has a type string, got "+addType, len(addType) > 0)
 145  
 146  	ptName := []byte("Point")
 147  	n = scopeTypeOf(h, uintptr(unsafe.Pointer(&ptName[0])), int32(len(ptName)),
 148  		uintptr(unsafe.Pointer(&buf[0])), 256)
 149  	ptType := string(buf[:n])
 150  	assert("Point has a type string, got "+ptType, len(ptType) > 0)
 151  
 152  	tcFree(h)
 153  
 154  	po := parseOnly(
 155  		uintptr(unsafe.Pointer(&src1[0])), int32(len(src1)),
 156  		uintptr(unsafe.Pointer(&name1[0])), int32(len(name1)),
 157  	)
 158  	fmt.Printf("parseOnly: %d decls\n", po)
 159  	assert("parse works", po >= 3)
 160  
 161  	p1i := pass1Insert(
 162  		uintptr(unsafe.Pointer(&src1[0])), int32(len(src1)),
 163  		uintptr(unsafe.Pointer(&name1[0])), int32(len(name1)),
 164  	)
 165  	fmt.Printf("pass1Insert: %d names\n", p1i)
 166  	assert("pass1 insert works", p1i == 3)
 167  
 168  	runtime.KeepAlive(src1)
 169  	runtime.KeepAlive(name1)
 170  
 171  	fmt.Printf("%d/%d tests passed\n", pass, pass+fail)
 172  	if fail > 0 {
 173  		os.Exit(1)
 174  	}
 175  }
 176