Skip to content

ixgo调研 #23

@MeteorsLiu

Description

@MeteorsLiu

初步使用

XGO声明,类似于gop.mod,需要声明项目信息:

	xgobuild.RegisterProject(&modfile.Project{
		Ext:      "_llar.gox",
		Class:    "FormulaApp",
		Works:   nil,
		PkgPaths: nil,
		Import:  nil,
	})

Work:如果不为空,一个Project里面会存在多个基类,以文件后缀进行区分
PkgPaths:主要是解决泛性问题,和ixgo实现有关系(ixgo通过reflect模块进行动态函数调用,但是reflect暂时不支持泛性)
Import:添加模块导入

执行

完成项目注册后,就可以添加相关文件进行执行

	ctx := ixgo.NewContext(0)
	// XGO 编译成Go 代码
	source, err := xgobuild.BuildFSDir(ctx, fs, "")
	if err != nil {
		panic(fmt.Errorf("Failed to build XGo source: %w", err))
	}
	// 生成解释器需要的SSA Package结构
	pkg, err := ctx.LoadFile("main.go", source)
	if err != nil {
		panic(fmt.Errorf("Failed to load XGo source: %w", err))
	}
	// 新建运行时解释器
	interp, err := ctx.NewInterp(pkg)
	if err != nil {
		panic(fmt.Errorf("Failed to create interp: %w", err))
	}
	// 执行,这个main.go只是名称,并不影响执行
	code, err := ctx.RunInterp(interp, "main.go", source)
	if err != nil {
		panic(fmt.Errorf("Failed to run XGo source (code %d): %w", code, err))
	}

ixgo原理

ixgo是XGO运行时解释器,与常规Go静态编译不一样,ixgo允许动态执行XGO语言

其原理就是通过Go的reflect模块。如果是XGO文件,会先编译成Go。解析Go AST后,完成SSA编译。得到SSA格式后,再对其进行reflect类型翻译,和修改函数调用。

具体流程

graph TD
A[输入XGO文件] --> B[XGO源码]
B --> C[Go源码]
C --> |解析|D[Go AST]
D --> |生成|E[Go SSA]
E --> |翻译|F[Reflect 指令]
F --> |运行|G[调用 Reflect 指令]
Loading

函数调用边界

分为ExternalBuiltin两类,这两类一般是,无法获取SSA的,例如cgo和ASM调用,或者通过Go编译器直接生成无具体实现的函数。

这两类前者会查找签名,并通过reflect.Call进行调用,后者则通过reflect.Copy类似的函数进行模拟

额外功能

自定义模块查找

	ctx := ixgo.NewContext(0)
	ctx.Lookup =  func(root string, path string) (dir string, found bool) {
		// 传入当前project根目录和查找的Module Path
		// 返回Module Path所在的目录
	}

如果自定义查找找不到,会自动回退到Go Module查找

RegisterExternal

为声明函数注册具体实现

package main

import "fmt"

type myint int
type point struct {
	x int
	y int
}

func mytest1(n myint, pt point) myint
func mytest2(n myint, pt *point) myint

func main() {
	n := mytest1(100, point{100,200})
	if n != 400 {
		panic(fmt.Errorf("error mytest1, must 400, have %v",n))
	}
	n = mytest2(100, &point{100,200})
	if n != 30000 {
		panic(fmt.Errorf("error mytest2, must 30000, have %v",n))
	}
}

ixgo部分:

	ctx := ixgo.NewContext(0)
	ixgo.RegisterExternal("main.mytest1", func(n int, pt struct {
		x int
		y int
	}) int {
		return n + pt.x + pt.y
	})
	ctx.RegisterExternal("main.mytest2", func(n int, pt *struct {
		x int
		y int
	}) int {
		return n * (pt.x + pt.y)
	})
	_, err := ctx.RunFile("main.go", src, nil)
	if err != nil {
		t.Fatal(err)
	}

如果已经存在对应符号,那么ixgo优先查找RegisterExternal实现

RegisterPatch

给指定package的特定函数进行patch操作

patch操作用于添加额外函数

package main

import "fmt"

func main() {
	fmt.Println1(fmt.Pi,fmt.Patch)
	fmt.Println2("hello world")
}

ixgo代码:

	ctx := ixgo.NewContext(0)
	err := ctx.RegisterPatch("fmt", `
package fmt

import (
	"fmt"
	"math"
)

const Pi = math.Pi

var (
	Patch = "v1.0"
)

func Println1(v ...interface{}) {
	fmt.Println(v...)
}
`)
	if err != nil {
		t.Fatal(err)
	}
	err = ctx.RegisterPatch("fmt", `
package fmt
import "fmt"

func Println2(v ...interface{}) {
	fmt.Println(v...)
}
`)

RegisterCustomBuiltin

添加自定义Go Builtin函数

package main

import "fmt"

func main() {
	fmt.Println(typeof("hello"))
}

ixgo 代码:

	ixgo.RegisterCustomBuiltin("typeof", reflect.TypeOf)

RegisterPackage

注册一个Package

type Package struct {
	// 类型信息
	Interfaces    map[string]reflect.Type
	NamedTypes    map[string]reflect.Type
	AliasTypes    map[string]reflect.Type
	Vars          map[string]reflect.Value
	Funcs         map[string]reflect.Value
	TypedConsts   map[string]TypedConst
	UntypedConsts map[string]UntypedConst

	// Package 依赖
	Deps          map[string]string // K->V: path -> name

	// Package ModuleName
	Name          string

	// Package ModulePath
	Path          string

	Source        string
}

AddImport

为当前构建项目导入package

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions