Skip to content

Commit

Permalink
add workaround when patching variables with selector
Browse files Browse the repository at this point in the history
  • Loading branch information
xhd2015 committed May 6, 2024
1 parent e7b8572 commit 818c18f
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 18 deletions.
6 changes: 3 additions & 3 deletions cmd/xgo/runtime_gen/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.28"
const REVISION = "2eaf6cf94cd17d2888d6708cb76d194b334c8957+1"
const NUMBER = 202
const VERSION = "1.0.29"
const REVISION = "5d121a999ca8ce5c744a110d4f42b31ef4490e8f+1"
const NUMBER = 204

// these fields will be filled by compiler
const XGO_VERSION = ""
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.28"
const REVISION = "2eaf6cf94cd17d2888d6708cb76d194b334c8957+1"
const NUMBER = 202
const VERSION = "1.0.29"
const REVISION = "5d121a999ca8ce5c744a110d4f42b31ef4490e8f+1"
const NUMBER = 204

func getRevision() string {
revSuffix := ""
Expand Down
13 changes: 11 additions & 2 deletions patch/syntax/vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,8 +584,16 @@ func (ctx *BlockContext) traverseCallExpr(node *syntax.CallExpr, globaleNames ma
}
}

// NOTE: we skip capturing a name as a function
// node.Fun = ctx.traverseExpr(node.Fun, globaleNames, imports)
// NOTE: previousely we skip capturing a name as a function(i.e. the next statement is commented out)
// reason: we cannot tell if the receiver is
// a pointer or a value. If the target function requires a
// pointer while we assign a value, then effect will lost, such
// as lock and unlock.
//
// however, since we have isVarOKToTrap() to check if a variable is ok to trap, so this can be turned on, resulting in workaround:
// A.B() -> typeOfA(A).B() which makes things work

node.Fun = ctx.traverseExpr(node.Fun, globaleNames, imports)
for i, arg := range node.ArgList {
node.ArgList[i] = ctx.traverseExpr(arg, globaleNames, imports)
}
Expand Down Expand Up @@ -762,6 +770,7 @@ func (ctx *BlockContext) trapSelector(node syntax.Expr, sel *syntax.SelectorExpr
return newName, true
}

