@@ -19,20 +19,21 @@ import (
19
19
20
20
// newFunctionContext creates a new nested context for a function corresponding
21
21
// to the provided info.
22
- func (fc * funcContext ) nestedFunctionContext (info * analysis.FuncInfo , sig * types.Signature ) * funcContext {
22
+ func (fc * funcContext ) nestedFunctionContext (info * analysis.FuncInfo , o * types.Func ) * funcContext {
23
23
if info == nil {
24
24
panic (errors .New ("missing *analysis.FuncInfo" ))
25
25
}
26
- if sig == nil {
27
- panic (errors .New ("missing *types.Signature " ))
26
+ if o == nil {
27
+ panic (errors .New ("missing *types.Func " ))
28
28
}
29
29
30
30
c := & funcContext {
31
31
FuncInfo : info ,
32
+ funcObject : o ,
32
33
pkgCtx : fc .pkgCtx ,
33
34
genericCtx : fc .genericCtx ,
34
35
parent : fc ,
35
- sigTypes : & signatureTypes {Sig : sig },
36
+ sigTypes : & signatureTypes {Sig : o . Type ().( * types. Signature ) },
36
37
allVars : make (map [string ]int , len (fc .allVars )),
37
38
localVars : []string {},
38
39
flowDatas : map [* types.Label ]* flowData {nil : {}},
@@ -44,6 +45,42 @@ func (fc *funcContext) nestedFunctionContext(info *analysis.FuncInfo, sig *types
44
45
c .allVars [k ] = v
45
46
}
46
47
48
+ // Synthesize an identifier by which the function may reference itself. Since
49
+ // it appears in the stack trace, it's useful to include the receiver type in
50
+ // it.
51
+ funcRef := o .Name ()
52
+ if typeName := c .sigTypes .RecvTypeName (); typeName != "" {
53
+ funcRef = typeName + midDot + funcRef
54
+ }
55
+ c .funcRef = c .newVariable (funcRef , varPackage )
56
+
57
+ // If the function has type parameters, create a new generic context for it.
58
+ if c .sigTypes .IsGeneric () {
59
+ c .genericCtx = & genericCtx {}
60
+ }
61
+
62
+ return c
63
+ }
64
+
65
+ // namedFuncContext creates a new funcContext for a named Go function
66
+ // (standalone or method).
67
+ func (fc * funcContext ) namedFuncContext (fun * ast.FuncDecl ) * funcContext {
68
+ o := fc .pkgCtx .Defs [fun .Name ].(* types.Func )
69
+ info := fc .pkgCtx .FuncDeclInfos [o ]
70
+ c := fc .nestedFunctionContext (info , o )
71
+
72
+ return c
73
+ }
74
+
75
+ // literalFuncContext creates a new funcContext for a function literal. Since
76
+ // go/types doesn't generate *types.Func objects for function literals, we
77
+ // generate a synthetic one for it.
78
+ func (fc * funcContext ) literalFuncContext (fun * ast.FuncLit ) * funcContext {
79
+ info := fc .pkgCtx .FuncLitInfos [fun ]
80
+ sig := fc .pkgCtx .TypeOf (fun ).(* types.Signature )
81
+ o := types .NewFunc (fun .Pos (), fc .pkgCtx .Pkg , fc .newLitFuncName (), sig )
82
+
83
+ c := fc .nestedFunctionContext (info , o )
47
84
return c
48
85
}
49
86
@@ -68,9 +105,6 @@ func (fc *funcContext) translateTopLevelFunction(fun *ast.FuncDecl) []byte {
68
105
// package context. Exported functions are also assigned to the `$pkg` object.
69
106
func (fc * funcContext ) translateStandaloneFunction (fun * ast.FuncDecl ) []byte {
70
107
o := fc .pkgCtx .Defs [fun .Name ].(* types.Func )
71
- info := fc .pkgCtx .FuncDeclInfos [o ]
72
- sig := o .Type ().(* types.Signature )
73
-
74
108
if fun .Recv != nil {
75
109
panic (fmt .Errorf ("expected standalone function, got method: %s" , o ))
76
110
}
@@ -79,7 +113,7 @@ func (fc *funcContext) translateStandaloneFunction(fun *ast.FuncDecl) []byte {
79
113
if fun .Body == nil {
80
114
return []byte (fmt .Sprintf ("\t %s = %s;\n " , lvalue , fc .unimplementedFunction (o )))
81
115
}
82
- body := fc .nestedFunctionContext ( info , sig ).translateFunctionBody (fun .Type , nil , fun .Body , lvalue )
116
+ body := fc .namedFuncContext ( fun ).translateFunctionBody (fun .Type , nil , fun .Body )
83
117
84
118
code := & bytes.Buffer {}
85
119
fmt .Fprintf (code , "\t %s = %s;\n " , lvalue , body )
@@ -99,14 +133,11 @@ func (fc *funcContext) translateMethod(fun *ast.FuncDecl) []byte {
99
133
}
100
134
101
135
o := fc .pkgCtx .Defs [fun .Name ].(* types.Func )
102
- info := fc .pkgCtx .FuncDeclInfos [o ]
103
-
104
- sig := o .Type ().(* types.Signature )
105
136
var recv * ast.Ident
106
137
if fun .Recv .List [0 ].Names != nil {
107
138
recv = fun .Recv .List [0 ].Names [0 ]
108
139
}
109
- nestedFC := fc .nestedFunctionContext ( info , sig )
140
+ nestedFC := fc .namedFuncContext ( fun )
110
141
111
142
// primaryFunction generates a JS function equivalent of the current Go function
112
143
// and assigns it to the JS expression defined by lvalue.
@@ -115,11 +146,11 @@ func (fc *funcContext) translateMethod(fun *ast.FuncDecl) []byte {
115
146
return []byte (fmt .Sprintf ("\t %s = %s;\n " , lvalue , fc .unimplementedFunction (o )))
116
147
}
117
148
118
- funDef := nestedFC .translateFunctionBody (fun .Type , recv , fun .Body , lvalue )
149
+ funDef := nestedFC .translateFunctionBody (fun .Type , recv , fun .Body )
119
150
return []byte (fmt .Sprintf ("\t %s = %s;\n " , lvalue , funDef ))
120
151
}
121
152
122
- recvType := sig .Recv ().Type ()
153
+ recvType := nestedFC . sigTypes . Sig .Recv ().Type ()
123
154
ptr , isPointer := recvType .(* types.Pointer )
124
155
namedRecvType , _ := recvType .(* types.Named )
125
156
if isPointer {
@@ -131,7 +162,7 @@ func (fc *funcContext) translateMethod(fun *ast.FuncDecl) []byte {
131
162
// Objects the method should be assigned to.
132
163
prototypeVar := fmt .Sprintf ("%s.prototype.%s" , typeName , funName )
133
164
ptrPrototypeVar := fmt .Sprintf ("$ptrType(%s).prototype.%s" , typeName , funName )
134
- isGeneric := signatureTypes { Sig : sig } .IsGeneric ()
165
+ isGeneric := nestedFC . sigTypes .IsGeneric ()
135
166
if isGeneric {
136
167
// Generic method factories are assigned to the generic type factory
137
168
// properties, to be invoked at type construction time rather than method
@@ -194,23 +225,7 @@ func (fc *funcContext) unimplementedFunction(o *types.Func) string {
194
225
return fmt .Sprintf ("function() {\n \t \t $throwRuntimeError(\" native function not implemented: %s\" );\n \t }" , o .FullName ())
195
226
}
196
227
197
- func (fc * funcContext ) translateFunctionBody (typ * ast.FuncType , recv * ast.Ident , body * ast.BlockStmt , funcRef string ) string {
198
- functionName := "" // Function object name, i.e. identifier after the "function" keyword.
199
- if funcRef == "" {
200
- // Assign a name for the anonymous function.
201
- funcRef = "$b"
202
- functionName = " $b"
203
- }
204
-
205
- // For regular functions instance is directly in the function variable name.
206
- instanceVar := funcRef
207
- if fc .sigTypes .IsGeneric () {
208
- fc .genericCtx = & genericCtx {}
209
- // For generic function, funcRef refers to the generic factory function,
210
- // allocate a separate variable for a function instance.
211
- instanceVar = fc .newVariable ("instance" , varGenericFactory )
212
- }
213
-
228
+ func (fc * funcContext ) translateFunctionBody (typ * ast.FuncType , recv * ast.Ident , body * ast.BlockStmt ) string {
214
229
prevEV := fc .pkgCtx .escapingVars
215
230
216
231
params := fc .funcParamVars (typ )
@@ -272,10 +287,13 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
272
287
// $c indicates that a function is being resumed after a blocking call when set to true.
273
288
// $f is an object used to save and restore function context for blocking calls.
274
289
localVars = append (localVars , "$r" )
290
+ // funcRef identifies the function object itself, so it doesn't need to be saved
291
+ // or restored.
292
+ localVars = removeMatching (localVars , fc .funcRef )
275
293
// If a blocking function is being resumed, initialize local variables from the saved context.
276
294
localVarDefs = fmt .Sprintf ("var {%s, $c} = $restore(this, {%s});\n " , strings .Join (localVars , ", " ), strings .Join (params , ", " ))
277
295
// If the function gets blocked, save local variables for future.
278
- saveContext := fmt .Sprintf ("var $f = {$blk: %s, $c: true, $r, %s};" , instanceVar , strings .Join (fc . localVars , ", " ))
296
+ saveContext := fmt .Sprintf ("var $f = {$blk: %s, $c: true, %s};" , fc . funcRef , strings .Join (localVars , ", " ))
279
297
280
298
suffix = " " + saveContext + "return $f;" + suffix
281
299
} else if len (fc .localVars ) > 0 {
@@ -324,9 +342,13 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
324
342
fc .pkgCtx .escapingVars = prevEV
325
343
326
344
if ! fc .sigTypes .IsGeneric () {
327
- return fmt .Sprintf ("function%s(%s) {\n %s%s}" , functionName , strings .Join (params , ", " ), bodyOutput , fc .Indentation (0 ))
345
+ return fmt .Sprintf ("function %s(%s) {\n %s%s}" , fc . funcRef , strings .Join (params , ", " ), bodyOutput , fc .Indentation (0 ))
328
346
}
329
347
348
+ // For generic function, funcRef refers to the generic factory function,
349
+ // allocate a separate variable for a function instance.
350
+ instanceVar := fc .newVariable ("instance" , varGenericFactory )
351
+
330
352
// Generic functions are generated as factories to allow passing type parameters
331
353
// from the call site.
332
354
// TODO(nevkontakte): Cache function instances for a given combination of type
@@ -341,9 +363,9 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
341
363
}
342
364
343
365
code := & strings.Builder {}
344
- fmt .Fprintf (code , "function%s (%s){\n " , functionName , strings .Join (typeParams , ", " ))
366
+ fmt .Fprintf (code , "function(%s){\n " , strings .Join (typeParams , ", " ))
345
367
fmt .Fprintf (code , "%s" , typesInit .String ())
346
- fmt .Fprintf (code , "%sconst %s = function(%s) {\n " , fc .Indentation (1 ), instanceVar , strings .Join (params , ", " ))
368
+ fmt .Fprintf (code , "%sconst %s = function %s (%s) {\n " , fc .Indentation (1 ), instanceVar , fc . funcRef , strings .Join (params , ", " ))
347
369
fmt .Fprintf (code , "%s" , bodyOutput )
348
370
fmt .Fprintf (code , "%s};\n " , fc .Indentation (1 ))
349
371
// TODO(nevkontakte): Method list entries for generic type methods should be
0 commit comments