From 0fde5a21733ce4af645570a164db472cad5f2082 Mon Sep 17 00:00:00 2001 From: Gianluca Mondini Date: Mon, 27 Jan 2025 17:04:19 +0100 Subject: [PATCH] internal/compiler: fix emitting of receiver in method calls This commit fixes the emitting of method calls, where the receiver of the method call was erroneously placed as first "general" argument in the call stack, causing an incorrect stack shift and altering the parameters received from the called method. Also adds tests on this. Fix #967. Co-authored-by: Marco Gazerro --- internal/compiler/emitter.go | 3 +- test/misc/program_test.go | 95 ++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/internal/compiler/emitter.go b/internal/compiler/emitter.go index 52705ed9e..9db1ced8c 100644 --- a/internal/compiler/emitter.go +++ b/internal/compiler/emitter.go @@ -579,11 +579,10 @@ func (em *emitter) emitCallNode(call *ast.Call, goStmt bool, deferStmt bool, toF name := call.Func.(*ast.Selector).Ident s := em.fb.makeStringValue(name) em.fb.emitMethodValue(s, rcvr, method, call.Func.Pos()) - call.Args = append([]ast.Expression{rcvrExpr}, call.Args...) stackShift := em.fb.currentStackShift() opts := callOptions{ predefined: true, - receiverAsArg: true, + receiverAsArg: false, callHasDots: call.IsVariadic, } regs, types := em.prepareCallParameters(funTi.Type, call.Args, opts) diff --git a/test/misc/program_test.go b/test/misc/program_test.go index 6d8e389eb..09e8ce41f 100644 --- a/test/misc/program_test.go +++ b/test/misc/program_test.go @@ -5,7 +5,9 @@ package misc import ( + "bytes" "context" + "fmt" "io/fs" "reflect" "strings" @@ -552,3 +554,96 @@ func TestIssue855(t *testing.T) { t.Fatalf("expected error %q, got %q", expectedErr, gotErr) } } + +type ICopier interface { + CopyFrom(v Container) +} + +type ValueHold struct{} + +func (t *ValueHold) CopyFrom(v Container) {} + +type Container struct{} + +var printBuf bytes.Buffer + +type PrintSomething struct{} + +func (t *PrintSomething) Print(v any) { + fmt.Fprintf(&printBuf, "%v (%T)", v, v) +} + +type IPrinter interface { + Print(any) +} + +// TestIssue967 executes a test the issue +// https://github.com/open2b/scriggo/issues/967. +func TestIssue967(t *testing.T) { + t.Run("Case 1", func(t *testing.T) { + src := ` + package main + + import "com/interop" + + func main() { + interop.GetHolder().CopyFrom(interop.Container{}) + } + + ` + prog, err := scriggo.Build(scriggo.Files{"main.go": []byte(src)}, &scriggo.BuildOptions{ + Packages: native.Packages{ + "com/interop": native.Package{ + Name: "interop", + Declarations: native.Declarations{ + "GetHolder": func() ICopier { return &ValueHold{} }, + "Container": reflect.TypeOf(Container{}), + }, + }, + }, + }) + if err != nil { + t.Fatalf("unexpected build error: %s", err) + } + if err := prog.Run(&scriggo.RunOptions{}); err != nil { + t.Fatalf("unexpected run error: %s", err) + } + }) + t.Run("Case 2", func(t *testing.T) { + packages := native.Packages{ + "com/interop": native.Package{ + Name: "interop", + Declarations: native.Declarations{ + "GetPrinter": func() IPrinter { return &PrintSomething{} }, + "Value": 15, + }, + }, + } + main := ` + package main + + import "com/interop" + + func main() { + interop.GetPrinter().Print(interop.Value) + }` + fsys := fstest.Files{"main.go": main} + program, err := scriggo.Build(fsys, &scriggo.BuildOptions{Packages: packages}) + if err != nil { + t.Fatalf("unexpected build error: %s ", err) + } + buf, err := program.Disassemble("main") + if err != nil { + t.Fatalf("unexpected disasseble error: %s ", err) + } + fmt.Println(string(buf)) + err = program.Run(nil) + if err != nil { + t.Fatalf("unexpected run error: %s ", err) + } + const expected = "15 (int)" + if got := printBuf.String(); got != expected { + t.Fatalf(`expected %q, got %q`, expected, got) + } + }) +}