Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make xgo/runtime build with go1.14 #31

Merged
merged 2 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ jobs:
- name: Test
run: go run ./script/run-test --reset-instrument --debug -v -cover -coverpkg github.com/xhd2015/xgo/runtime/... -coverprofile cover.out

- name: Merge Coverages
run: go run ./script/cover merge ./cover-runtime.out ./cover-runtime-sub.out -o cover-runtime-merged.out

- name: Print coverage
run: cd runtime && go tool cover --func ../cover-runtime.out
run: cd runtime && go tool cover --func ../cover-runtime-merged.out

- name: Build Release
run: go run ./script/build-release --include-install-src --include-local
Expand Down
6 changes: 3 additions & 3 deletions cmd/xgo/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package main

import "fmt"

const VERSION = "1.0.16"
const REVISION = "b27192a64f46ddf3ddd2b02ca6fe52c8c4e03ffd+1"
const NUMBER = 154
const VERSION = "1.0.17"
const REVISION = "77848295e3d73a3eba8ce2bbd1b95d8c988929d5+1"
const NUMBER = 156

func getRevision() string {
return fmt.Sprintf("%s %s BUILD_%d", VERSION, REVISION, NUMBER)
Expand Down
6 changes: 3 additions & 3 deletions runtime/core/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"os"
)

const VERSION = "1.0.16"
const REVISION = "b27192a64f46ddf3ddd2b02ca6fe52c8c4e03ffd+1"
const NUMBER = 154
const VERSION = "1.0.17"
const REVISION = "77848295e3d73a3eba8ce2bbd1b95d8c988929d5+1"
const NUMBER = 156

// these fields will be filled by compiler
const XGO_VERSION = ""
Expand Down
2 changes: 1 addition & 1 deletion runtime/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/xhd2015/xgo/runtime

go 1.18
go 1.14
138 changes: 135 additions & 3 deletions runtime/mock/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,72 @@ import (
"context"
"fmt"
"reflect"
"strings"

"github.com/xhd2015/xgo/runtime/core"
)

// Patch replaces `fn` with `replacer` in current goroutine,
// it returns a cleanup function to remove `replacer`.
// the `replacer` will be automatically cleared when current
// gorotuine exits if the returned cleanup function is not
// called.
func Patch(fn interface{}, replacer interface{}) func() {
if fn == nil {
panic("fn cannot be nil")
}
if replacer == nil {
panic("replacer cannot be nil")
}
fnType := reflect.TypeOf(fn)
if fnType.Kind() != reflect.Func {
panic(fmt.Errorf("fn should be func, actual: %T", fn))
}
if fnType != reflect.TypeOf(replacer) {
panic(fmt.Errorf("replacer should have type: %T, actual: %T", fn, replacer))
}

recvPtr, fnInfo, funcPC, trappingPC := getFunc(fn)
return mock(recvPtr, fnInfo, funcPC, trappingPC, buildInterceptorFromPatch(recvPtr, replacer))
}

func PatchByName(pkgPath string, funcName string, replacer interface{}) func() {
if replacer == nil {
panic("replacer cannot be nil")
}
t := reflect.TypeOf(replacer)
if t.Kind() != reflect.Func {
panic(fmt.Errorf("replacer should be func, actual: %T", replacer))
}

// check type
recvPtr, funcInfo, funcPC, trappingPC := getFuncByName(pkgPath, funcName)
if funcInfo.Func != nil {
calledType, replacerType, match := checkFuncTypeMatch(reflect.TypeOf(funcInfo.Func), t, recvPtr != nil)
if !match {
panic(fmt.Errorf("replacer should have type: %s, actual: %s", calledType, replacerType))
}
}
return mock(recvPtr, funcInfo, funcPC, trappingPC, buildInterceptorFromPatch(recvPtr, replacer))
}

func PatchMethodByName(instance interface{}, method string, replacer interface{}) func() {
if replacer == nil {
panic("replacer cannot be nil")
}
t := reflect.TypeOf(replacer)
if t.Kind() != reflect.Func {
panic(fmt.Errorf("replacer should be func, actual: %T", replacer))
}

// check type
recvPtr, funcInfo, funcPC, trappingPC := getMethodByName(instance, method)
if funcInfo.Func != nil {
calledType, replacerType, match := checkFuncTypeMatch(reflect.TypeOf(funcInfo.Func), t, recvPtr != nil)
if !match {
panic(fmt.Errorf("replacer should have type: %s, actual: %s", calledType, replacerType))
}
}
return mock(recvPtr, funcInfo, funcPC, trappingPC, buildInterceptorFromPatch(recvPtr, replacer))
}

