@@ -18,18 +18,21 @@ import (
18
18
"github.com/gopherjs/gopherjs/compiler/typesutil"
19
19
)
20
20
21
- // newFunctionContext creates a new nested context for a function corresponding
21
+ // nestedFunctionContext creates a new nested context for a function corresponding
22
22
// to the provided info and instance.
23
- func (fc * funcContext ) nestedFunctionContext (info * analysis.FuncInfo , sig * types. Signature , inst typeparams.Instance ) * funcContext {
23
+ func (fc * funcContext ) nestedFunctionContext (info * analysis.FuncInfo , inst typeparams.Instance ) * funcContext {
24
24
if info == nil {
25
25
panic (errors .New ("missing *analysis.FuncInfo" ))
26
26
}
27
- if sig == nil {
28
- panic (errors .New ("missing *types.Signature " ))
27
+ if inst . Object == nil {
28
+ panic (errors .New ("missing inst.Object " ))
29
29
}
30
+ o := inst .Object .(* types.Func )
31
+ sig := o .Type ().(* types.Signature )
30
32
31
33
c := & funcContext {
32
34
FuncInfo : info ,
35
+ instance : inst ,
33
36
pkgCtx : fc .pkgCtx ,
34
37
parent : fc ,
35
38
allVars : make (map [string ]int , len (fc .allVars )),
@@ -54,43 +57,73 @@ func (fc *funcContext) nestedFunctionContext(info *analysis.FuncInfo, sig *types
54
57
c .objectNames = map [types.Object ]string {}
55
58
}
56
59
60
+ // Synthesize an identifier by which the function may reference itself. Since
61
+ // it appears in the stack trace, it's useful to include the receiver type in
62
+ // it.
63
+ funcRef := o .Name ()
64
+ if recvType := typesutil .RecvType (sig ); recvType != nil {
65
+ funcRef = recvType .Obj ().Name () + midDot + funcRef
66
+ }
67
+ c .funcRef = c .newVariable (funcRef , true /*pkgLevel*/ )
68
+
69
+ return c
70
+ }
71
+
72
+ // namedFuncContext creates a new funcContext for a named Go function
73
+ // (standalone or method).
74
+ func (fc * funcContext ) namedFuncContext (inst typeparams.Instance ) * funcContext {
75
+ info := fc .pkgCtx .FuncDeclInfos [inst .Object .(* types.Func )]
76
+ c := fc .nestedFunctionContext (info , inst )
77
+
78
+ return c
79
+ }
80
+
81
+ // literalFuncContext creates a new funcContext for a function literal. Since
82
+ // go/types doesn't generate *types.Func objects for function literals, we
83
+ // generate a synthetic one for it.
84
+ func (fc * funcContext ) literalFuncContext (fun * ast.FuncLit ) * funcContext {
85
+ info := fc .pkgCtx .FuncLitInfos [fun ]
86
+ sig := fc .pkgCtx .TypeOf (fun ).(* types.Signature )
87
+ o := types .NewFunc (fun .Pos (), fc .pkgCtx .Pkg , fc .newLitFuncName (), sig )
88
+ inst := typeparams.Instance {Object : o }
89
+
90
+ c := fc .nestedFunctionContext (info , inst )
57
91
return c
58
92
}
59
93
60
94
// translateTopLevelFunction translates a top-level function declaration
61
- // (standalone function or method) into a corresponding JS function.
95
+ // (standalone function or method) into a corresponding JS function. Must be
96
+ // called on the function context created for the function corresponding instance.
62
97
//
63
98
// Returns a string with JavaScript statements that define the function or
64
99
// method. For methods it returns declarations for both value- and
65
100
// pointer-receiver (if appropriate).
66
- func (fc * funcContext ) translateTopLevelFunction (fun * ast.FuncDecl , inst typeparams. Instance ) []byte {
101
+ func (fc * funcContext ) translateTopLevelFunction (fun * ast.FuncDecl ) []byte {
67
102
if fun .Recv == nil {
68
- return fc .translateStandaloneFunction (fun , inst )
103
+ return fc .translateStandaloneFunction (fun )
69
104
}
70
105
71
- return fc .translateMethod (fun , inst )
106
+ return fc .translateMethod (fun )
72
107
}
73
108
74
109
// translateStandaloneFunction translates a package-level function.
75
110
//
76
111
// It returns JS statements which define the corresponding function in a
77
112
// package context. Exported functions are also assigned to the `$pkg` object.
78
- func (fc * funcContext ) translateStandaloneFunction (fun * ast.FuncDecl , inst typeparams.Instance ) []byte {
79
- o := inst .Object .(* types.Func )
80
- info := fc .pkgCtx .FuncDeclInfos [o ]
81
- sig := o .Type ().(* types.Signature )
113
+ func (fc * funcContext ) translateStandaloneFunction (fun * ast.FuncDecl ) []byte {
114
+ o := fc .instance .Object .(* types.Func )
82
115
83
116
if fun .Recv != nil {
84
117
panic (fmt .Errorf ("expected standalone function, got method: %s" , o ))
85
118
}
86
119
87
- lvalue := fc .instName (inst )
120
+ lvalue := fc .instName (fc . instance )
88
121
89
122
if fun .Body == nil {
90
123
return []byte (fmt .Sprintf ("\t %s = %s;\n " , lvalue , fc .unimplementedFunction (o )))
91
124
}
92
125
93
- body := fc .nestedFunctionContext ( info , sig , inst ). translateFunctionBody (fun .Type , nil , fun .Body , lvalue )
126
+ body := fc .translateFunctionBody (fun .Type , nil , fun .Body )
94
127
code := bytes .NewBuffer (nil )
95
128
fmt .Fprintf (code , "\t %s = %s;\n " , lvalue , body )
96
129
if fun .Name .IsExported () {
@@ -103,12 +136,10 @@ func (fc *funcContext) translateStandaloneFunction(fun *ast.FuncDecl, inst typep
103
136
//
104
137
// It returns one or more JS statements which define the method. Methods with
105
138
// non-pointer receiver are automatically defined for the pointer-receiver type.
106
- func (fc * funcContext ) translateMethod (fun * ast.FuncDecl , inst typeparams.Instance ) []byte {
107
- o := inst .Object .(* types.Func )
108
- info := fc .pkgCtx .FuncDeclInfos [o ]
139
+ func (fc * funcContext ) translateMethod (fun * ast.FuncDecl ) []byte {
140
+ o := fc .instance .Object .(* types.Func )
109
141
funName := fc .methodName (o )
110
142
111
- sig := o .Type ().(* types.Signature )
112
143
// primaryFunction generates a JS function equivalent of the current Go function
113
144
// and assigns it to the JS expression defined by lvalue.
114
145
primaryFunction := func (lvalue string ) []byte {
@@ -120,11 +151,11 @@ func (fc *funcContext) translateMethod(fun *ast.FuncDecl, inst typeparams.Instan
120
151
if fun .Recv != nil && fun .Recv .List [0 ].Names != nil {
121
152
recv = fun .Recv .List [0 ].Names [0 ]
122
153
}
123
- fun := fc .nestedFunctionContext ( info , sig , inst ). translateFunctionBody (fun .Type , recv , fun .Body , lvalue )
154
+ fun := fc .translateFunctionBody (fun .Type , recv , fun .Body )
124
155
return []byte (fmt .Sprintf ("\t %s = %s;\n " , lvalue , fun ))
125
156
}
126
157
127
- recvInst := inst .Recv ()
158
+ recvInst := fc . instance .Recv ()
128
159
recvInstName := fc .instName (recvInst )
129
160
recvType := recvInst .Object .Type ().(* types.Named )
130
161
@@ -134,7 +165,7 @@ func (fc *funcContext) translateMethod(fun *ast.FuncDecl, inst typeparams.Instan
134
165
ptrPrototypeVar := fmt .Sprintf ("$ptrType(%s).prototype.%s" , recvInstName , funName )
135
166
136
167
// Methods with pointer-receiver are only attached to the pointer-receiver type.
137
- if _ , isPointer := sig .Recv ().Type ().(* types.Pointer ); isPointer {
168
+ if _ , isPointer := fc . sig . Sig .Recv ().Type ().(* types.Pointer ); isPointer {
138
169
return primaryFunction (ptrPrototypeVar )
139
170
}
140
171
@@ -185,7 +216,7 @@ func (fc *funcContext) unimplementedFunction(o *types.Func) string {
185
216
// It returns a JS function expression that represents the given Go function.
186
217
// Function receiver must have been created with nestedFunctionContext() to have
187
218
// required metadata set up.
188
- func (fc * funcContext ) translateFunctionBody (typ * ast.FuncType , recv * ast.Ident , body * ast.BlockStmt , funcRef string ) string {
219
+ func (fc * funcContext ) translateFunctionBody (typ * ast.FuncType , recv * ast.Ident , body * ast.BlockStmt ) string {
189
220
prevEV := fc .pkgCtx .escapingVars
190
221
191
222
// Generate a list of function argument variables. Since Go allows nameless
@@ -239,7 +270,7 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
239
270
240
271
sort .Strings (fc .localVars )
241
272
242
- var prefix , suffix , functionName string
273
+ var prefix , suffix string
243
274
244
275
if len (fc .Flattened ) != 0 {
245
276
// $s contains an index of the switch case a blocking function reached
@@ -260,21 +291,19 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
260
291
localVarDefs := "" // Function-local var declaration at the top.
261
292
262
293
if len (fc .Blocking ) != 0 {
263
- if funcRef == "" {
264
- funcRef = "$b"
265
- functionName = " $b"
266
- }
267
-
268
294
localVars := append ([]string {}, fc .localVars ... )
269
295
// There are several special variables involved in handling blocking functions:
270
296
// $r is sometimes used as a temporary variable to store blocking call result.
271
297
// $c indicates that a function is being resumed after a blocking call when set to true.
272
298
// $f is an object used to save and restore function context for blocking calls.
273
299
localVars = append (localVars , "$r" )
300
+ // funcRef identifies the function object itself, so it doesn't need to be saved
301
+ // or restored.
302
+ localVars = removeMatching (localVars , fc .funcRef )
274
303
// If a blocking function is being resumed, initialize local variables from the saved context.
275
304
localVarDefs = fmt .Sprintf ("var {%s, $c} = $restore(this, {%s});\n " , strings .Join (localVars , ", " ), strings .Join (args , ", " ))
276
305
// If the function gets blocked, save local variables for future.
277
- saveContext := fmt .Sprintf ("var $f = {$blk: " + funcRef + ", $c: true, $r, %s};" , strings .Join (fc .localVars , ", " ))
306
+ saveContext := fmt .Sprintf ("var $f = {$blk: " + fc . funcRef + ", $c: true, $r, %s};" , strings .Join (fc .localVars , ", " ))
278
307
279
308
suffix = " " + saveContext + "return $f;" + suffix
280
309
} else if len (fc .localVars ) > 0 {
@@ -322,5 +351,5 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
322
351
323
352
fc .pkgCtx .escapingVars = prevEV
324
353
325
- return fmt .Sprintf ("function%s(%s) {\n %s%s}" , functionName , strings .Join (args , ", " ), bodyOutput , fc .Indentation (0 ))
354
+ return fmt .Sprintf ("function %s(%s) {\n %s%s}" , fc . funcRef , strings .Join (args , ", " ), bodyOutput , fc .Indentation (0 ))
326
355
}
0 commit comments