Skip to content

Commit

Permalink
internal/compiler: fix emitting of receiver in method calls
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
zapateo and gazerro authored Jan 27, 2025
1 parent 07008c0 commit 0fde5a2
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 2 deletions.
3 changes: 1 addition & 2 deletions internal/compiler/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
95 changes: 95 additions & 0 deletions test/misc/program_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
package misc

import (
"bytes"
"context"
"fmt"
"io/fs"
"reflect"
"strings"
Expand Down Expand Up @@ -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)
}
})
}

0 comments on commit 0fde5a2

Please sign in to comment.