goscript 0.0.10 → 0.0.11

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.
Files changed (34) hide show
  1. package/builtin/builtin.ts +161 -65
  2. package/cmd/goscript/cmd_compile.go +8 -6
  3. package/compiler/compile.go +28 -11
  4. package/compiler/compile_decls.go +12 -0
  5. package/compiler/compile_expr.go +59 -2
  6. package/compiler/compile_spec.go +110 -3
  7. package/compiler/compile_stmt.go +89 -15
  8. package/dist/builtin/builtin.d.ts +57 -1
  9. package/dist/builtin/builtin.js +73 -0
  10. package/dist/builtin/builtin.js.map +1 -1
  11. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.d.ts +1 -0
  12. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +82 -0
  13. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +1 -0
  14. package/dist/compliance/tests/defer_statement/defer_statement.gs.d.ts +1 -0
  15. package/dist/compliance/tests/defer_statement/defer_statement.gs.js +75 -0
  16. package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +1 -0
  17. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +1 -1
  18. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +1 -1
  19. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.d.ts +1 -0
  20. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js +14 -0
  21. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js.map +1 -0
  22. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +1 -1
  23. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +1 -1
  24. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +11 -1
  25. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +1 -1
  26. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.d.ts +1 -0
  27. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +51 -0
  28. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +1 -0
  29. package/dist/compliance/tests/slices/slices.gs.js +280 -7
  30. package/dist/compliance/tests/slices/slices.gs.js.map +1 -1
  31. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.d.ts +1 -0
  32. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +48 -0
  33. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +1 -0
  34. package/package.json +4 -3
@@ -13,6 +13,34 @@ export const makeSlice = <T>(
13
13
  return slice
14
14
  }
15
15
 
16
+ /**
17
+ * Creates a new slice header that shares the backing array.
18
+ * Arguments mirror Go semantics; omitted indices are undefined.
19
+ *
20
+ * @param arr The original slice/array produced by makeSlice or another slice
21
+ * @param low Starting index (defaults to 0)
22
+ * @param high Ending index (defaults to arr.length)
23
+ * @param max Capacity limit (defaults to original capacity)
24
+ */
25
+ export const slice = <T>(
26
+ arr: Array<T> & { __capacity?: number },
27
+ low?: number,
28
+ high?: number,
29
+ max?: number,
30
+ ): Array<T> & { __capacity?: number } => {
31
+ const start = low ?? 0
32
+ const origLen = arr.length
33
+ const origCap = arr.__capacity !== undefined ? arr.__capacity : origLen
34
+ const end = high !== undefined ? high : origLen
35
+ const newCap = max !== undefined ? max - start : origCap - start
36
+
37
+ const newArr = arr.slice(start, end) as Array<T> & {
38
+ __capacity?: number
39
+ }
40
+ newArr.__capacity = newCap
41
+ return newArr
42
+ }
43
+
16
44
  /**
17
45
  * Creates a new map (TypeScript Map).
18
46
  * @returns A new TypeScript Map.
@@ -50,8 +78,8 @@ export const cap = <T>(slice: Array<T> & { __capacity?: number }): number => {
50
78
  * Represents the Go error type (interface).
51
79
  */
52
80
  export type Error = {
53
- Error(): string;
54
- } | null;
81
+ Error(): string
82
+ } | null
55
83
 
56
84
  /**
57
85
  * Converts a string to an array of Unicode code points (runes).
@@ -113,8 +141,14 @@ export const mapHas = <K, V>(map: Map<K, V>, key: K): boolean => {
113
141
  * @param elements The elements to append.
114
142
  * @returns The modified slice (TypeScript array).
115
143
  */