Expand All @@ -36,15 +91,19 @@ func buildInterceptorFromPatch(recvPtr interface{}, replacer interface{}) func(c
callArgs := make([]reflect.Value, nIn)
src := 0
dst := 0

if fn.RecvType != "" {
if recvPtr != nil {
// patching an instance method
src++
// replacer's does not have receiver
} else {
// set receiver
callArgs[dst] = reflect.ValueOf(args.GetFieldIndex(0).Value())
dst++
src++
if nIn > 0 {
callArgs[dst] = reflect.ValueOf(args.GetFieldIndex(0).Value())
dst++
src++
}
}
}
if fn.FirstArgCtx {
Expand Down Expand Up @@ -80,3 +139,76 @@ func buildInterceptorFromPatch(recvPtr interface{}, replacer interface{}) func(c
return nil
}
}

func checkFuncTypeMatch(a reflect.Type, b reflect.Type, skipAFirst bool) (atype string, btype string, match bool) {
na := a.NumIn()
nb := b.NumIn()

base := 0
if skipAFirst {
base++
}
if na-base != nb {
return formatFuncType(a, skipAFirst), formatFuncType(b, false), false
}

for i := 0; i < na; i++ {
ta := a.In(i + base)
tb := b.In(i)
if ta != tb {
return formatFuncType(a, skipAFirst), formatFuncType(b, false), false
}
}

nouta := a.NumOut()
noutb := b.NumOut()
if nouta != noutb {
return formatFuncType(a, skipAFirst), formatFuncType(b, false), false
}
for i := 0; i < nouta; i++ {
ta := a.Out(i)
tb := b.Out(i)
if ta != tb {
return formatFuncType(a, skipAFirst), formatFuncType(b, false), false
}
}
return "", "", true
}

func formatFuncType(f reflect.Type, skipFirst bool) string {
n := f.NumIn()
i := 0
if skipFirst {
i++
}
var strBuilder strings.Builder
strBuilder.WriteString("func(")
for ; i < n; i++ {
t := f.In(i)
strBuilder.WriteString(t.String())
if i < n-1 {
strBuilder.WriteString(",")
}
}
strBuilder.WriteString(")")

nout := f.NumOut()
if nout > 0 {
strBuilder.WriteString(" ")
if nout > 1 {
strBuilder.WriteString("(")
}
for i := 0; i < nout; i++ {
t := f.Out(i)
strBuilder.WriteString(t.String())
if i < nout-1 {
strBuilder.WriteString(",")
}
}
if nout > 1 {
strBuilder.WriteString(")")
}
}

return strBuilder.String()
}
9 changes: 0 additions & 9 deletions runtime/mock/patch_go1.17.go

This file was deleted.

21 changes: 17 additions & 4 deletions runtime/mock/patch_go1.18.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ package mock

// TODO: what if `fn` is a Type function
// instead of an instance method?
func Patch[T any](fn T, replacer T) func() {
recvPtr, fnInfo, funcPC, trappingPC := getFunc(fn)
return mock(recvPtr, fnInfo, funcPC, trappingPC, buildInterceptorFromPatch(recvPtr, replacer))
}
// func Patch[T any](fn T, replacer T) func() {
// recvPtr, fnInfo, funcPC, trappingPC := getFunc(fn)
// return mock(recvPtr, fnInfo, funcPC, trappingPC, buildInterceptorFromPatch(recvPtr, replacer))
// }

// NOTE: as a library targeting under go1.18, the library itself should not
// use any generic thing
//
// situiation:
// go.mod: 1.16
// runtime/go.mod: 1.18
// go version: 1.20

// compile error:
// implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
// mock.Patch(...)
// because mock.Patch was defined as generic
3 changes: 3 additions & 0 deletions runtime/test/atomic_generic/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build go1.18
// +build go1.18

package main

import (
Expand Down
90 changes: 0 additions & 90 deletions runtime/test/demo/main.go

This file was deleted.

12 changes: 0 additions & 12 deletions runtime/test/demo/main_test.go

This file was deleted.

3 changes: 3 additions & 0 deletions runtime/test/generic/main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build go1.18
// +build go1.18

package main

func main() {
Expand Down
9 changes: 9 additions & 0 deletions runtime/test/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/xhd2015/xgo/runtime/test

go 1.18

require (
github.com/xhd2015/xgo/runtime v1.0.16
)

replace github.com/xhd2015/xgo/runtime => ../
Loading
Loading