-
Notifications
You must be signed in to change notification settings - Fork 60
/
lazy.go
340 lines (282 loc) · 9.27 KB
/
lazy.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
package types
import (
"encoding/binary"
"encoding/hex"
"fmt"
"log"
"strings"
)
const (
// Constants for lazy ("wrap") types:
// Here "<string>" means 2 bytes of length followed by string contents.
// "<uint8>" means 1 byte field.
// <uint8> fields must go before <string> fields.
//
// Note: both string length and <uint8> are represented using hex encoding.
// One of the reasons is to avoid `|` inside type strings that were wrapped.
// See tests for more details.
// WStaticMethodCall type is "Wrap Static Method Call":
// E.g. Class::doSomething()
// Params: [Class name <string>] [Method name <string>]
WStaticMethodCall byte = iota
// WInstanceMethodCall is a method call on some expression.
// You need to specify expression type (might be lazy type, e.g. <WStaticMethodCall, SomeClass, instance> ).
// E.g. $var->callSomething()
// Params: [Expression type <string>] [Method <string>]
WInstanceMethodCall
// WStaticPropertyFetch is a property fetch for static property :).
// E.g. Test::$something
// Params: [Class name <string>] [Property name with $ <string>]
WStaticPropertyFetch
// WClassConstFetch is a const fetch from a class.
// E.g. Test::CONSTANT
// Params: [Class name <string>] [Constant name <string>]
WClassConstFetch
// WInstancePropertyFetch is a property fetch from some instance.
// You need to provide expression type, see example for WInstanceMethodCall.
// E.g. $var->something
// Params: [Expression type <string>] [Property name <string>]
WInstancePropertyFetch
// WFunctionCall represents a function call.
// Function name must contain namespace. It will be first searched in the defined namespace
// and then it will fall back to root namespace.
// E.g. callSomething()
// Params: [Function name with full namespace <string>]
WFunctionCall
// WArrayOf means that expression is array of another expression
// E.g. <WArrayOf, string> would be normally written as "string[]"
// <WArrayOf, <WFunctionCall, callSomething>>
// Params: [Expression type <string>]
WArrayOf
// WElemOf is the opposite of WArrayOf: it means the type of an element of the expression
// E.g. $arr[0] would be "string" if $arr type is "string[]"
// Params: [Expression type <string>]
WElemOf
// WElemOfKey is extended for of WElemOf where we also save the key that
// was used during the indexing.
// Params: [Expression type <string>] [Key <string>]
WElemOfKey
// WGlobal means global variable.
// E.g. global $Something;
// Params: [Global variable name <string>]
WGlobal
// WConstant means constant
// e.g. type of MINUTE constant
// Params: [Constant name <string>]
WConstant
// WBaseMethodParam is a way to inherit base type method type of nth parameter.
// e.g. type of $x param of foo method from one of the implemented interfaces.
// Params: [Index <uint8>] [Class name <string>] [Method name <string>]
WBaseMethodParam
// WMax must always be last to indicate which byte is the maximum value of a type byte
WMax
)
func slice(typ byte, byteFields []uint8, args ...string) []byte {
bufLen := 1 // hold type info
bufLen += len(byteFields) * 2
for _, a := range args {
bufLen += stringLenBytes // string len
bufLen += len(a)
}
res := make([]byte, 1, bufLen)
res[0] = typ
return res
}
const stringLenBytes = 4
const uint8fieldBytes = 2
func wrap(typ byte, byteFields []uint8, args ...string) string {
var rawBuf [stringLenBytes / 2]byte
var b [stringLenBytes]byte
buf := slice(typ, byteFields, args...)
for _, field := range byteFields {
rawBuf[0] = field
hex.Encode(b[:], rawBuf[:1])
buf = append(buf, b[:uint8fieldBytes]...)
}
for _, s := range args {
binary.LittleEndian.PutUint16(rawBuf[:], uint16(len(s)))
hex.Encode(b[:], rawBuf[:])
buf = append(buf, b[:]...)
buf = append(buf, s...)
}
return string(buf)
}
func unwrap1(s string) (one string) {
return s[stringLenBytes+1:] // do not care about length, there is only 1 param
}
func unwrap2(s string) (one, two string) {
var l int
var b [stringLenBytes]byte
var rawBuf [stringLenBytes / 2]byte
pos := 1
copy(b[:], s[pos:pos+stringLenBytes])
if _, err := hex.Decode(rawBuf[:], b[:]); err != nil {
log.Printf("decode type string error: unwrap2: %v", err)
}
l = int(binary.LittleEndian.Uint16(rawBuf[:]))
pos += stringLenBytes
one = s[pos : pos+l]
pos += l
two = s[pos+stringLenBytes:] // do not care about length of last param
return one, two
}
func unwrap3(s string) (b1 uint8, one, two string) {
var l int
var b [stringLenBytes]byte
var rawBuf [stringLenBytes / 2]byte
pos := 1
copy(b[:], s[pos:pos+uint8fieldBytes])
if _, err := hex.Decode(rawBuf[:], b[:uint8fieldBytes]); err != nil {
log.Printf("decode type string error: unwrap3: %v", err)
}
b1 = rawBuf[0]
pos += uint8fieldBytes
copy(b[:], s[pos:pos+stringLenBytes])
if _, err := hex.Decode(rawBuf[:], b[:]); err != nil {
log.Printf("decode type string error: unwrap3: %v", err)
}
l = int(binary.LittleEndian.Uint16(rawBuf[:]))
pos += stringLenBytes
one = s[pos : pos+l]
pos += l
two = s[pos+stringLenBytes:] // do not care about length of last param
return b1, one, two
}
func WrapBaseMethodParam(paramIndex int, className, methodName string) string {
return wrap(WBaseMethodParam, []uint8{uint8(paramIndex)}, className, methodName)
}
func UnwrapBaseMethodParam(s string) (paramIndex uint8, className, methodName string) {
return unwrap3(s)
}
func WrapStaticMethodCall(className, methodName string) string {
return wrap(WStaticMethodCall, nil, className, methodName)
}
func UnwrapStaticMethodCall(s string) (className, methodName string) {
return unwrap2(s)
}
func WrapInstanceMethodCall(typ, methodName string) string {
return wrap(WInstanceMethodCall, nil, typ, methodName)
}
func UnwrapInstanceMethodCall(s string) (typ, methodName string) {
return unwrap2(s)
}
func WrapClassConstFetch(className, constName string) string {
return wrap(WClassConstFetch, nil, className, constName)
}
func UnwrapClassConstFetch(s string) (className, constName string) {
return unwrap2(s)
}
func WrapStaticPropertyFetch(className, propName string) string {
if !strings.HasPrefix(propName, "$") {
propName = "$" + propName
}
return wrap(WStaticPropertyFetch, nil, className, propName)
}
func UnwrapStaticPropertyFetch(s string) (className, propName string) {
return unwrap2(s)
}
func WrapInstancePropertyFetch(typ, propName string) string {
return wrap(WInstancePropertyFetch, nil, typ, propName)
}
func UnwrapInstancePropertyFetch(s string) (typ, propName string) {
return unwrap2(s)
}
func WrapFunctionCall(funcName string) string {
return wrap(WFunctionCall, nil, funcName)
}
func UnwrapFunctionCall(s string) (funcName string) {
return unwrap1(s)
}
func WrapArrayOf(typ string) string {
return wrap(WArrayOf, nil, typ)
}
func UnwrapArrayOf(s string) (typ string) {
return unwrap1(s)
}
func WrapArray2(ktyp, vtyp string) string {
// TODO: actually support types of keys
return WrapArrayOf(vtyp)
}
func WrapElemOf(typ string) string {
// ElemOf(ArrayOf(typ)) == typ
if len(typ) >= 1+stringLenBytes && typ[0] == WArrayOf {
return typ[1+stringLenBytes:]
}
return wrap(WElemOf, nil, typ)
}
func UnwrapElemOf(s string) (typ string) {
return unwrap1(s)
}
func WrapElemOfKey(typ, key string) string {
if len(typ) >= 1+stringLenBytes && typ[0] == WArrayOf {
return typ[1+stringLenBytes:]
}
return wrap(WElemOfKey, nil, typ, key)
}
func UnwrapElemOfKey(s string) (typ, key string) {
return unwrap2(s)
}
func WrapGlobal(varName string) string {
return wrap(WGlobal, nil, varName)
}
func UnwrapGlobal(s string) (varName string) {
return unwrap1(s)
}
func WrapConstant(constName string) string {
return wrap(WConstant, nil, constName)
}
func UnwrapConstant(s string) (constName string) {
return unwrap1(s)
}
func FormatSimpleType(s string) (res string) {
if IsShape(s) {
s = strings.TrimPrefix(s, `\shape$`)
s = strings.TrimSuffix(s, `$`)
return "shape{" + s + "}"
}
return s
}
func FormatType(s string) (res string) {
if s == "" || s[0] >= WMax {
return FormatSimpleType(s)
}
defer func() {
if r := recover(); r != nil {
res = fmt.Sprintf("panic!(orig='%s', hex='%x')", s, s)
}
}()
switch s[0] {
case WGlobal:
return "global_$" + FormatType(UnwrapGlobal(s))
case WConstant:
return "constant(" + UnwrapConstant(s) + ")"
case WArrayOf:
return FormatType(UnwrapArrayOf(s)) + "[]"
case WElemOf:
return "elem(" + FormatType(UnwrapElemOf(s)) + ")"
case WElemOfKey:
typ, key := UnwrapElemOfKey(s)
return fmt.Sprintf("elem(%s)[%s]", FormatType(typ), key)
case WFunctionCall:
return UnwrapFunctionCall(s) + "()"
case WInstanceMethodCall:
expr, methodName := UnwrapInstanceMethodCall(s)
return "(" + FormatType(expr) + ")->" + methodName + "()"
case WInstancePropertyFetch:
expr, propertyName := UnwrapInstancePropertyFetch(s)
return "(" + FormatType(expr) + ")->" + propertyName
case WBaseMethodParam:
index, className, methodName := unwrap3(s)
return fmt.Sprintf("param(%s)::%s[%d]", className, methodName, index)
case WStaticMethodCall:
className, methodName := UnwrapStaticMethodCall(s)
return className + "::" + methodName + "()"
case WStaticPropertyFetch:
className, propertyName := UnwrapStaticPropertyFetch(s)
return className + "::" + propertyName
case WClassConstFetch:
className, constName := UnwrapClassConstFetch(s)
return className + "::" + constName
}
return "unknown(" + s + ")"
}