-
Notifications
You must be signed in to change notification settings - Fork 171
/
call_object.go
178 lines (143 loc) · 4.21 KB
/
call_object.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package vm
import (
"fmt"
"github.com/goby-lang/goby/compiler/bytecode"
)
type callObject struct {
method *MethodObject
receiverPtr int
argCount int
argSet *bytecode.ArgSet
argIndex int
lastArgIndex int
callFrame *normalCallFrame
sourceLine int
}
func newCallObject(receiver Object, method *MethodObject, receiverPtr, argCount int, argSet *bytecode.ArgSet, blockFrame *normalCallFrame, sourceLine int) *callObject {
cf := newNormalCallFrame(method.instructionSet, method.instructionSet.filename, sourceLine)
cf.self = receiver
cf.blockFrame = blockFrame
return &callObject{
method: method,
receiverPtr: receiverPtr,
argCount: argCount,
argSet: argSet,
// This is only for normal/optioned arguments
lastArgIndex: -1,
callFrame: cf,
sourceLine: sourceLine,
}
}
func (co *callObject) instructionSet() *instructionSet {
return co.method.instructionSet
}
func (co *callObject) paramTypes() []int {
return co.instructionSet().paramTypes.Types()
}
func (co *callObject) paramNames() []string {
return co.instructionSet().paramTypes.Names()
}
func (co *callObject) methodName() string {
return co.method.Name
}
func (co *callObject) argTypes() []int {
if co.argSet == nil {
return []int{}
}
return co.argSet.Types()
}
func (co *callObject) argPtr() int {
return co.receiverPtr + 1
}
func (co *callObject) argPosition() int {
return co.argPtr() + co.argIndex
}
func (co *callObject) assignNormalArguments(stack []*Pointer) {
for i, paramType := range co.paramTypes() {
if paramType == bytecode.NormalArg {
co.callFrame.insertLCL(i, 0, stack[co.argPosition()].Target)
co.argIndex++
}
}
}
func (co *callObject) assignNormalAndOptionedArguments(paramIndex int, stack []*Pointer) {
/*
Find first usable value as normal argument, for example:
```ruby
def foo(x, y:); end
foo(y: 100, 10)
```
In the example we can see that 'x' is the first parameter,
but in the method call it's the second argument.
This loop is for skipping other types of arguments and get the correct argument index.
*/
for argIndex, at := range co.argTypes() {
if co.lastArgIndex < argIndex && (at == bytecode.NormalArg || at == bytecode.OptionedArg) {
co.callFrame.insertLCL(paramIndex, 0, stack[co.argPtr()+argIndex].Target)
// Store latest index value (and compare them to current argument index)
// This is to make sure we won't get same argument's index twice.
co.lastArgIndex = argIndex
break
}
}
}
func (co *callObject) assignKeywordArguments(stack []*Pointer) (err error) {
for argIndex, argType := range co.argTypes() {
if argType == bytecode.RequiredKeywordArg || argType == bytecode.OptionalKeywordArg {
argName := co.argSet.Names()[argIndex]
paramIndex, ok := co.hasKeywordParam(argName)
if ok {
co.callFrame.insertLCL(paramIndex, 0, stack[co.argPtr()+argIndex].Target)
} else {
err = fmt.Errorf("unknown key %s for method %s", argName, co.methodName())
}
}
}
return
}
func (co *callObject) assignSplatArgument(stack []*Pointer, arr *ArrayObject) {
index := len(co.paramTypes()) - 1
for co.argIndex < co.argCount {
arr.Elements = append(arr.Elements, stack[co.argPosition()].Target)
co.argIndex++
}
co.callFrame.insertLCL(index, 0, arr)
}
func (co *callObject) hasKeywordParam(name string) (index int, result bool) {
for paramIndex, paramType := range co.paramTypes() {
paramName := co.paramNames()[paramIndex]
if paramName == name && (paramType == bytecode.RequiredKeywordArg || paramType == bytecode.OptionalKeywordArg) {
index = paramIndex
result = true
return
}
}
return
}
func (co *callObject) hasKeywordArgument(name string) (index int, result bool) {
for argIndex, argType := range co.argTypes() {
argName := co.argSet.Names()[argIndex]
if argName == name && (argType == bytecode.RequiredKeywordArg || argType == bytecode.OptionalKeywordArg) {
index = argIndex
result = true
return
}
}
return
}
func (co *callObject) normalParamsCount() (n int) {
for _, at := range co.paramTypes() {
if at == bytecode.NormalArg {
n++
}
}
return
}
func (co *callObject) normalArgsCount() (n int) {
for _, at := range co.argTypes() {
if at == bytecode.NormalArg {
n++
}
}
return
}