@smoothbricks/lmao-ttsc 0.0.0-bootstrap.0 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bun-register.d.ts +2 -0
- package/dist/bun-register.d.ts.map +1 -0
- package/dist/bun-register.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/package.json +113 -4
- package/plugin/go.mod +9 -0
- package/plugin/loginline.go +296 -0
- package/plugin/main.go +572 -0
- package/plugin/main_test.go +43 -0
- package/plugin/optimization.go +462 -0
- package/plugin/resultinline.go +211 -0
- package/plugin/taginline.go +453 -0
- package/plugin/template_test.go +264 -0
- package/plugin.cjs +19 -0
- package/plugin.d.cts +5 -0
- package/README.md +0 -3
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"sort"
|
|
5
|
+
"strings"
|
|
6
|
+
|
|
7
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
8
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
const (
|
|
12
|
+
runtimeHintTag uint32 = 1 << 16
|
|
13
|
+
runtimeHintLog uint32 = 1 << 17
|
|
14
|
+
runtimeHintFF uint32 = 1 << 18
|
|
15
|
+
runtimeHintSpan uint32 = 1 << 19
|
|
16
|
+
runtimeHintResult uint32 = 1 << 20
|
|
17
|
+
runtimeHintScope uint32 = 1 << 21
|
|
18
|
+
runtimeHintDeps uint32 = 1 << 22
|
|
19
|
+
runtimeHintAnalyzed uint32 = 1 << 23
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
var contextCapability = map[string]uint32{
|
|
23
|
+
"tag": runtimeHintTag, "log": runtimeHintLog, "ff": runtimeHintFF,
|
|
24
|
+
"span": runtimeHintSpan, "spanSync": runtimeHintSpan,
|
|
25
|
+
"ok": runtimeHintResult, "err": runtimeHintResult,
|
|
26
|
+
"scope": runtimeHintScope, "setScope": runtimeHintScope, "deps": runtimeHintDeps,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type opCompileAnalysis struct {
|
|
30
|
+
runtimeHint uint32
|
|
31
|
+
logTemplateIds []string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type hintRewrite struct {
|
|
35
|
+
call *shimast.CallExpression
|
|
36
|
+
hints map[string]opCompileAnalysis
|
|
37
|
+
single opCompileAnalysis
|
|
38
|
+
isGroup bool
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const maxLogTemplateID = 0xffff
|
|
42
|
+
|
|
43
|
+
func functionParts(node *shimast.Node) (params *shimast.ParameterList, body *shimast.Node, async bool, ok bool) {
|
|
44
|
+
switch node.Kind {
|
|
45
|
+
case shimast.KindArrowFunction:
|
|
46
|
+
fn := node.AsArrowFunction()
|
|
47
|
+
return fn.Parameters, fn.Body, node.ModifierFlags()&shimast.ModifierFlagsAsync != 0, true
|
|
48
|
+
case shimast.KindFunctionExpression:
|
|
49
|
+
fn := node.AsFunctionExpression()
|
|
50
|
+
return fn.Parameters, fn.Body, node.ModifierFlags()&shimast.ModifierFlagsAsync != 0, true
|
|
51
|
+
case shimast.KindMethodDeclaration:
|
|
52
|
+
fn := node.AsMethodDeclaration()
|
|
53
|
+
return fn.Parameters, fn.Body, node.ModifierFlags()&shimast.ModifierFlagsAsync != 0, true
|
|
54
|
+
default:
|
|
55
|
+
return nil, nil, false, false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func isNestedFunction(node *shimast.Node) bool {
|
|
60
|
+
switch node.Kind {
|
|
61
|
+
case shimast.KindArrowFunction, shimast.KindFunctionExpression, shimast.KindFunctionDeclaration,
|
|
62
|
+
shimast.KindMethodDeclaration, shimast.KindGetAccessor, shimast.KindSetAccessor, shimast.KindConstructor:
|
|
63
|
+
return true
|
|
64
|
+
default:
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func isLoop(node *shimast.Node) bool {
|
|
70
|
+
switch node.Kind {
|
|
71
|
+
case shimast.KindForStatement, shimast.KindForInStatement, shimast.KindForOfStatement,
|
|
72
|
+
shimast.KindWhileStatement, shimast.KindDoStatement:
|
|
73
|
+
return true
|
|
74
|
+
default:
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// analyzeOpFunction is intentionally closed-world. A hint is emitted only when
|
|
80
|
+
// every use of the context parameter is a direct access to a known capability.
|
|
81
|
+
func analyzeOpFunction(fn *shimast.Node) uint32 {
|
|
82
|
+
params, body, _, ok := functionParts(fn)
|
|
83
|
+
if !ok || params == nil || len(params.Nodes) == 0 || body == nil {
|
|
84
|
+
return 0
|
|
85
|
+
}
|
|
86
|
+
first := params.Nodes[0]
|
|
87
|
+
if first.Kind != shimast.KindParameter || first.Name() == nil || first.Name().Kind != shimast.KindIdentifier {
|
|
88
|
+
return 0
|
|
89
|
+
}
|
|
90
|
+
ctxName := shimast.NodeText(first.Name())
|
|
91
|
+
if ctxName == "" {
|
|
92
|
+
return 0
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
valid := true
|
|
96
|
+
capacityKnown := true
|
|
97
|
+
capacity := uint32(2) // rows 0 (tags) and 1 (terminal result)
|
|
98
|
+
caps := uint32(0)
|
|
99
|
+
var visit func(*shimast.Node, bool)
|
|
100
|
+
visit = func(node *shimast.Node, root bool) {
|
|
101
|
+
if node == nil || !valid {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
if !root && isNestedFunction(node) {
|
|
105
|
+
valid = false
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
if isLoop(node) {
|
|
109
|
+
capacityKnown = false
|
|
110
|
+
}
|
|
111
|
+
if node.Kind == shimast.KindIdentifier && shimast.NodeText(node) == ctxName {
|
|
112
|
+
parent := node.Parent
|
|
113
|
+
if parent == nil || parent.Kind != shimast.KindPropertyAccessExpression {
|
|
114
|
+
valid = false
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
pa := parent.AsPropertyAccessExpression()
|
|
118
|
+
if pa.Expression != node || pa.Name() == nil {
|
|
119
|
+
valid = false
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
name := shimast.NodeText(pa.Name())
|
|
123
|
+
capability, recognized := contextCapability[name]
|
|
124
|
+
if !recognized || !isClosedWorldCapabilityUse(parent, name) {
|
|
125
|
+
valid = false
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
caps |= capability
|
|
129
|
+
}
|
|
130
|
+
if node.Kind == shimast.KindCallExpression {
|
|
131
|
+
call := node.AsCallExpression()
|
|
132
|
+
if call.Expression.Kind == shimast.KindPropertyAccessExpression {
|
|
133
|
+
method := call.Expression.AsPropertyAccessExpression()
|
|
134
|
+
if logMethods[shimast.NodeText(method.Name())] && method.Expression.Kind == shimast.KindPropertyAccessExpression {
|
|
135
|
+
logAccess := method.Expression.AsPropertyAccessExpression()
|
|
136
|
+
if shimast.NodeText(logAccess.Name()) == "log" && logAccess.Expression.Kind == shimast.KindIdentifier && shimast.NodeText(logAccess.Expression) == ctxName {
|
|
137
|
+
if capacityKnown && capacity < 0xffff {
|
|
138
|
+
capacity++
|
|
139
|
+
} else {
|
|
140
|
+
capacityKnown = false
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
147
|
+
visit(child, false)
|
|
148
|
+
return !valid
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
visit(body, true)
|
|
152
|
+
if !valid {
|
|
153
|
+
return 0
|
|
154
|
+
}
|
|
155
|
+
if !capacityKnown {
|
|
156
|
+
capacity = 0
|
|
157
|
+
}
|
|
158
|
+
return runtimeHintAnalyzed | caps | capacity
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
func literalLogMessage(node *shimast.Node) (string, bool) {
|
|
162
|
+
if node == nil {
|
|
163
|
+
return "", false
|
|
164
|
+
}
|
|
165
|
+
switch node.Kind {
|
|
166
|
+
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
167
|
+
return shimast.NodeText(node), true
|
|
168
|
+
default:
|
|
169
|
+
return "", false
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// analyzeOpCompileMetadata runs against the untouched tree. It assigns IDs in
|
|
174
|
+
// lexical order and records the exact call nodes consumed later by the emitter.
|
|
175
|
+
func (t *fileTransformer) analyzeOpCompileMetadata(fn *shimast.Node) opCompileAnalysis {
|
|
176
|
+
analysis := opCompileAnalysis{runtimeHint: analyzeOpFunction(fn)}
|
|
177
|
+
params, body, _, ok := functionParts(fn)
|
|
178
|
+
if !ok || params == nil || len(params.Nodes) == 0 || body == nil {
|
|
179
|
+
return analysis
|
|
180
|
+
}
|
|
181
|
+
first := params.Nodes[0]
|
|
182
|
+
if first.Kind != shimast.KindParameter || first.Name() == nil || first.Name().Kind != shimast.KindIdentifier {
|
|
183
|
+
return analysis
|
|
184
|
+
}
|
|
185
|
+
contextName := shimast.NodeText(first.Name())
|
|
186
|
+
idsByMessage := map[string]uint16{}
|
|
187
|
+
var visit func(*shimast.Node)
|
|
188
|
+
visit = func(node *shimast.Node) {
|
|
189
|
+
if node == nil {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
if isNestedFunction(node) {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
if node.Kind == shimast.KindCallExpression {
|
|
196
|
+
call := node.AsCallExpression()
|
|
197
|
+
if len(call.Arguments.Nodes) == 1 && call.Expression.Kind == shimast.KindPropertyAccessExpression {
|
|
198
|
+
method := call.Expression.AsPropertyAccessExpression()
|
|
199
|
+
logNode := method.Expression
|
|
200
|
+
if logMethods[shimast.NodeText(method.Name())] && logNode.Kind == shimast.KindPropertyAccessExpression {
|
|
201
|
+
logAccess := logNode.AsPropertyAccessExpression()
|
|
202
|
+
if shimast.NodeText(logAccess.Name()) == "log" && logAccess.Expression.Kind == shimast.KindIdentifier &&
|
|
203
|
+
shimast.NodeText(logAccess.Expression) == contextName {
|
|
204
|
+
logType := t.checker.GetTypeAtLocation(logNode)
|
|
205
|
+
if logType != nil && isSpanLoggerType(t.checker, logType) {
|
|
206
|
+
if message, literal := literalLogMessage(call.Arguments.Nodes[0]); literal {
|
|
207
|
+
id, exists := idsByMessage[message]
|
|
208
|
+
if !exists && len(analysis.logTemplateIds) < maxLogTemplateID {
|
|
209
|
+
analysis.logTemplateIds = append(analysis.logTemplateIds, message)
|
|
210
|
+
id = uint16(len(analysis.logTemplateIds))
|
|
211
|
+
idsByMessage[message] = id
|
|
212
|
+
exists = true
|
|
213
|
+
}
|
|
214
|
+
if exists {
|
|
215
|
+
t.logTemplateIDs[call] = id
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
224
|
+
visit(child)
|
|
225
|
+
return false
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
visit(body)
|
|
229
|
+
return analysis
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
func isClosedWorldCapabilityUse(access *shimast.Node, name string) bool {
|
|
233
|
+
parent := access.Parent
|
|
234
|
+
switch name {
|
|
235
|
+
case "log", "tag":
|
|
236
|
+
if parent == nil || parent.Kind != shimast.KindPropertyAccessExpression || parent.AsPropertyAccessExpression().Expression != access {
|
|
237
|
+
return false
|
|
238
|
+
}
|
|
239
|
+
if name == "log" && !logMethods[shimast.NodeText(parent.AsPropertyAccessExpression().Name())] {
|
|
240
|
+
return false
|
|
241
|
+
}
|
|
242
|
+
grandparent := parent.Parent
|
|
243
|
+
return grandparent != nil && grandparent.Kind == shimast.KindCallExpression && grandparent.AsCallExpression().Expression == parent
|
|
244
|
+
case "ff":
|
|
245
|
+
if parent != nil && parent.Kind == shimast.KindCallExpression && parent.AsCallExpression().Expression == access {
|
|
246
|
+
return true
|
|
247
|
+
}
|
|
248
|
+
if parent == nil || parent.Kind != shimast.KindPropertyAccessExpression || parent.AsPropertyAccessExpression().Expression != access {
|
|
249
|
+
return false
|
|
250
|
+
}
|
|
251
|
+
grandparent := parent.Parent
|
|
252
|
+
return grandparent != nil && grandparent.Kind == shimast.KindCallExpression && grandparent.AsCallExpression().Expression == parent
|
|
253
|
+
case "span", "spanSync", "ok", "err", "setScope":
|
|
254
|
+
return parent != nil && parent.Kind == shimast.KindCallExpression && parent.AsCallExpression().Expression == access
|
|
255
|
+
case "deps", "scope":
|
|
256
|
+
return parent != nil && parent.Kind == shimast.KindPropertyAccessExpression && parent.AsPropertyAccessExpression().Expression == access
|
|
257
|
+
default:
|
|
258
|
+
return false
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
func literalPropertyName(node *shimast.Node) (string, bool) {
|
|
263
|
+
if node == nil {
|
|
264
|
+
return "", false
|
|
265
|
+
}
|
|
266
|
+
switch node.Kind {
|
|
267
|
+
case shimast.KindIdentifier, shimast.KindStringLiteral, shimast.KindNumericLiteral:
|
|
268
|
+
return shimast.NodeText(node), true
|
|
269
|
+
default:
|
|
270
|
+
return "", false
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
func collectDefineOpsHints(t *fileTransformer, object *shimast.ObjectLiteralExpression) map[string]opCompileAnalysis {
|
|
275
|
+
hints := map[string]opCompileAnalysis{}
|
|
276
|
+
for _, prop := range object.Properties.Nodes {
|
|
277
|
+
if prop.Name() == nil {
|
|
278
|
+
continue
|
|
279
|
+
}
|
|
280
|
+
key, named := literalPropertyName(prop.Name())
|
|
281
|
+
if !named {
|
|
282
|
+
continue
|
|
283
|
+
}
|
|
284
|
+
switch prop.Kind {
|
|
285
|
+
case shimast.KindPropertyAssignment:
|
|
286
|
+
initializer := prop.AsPropertyAssignment().Initializer
|
|
287
|
+
if _, _, _, ok := functionParts(initializer); ok {
|
|
288
|
+
hints[key] = t.analyzeOpCompileMetadata(initializer)
|
|
289
|
+
}
|
|
290
|
+
case shimast.KindMethodDeclaration:
|
|
291
|
+
hints[key] = t.analyzeOpCompileMetadata(prop)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return hints
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
func isLmaoDeclarationNode(declaration *shimast.Node) bool {
|
|
298
|
+
if declaration == nil {
|
|
299
|
+
return false
|
|
300
|
+
}
|
|
301
|
+
if source := shimast.GetSourceFileOfNode(declaration); source != nil {
|
|
302
|
+
fileName := strings.ReplaceAll(source.FileName(), "\\", "/")
|
|
303
|
+
if strings.HasPrefix(fileName, "packages/lmao/") || strings.Contains(fileName, "/packages/lmao/") || strings.Contains(fileName, "/node_modules/@smoothbricks/lmao/") {
|
|
304
|
+
return true
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for current := declaration; current != nil; current = current.Parent {
|
|
308
|
+
if current.Kind == shimast.KindModuleDeclaration && current.Name() != nil && shimast.NodeText(current.Name()) == "@smoothbricks/lmao" {
|
|
309
|
+
return true
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return false
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
func isNamedType(chk *shimchecker.Checker, node *shimast.Node, name string) bool {
|
|
316
|
+
typ := chk.GetTypeAtLocation(node)
|
|
317
|
+
if typ == nil {
|
|
318
|
+
return false
|
|
319
|
+
}
|
|
320
|
+
if sym := shimchecker.Type_getTypeNameSymbol(typ); sym != nil && sym.Name == name {
|
|
321
|
+
return true
|
|
322
|
+
}
|
|
323
|
+
return strings.HasPrefix(chk.TypeToString(typ), name+"<")
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
func isNamedLmaoType(chk *shimchecker.Checker, node *shimast.Node, name string) bool {
|
|
327
|
+
typ := chk.GetTypeAtLocation(node)
|
|
328
|
+
if typ == nil {
|
|
329
|
+
return false
|
|
330
|
+
}
|
|
331
|
+
sym := shimchecker.Type_getTypeNameSymbol(typ)
|
|
332
|
+
if sym == nil || sym.Name != name {
|
|
333
|
+
return false
|
|
334
|
+
}
|
|
335
|
+
for _, declaration := range sym.Declarations {
|
|
336
|
+
if isLmaoDeclarationNode(declaration) {
|
|
337
|
+
return true
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return false
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
func hasLmaoCallProvenance(chk *shimchecker.Checker, call *shimast.CallExpression) bool {
|
|
344
|
+
signature := chk.GetResolvedSignature(call.AsNode())
|
|
345
|
+
return signature != nil && isLmaoDeclarationNode(signature.Declaration())
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
func (t *fileTransformer) collectOptimizations(root *shimast.Node) []hintRewrite {
|
|
349
|
+
if t.checker == nil {
|
|
350
|
+
return nil
|
|
351
|
+
}
|
|
352
|
+
var hints []hintRewrite
|
|
353
|
+
var visit func(*shimast.Node)
|
|
354
|
+
visit = func(node *shimast.Node) {
|
|
355
|
+
if node == nil {
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
if node.Kind == shimast.KindCallExpression {
|
|
359
|
+
call := node.AsCallExpression()
|
|
360
|
+
_, name := calleeNames(call)
|
|
361
|
+
provenLmaoCall := hasLmaoCallProvenance(t.checker, call)
|
|
362
|
+
switch name {
|
|
363
|
+
case "defineOp":
|
|
364
|
+
if provenLmaoCall && len(call.Arguments.Nodes) >= 2 && len(call.Arguments.Nodes) < 4 && isNamedType(t.checker, call.AsNode(), "Op") {
|
|
365
|
+
hints = append(hints, hintRewrite{call: call, single: t.analyzeOpCompileMetadata(call.Arguments.Nodes[1])})
|
|
366
|
+
}
|
|
367
|
+
case "defineOps":
|
|
368
|
+
if provenLmaoCall && len(call.Arguments.Nodes) == 1 && call.Arguments.Nodes[0].Kind == shimast.KindObjectLiteralExpression && isNamedType(t.checker, call.AsNode(), "OpGroup") {
|
|
369
|
+
group := collectDefineOpsHints(t, call.Arguments.Nodes[0].AsObjectLiteralExpression())
|
|
370
|
+
if len(group) > 0 {
|
|
371
|
+
hints = append(hints, hintRewrite{call: call, hints: group, isGroup: true})
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
case "span":
|
|
375
|
+
recv, _ := calleeNames(call)
|
|
376
|
+
if recv != nil && len(call.Arguments.Nodes) >= 2 && len(call.Arguments.Nodes) <= 10 &&
|
|
377
|
+
(recv.Kind == shimast.KindIdentifier || recv.Kind == shimast.KindThisKeyword) &&
|
|
378
|
+
call.Arguments.Nodes[1].Kind == shimast.KindIdentifier &&
|
|
379
|
+
isNamedLmaoType(t.checker, call.Arguments.Nodes[1], "Op") {
|
|
380
|
+
recvType := t.checker.GetTypeAtLocation(recv)
|
|
381
|
+
if recvType != nil && isLmaoContextType(t.checker, recvType) {
|
|
382
|
+
t.opSpans[call] = true
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
388
|
+
visit(child)
|
|
389
|
+
return false
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
visit(root)
|
|
393
|
+
return hints
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
func isOpType(chk *shimchecker.Checker, typ *shimchecker.Type) bool {
|
|
397
|
+
name := chk.TypeToString(typ)
|
|
398
|
+
if strings.HasPrefix(name, "Op<") {
|
|
399
|
+
return true
|
|
400
|
+
}
|
|
401
|
+
sym := shimchecker.Type_getTypeNameSymbol(typ)
|
|
402
|
+
return sym != nil && sym.Name == "Op"
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
func (t *fileTransformer) spanContextApproved(node *shimast.Node) bool {
|
|
406
|
+
parent := node.Parent
|
|
407
|
+
if parent == nil {
|
|
408
|
+
return false
|
|
409
|
+
}
|
|
410
|
+
if parent.Kind == shimast.KindAwaitExpression && parent.AsAwaitExpression().Expression == node {
|
|
411
|
+
return true
|
|
412
|
+
}
|
|
413
|
+
if parent.Kind == shimast.KindReturnStatement && parent.AsReturnStatement().Expression == node {
|
|
414
|
+
for current := parent.Parent; current != nil; current = current.Parent {
|
|
415
|
+
if isNestedFunction(current) {
|
|
416
|
+
return current.ModifierFlags()&shimast.ModifierFlagsAsync != 0
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return false
|
|
420
|
+
}
|
|
421
|
+
if parent.Kind == shimast.KindArrowFunction {
|
|
422
|
+
fn := parent.AsArrowFunction()
|
|
423
|
+
return fn.Body == node && parent.ModifierFlags()&shimast.ModifierFlagsAsync != 0
|
|
424
|
+
}
|
|
425
|
+
return false
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
func compileMetadataNode(analysis opCompileAnalysis) *shimast.Node {
|
|
429
|
+
templates := make([]*shimast.Node, len(analysis.logTemplateIds))
|
|
430
|
+
for i, message := range analysis.logTemplateIds {
|
|
431
|
+
templates[i] = str(message)
|
|
432
|
+
}
|
|
433
|
+
return factory.NewObjectLiteralExpression(factory.NewNodeList([]*shimast.Node{
|
|
434
|
+
factory.NewPropertyAssignment(nil, ident("runtimeHint"), nil, nil, num(int(analysis.runtimeHint))),
|
|
435
|
+
factory.NewPropertyAssignment(nil, ident("logTemplateIds"), nil, nil,
|
|
436
|
+
factory.NewArrayLiteralExpression(factory.NewNodeList(templates), false)),
|
|
437
|
+
}), true)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
func applyHintRewrites(rewrites []hintRewrite) {
|
|
441
|
+
for _, rewrite := range rewrites {
|
|
442
|
+
args := append([]*shimast.Node{}, rewrite.call.Arguments.Nodes...)
|
|
443
|
+
if rewrite.isGroup {
|
|
444
|
+
names := make([]string, 0, len(rewrite.hints))
|
|
445
|
+
for name := range rewrite.hints {
|
|
446
|
+
names = append(names, name)
|
|
447
|
+
}
|
|
448
|
+
sort.Strings(names)
|
|
449
|
+
props := make([]*shimast.Node, 0, len(names))
|
|
450
|
+
for _, name := range names {
|
|
451
|
+
props = append(props, factory.NewPropertyAssignment(nil, str(name), nil, nil, compileMetadataNode(rewrite.hints[name])))
|
|
452
|
+
}
|
|
453
|
+
args = append(args, factory.NewObjectLiteralExpression(factory.NewNodeList(props), true))
|
|
454
|
+
} else {
|
|
455
|
+
for len(args) < 3 {
|
|
456
|
+
args = append(args, ident("undefined"))
|
|
457
|
+
}
|
|
458
|
+
args = append(args, compileMetadataNode(rewrite.single))
|
|
459
|
+
}
|
|
460
|
+
rewrite.call.Arguments = factory.NewNodeList(args)
|
|
461
|
+
}
|
|
462
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// resultinline.go — partial inlining of result chains (ctx.ok/ctx.err).
|
|
2
|
+
//
|
|
3
|
+
// The runtime's Ok/Err fluent methods (.line/.message/.with/schema setters)
|
|
4
|
+
// delegate to a GeneratedResultWriter — a fixed-position writer at ROW 1
|
|
5
|
+
// (packages/lmao/src/lib/codegen/fixedPositionWriterGenerator.ts), the same
|
|
6
|
+
// family as the TagWriter at row 0. That makes result chains tag-shaped:
|
|
7
|
+
// every chained write lands at a compile-time-known index. The partial
|
|
8
|
+
// inline keeps the ok()/err() call itself (entry semantics, value capture)
|
|
9
|
+
// and replaces the fluent chain with direct writes at [1], guarded on the
|
|
10
|
+
// result's buffer being present (reproducing the runtime's
|
|
11
|
+
// `_resultWriter()?.` no-op when there is no buffer):
|
|
12
|
+
//
|
|
13
|
+
// return ctx.ok(user).line(42).with({ userId: u });
|
|
14
|
+
//
|
|
15
|
+
// becomes
|
|
16
|
+
//
|
|
17
|
+
// {
|
|
18
|
+
// const $$r = ctx.ok(user);
|
|
19
|
+
// const $$b = $$r._buffer;
|
|
20
|
+
// if ($$b) {
|
|
21
|
+
// if ($$b.line_values) { $$b.line_values[1] = 42; ...nulls bit 1... }
|
|
22
|
+
// if ($$b.userId_values) { $$b.userId_values[1] = u; ... }
|
|
23
|
+
// }
|
|
24
|
+
// return $$r;
|
|
25
|
+
// }
|
|
26
|
+
//
|
|
27
|
+
// Fires in ExpressionStatement and ReturnStatement contexts. Conservative
|
|
28
|
+
// bails: receiver not Checker-proved LMAO context (TaskContext/SpanContext/
|
|
29
|
+
// ModuleContext/RequestContext), chained method that is not line/message/
|
|
30
|
+
// with/schema field, non-literal with(), non-single-arg links.
|
|
31
|
+
package main
|
|
32
|
+
|
|
33
|
+
import (
|
|
34
|
+
"strings"
|
|
35
|
+
|
|
36
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
37
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
var lmaoContextTypeNames = []string{"TaskContext", "SpanContext", "ModuleContext", "RequestContext"}
|
|
41
|
+
|
|
42
|
+
type resultInline struct {
|
|
43
|
+
list *shimast.NodeList
|
|
44
|
+
index int
|
|
45
|
+
isReturn bool
|
|
46
|
+
okCall *shimast.Node // the ctx.ok(...) / ctx.err(...) call, kept verbatim
|
|
47
|
+
writes []tagWrite // line/message/schema writes in execution order
|
|
48
|
+
schema map[string]schemaField
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func isLmaoContextType(chk *shimchecker.Checker, t *shimchecker.Type) bool {
|
|
52
|
+
name := chk.TypeToString(t)
|
|
53
|
+
for _, w := range lmaoContextTypeNames {
|
|
54
|
+
if name == w || strings.HasPrefix(name, w+"<") {
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if sym := shimchecker.Type_getTypeNameSymbol(t); sym != nil {
|
|
59
|
+
for _, w := range lmaoContextTypeNames {
|
|
60
|
+
if sym.Name == w {
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// findResultInline analyzes an ok/err chain. Checker queries only; no mutation.
|
|
69
|
+
func (t *fileTransformer) findResultInline(call *shimast.CallExpression) (*resultInline, bool) {
|
|
70
|
+
if t.checker == nil {
|
|
71
|
+
return nil, false
|
|
72
|
+
}
|
|
73
|
+
type link struct {
|
|
74
|
+
name string
|
|
75
|
+
args []*shimast.Node
|
|
76
|
+
}
|
|
77
|
+
var links []link
|
|
78
|
+
current := call
|
|
79
|
+
for {
|
|
80
|
+
expr := current.Expression
|
|
81
|
+
if expr.Kind != shimast.KindPropertyAccessExpression {
|
|
82
|
+
return nil, false
|
|
83
|
+
}
|
|
84
|
+
pa := expr.AsPropertyAccessExpression()
|
|
85
|
+
name := shimast.NodeText(pa.Name())
|
|
86
|
+
|
|
87
|
+
if resultMethods[name] {
|
|
88
|
+
// Chain root: receiver must be a Checker-proved LMAO context.
|
|
89
|
+
recvType := t.checker.GetTypeAtLocation(pa.Expression)
|
|
90
|
+
if recvType == nil || !isLmaoContextType(t.checker, recvType) {
|
|
91
|
+
return nil, false
|
|
92
|
+
}
|
|
93
|
+
if len(links) == 0 {
|
|
94
|
+
return nil, false // bare ok()/err(): nothing to inline
|
|
95
|
+
}
|
|
96
|
+
// Schema comes from the Ok/Err type's own fluent surface.
|
|
97
|
+
okType := t.checker.GetTypeAtLocation(current.AsNode())
|
|
98
|
+
schema := map[string]schemaField{}
|
|
99
|
+
if okType != nil {
|
|
100
|
+
schema = extractSchema(t.checker, okType)
|
|
101
|
+
for _, sys := range []string{"line", "message", "isOk", "isErr", "map", "match", "unwrapOr", "success"} {
|
|
102
|
+
delete(schema, sys)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Mark the kept ok/err call processed so the §6 chain-line pass
|
|
106
|
+
// does not ALSO append .line() to it inside our block.
|
|
107
|
+
t.processed[current] = true
|
|
108
|
+
in := &resultInline{okCall: current.AsNode(), schema: schema}
|
|
109
|
+
hasLine := false
|
|
110
|
+
for _, l := range links {
|
|
111
|
+
if l.name == "line" {
|
|
112
|
+
hasLine = true
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if !hasLine {
|
|
116
|
+
// §6 semantics folded in: inject the call-site line.
|
|
117
|
+
in.writes = append(in.writes, tagWrite{field: "line", arg: num(t.lineOf(current.AsNode()))})
|
|
118
|
+
}
|
|
119
|
+
for _, l := range links {
|
|
120
|
+
switch {
|
|
121
|
+
case l.name == "line" || l.name == "message":
|
|
122
|
+
if len(l.args) != 1 {
|
|
123
|
+
return nil, false
|
|
124
|
+
}
|
|
125
|
+
in.writes = append(in.writes, tagWrite{field: l.name, arg: l.args[0]})
|
|
126
|
+
case l.name == "with":
|
|
127
|
+
if len(l.args) != 1 || l.args[0].Kind != shimast.KindObjectLiteralExpression {
|
|
128
|
+
return nil, false
|
|
129
|
+
}
|
|
130
|
+
for _, prop := range l.args[0].AsObjectLiteralExpression().Properties.Nodes {
|
|
131
|
+
if prop.Kind != shimast.KindPropertyAssignment {
|
|
132
|
+
return nil, false
|
|
133
|
+
}
|
|
134
|
+
p := prop.AsPropertyAssignment()
|
|
135
|
+
nameNode := p.Name()
|
|
136
|
+
if nameNode.Kind != shimast.KindIdentifier && nameNode.Kind != shimast.KindStringLiteral {
|
|
137
|
+
return nil, false
|
|
138
|
+
}
|
|
139
|
+
field := shimast.NodeText(nameNode)
|
|
140
|
+
if _, known := in.schema[field]; !known {
|
|
141
|
+
return nil, false
|
|
142
|
+
}
|
|
143
|
+
in.writes = append(in.writes, tagWrite{field: field, arg: p.Initializer})
|
|
144
|
+
}
|
|
145
|
+
default:
|
|
146
|
+
if len(l.args) != 1 {
|
|
147
|
+
return nil, false
|
|
148
|
+
}
|
|
149
|
+
if _, known := in.schema[l.name]; !known {
|
|
150
|
+
return nil, false
|
|
151
|
+
}
|
|
152
|
+
in.writes = append(in.writes, tagWrite{field: l.name, arg: l.args[0]})
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return in, true
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
links = append([]link{{name: name, args: current.Arguments.Nodes}}, links...)
|
|
159
|
+
next := pa.Expression
|
|
160
|
+
if next.Kind != shimast.KindCallExpression {
|
|
161
|
+
return nil, false
|
|
162
|
+
}
|
|
163
|
+
current = next.AsCallExpression()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// applyResultInlines splices replacement blocks (phase B — no checker use).
|
|
168
|
+
func (t *fileTransformer) applyResultInlines(inlines []resultInline) {
|
|
169
|
+
for _, in := range inlines {
|
|
170
|
+
res := ident("$$r")
|
|
171
|
+
buf := ident("$$b")
|
|
172
|
+
|
|
173
|
+
var writes []*shimast.Node
|
|
174
|
+
for _, w := range in.writes {
|
|
175
|
+
info, known := in.schema[w.field]
|
|
176
|
+
switch {
|
|
177
|
+
case known && info.kind == fieldEnum:
|
|
178
|
+
var enumIdx *shimast.Node
|
|
179
|
+
if isCompileTimeLiteral(w.arg) && w.arg.Kind == shimast.KindStringLiteral {
|
|
180
|
+
v := 0
|
|
181
|
+
for i, ev := range info.enumValues {
|
|
182
|
+
if ev == shimast.NodeText(w.arg) {
|
|
183
|
+
v = i
|
|
184
|
+
break
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
enumIdx = num(v)
|
|
188
|
+
} else {
|
|
189
|
+
enumIdx = enumSwitchIIFE(w.arg, info.enumValues)
|
|
190
|
+
}
|
|
191
|
+
writes = append(writes, factory.NewExpressionStatement(
|
|
192
|
+
callExpr(propAccess(buf, w.field), []*shimast.Node{num(1), enumIdx})))
|
|
193
|
+
case known && info.kind == fieldBool:
|
|
194
|
+
writes = append(writes, factory.NewExpressionStatement(
|
|
195
|
+
callExpr(propAccess(buf, w.field), []*shimast.Node{num(1), w.arg})))
|
|
196
|
+
default:
|
|
197
|
+
writes = append(writes, guardedRawWrite(buf, num(1), w.field, w.arg))
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
stmts := []*shimast.Node{
|
|
202
|
+
constDecl(res, in.okCall),
|
|
203
|
+
constDecl(buf, propAccess(res, "_buffer")),
|
|
204
|
+
factory.NewIfStatement(buf, factory.NewBlock(factory.NewNodeList(writes), true), nil),
|
|
205
|
+
}
|
|
206
|
+
if in.isReturn {
|
|
207
|
+
stmts = append(stmts, factory.NewReturnStatement(res))
|
|
208
|
+
}
|
|
209
|
+
in.list.Nodes[in.index] = factory.NewBlock(factory.NewNodeList(stmts), true)
|
|
210
|
+
}
|
|
211
|
+
}
|