116
- export const append = <T>(slice: Array<T>, ...elements: T[]): Array<T> => {
144
+ export const append = <T>(
145
+ slice: Array<T> & { __capacity?: number },
146
+ ...elements: T[]
147
+ ): Array<T> & { __capacity?: number } => {
117
148
  slice.push(...elements)
149
+ if (slice.__capacity !== undefined && slice.length > slice.__capacity) {
150
+ slice.__capacity = slice.length
151
+ }
118
152
  return slice
119
153
  }
120
154
 
@@ -122,35 +156,35 @@ export const append = <T>(slice: Array<T>, ...elements: T[]): Array<T> => {
122
156
  * Represents the kinds of Go types that can be registered at runtime.
123
157
  */
124
158
  export enum TypeKind {
125
- Struct = "struct",
126
- Interface = "interface",
127
- Basic = "basic",
128
- Pointer = "pointer",
129
- Slice = "slice",
130
- Map = "map",
131
- Channel = "channel",
132
- Function = "function",
159
+ Struct = 'struct',
160
+ Interface = 'interface',
161
+ Basic = 'basic',
162
+ Pointer = 'pointer',
163
+ Slice = 'slice',
164
+ Map = 'map',
165
+ Channel = 'channel',
166
+ Function = 'function',
133
167
  }
134
168
 
135
169
  /**
136
170
  * Represents type information for a Go type in the runtime.
137
171
  */
138
172
  export interface TypeInfo {
139
- name: string;
140
- kind: TypeKind;
141
- zeroValue: any;
173
+ name: string
174
+ kind: TypeKind
175
+ zeroValue: any
142
176
  // For interfaces, the set of methods
143
- methods?: Set<string>;
177
+ methods?: Set<string>
144
178
  // For structs, the constructor
145
- constructor?: new (...args: any[]) => any;
179
+ constructor?: new (...args: any[]) => any
146
180
  }
147
181
 
148
182
  // Registry to store runtime type information
149
- const typeRegistry = new Map<string, TypeInfo>();
183
+ const typeRegistry = new Map<string, TypeInfo>()
150
184
 
151
185
  /**
152
186
  * Registers a type with the runtime type system.
153
- *
187
+ *
154
188
  * @param name The name of the type.
155
189
  * @param kind The kind of the type.
156
190
  * @param zeroValue The zero value for the type.
@@ -163,7 +197,7 @@ export const registerType = (
163
197
  kind: TypeKind,
164
198
  zeroValue: any,
165
199
  methods?: Set<string>,
166
- constructor?: new (...args: any[]) => any
200
+ constructor?: new (...args: any[]) => any,
167
201
  ): TypeInfo => {
168
202
  const typeInfo: TypeInfo = {
169
203
  name,
@@ -171,37 +205,40 @@ export const registerType = (
171
205
  zeroValue,
172
206
  methods,
173
207
  constructor,
174
- };
175
- typeRegistry.set(name, typeInfo);
176
- return typeInfo;
177
- };
208
+ }
209
+ typeRegistry.set(name, typeInfo)
210
+ return typeInfo
211
+ }
178
212
 
179
213
  /**
180
214
  * Represents the result of a type assertion.
181
215
  */
182
216
  export interface TypeAssertResult<T> {
183
- value: T;
184
- ok: boolean;
217
+ value: T
218
+ ok: boolean
185
219
  }
186
220
 
187
221
  /**
188
222
  * Performs a type assertion at runtime.
189
- *
223
+ *
190
224
  * @param value The value to assert.
191
225
  * @param typeName The name of the target type.
192
226
  * @returns An object with the asserted value and whether the assertion succeeded.
193
227
  */
194
- export function typeAssert<T>(value: any, typeName: string): TypeAssertResult<T> {
228
+ export function typeAssert<T>(
229
+ value: any,
230
+ typeName: string,
231
+ ): TypeAssertResult<T> {
195
232
  // Get the type information from the registry
196
- const typeInfo = typeRegistry.get(typeName);
233
+ const typeInfo = typeRegistry.get(typeName)
197
234
  if (!typeInfo) {
198
- console.warn(`Type information for '${typeName}' not found in registry.`);
199
- return { value: null as unknown as T, ok: false };
235
+ console.warn(`Type information for '${typeName}' not found in registry.`)
236
+ return { value: null as unknown as T, ok: false }
200
237
  }
201
238
 
202
239
  // If value is null or undefined, assertion fails
203
240
  if (value === null || value === undefined) {
204
- return { value: typeInfo.zeroValue as T, ok: false };
241
+ return { value: typeInfo.zeroValue as T, ok: false }
205
242
  }
206
243
 
207
244
  // Check based on the kind of the target type
@@ -209,86 +246,88 @@ export function typeAssert<T>(value: any, typeName: string): TypeAssertResult<T>
209
246
  case TypeKind.Struct:
210
247
  // For structs, use instanceof with the constructor
211
248
  if (typeInfo.constructor && value instanceof typeInfo.constructor) {
212
- return { value: value as T, ok: true };
249
+ return { value: value as T, ok: true }
213
250
  }
214
- break;
215
-
251
+ break
252
+
216
253
  case TypeKind.Interface:
217
254
  // For interfaces, check if the value has all the required methods
218
255
  if (typeInfo.methods && typeof value === 'object') {
219
256
  const allMethodsPresent = Array.from(typeInfo.methods).every(
220
- (method) => typeof (value as any)[method] === 'function'
221
- );
257
+ (method) => typeof (value as any)[method] === 'function',
258
+ )
222
259
  if (allMethodsPresent) {
223
- return { value: value as T, ok: true };
260
+ return { value: value as T, ok: true }
224
261
  }
225
262
  }
226
- break;
227
-
263
+ break
264
+
228
265
  case TypeKind.Basic:
229
266
  // For basic types, check if the value matches the expected JavaScript type
230
267
  // This is a simple check for common basic types
231
- const basicType = typeof value;
268
+ const basicType = typeof value
232
269
  if (
233
- basicType === 'string' ||
234
- basicType === 'number' ||
270
+ basicType === 'string' ||
271
+ basicType === 'number' ||
235
272
  basicType === 'boolean'
236
273
  ) {
237
- return { value: value as T, ok: true };
274
+ return { value: value as T, ok: true }
238
275
  }
239
- break;
240
-
276
+ break
277
+
241
278
  case TypeKind.Pointer:
242
279
  // For pointers, check if value is not null or undefined
243
280
  // In Go, pointers can be nil which we represent as null/undefined in TS
244
281
  if (value !== null && value !== undefined) {
245
- return { value: value as T, ok: true };
282
+ return { value: value as T, ok: true }
246
283
  }
247
- break;
248
-
284
+ break
285
+
249
286
  case TypeKind.Slice:
250
287
  // For slices, check if the value is an array
251
288
  if (Array.isArray(value)) {
252
- return { value: value as T, ok: true };
289
+ return { value: value as T, ok: true }
253
290
  }
254
- break;
255
-
291
+ break
292
+
256
293
  case TypeKind.Map:
257
294
  // For maps, check if the value is a Map
258
295
  if (value instanceof Map) {
259
- return { value: value as T, ok: true };
296
+ return { value: value as T, ok: true }
260
297
  }
261
- break;
262
-
298
+ break
299
+
263
300
  case TypeKind.Channel:
264
301
  // For channels, check if the value has the required Channel interface methods
265
302
  if (
266
- typeof value === 'object' &&
303
+ typeof value === 'object' &&
267
304
  value !== null &&
268
- 'send' in value &&
269
- 'receive' in value &&
305
+ 'send' in value &&
306
+ 'receive' in value &&
270
307
  'close' in value &&
271
308
  typeof value.send === 'function' &&
272
309
  typeof value.receive === 'function' &&
273
310
  typeof value.close === 'function'
274
311
  ) {
275
- return { value: value as T, ok: true };
312
+ return { value: value as T, ok: true }
276
313
  }
277
- break;
278
-
314
+ break
315
+
279
316
  case TypeKind.Function:
280
317
  // For functions, check if the value is a function
281
318
  if (typeof value === 'function') {
282
- return { value: value as T, ok: true };
319
+ return { value: value as T, ok: true }
283
320
  }
284
- break;
285
-
321
+ break
322
+
286
323
  default:
287
- console.warn(`Type assertion for kind '${typeInfo.kind}' not implemented.`);
324
+ console.warn(
325
+ `Type assertion for kind '${typeInfo.kind}' not implemented.`,
326
+ )
288
327
  }
289
328
 
290
329
  // Assertion failed
291
- return { value: typeInfo.zeroValue as T, ok: false };
330
+ return { value: typeInfo.zeroValue as T, ok: false }
292
331
  }
293
332
 
294
333
  /**
@@ -715,3 +754,60 @@ export const makeChannel = <T>(
715
754
  ): Channel<T> => {
716
755
  return new BufferedChannel<T>(bufferSize, zeroValue)
717
756
  }
757
+
758
+ /**
759
+ * DisposableStack manages synchronous disposable resources, mimicking Go's defer behavior.
760
+ * Functions added via `defer` are executed in LIFO order when the stack is disposed.
761
+ * Implements the `Disposable` interface for use with `using` declarations.
762
+ */
763
+ export class DisposableStack implements Disposable {
764
+ #stack: (() => void)[] = [];
765
+
766
+ /**
767
+ * Adds a function to be executed when the stack is disposed.
768
+ * @param fn The function to defer.
769
+ */
770
+ defer(fn: () => void): void { this.#stack.push(fn); }
771
+
772
+ /**
773
+ * Disposes of the resources in the stack by executing the deferred functions
774
+ * in Last-In, First-Out (LIFO) order.
775
+ * If a deferred function throws an error, disposal stops, and the error is rethrown,
776
+ * similar to Go's panic behavior during defer execution.
777
+ */
778
+ [Symbol.dispose](): void {
779
+ // Emulate Go: if a deferred throws, stop and rethrow
780
+ while (this.#stack.length) {
781
+ const fn = this.#stack.pop()!;
782
+ fn();
783
+ }
784
+ }
785
+ }
786
+
787
+ /**
788
+ * AsyncDisposableStack manages asynchronous disposable resources, mimicking Go's defer behavior.
789
+ * Functions added via `defer` are executed sequentially in LIFO order when the stack is disposed.
790
+ * Implements the `AsyncDisposable` interface for use with `await using` declarations.
791
+ */
792
+ export class AsyncDisposableStack implements AsyncDisposable {
793
+ #stack: (() => Promise<void> | void)[] = [];
794
+
795
+ /**
796
+ * Adds a synchronous or asynchronous function to be executed when the stack is disposed.
797
+ * @param fn The function to defer. Can return void or a Promise<void>.
798
+ */
799
+ defer(fn: () => Promise<void> | void): void {
800
+ this.#stack.push(fn);
801
+ }
802
+
803
+ /**
804
+ * Asynchronously disposes of the resources in the stack by executing the deferred functions
805
+ * sequentially in Last-In, First-Out (LIFO) order. It awaits each function if it returns a promise.
806
+ */
807
+ async [Symbol.asyncDispose](): Promise<void> {
808
+ // Execute in LIFO order, awaiting each potentially async function
809
+ for (let i = this.#stack.length - 1; i >= 0; --i) {
810
+ await this.#stack[i]();
811
+ }
812
+ }
813
+ }
@@ -12,7 +12,7 @@ import (
12
12
  var (
13
13
  cliCompiler *compiler.Compiler
14
14
  cliCompilerConfig compiler.Config
15
- cliCompilerPkg string
15
+ cliCompilerPkg cli.StringSlice
16
16
  )
17
17
 
18
18
  // CompileCommands are commands related to compiling code.
@@ -29,9 +29,10 @@ var CompileCommands = []*cli.Command{{
29
29
  return
30
30
  },
31
31
  Flags: []cli.Flag{
32
- &cli.StringFlag{
32
+ &cli.StringSliceFlag{
33
33
  Name: "package",
34
- Usage: "the package to compile",
34
+ Usage: "the package(s) to compile",
35
+ Aliases: []string{"p", "packages"},
35
36
  Destination: &cliCompilerPkg,
36
37
  },
37
38
  &cli.StringFlag{
@@ -51,9 +52,10 @@ var CompileCommands = []*cli.Command{{
51
52
 
52
53
  // compilePackage tries to compile the package.
53
54
  func compilePackage(c *cli.Context) error {
54
- if cliCompilerPkg == "" {
55
- return errors.New("package must be specified")
55
+ pkgs := cliCompilerPkg.Value()
56
+ if len(pkgs) == 0 {
57
+ return errors.New("package(s) must be specified")
56
58
  }
57
59
 
58
- return cliCompiler.CompilePackages(context.Background(), cliCompilerPkg)
60
+ return cliCompiler.CompilePackages(context.Background(), pkgs...)
59
61
  }
@@ -12,11 +12,13 @@ import (
12
12
 
13
13
  // GoToTSCompiler compiles Go code to TypeScript code.
14
14
  type GoToTSCompiler struct {
15
- tsw *TSCodeWriter
16
- imports map[string]*fileImport
17
- pkg *packages.Package
18
- cmap ast.CommentMap
19
- asyncFuncs map[string]bool // Track which functions are async
15
+ tsw *TSCodeWriter
16
+ imports map[string]*fileImport
17
+ pkg *packages.Package
18
+ cmap ast.CommentMap
19
+ asyncFuncs map[string]bool // Track which functions are async
20
+ nextBlockNeedsDefer bool // Track if the next block should have a "using" statement
21
+ inAsyncFunction bool // Track if we're inside an async function
20
22
 
21
23
  tempVarCounter int // Counter for generating unique temporary variable names
22
24
  }
@@ -68,15 +70,30 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
68
70
  }
69
71
  }
70
72
 
73
+ // scanForDefer checks if a block contains any defer statements (recursively).
74
+ func (c *GoToTSCompiler) scanForDefer(block *ast.BlockStmt) bool {
75
+ hasDefer := false
76
+ ast.Inspect(block, func(n ast.Node) bool {
77
+ if _, ok := n.(*ast.DeferStmt); ok {
78
+ hasDefer = true
79
+ return false // Stop traversal
80
+ }
81
+ return true // Continue traversal
82
+ })
83
+ return hasDefer
84
+ }
85
+
71
86
  // NewGoToTSCompiler builds a new GoToTSCompiler
72
87
  func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, cmap ast.CommentMap) *GoToTSCompiler {
73
88
  return &GoToTSCompiler{
74
- tsw: tsw,
75
- imports: make(map[string]*fileImport),
76
- pkg: pkg,
77
- cmap: cmap,
78
- asyncFuncs: make(map[string]bool),
79
- tempVarCounter: 0, // Initialize counter
89
+ tsw: tsw,
90
+ imports: make(map[string]*fileImport),
91
+ pkg: pkg,
92
+ cmap: cmap,
93
+ asyncFuncs: make(map[string]bool),
94
+ nextBlockNeedsDefer: false,
95
+ inAsyncFunction: false,
96
+ tempVarCounter: 0, // Initialize counter
80
97
  }
81
98
  }
82
99
 
@@ -65,8 +65,20 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
65
65
  // WriteFuncType needs to be aware if the function is async
66
66
  c.WriteFuncType(decl.Type, isAsync) // Write signature (params, return type)
67
67
  c.tsw.WriteLiterally(" ")
68
+
69
+ // Check if function body has defer statements
70
+ c.nextBlockNeedsDefer = c.scanForDefer(decl.Body)
71
+
72
+ // Save previous async state and set current state based on isAsync
73
+ previousAsyncState := c.inAsyncFunction
74
+ c.inAsyncFunction = isAsync
75
+
68
76
  if err := c.WriteStmt(decl.Body); err != nil {
77
+ c.inAsyncFunction = previousAsyncState // Restore state before returning error
69
78
  return fmt.Errorf("failed to write function body: %w", err)
70
79
  }
80
+
81
+ // Restore previous async state
82
+ c.inAsyncFunction = previousAsyncState
71
83
  return nil
72
84
  }
@@ -108,6 +108,43 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
108
108
  }
109
109
  c.tsw.WriteLiterally("]")
110
110
  return nil
111
+ case *ast.SliceExpr:
112
+ // Translate Go slice expression to goscript.slice(x, low, high, max)
113
+ c.tsw.WriteLiterally("goscript.slice(")
114
+ if err := c.WriteValueExpr(exp.X); err != nil {
115
+ return err
116
+ }
117
+ // low argument
118
+ c.tsw.WriteLiterally(", ")
119
+ if exp.Low != nil {
120
+ if err := c.WriteValueExpr(exp.Low); err != nil {
121
+ return err
122
+ }
123
+ } else {
124
+ c.tsw.WriteLiterally("undefined")
125
+ }
126
+ // high argument
127
+ c.tsw.WriteLiterally(", ")
128
+ if exp.High != nil {
129
+ if err := c.WriteValueExpr(exp.High); err != nil {
130
+ return err
131
+ }
132
+ } else {
133
+ c.tsw.WriteLiterally("undefined")
134
+ }
135
+ // max argument (only for full slice expressions)
136
+ if exp.Slice3 {
137
+ c.tsw.WriteLiterally(", ")
138
+ if exp.Max != nil {
139
+ if err := c.WriteValueExpr(exp.Max); err != nil {
140
+ return err
141
+ }
142
+ } else {
143
+ c.tsw.WriteLiterally("undefined")
144
+ }
145
+ }
146
+ c.tsw.WriteLiterally(")")
147
+ return nil
111
148
  case *ast.ParenExpr:
112
149
  // Translate (X) to (X)
113
150
  c.tsw.WriteLiterally("(")
@@ -433,7 +470,20 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
433
470
 
434
471
  // Handle slice creation
435
472
  if _, ok := exp.Args[0].(*ast.ArrayType); ok {
436
- c.tsw.WriteLiterally("goscript.makeSlice(")
473
+ // Get the slice type information
474
+ sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
475
+ if sliceType == nil {
476
+ return errors.New("could not get type information for slice in make call")
477
+ }
478
+ goElemType, ok := sliceType.Underlying().(*gtypes.Slice)
479
+ if !ok {
480
+ return errors.New("expected slice type for make call")
481
+ }
482
+
483
+ c.tsw.WriteLiterally("goscript.makeSlice<")
484
+ c.WriteGoType(goElemType.Elem()) // Write the element type
485
+ c.tsw.WriteLiterally(">(")
486
+
437
487
  if len(exp.Args) >= 2 {
438
488
  if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
439
489
  return err
@@ -885,11 +935,18 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
885
935
  }
886
936
 
887
937
  c.tsw.WriteLiterally(" => ")
938
+
939
+ // Save previous async state and set current state based on isAsync
940
+ previousAsyncState := c.inAsyncFunction
941
+ c.inAsyncFunction = isAsync
888
942
 
889
943
  // Write function body
890
944
  if err := c.WriteStmt(exp.Body); err != nil {
945
+ c.inAsyncFunction = previousAsyncState // Restore state before returning error
891
946
  return fmt.Errorf("failed to write function literal body: %w", err)
892
947
  }
893
-
948
+
949
+ // Restore previous async state
950
+ c.inAsyncFunction = previousAsyncState
894
951
  return nil
895
952
  }