// check if node is either X of an X.Y, or (X).Y or ((X)).Y...
func (ctx *BlockContext) isVarOKToTrap(node syntax.Node) bool {
// a variable can only trapped when it will not
// cause an implicit pointer
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.28"
const REVISION = "2eaf6cf94cd17d2888d6708cb76d194b334c8957+1"
const NUMBER = 202
const VERSION = "1.0.29"
const REVISION = "5d121a999ca8ce5c744a110d4f42b31ef4490e8f+1"
const NUMBER = 204

// these fields will be filled by compiler
const XGO_VERSION = ""
Expand Down
10 changes: 10 additions & 0 deletions runtime/test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Tests of the runtime package
Runnable tests are listed in file [../../script/run-test/main.go](../../script/run-test/main.go).

# Run tests
```sh
# all

# specific test
go run -tags dev ./cmd/xgo test --with-goroot go1.17.13 --project-dir ./runtime/test/mock_var -v -run TestThirdPartyTypeMethodVar
```
23 changes: 18 additions & 5 deletions runtime/test/debug/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,25 @@

package debug

import "testing"
import (
"testing"

const XGO_VERSION = ""
"github.com/xhd2015/xgo/runtime/mock"
"github.com/xhd2015/xgo/runtime/test/mock_var/sub"
)

func TestListStdlib(t *testing.T) {
if XGO_VERSION == "" {
t.Fatalf("fail")
var c sub.Mapping = sub.Mapping{
1: "hello",
}

func TestThirdPartyTypeMethodVar(t *testing.T) {
mock.Patch(&c, func() sub.Mapping {
return sub.Mapping{
1: "mock",
}
})
txt := c.Get(1)
if txt != "mock" {
t.Fatalf("expect c[1] to be %s, actual: %s", "mock", txt)
}
}
28 changes: 28 additions & 0 deletions runtime/test/mock_var/mock_var_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ var a int = 123
// xgo:notrap
var b int

var c sub.Mapping = sub.Mapping{
1: "hello",
}

func TestMockVarTest(t *testing.T) {
mock.Mock(&a, func(ctx context.Context, fn *core.FuncInfo, args, results core.Object) error {
results.GetFieldIndex(0).Set(456)
Expand All @@ -36,3 +40,27 @@ func TestMockVarInOtherPkg(t *testing.T) {
t.Fatalf("expect sub.A to be %s, actual: %s", "mockA", b)
}
}

func TestThirdPartyTypeMethodVarWithoutWrapShouldNotWork(t *testing.T) {
mock.Patch(&c, func() sub.Mapping {
return sub.Mapping{
1: "mock",
}
})
txt := c.Get(1)
if txt != "hello" {
t.Fatalf("expect c[1] to be %s, actual: %s", "hello", txt)
}
}

func TestThirdPartyTypeMethodVarWithWrapShouldWork(t *testing.T) {
mock.Patch(&c, func() sub.Mapping {
return sub.Mapping{
1: "mock",
}
})
txt := sub.Mapping(c).Get(1)
if txt != "mock" {
t.Fatalf("expect c[1] to be %s, actual: %s", "mock", txt)
}
}
6 changes: 6 additions & 0 deletions runtime/test/mock_var/sub/sub.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
package sub

var A string = "subA"

type Mapping map[int]string

func (c Mapping) Get(i int) string {
return c[i]
}
8 changes: 6 additions & 2 deletions runtime/test/trap_args/closure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ var gcUnnamed = func(context.Context) {
func TestClosureShouldRetrieveCtxInfoAtTrapTime(t *testing.T) {
ctx := context.Background()
ctx = context.WithValue(ctx, "test", "mock")

// avoid local variable affects interceptor
gclocal := gc
callAndCheck(func() {
gc(ctx)
gclocal(ctx)
}, func(trapCtx context.Context, f *core.FuncInfo, args, result core.Object) error {
if !f.FirstArgCtx {
t.Fatalf("expect closure also mark firstArgCtx, actually not marked")
Expand All @@ -37,8 +40,9 @@ func TestClosureShouldRetrieveCtxInfoAtTrapTime(t *testing.T) {
func TestClosureUnnamedArgShouldRetrieveCtxInfo(t *testing.T) {
ctx := context.Background()
ctx = context.WithValue(ctx, "test", "mock")
localGcUnnamed := gcUnnamed
callAndCheck(func() {
gcUnnamed(ctx)
localGcUnnamed(ctx)
}, func(trapCtx context.Context, f *core.FuncInfo, args, result core.Object) error {
if !f.FirstArgCtx {
t.Fatalf("expect closure also mark firstArgCtx, actually not marked")
Expand Down
47 changes: 47 additions & 0 deletions test/xgo_test/proof_of_var_mock/proof_of_var_mock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// var mock:
//
// if variable is not a pointer, taking its address, then use it as receiver, it can be converted to value receiver implicitly
// otherwise if variable is a pointer, taking its address does not work.
package proof_of_var_mock

import (
"testing"
)

type Stub struct {
}

type PtrStub *Stub

func (c Stub) A() {
}
func (c *Stub) B() {
}

// invalid receiver type PtrStub (pointer or interface type)
//
// func (c PtrStub) X(){
//
// }

var stub Stub = Stub{}
var pstub *Stub = &Stub{}

func TestVariableCanBeTrapped(t *testing.T) {

stub.A()
stub.B()

__mock_stub := &stub
__mock_stub.A()
__mock_stub.B()

pstub.A()
pstub.B()

// __mock_pstub.B undefined (type **Stub has no field or method
//
// __mock_pstub := &pstub
// __mock_pstub.A()
// __mock_pstub.B()
}

0 comments on commit 818c18f

Please sign in to comment.