compile_exports.mx raw

   1  package main
   2  
   3  import (
   4  	"runtime"
   5  	"unsafe"
   6  
   7  	. "git.smesh.lol/moxie/pkg/types"
   8  )
   9  
  10  
  11  //export moxie_compile_to_ir
  12  func moxie_compile_to_ir(srcPtr unsafe.Pointer, srcLen int32, namePtr unsafe.Pointer, nameLen int32, triplePtr unsafe.Pointer, tripleLen int32) (n int32) {
  13  	runtime.InitCShared()
  14  	initCompileCtx()
  15  	src := unsafe.Slice((*byte)(srcPtr), srcLen)
  16  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
  17  	triple := string(unsafe.Slice((*byte)(triplePtr), tripleLen))
  18  
  19  	ir := CompileToIR(src, name, triple)
  20  
  21  	idx := int32(len(cctx.irResults))
  22  	push(cctx.irResults, ir)
  23  	return idx
  24  }
  25  
  26  //export moxie_compile_ir_len
  27  func moxie_compile_ir_len(h int32) (n int32) {
  28  	runtime.InitCShared()
  29  	if h < 0 || int32(h) >= len(cctx.irResults) {
  30  		return 0
  31  	}
  32  	return int32(len(cctx.irResults[h]))
  33  }
  34  
  35  //export moxie_compile_ir_copy
  36  func moxie_compile_ir_copy(h int32, dst unsafe.Pointer, capVal int32) (n int32) {
  37  	runtime.InitCShared()
  38  	if h < 0 || int32(h) >= len(cctx.irResults) {
  39  		return 0
  40  	}
  41  	ir := cctx.irResults[h]
  42  	n = int32(len(ir))
  43  	if n > capVal {
  44  		n = capVal
  45  	}
  46  	if n > 0 {
  47  		buf := unsafe.Slice((*byte)(dst), n)
  48  		copy(buf, ir[:n])
  49  	}
  50  	return n
  51  }
  52  
  53  //export moxie_compile_ir_free
  54  func moxie_compile_ir_free(h int32) {
  55  	runtime.InitCShared()
  56  	if h >= 0 && int32(h) < len(cctx.irResults) {
  57  		cctx.irResults[h] = ""
  58  	}
  59  }
  60  
  61  //export moxie_register_package
  62  func moxie_register_package(pathPtr unsafe.Pointer, pathLen int32, namePtr unsafe.Pointer, nameLen int32) {
  63  	runtime.InitCShared()
  64  	path := string(unsafe.Slice((*byte)(pathPtr), pathLen))
  65  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
  66  	cctx.universe.Registry[path] = NewTCPackage(path, name)
  67  }
  68  
  69  //export moxie_register_func
  70  func moxie_register_func(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, sigPtr unsafe.Pointer, sigLen int32) {
  71  	runtime.InitCShared()
  72  	pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
  73  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
  74  	sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen))
  75  	pkg := cctx.universe.Registry[pkgPath]
  76  	if pkg == nil {
  77  		return
  78  	}
  79  	old := cctx.parseTypePkgScope
  80  	cctx.parseTypePkgScope = pkg.Scope
  81  	sig := parseSignatureDesc(sigDesc)
  82  	cctx.parseTypePkgScope = old
  83  	pkg.Scope.Insert(NewTCFunc(pkg, name, sig))
  84  }
  85  
  86  //export moxie_register_var
  87  func moxie_register_var(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32) {
  88  	runtime.InitCShared()
  89  	pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
  90  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
  91  	typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen))
  92  	pkg := cctx.universe.Registry[pkgPath]
  93  	if pkg == nil {
  94  		return
  95  	}
  96  	typ := parseTypeDesc(typDesc)
  97  	pkg.Scope.Insert(NewTCVar(pkg, name, typ))
  98  }
  99  
 100  //export moxie_register_const
 101  func moxie_register_const(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, typDescPtr unsafe.Pointer, typDescLen int32, val int64) {
 102  	runtime.InitCShared()
 103  	pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
 104  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
 105  	typDesc := string(unsafe.Slice((*byte)(typDescPtr), typDescLen))
 106  	pkg := cctx.universe.Registry[pkgPath]
 107  	if pkg == nil {
 108  		return
 109  	}
 110  	typ := parseTypeDesc(typDesc)
 111  	pkg.Scope.Insert(NewTCConst(pkg, name, typ, &ConstInt{V:val}))
 112  }
 113  
 114  //export moxie_register_const_string
 115  func moxie_register_const_string(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, valPtr unsafe.Pointer, valLen int32) {
 116  	runtime.InitCShared()
 117  	pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
 118  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
 119  	val := string(unsafe.Slice((*byte)(valPtr), valLen))
 120  	pkg := cctx.universe.Registry[pkgPath]
 121  	if pkg == nil {
 122  		return
 123  	}
 124  	pkg.Scope.Insert(NewTCConst(pkg, name, Typ[UntypedString], &ConstStr{S:val}))
 125  }
 126  
 127  //export moxie_register_iface
 128  func moxie_register_iface(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, methodsPtr unsafe.Pointer, methodsLen int32) {
 129  	runtime.InitCShared()
 130  	pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
 131  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
 132  	methodsDesc := string(unsafe.Slice((*byte)(methodsPtr), methodsLen))
 133  	pkg := cctx.universe.Registry[pkgPath]
 134  	if pkg == nil {
 135  		return
 136  	}
 137  	var methods []*IfaceMethod
 138  	if methodsDesc != "" {
 139  		parts := splitSemicolon(methodsDesc)
 140  		for _, p := range parts {
 141  			eqIdx := -1
 142  			for i := 0; i < len(p); i++ {
 143  				if p[i] == '=' {
 144  					eqIdx = i
 145  					break
 146  				}
 147  			}
 148  			if eqIdx < 0 {
 149  				continue
 150  			}
 151  			mname := p[:eqIdx]
 152  			msig := parseSignatureDesc(p[eqIdx+1:])
 153  			push(methods, NewTCIfaceMethod(mname, msig))
 154  		}
 155  	}
 156  	iface := NewTCInterface(methods, nil)
 157  	iface.Complete()
 158  	tn := NewTypeName(pkg, name, iface)
 159  	pkg.Scope.Insert(tn)
 160  }
 161  
 162  //export moxie_register_type
 163  func moxie_register_type(pkgPathPtr unsafe.Pointer, pkgPathLen int32, namePtr unsafe.Pointer, nameLen int32, underlyingPtr unsafe.Pointer, underlyingLen int32) {
 164  	runtime.InitCShared()
 165  	pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
 166  	name := string(unsafe.Slice((*byte)(namePtr), nameLen))
 167  	pkg := cctx.universe.Registry[pkgPath]
 168  	if pkg == nil {
 169  		return
 170  	}
 171  	var underlying Type
 172  	if underlyingLen > 0 {
 173  		desc := string(unsafe.Slice((*byte)(underlyingPtr), underlyingLen))
 174  		underlying = parseTypeDesc(desc)
 175  	} else {
 176  		underlying = NewTCStruct(nil, nil)
 177  	}
 178  	tn := NewTypeName(pkg, name, nil)
 179  	NewNamed(tn, underlying)
 180  	pkg.Scope.Insert(tn)
 181  }
 182  
 183  //export moxie_register_method
 184  func moxie_register_method(pkgPathPtr unsafe.Pointer, pkgPathLen int32, typeNamePtr unsafe.Pointer, typeNameLen int32, methodNamePtr unsafe.Pointer, methodNameLen int32, sigPtr unsafe.Pointer, sigLen int32, ptrRecv int32) {
 185  	runtime.InitCShared()
 186  	pkgPath := string(unsafe.Slice((*byte)(pkgPathPtr), pkgPathLen))
 187  	typeName := string(unsafe.Slice((*byte)(typeNamePtr), typeNameLen))
 188  	methodName := string(unsafe.Slice((*byte)(methodNamePtr), methodNameLen))
 189  	sigDesc := string(unsafe.Slice((*byte)(sigPtr), sigLen))
 190  	pkg := cctx.universe.Registry[pkgPath]
 191  	if pkg == nil {
 192  		return
 193  	}
 194  	obj := pkg.Scope.Lookup(typeName)
 195  	if obj == nil {
 196  		return
 197  	}
 198  	tn, ok := obj.(*TypeName)
 199  	if !ok {
 200  		return
 201  	}
 202  	named, ok := tn.Typ.(*Named)
 203  	if !ok {
 204  		return
 205  	}
 206  	old := cctx.parseTypePkgScope
 207  	cctx.parseTypePkgScope = pkg.Scope
 208  	sig := parseSignatureDesc(sigDesc)
 209  	cctx.parseTypePkgScope = old
 210  	var recvType Type = named
 211  	if ptrRecv != 0 {
 212  		recvType = NewPointer(named)
 213  	}
 214  	sig = NewSignature(NewTCVar(pkg, "", recvType), sig.Params, sig.Results, sig.Variadic)
 215  	fn := NewTCFunc(pkg, methodName, sig)
 216  	if ptrRecv != 0 {
 217  		fn.PtrRecv = true
 218  	}
 219  	named.AddMethod(fn)
 220  }
 221  
 222  func splitSemicolon(s string) (ss []string) {
 223  	var parts []string
 224  	start := 0
 225  	for i := 0; i < len(s); i++ {
 226  		if s[i] == ';' {
 227  			push(parts, s[start:i])
 228  			start = i + 1
 229  		}
 230  	}
 231  	push(parts, s[start:])
 232  	return parts
 233  }
 234  
 235  //export moxie_clear_imports
 236  func moxie_clear_imports() {
 237  	runtime.InitCShared()
 238  	for k := range cctx.universe.Registry {
 239  		delete(cctx.universe.Registry, k)
 240  	}
 241  }
 242  
 243  
 244  func parseTypeDesc(desc string) (t Type) {
 245  	switch desc {
 246  	case "bool":
 247  		return Typ[Bool]
 248  	case "int8":
 249  		return Typ[Int8]
 250  	case "int16":
 251  		return Typ[Int16]
 252  	case "int32", "int32":
 253  		return Typ[Int32]
 254  	case "int64":
 255  		return Typ[Int64]
 256  	case "uint8", "byte":
 257  		return Typ[Uint8]
 258  	case "uint16":
 259  		return Typ[Uint16]
 260  	case "uint32", "uint32":
 261  		return Typ[Uint32]
 262  	case "uint64":
 263  		return Typ[Uint64]
 264  	case "float32":
 265  		return Typ[Float32]
 266  	case "float64":
 267  		return Typ[Float64]
 268  	case "string":
 269  		return Typ[TCString]
 270  	case "ptr":
 271  		return Typ[UnsafePointer]
 272  	case "error":
 273  		errorSig := NewSignature(nil, nil, NewTuple(NewTCVar(nil, "", Typ[TCString])), false)
 274  		errorIface := NewTCInterface([]*IfaceMethod{NewTCIfaceMethod("Error", errorSig), NewTCIfaceMethod("String", errorSig)}, nil)
 275  		errorIface.Complete()
 276  		return errorIface
 277  	case "struct", "struct{}":
 278  		return NewTCStruct(nil, nil)
 279  	case "interface{}":
 280  		emptyIface := NewTCInterface(nil, nil)
 281  		emptyIface.Complete()
 282  		return emptyIface
 283  	}
 284  	// TypeParam marker: $P -> reconstruct a TypeParam so substTypeParams can
 285  	// substitute it during monomorphization.
 286  	if len(desc) > 1 && desc[0] == '$' {
 287  		tpName := desc[1:]
 288  		tn := NewTypeName(nil, tpName, nil)
 289  		return NewTypeParam(tn, nil)
 290  	}
 291  	if len(desc) > 7 && desc[:7] == "struct{" && desc[len(desc)-1] == '}' {
 292  		inner := desc[7 : len(desc)-1]
 293  		if inner == "" {
 294  			return NewTCStruct(nil, nil)
 295  		}
 296  		fields := splitCommaBalanced(inner)
 297  		vars := []*TCVar{:0:len(fields)}
 298  		tags := []string{:0:len(fields)}
 299  		for _, f := range fields {
 300  			ci := -1
 301  			for k := 0; k < len(f); k++ {
 302  				if f[k] == ':' {
 303  					ci = k
 304  					break
 305  				}
 306  			}
 307  			if ci < 0 {
 308  				continue
 309  			}
 310  			fname := f[:ci]
 311  			anon := false
 312  			if len(fname) > 0 && fname[0] == '@' {
 313  				fname = fname[1:]
 314  				anon = true
 315  			}
 316  			ftype := parseTypeDesc(f[ci+1:])
 317  			v := NewTCVar(nil, fname, ftype)
 318  			v.Anonymous = anon
 319  			push(vars, v)
 320  			push(tags, "")
 321  		}
 322  		return NewTCStruct(vars, tags)
 323  	}
 324  	if len(desc) > 2 && desc[0] == '[' && desc[1] == ']' {
 325  		elem := parseTypeDesc(desc[2:])
 326  		if b, ok := elem.(*Basic); ok && b.Kind == Uint8 {
 327  			return Typ[TCString]
 328  		}
 329  		return NewSlice(elem)
 330  	}
 331  	if len(desc) > 2 && desc[0] == '[' && desc[1] != ']' {
 332  		closeBrack := int32(-1)
 333  		for k := int32(1); k < int32(len(desc)); k++ {
 334  			if desc[k] == ']' {
 335  				closeBrack = k
 336  				break
 337  			}
 338  		}
 339  		if closeBrack > 1 {
 340  			n := ParseIntLocal(desc[1:closeBrack])
 341  			elem := parseTypeDesc(desc[closeBrack+1:])
 342  			return NewArray(elem, n)
 343  		}
 344  	}
 345  	if len(desc) > 1 && desc[0] == '*' {
 346  		return NewPointer(parseTypeDesc(desc[1:]))
 347  	}
 348  	if len(desc) > 5 && desc[:5] == "func(" && desc[len(desc)-1] == ')' {
 349  		return parseSignatureDesc(desc[5 : len(desc)-1])
 350  	}
 351  	if len(desc) > 5 && desc[:5] == "chan:" {
 352  		return NewTCChan(TCSendRecv, parseTypeDesc(desc[5:]))
 353  	}
 354  	if len(desc) > 7 && desc[:7] == "chan<-:" {
 355  		return NewTCChan(TCSendOnly, parseTypeDesc(desc[7:]))
 356  	}
 357  	if len(desc) > 7 && desc[:7] == "<-chan:" {
 358  		return NewTCChan(TCRecvOnly, parseTypeDesc(desc[7:]))
 359  	}
 360  	if len(desc) > 4 && desc[:4] == "map[" {
 361  		depth := int32(0)
 362  		closeBrack := int32(-1)
 363  		for k := int32(4); k < int32(len(desc)); k++ {
 364  			if desc[k] == '[' {
 365  				depth++
 366  			} else if desc[k] == ']' {
 367  				if depth == 0 {
 368  					closeBrack = k
 369  					break
 370  				}
 371  				depth--
 372  			}
 373  		}
 374  		if closeBrack > 4 {
 375  			key := parseTypeDesc(desc[4:closeBrack])
 376  			val := parseTypeDesc(desc[closeBrack+1:])
 377  			return NewTCMap(key, val)
 378  		}
 379  	}
 380  	dotIdx := int32(-1)
 381  	for i := int32(len(desc)) - 1; i >= 0; i-- {
 382  		if desc[i] == '.' {
 383  			dotIdx = i
 384  			break
 385  		}
 386  	}
 387  	if dotIdx > 0 {
 388  		pkgPath := desc[:dotIdx]
 389  		typeName := desc[dotIdx+1:]
 390  		if extPkg, ok := cctx.universe.Registry[pkgPath]; ok {
 391  			obj := extPkg.Scope.Lookup(typeName)
 392  			if tn, _ := obj.(*TypeName); tn != nil && tn.Typ != nil {
 393  				return tn.Typ
 394  			}
 395  		}
 396  	}
 397  	if cctx.parseTypePkgScope != nil {
 398  		obj := cctx.parseTypePkgScope.Lookup(desc)
 399  		if tn, ok := obj.(*TypeName); ok && tn.Typ != nil {
 400  			return tn.Typ
 401  		}
 402  	}
 403  	// Universe-scope named types (uintptr, byte, rune) appear in mxh descs by
 404  	// name; without this lookup they degrade to int32 and receiver widths in
 405  	// cached packages diverge from the source-compiled layout.
 406  	if Universe != nil {
 407  		obj := Universe.Lookup(desc)
 408  		if tn, ok := obj.(*TypeName); ok && tn.Typ != nil {
 409  			return tn.Typ
 410  		}
 411  	}
 412  	return Typ[Int32]
 413  }
 414  
 415  func parseSignatureDesc(desc string) (s *Signature) {
 416  	// format: "param1,param2->result1,result2" or "param1,param2" (void) or "->result" (no params)
 417  	// The arrow and commas are matched at depth 0 only: nested func/struct
 418  	// descs carry their own arrows and commas inside brackets.
 419  	arrow := -1
 420  	depth := 0
 421  	for i := 0; i < len(desc)-1; i++ {
 422  		switch desc[i] {
 423  		case '{', '[', '(':
 424  			depth++
 425  		case '}', ']', ')':
 426  			depth--
 427  		case '-':
 428  			if depth == 0 && desc[i+1] == '>' {
 429  				arrow = i
 430  			}
 431  		}
 432  		if arrow >= 0 {
 433  			break
 434  		}
 435  	}
 436  	var paramStr, resultStr string
 437  	if arrow >= 0 {
 438  		paramStr = desc[:arrow]
 439  		resultStr = desc[arrow+2:]
 440  	} else {
 441  		paramStr = desc
 442  	}
 443  	variadic := false
 444  	var params []*TCVar
 445  	if paramStr != "" {
 446  		parts := splitCommaBalanced(paramStr)
 447  		params = []*TCVar{:0:len(parts)}
 448  		for pi, p := range parts {
 449  			if pi == len(parts)-1 && len(p) > 3 && p[:3] == "..." {
 450  				variadic = true
 451  				push(params, NewTCVar(nil, "", NewSlice(parseTypeDesc(p[3:]))))
 452  				continue
 453  			}
 454  			push(params, NewTCVar(nil, "", parseTypeDesc(p)))
 455  		}
 456  	}
 457  	var results []*TCVar
 458  	if resultStr != "" {
 459  		parts := splitCommaBalanced(resultStr)
 460  		results = []*TCVar{:0:len(parts)}
 461  		for _, r := range parts {
 462  			push(results, NewTCVar(nil, "", parseTypeDesc(r)))
 463  		}
 464  	}
 465  	var pTuple, rTuple *Tuple
 466  	if len(params) > 0 {
 467  		pTuple = NewTuple(params...)
 468  	}
 469  	if len(results) > 0 {
 470  		rTuple = NewTuple(results...)
 471  	}
 472  	return NewSignature(nil, pTuple, rTuple, variadic)
 473  }
 474  
 475  func splitCommaBalanced(s string) (ss []string) {
 476  	// Count commas at depth 0.
 477  	n := int32(1)
 478  	depth := int32(0)
 479  	for i := int32(0); i < len(s); i++ {
 480  		switch s[i] {
 481  		case '{', '[', '(':
 482  			depth++
 483  		case '}', ']', ')':
 484  			depth--
 485  		case ',':
 486  			if depth == 0 {
 487  				n++
 488  			}
 489  		}
 490  	}
 491  	parts := []string{:0:n}
 492  	start := int32(0)
 493  	depth = 0
 494  	for i := int32(0); i < len(s); i++ {
 495  		switch s[i] {
 496  		case '{', '[', '(':
 497  			depth++
 498  		case '}', ']', ')':
 499  			depth--
 500  		case ',':
 501  			if depth == 0 {
 502  				push(parts, s[start:i])
 503  				start = i + 1
 504  			}
 505  		}
 506  	}
 507  	push(parts, s[start:])
 508  	return parts
 509  }
 510  
 511  func splitComma(s string) (ss []string) {
 512  	n := int32(1)
 513  	for i := int32(0); i < len(s); i++ {
 514  		if s[i] == ',' {
 515  			n++
 516  		}
 517  	}
 518  	parts := []string{:0:n}
 519  	start := int32(0)
 520  	for i := int32(0); i < len(s); i++ {
 521  		if s[i] == ',' {
 522  			push(parts, s[start:i])
 523  			start = i + 1
 524  		}
 525  	}
 526  	push(parts, s[start:])
 527  	return parts
 528  }
 529  
 530