1package prelude
2
3const goroutines = `
4var $stackDepthOffset = 0;
5var $getStackDepth = function() {
6  var err = new Error();
7  if (err.stack === undefined) {
8    return undefined;
9  }
10  return $stackDepthOffset + err.stack.split("\n").length;
11};
12
13var $panicStackDepth = null, $panicValue;
14var $callDeferred = function(deferred, jsErr, fromPanic) {
15  if (!fromPanic && deferred !== null && deferred.index >= $curGoroutine.deferStack.length) {
16    throw jsErr;
17  }
18  if (jsErr !== null) {
19    var newErr = null;
20    try {
21      $curGoroutine.deferStack.push(deferred);
22      $panic(new $jsErrorPtr(jsErr));
23    } catch (err) {
24      newErr = err;
25    }
26    $curGoroutine.deferStack.pop();
27    $callDeferred(deferred, newErr);
28    return;
29  }
30  if ($curGoroutine.asleep) {
31    return;
32  }
33
34  $stackDepthOffset--;
35  var outerPanicStackDepth = $panicStackDepth;
36  var outerPanicValue = $panicValue;
37
38  var localPanicValue = $curGoroutine.panicStack.pop();
39  if (localPanicValue !== undefined) {
40    $panicStackDepth = $getStackDepth();
41    $panicValue = localPanicValue;
42  }
43
44  try {
45    while (true) {
46      if (deferred === null) {
47        deferred = $curGoroutine.deferStack[$curGoroutine.deferStack.length - 1];
48        if (deferred === undefined) {
49          /* The panic reached the top of the stack. Clear it and throw it as a JavaScript error. */
50          $panicStackDepth = null;
51          if (localPanicValue.Object instanceof Error) {
52            throw localPanicValue.Object;
53          }
54          var msg;
55          if (localPanicValue.constructor === $String) {
56            msg = localPanicValue.$val;
57          } else if (localPanicValue.Error !== undefined) {
58            msg = localPanicValue.Error();
59          } else if (localPanicValue.String !== undefined) {
60            msg = localPanicValue.String();
61          } else {
62            msg = localPanicValue;
63          }
64          throw new Error(msg);
65        }
66      }
67      var call = deferred.pop();
68      if (call === undefined) {
69        $curGoroutine.deferStack.pop();
70        if (localPanicValue !== undefined) {
71          deferred = null;
72          continue;
73        }
74        return;
75      }
76      var r = call[0].apply(call[2], call[1]);
77      if (r && r.$blk !== undefined) {
78        deferred.push([r.$blk, [], r]);
79        if (fromPanic) {
80          throw null;
81        }
82        return;
83      }
84
85      if (localPanicValue !== undefined && $panicStackDepth === null) {
86        throw null; /* error was recovered */
87      }
88    }
89  } finally {
90    if (localPanicValue !== undefined) {
91      if ($panicStackDepth !== null) {
92        $curGoroutine.panicStack.push(localPanicValue);
93      }
94      $panicStackDepth = outerPanicStackDepth;
95      $panicValue = outerPanicValue;
96    }
97    $stackDepthOffset++;
98  }
99};
100
101var $panic = function(value) {
102  $curGoroutine.panicStack.push(value);
103  $callDeferred(null, null, true);
104};
105var $recover = function() {
106  if ($panicStackDepth === null || ($panicStackDepth !== undefined && $panicStackDepth !== $getStackDepth() - 2)) {
107    return $ifaceNil;
108  }
109  $panicStackDepth = null;
110  return $panicValue;
111};
112var $throw = function(err) { throw err; };
113
114var $noGoroutine = { asleep: false, exit: false, deferStack: [], panicStack: [] };
115var $curGoroutine = $noGoroutine, $totalGoroutines = 0, $awakeGoroutines = 0, $checkForDeadlock = true;
116var $mainFinished = false;
117var $go = function(fun, args) {
118  $totalGoroutines++;
119  $awakeGoroutines++;
120  var $goroutine = function() {
121    try {
122      $curGoroutine = $goroutine;
123      var r = fun.apply(undefined, args);
124      if (r && r.$blk !== undefined) {
125        fun = function() { return r.$blk(); };
126        args = [];
127        return;
128      }
129      $goroutine.exit = true;
130    } catch (err) {
131      if (!$goroutine.exit) {
132        throw err;
133      }
134    } finally {
135      $curGoroutine = $noGoroutine;
136      if ($goroutine.exit) { /* also set by runtime.Goexit() */
137        $totalGoroutines--;
138        $goroutine.asleep = true;
139      }
140      if ($goroutine.asleep) {
141        $awakeGoroutines--;
142        if (!$mainFinished && $awakeGoroutines === 0 && $checkForDeadlock) {
143          console.error("fatal error: all goroutines are asleep - deadlock!");
144          if ($global.process !== undefined) {
145            $global.process.exit(2);
146          }
147        }
148      }
149    }
150  };
151  $goroutine.asleep = false;
152  $goroutine.exit = false;
153  $goroutine.deferStack = [];
154  $goroutine.panicStack = [];
155  $schedule($goroutine);
156};
157
158var $scheduled = [];
159var $runScheduled = function() {
160  try {
161    var r;
162    while ((r = $scheduled.shift()) !== undefined) {
163      r();
164    }
165  } finally {
166    if ($scheduled.length > 0) {
167      setTimeout($runScheduled, 0);
168    }
169  }
170};
171
172var $schedule = function(goroutine) {
173  if (goroutine.asleep) {
174    goroutine.asleep = false;
175    $awakeGoroutines++;
176  }
177  $scheduled.push(goroutine);
178  if ($curGoroutine === $noGoroutine) {
179    $runScheduled();
180  }
181};
182
183var $setTimeout = function(f, t) {
184  $awakeGoroutines++;
185  return setTimeout(function() {
186    $awakeGoroutines--;
187    f();
188  }, t);
189};
190
191var $block = function() {
192  if ($curGoroutine === $noGoroutine) {
193    $throwRuntimeError("cannot block in JavaScript callback, fix by wrapping code in goroutine");
194  }
195  $curGoroutine.asleep = true;
196};
197
198var $send = function(chan, value) {
199  if (chan.$closed) {
200    $throwRuntimeError("send on closed channel");
201  }
202  var queuedRecv = chan.$recvQueue.shift();
203  if (queuedRecv !== undefined) {
204    queuedRecv([value, true]);
205    return;
206  }
207  if (chan.$buffer.length < chan.$capacity) {
208    chan.$buffer.push(value);
209    return;
210  }
211
212  var thisGoroutine = $curGoroutine;
213  var closedDuringSend;
214  chan.$sendQueue.push(function(closed) {
215    closedDuringSend = closed;
216    $schedule(thisGoroutine);
217    return value;
218  });
219  $block();
220  return {
221    $blk: function() {
222      if (closedDuringSend) {
223        $throwRuntimeError("send on closed channel");
224      }
225    }
226  };
227};
228var $recv = function(chan) {
229  var queuedSend = chan.$sendQueue.shift();
230  if (queuedSend !== undefined) {
231    chan.$buffer.push(queuedSend(false));
232  }
233  var bufferedValue = chan.$buffer.shift();
234  if (bufferedValue !== undefined) {
235    return [bufferedValue, true];
236  }
237  if (chan.$closed) {
238    return [chan.$elem.zero(), false];
239  }
240
241  var thisGoroutine = $curGoroutine;
242  var f = { $blk: function() { return this.value; } };
243  var queueEntry = function(v) {
244    f.value = v;
245    $schedule(thisGoroutine);
246  };
247  chan.$recvQueue.push(queueEntry);
248  $block();
249  return f;
250};
251var $close = function(chan) {
252  if (chan.$closed) {
253    $throwRuntimeError("close of closed channel");
254  }
255  chan.$closed = true;
256  while (true) {
257    var queuedSend = chan.$sendQueue.shift();
258    if (queuedSend === undefined) {
259      break;
260    }
261    queuedSend(true); /* will panic */
262  }
263  while (true) {
264    var queuedRecv = chan.$recvQueue.shift();
265    if (queuedRecv === undefined) {
266      break;
267    }
268    queuedRecv([chan.$elem.zero(), false]);
269  }
270};
271var $select = function(comms) {
272  var ready = [];
273  var selection = -1;
274  for (var i = 0; i < comms.length; i++) {
275    var comm = comms[i];
276    var chan = comm[0];
277    switch (comm.length) {
278    case 0: /* default */
279      selection = i;
280      break;
281    case 1: /* recv */
282      if (chan.$sendQueue.length !== 0 || chan.$buffer.length !== 0 || chan.$closed) {
283        ready.push(i);
284      }
285      break;
286    case 2: /* send */
287      if (chan.$closed) {
288        $throwRuntimeError("send on closed channel");
289      }
290      if (chan.$recvQueue.length !== 0 || chan.$buffer.length < chan.$capacity) {
291        ready.push(i);
292      }
293      break;
294    }
295  }
296
297  if (ready.length !== 0) {
298    selection = ready[Math.floor(Math.random() * ready.length)];
299  }
300  if (selection !== -1) {
301    var comm = comms[selection];
302    switch (comm.length) {
303    case 0: /* default */
304      return [selection];
305    case 1: /* recv */
306      return [selection, $recv(comm[0])];
307    case 2: /* send */
308      $send(comm[0], comm[1]);
309      return [selection];
310    }
311  }
312
313  var entries = [];
314  var thisGoroutine = $curGoroutine;
315  var f = { $blk: function() { return this.selection; } };
316  var removeFromQueues = function() {
317    for (var i = 0; i < entries.length; i++) {
318      var entry = entries[i];
319      var queue = entry[0];
320      var index = queue.indexOf(entry[1]);
321      if (index !== -1) {
322        queue.splice(index, 1);
323      }
324    }
325  };
326  for (var i = 0; i < comms.length; i++) {
327    (function(i) {
328      var comm = comms[i];
329      switch (comm.length) {
330      case 1: /* recv */
331        var queueEntry = function(value) {
332          f.selection = [i, value];
333          removeFromQueues();
334          $schedule(thisGoroutine);
335        };
336        entries.push([comm[0].$recvQueue, queueEntry]);
337        comm[0].$recvQueue.push(queueEntry);
338        break;
339      case 2: /* send */
340        var queueEntry = function() {
341          if (comm[0].$closed) {
342            $throwRuntimeError("send on closed channel");
343          }
344          f.selection = [i];
345          removeFromQueues();
346          $schedule(thisGoroutine);
347          return comm[1];
348        };
349        entries.push([comm[0].$sendQueue, queueEntry]);
350        comm[0].$sendQueue.push(queueEntry);
351        break;
352      }
353    })(i);
354  }
355  $block();
356  return f;
357};
358`
359