1package prelude
2
3const types = `
4var $kindBool = 1;
5var $kindInt = 2;
6var $kindInt8 = 3;
7var $kindInt16 = 4;
8var $kindInt32 = 5;
9var $kindInt64 = 6;
10var $kindUint = 7;
11var $kindUint8 = 8;
12var $kindUint16 = 9;
13var $kindUint32 = 10;
14var $kindUint64 = 11;
15var $kindUintptr = 12;
16var $kindFloat32 = 13;
17var $kindFloat64 = 14;
18var $kindComplex64 = 15;
19var $kindComplex128 = 16;
20var $kindArray = 17;
21var $kindChan = 18;
22var $kindFunc = 19;
23var $kindInterface = 20;
24var $kindMap = 21;
25var $kindPtr = 22;
26var $kindSlice = 23;
27var $kindString = 24;
28var $kindStruct = 25;
29var $kindUnsafePointer = 26;
30
31var $methodSynthesizers = [];
32var $addMethodSynthesizer = function(f) {
33  if ($methodSynthesizers === null) {
34    f();
35    return;
36  }
37  $methodSynthesizers.push(f);
38};
39var $synthesizeMethods = function() {
40  $methodSynthesizers.forEach(function(f) { f(); });
41  $methodSynthesizers = null;
42};
43
44var $ifaceKeyFor = function(x) {
45  if (x === $ifaceNil) {
46    return 'nil';
47  }
48  var c = x.constructor;
49  return c.string + '$' + c.keyFor(x.$val);
50};
51
52var $identity = function(x) { return x; };
53
54var $typeIDCounter = 0;
55
56var $idKey = function(x) {
57  if (x.$id === undefined) {
58    $idCounter++;
59    x.$id = $idCounter;
60  }
61  return String(x.$id);
62};
63
64var $newType = function(size, kind, string, named, pkg, exported, constructor) {
65  var typ;
66  switch(kind) {
67  case $kindBool:
68  case $kindInt:
69  case $kindInt8:
70  case $kindInt16:
71  case $kindInt32:
72  case $kindUint:
73  case $kindUint8:
74  case $kindUint16:
75  case $kindUint32:
76  case $kindUintptr:
77  case $kindUnsafePointer:
78    typ = function(v) { this.$val = v; };
79    typ.wrapped = true;
80    typ.keyFor = $identity;
81    break;
82
83  case $kindString:
84    typ = function(v) { this.$val = v; };
85    typ.wrapped = true;
86    typ.keyFor = function(x) { return "$" + x; };
87    break;
88
89  case $kindFloat32:
90  case $kindFloat64:
91    typ = function(v) { this.$val = v; };
92    typ.wrapped = true;
93    typ.keyFor = function(x) { return $floatKey(x); };
94    break;
95
96  case $kindInt64:
97    typ = function(high, low) {
98      this.$high = (high + Math.floor(Math.ceil(low) / 4294967296)) >> 0;
99      this.$low = low >>> 0;
100      this.$val = this;
101    };
102    typ.keyFor = function(x) { return x.$high + "$" + x.$low; };
103    break;
104
105  case $kindUint64:
106    typ = function(high, low) {
107      this.$high = (high + Math.floor(Math.ceil(low) / 4294967296)) >>> 0;
108      this.$low = low >>> 0;
109      this.$val = this;
110    };
111    typ.keyFor = function(x) { return x.$high + "$" + x.$low; };
112    break;
113
114  case $kindComplex64:
115    typ = function(real, imag) {
116      this.$real = $fround(real);
117      this.$imag = $fround(imag);
118      this.$val = this;
119    };
120    typ.keyFor = function(x) { return x.$real + "$" + x.$imag; };
121    break;
122
123  case $kindComplex128:
124    typ = function(real, imag) {
125      this.$real = real;
126      this.$imag = imag;
127      this.$val = this;
128    };
129    typ.keyFor = function(x) { return x.$real + "$" + x.$imag; };
130    break;
131
132  case $kindArray:
133    typ = function(v) { this.$val = v; };
134    typ.wrapped = true;
135    typ.ptr = $newType(4, $kindPtr, "*" + string, false, "", false, function(array) {
136      this.$get = function() { return array; };
137      this.$set = function(v) { typ.copy(this, v); };
138      this.$val = array;
139    });
140    typ.init = function(elem, len) {
141      typ.elem = elem;
142      typ.len = len;
143      typ.comparable = elem.comparable;
144      typ.keyFor = function(x) {
145        return Array.prototype.join.call($mapArray(x, function(e) {
146          return String(elem.keyFor(e)).replace(/\\/g, "\\\\").replace(/\$/g, "\\$");
147        }), "$");
148      };
149      typ.copy = function(dst, src) {
150        $copyArray(dst, src, 0, 0, src.length, elem);
151      };
152      typ.ptr.init(typ);
153      Object.defineProperty(typ.ptr.nil, "nilCheck", { get: $throwNilPointerError });
154    };
155    break;
156
157  case $kindChan:
158    typ = function(v) { this.$val = v; };
159    typ.wrapped = true;
160    typ.keyFor = $idKey;
161    typ.init = function(elem, sendOnly, recvOnly) {
162      typ.elem = elem;
163      typ.sendOnly = sendOnly;
164      typ.recvOnly = recvOnly;
165    };
166    break;
167
168  case $kindFunc:
169    typ = function(v) { this.$val = v; };
170    typ.wrapped = true;
171    typ.init = function(params, results, variadic) {
172      typ.params = params;
173      typ.results = results;
174      typ.variadic = variadic;
175      typ.comparable = false;
176    };
177    break;
178
179  case $kindInterface:
180    typ = { implementedBy: {}, missingMethodFor: {} };
181    typ.keyFor = $ifaceKeyFor;
182    typ.init = function(methods) {
183      typ.methods = methods;
184      methods.forEach(function(m) {
185        $ifaceNil[m.prop] = $throwNilPointerError;
186      });
187    };
188    break;
189
190  case $kindMap:
191    typ = function(v) { this.$val = v; };
192    typ.wrapped = true;
193    typ.init = function(key, elem) {
194      typ.key = key;
195      typ.elem = elem;
196      typ.comparable = false;
197    };
198    break;
199
200  case $kindPtr:
201    typ = constructor || function(getter, setter, target) {
202      this.$get = getter;
203      this.$set = setter;
204      this.$target = target;
205      this.$val = this;
206    };
207    typ.keyFor = $idKey;
208    typ.init = function(elem) {
209      typ.elem = elem;
210      typ.wrapped = (elem.kind === $kindArray);
211      typ.nil = new typ($throwNilPointerError, $throwNilPointerError);
212    };
213    break;
214
215  case $kindSlice:
216    typ = function(array) {
217      if (array.constructor !== typ.nativeArray) {
218        array = new typ.nativeArray(array);
219      }
220      this.$array = array;
221      this.$offset = 0;
222      this.$length = array.length;
223      this.$capacity = array.length;
224      this.$val = this;
225    };
226    typ.init = function(elem) {
227      typ.elem = elem;
228      typ.comparable = false;
229      typ.nativeArray = $nativeArray(elem.kind);
230      typ.nil = new typ([]);
231    };
232    break;
233
234  case $kindStruct:
235    typ = function(v) { this.$val = v; };
236    typ.wrapped = true;
237    typ.ptr = $newType(4, $kindPtr, "*" + string, false, pkg, exported, constructor);
238    typ.ptr.elem = typ;
239    typ.ptr.prototype.$get = function() { return this; };
240    typ.ptr.prototype.$set = function(v) { typ.copy(this, v); };
241    typ.init = function(pkgPath, fields) {
242      typ.pkgPath = pkgPath;
243      typ.fields = fields;
244      fields.forEach(function(f) {
245        if (!f.typ.comparable) {
246          typ.comparable = false;
247        }
248      });
249      typ.keyFor = function(x) {
250        var val = x.$val;
251        return $mapArray(fields, function(f) {
252          return String(f.typ.keyFor(val[f.prop])).replace(/\\/g, "\\\\").replace(/\$/g, "\\$");
253        }).join("$");
254      };
255      typ.copy = function(dst, src) {
256        for (var i = 0; i < fields.length; i++) {
257          var f = fields[i];
258          switch (f.typ.kind) {
259          case $kindArray:
260          case $kindStruct:
261            f.typ.copy(dst[f.prop], src[f.prop]);
262            continue;
263          default:
264            dst[f.prop] = src[f.prop];
265            continue;
266          }
267        }
268      };
269      /* nil value */
270      var properties = {};
271      fields.forEach(function(f) {
272        properties[f.prop] = { get: $throwNilPointerError, set: $throwNilPointerError };
273      });
274      typ.ptr.nil = Object.create(constructor.prototype, properties);
275      typ.ptr.nil.$val = typ.ptr.nil;
276      /* methods for embedded fields */
277      $addMethodSynthesizer(function() {
278        var synthesizeMethod = function(target, m, f) {
279          if (target.prototype[m.prop] !== undefined) { return; }
280          target.prototype[m.prop] = function() {
281            var v = this.$val[f.prop];
282            if (f.typ === $jsObjectPtr) {
283              v = new $jsObjectPtr(v);
284            }
285            if (v.$val === undefined) {
286              v = new f.typ(v);
287            }
288            return v[m.prop].apply(v, arguments);
289          };
290        };
291        fields.forEach(function(f) {
292          if (f.anonymous) {
293            $methodSet(f.typ).forEach(function(m) {
294              synthesizeMethod(typ, m, f);
295              synthesizeMethod(typ.ptr, m, f);
296            });
297            $methodSet($ptrType(f.typ)).forEach(function(m) {
298              synthesizeMethod(typ.ptr, m, f);
299            });
300          }
301        });
302      });
303    };
304    break;
305
306  default:
307    $panic(new $String("invalid kind: " + kind));
308  }
309
310  switch (kind) {
311  case $kindBool:
312  case $kindMap:
313    typ.zero = function() { return false; };
314    break;
315
316  case $kindInt:
317  case $kindInt8:
318  case $kindInt16:
319  case $kindInt32:
320  case $kindUint:
321  case $kindUint8 :
322  case $kindUint16:
323  case $kindUint32:
324  case $kindUintptr:
325  case $kindUnsafePointer:
326  case $kindFloat32:
327  case $kindFloat64:
328    typ.zero = function() { return 0; };
329    break;
330
331  case $kindString:
332    typ.zero = function() { return ""; };
333    break;
334
335  case $kindInt64:
336  case $kindUint64:
337  case $kindComplex64:
338  case $kindComplex128:
339    var zero = new typ(0, 0);
340    typ.zero = function() { return zero; };
341    break;
342
343  case $kindPtr:
344  case $kindSlice:
345    typ.zero = function() { return typ.nil; };
346    break;
347
348  case $kindChan:
349    typ.zero = function() { return $chanNil; };
350    break;
351
352  case $kindFunc:
353    typ.zero = function() { return $throwNilPointerError; };
354    break;
355
356  case $kindInterface:
357    typ.zero = function() { return $ifaceNil; };
358    break;
359
360  case $kindArray:
361    typ.zero = function() {
362      var arrayClass = $nativeArray(typ.elem.kind);
363      if (arrayClass !== Array) {
364        return new arrayClass(typ.len);
365      }
366      var array = new Array(typ.len);
367      for (var i = 0; i < typ.len; i++) {
368        array[i] = typ.elem.zero();
369      }
370      return array;
371    };
372    break;
373
374  case $kindStruct:
375    typ.zero = function() { return new typ.ptr(); };
376    break;
377
378  default:
379    $panic(new $String("invalid kind: " + kind));
380  }
381
382  typ.id = $typeIDCounter;
383  $typeIDCounter++;
384  typ.size = size;
385  typ.kind = kind;
386  typ.string = string;
387  typ.named = named;
388  typ.pkg = pkg;
389  typ.exported = exported;
390  typ.methods = [];
391  typ.methodSetCache = null;
392  typ.comparable = true;
393  return typ;
394};
395
396var $methodSet = function(typ) {
397  if (typ.methodSetCache !== null) {
398    return typ.methodSetCache;
399  }
400  var base = {};
401
402  var isPtr = (typ.kind === $kindPtr);
403  if (isPtr && typ.elem.kind === $kindInterface) {
404    typ.methodSetCache = [];
405    return [];
406  }
407
408  var current = [{typ: isPtr ? typ.elem : typ, indirect: isPtr}];
409
410  var seen = {};
411
412  while (current.length > 0) {
413    var next = [];
414    var mset = [];
415
416    current.forEach(function(e) {
417      if (seen[e.typ.string]) {
418        return;
419      }
420      seen[e.typ.string] = true;
421
422      if (e.typ.named) {
423        mset = mset.concat(e.typ.methods);
424        if (e.indirect) {
425          mset = mset.concat($ptrType(e.typ).methods);
426        }
427      }
428
429      switch (e.typ.kind) {
430      case $kindStruct:
431        e.typ.fields.forEach(function(f) {
432          if (f.anonymous) {
433            var fTyp = f.typ;
434            var fIsPtr = (fTyp.kind === $kindPtr);
435            next.push({typ: fIsPtr ? fTyp.elem : fTyp, indirect: e.indirect || fIsPtr});
436          }
437        });
438        break;
439
440      case $kindInterface:
441        mset = mset.concat(e.typ.methods);
442        break;
443      }
444    });
445
446    mset.forEach(function(m) {
447      if (base[m.name] === undefined) {
448        base[m.name] = m;
449      }
450    });
451
452    current = next;
453  }
454
455  typ.methodSetCache = [];
456  Object.keys(base).sort().forEach(function(name) {
457    typ.methodSetCache.push(base[name]);
458  });
459  return typ.methodSetCache;
460};
461
462var $Bool          = $newType( 1, $kindBool,          "bool",           true, "", false, null);
463var $Int           = $newType( 4, $kindInt,           "int",            true, "", false, null);
464var $Int8          = $newType( 1, $kindInt8,          "int8",           true, "", false, null);
465var $Int16         = $newType( 2, $kindInt16,         "int16",          true, "", false, null);
466var $Int32         = $newType( 4, $kindInt32,         "int32",          true, "", false, null);
467var $Int64         = $newType( 8, $kindInt64,         "int64",          true, "", false, null);
468var $Uint          = $newType( 4, $kindUint,          "uint",           true, "", false, null);
469var $Uint8         = $newType( 1, $kindUint8,         "uint8",          true, "", false, null);
470var $Uint16        = $newType( 2, $kindUint16,        "uint16",         true, "", false, null);
471var $Uint32        = $newType( 4, $kindUint32,        "uint32",         true, "", false, null);
472var $Uint64        = $newType( 8, $kindUint64,        "uint64",         true, "", false, null);
473var $Uintptr       = $newType( 4, $kindUintptr,       "uintptr",        true, "", false, null);
474var $Float32       = $newType( 4, $kindFloat32,       "float32",        true, "", false, null);
475var $Float64       = $newType( 8, $kindFloat64,       "float64",        true, "", false, null);
476var $Complex64     = $newType( 8, $kindComplex64,     "complex64",      true, "", false, null);
477var $Complex128    = $newType(16, $kindComplex128,    "complex128",     true, "", false, null);
478var $String        = $newType( 8, $kindString,        "string",         true, "", false, null);
479var $UnsafePointer = $newType( 4, $kindUnsafePointer, "unsafe.Pointer", true, "", false, null);
480
481var $nativeArray = function(elemKind) {
482  switch (elemKind) {
483  case $kindInt:
484    return Int32Array;
485  case $kindInt8:
486    return Int8Array;
487  case $kindInt16:
488    return Int16Array;
489  case $kindInt32:
490    return Int32Array;
491  case $kindUint:
492    return Uint32Array;
493  case $kindUint8:
494    return Uint8Array;
495  case $kindUint16:
496    return Uint16Array;
497  case $kindUint32:
498    return Uint32Array;
499  case $kindUintptr:
500    return Uint32Array;
501  case $kindFloat32:
502    return Float32Array;
503  case $kindFloat64:
504    return Float64Array;
505  default:
506    return Array;
507  }
508};
509var $toNativeArray = function(elemKind, array) {
510  var nativeArray = $nativeArray(elemKind);
511  if (nativeArray === Array) {
512    return array;
513  }
514  return new nativeArray(array);
515};
516var $arrayTypes = {};
517var $arrayType = function(elem, len) {
518  var typeKey = elem.id + "$" + len;
519  var typ = $arrayTypes[typeKey];
520  if (typ === undefined) {
521    typ = $newType(12, $kindArray, "[" + len + "]" + elem.string, false, "", false, null);
522    $arrayTypes[typeKey] = typ;
523    typ.init(elem, len);
524  }
525  return typ;
526};
527
528var $chanType = function(elem, sendOnly, recvOnly) {
529  var string = (recvOnly ? "<-" : "") + "chan" + (sendOnly ? "<- " : " ") + elem.string;
530  var field = sendOnly ? "SendChan" : (recvOnly ? "RecvChan" : "Chan");
531  var typ = elem[field];
532  if (typ === undefined) {
533    typ = $newType(4, $kindChan, string, false, "", false, null);
534    elem[field] = typ;
535    typ.init(elem, sendOnly, recvOnly);
536  }
537  return typ;
538};
539var $Chan = function(elem, capacity) {
540  if (capacity < 0 || capacity > 2147483647) {
541    $throwRuntimeError("makechan: size out of range");
542  }
543  this.$elem = elem;
544  this.$capacity = capacity;
545  this.$buffer = [];
546  this.$sendQueue = [];
547  this.$recvQueue = [];
548  this.$closed = false;
549};
550var $chanNil = new $Chan(null, 0);
551$chanNil.$sendQueue = $chanNil.$recvQueue = { length: 0, push: function() {}, shift: function() { return undefined; }, indexOf: function() { return -1; } };
552
553var $funcTypes = {};
554var $funcType = function(params, results, variadic) {
555  var typeKey = $mapArray(params, function(p) { return p.id; }).join(",") + "$" + $mapArray(results, function(r) { return r.id; }).join(",") + "$" + variadic;
556  var typ = $funcTypes[typeKey];
557  if (typ === undefined) {
558    var paramTypes = $mapArray(params, function(p) { return p.string; });
559    if (variadic) {
560      paramTypes[paramTypes.length - 1] = "..." + paramTypes[paramTypes.length - 1].substr(2);
561    }
562    var string = "func(" + paramTypes.join(", ") + ")";
563    if (results.length === 1) {
564      string += " " + results[0].string;
565    } else if (results.length > 1) {
566      string += " (" + $mapArray(results, function(r) { return r.string; }).join(", ") + ")";
567    }
568    typ = $newType(4, $kindFunc, string, false, "", false, null);
569    $funcTypes[typeKey] = typ;
570    typ.init(params, results, variadic);
571  }
572  return typ;
573};
574
575var $interfaceTypes = {};
576var $interfaceType = function(methods) {
577  var typeKey = $mapArray(methods, function(m) { return m.pkg + "," + m.name + "," + m.typ.id; }).join("$");
578  var typ = $interfaceTypes[typeKey];
579  if (typ === undefined) {
580    var string = "interface {}";
581    if (methods.length !== 0) {
582      string = "interface { " + $mapArray(methods, function(m) {
583        return (m.pkg !== "" ? m.pkg + "." : "") + m.name + m.typ.string.substr(4);
584      }).join("; ") + " }";
585    }
586    typ = $newType(8, $kindInterface, string, false, "", false, null);
587    $interfaceTypes[typeKey] = typ;
588    typ.init(methods);
589  }
590  return typ;
591};
592var $emptyInterface = $interfaceType([]);
593var $ifaceNil = {};
594var $error = $newType(8, $kindInterface, "error", true, "", false, null);
595$error.init([{prop: "Error", name: "Error", pkg: "", typ: $funcType([], [$String], false)}]);
596
597var $mapTypes = {};
598var $mapType = function(key, elem) {
599  var typeKey = key.id + "$" + elem.id;
600  var typ = $mapTypes[typeKey];
601  if (typ === undefined) {
602    typ = $newType(4, $kindMap, "map[" + key.string + "]" + elem.string, false, "", false, null);
603    $mapTypes[typeKey] = typ;
604    typ.init(key, elem);
605  }
606  return typ;
607};
608var $makeMap = function(keyForFunc, entries) {
609  var m = {};
610  for (var i = 0; i < entries.length; i++) {
611    var e = entries[i];
612    m[keyForFunc(e.k)] = e;
613  }
614  return m;
615};
616
617var $ptrType = function(elem) {
618  var typ = elem.ptr;
619  if (typ === undefined) {
620    typ = $newType(4, $kindPtr, "*" + elem.string, false, "", elem.exported, null);
621    elem.ptr = typ;
622    typ.init(elem);
623  }
624  return typ;
625};
626
627var $newDataPointer = function(data, constructor) {
628  if (constructor.elem.kind === $kindStruct) {
629    return data;
630  }
631  return new constructor(function() { return data; }, function(v) { data = v; });
632};
633
634var $indexPtr = function(array, index, constructor) {
635  array.$ptr = array.$ptr || {};
636  return array.$ptr[index] || (array.$ptr[index] = new constructor(function() { return array[index]; }, function(v) { array[index] = v; }));
637};
638
639var $sliceType = function(elem) {
640  var typ = elem.slice;
641  if (typ === undefined) {
642    typ = $newType(12, $kindSlice, "[]" + elem.string, false, "", false, null);
643    elem.slice = typ;
644    typ.init(elem);
645  }
646  return typ;
647};
648var $makeSlice = function(typ, length, capacity) {
649  capacity = capacity || length;
650  if (length < 0 || length > 2147483647) {
651    $throwRuntimeError("makeslice: len out of range");
652  }
653  if (capacity < 0 || capacity < length || capacity > 2147483647) {
654    $throwRuntimeError("makeslice: cap out of range");
655  }
656  var array = new typ.nativeArray(capacity);
657  if (typ.nativeArray === Array) {
658    for (var i = 0; i < capacity; i++) {
659      array[i] = typ.elem.zero();
660    }
661  }
662  var slice = new typ(array);
663  slice.$length = length;
664  return slice;
665};
666
667var $structTypes = {};
668var $structType = function(pkgPath, fields) {
669  var typeKey = $mapArray(fields, function(f) { return f.name + "," + f.typ.id + "," + f.tag; }).join("$");
670  var typ = $structTypes[typeKey];
671  if (typ === undefined) {
672    var string = "struct { " + $mapArray(fields, function(f) {
673      return f.name + " " + f.typ.string + (f.tag !== "" ? (" \"" + f.tag.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"") : "");
674    }).join("; ") + " }";
675    if (fields.length === 0) {
676      string = "struct {}";
677    }
678    typ = $newType(0, $kindStruct, string, false, "", false, function() {
679      this.$val = this;
680      for (var i = 0; i < fields.length; i++) {
681        var f = fields[i];
682        var arg = arguments[i];
683        this[f.prop] = arg !== undefined ? arg : f.typ.zero();
684      }
685    });
686    $structTypes[typeKey] = typ;
687    typ.init(pkgPath, fields);
688  }
689  return typ;
690};
691
692var $assertType = function(value, type, returnTuple) {
693  var isInterface = (type.kind === $kindInterface), ok, missingMethod = "";
694  if (value === $ifaceNil) {
695    ok = false;
696  } else if (!isInterface) {
697    ok = value.constructor === type;
698  } else {
699    var valueTypeString = value.constructor.string;
700    ok = type.implementedBy[valueTypeString];
701    if (ok === undefined) {
702      ok = true;
703      var valueMethodSet = $methodSet(value.constructor);
704      var interfaceMethods = type.methods;
705      for (var i = 0; i < interfaceMethods.length; i++) {
706        var tm = interfaceMethods[i];
707        var found = false;
708        for (var j = 0; j < valueMethodSet.length; j++) {
709          var vm = valueMethodSet[j];
710          if (vm.name === tm.name && vm.pkg === tm.pkg && vm.typ === tm.typ) {
711            found = true;
712            break;
713          }
714        }
715        if (!found) {
716          ok = false;
717          type.missingMethodFor[valueTypeString] = tm.name;
718          break;
719        }
720      }
721      type.implementedBy[valueTypeString] = ok;
722    }
723    if (!ok) {
724      missingMethod = type.missingMethodFor[valueTypeString];
725    }
726  }
727
728  if (!ok) {
729    if (returnTuple) {
730      return [type.zero(), false];
731    }
732    $panic(new $packages["runtime"].TypeAssertionError.ptr("", (value === $ifaceNil ? "" : value.constructor.string), type.string, missingMethod));
733  }
734
735  if (!isInterface) {
736    value = value.$val;
737  }
738  if (type === $jsObjectPtr) {
739    value = value.object;
740  }
741  return returnTuple ? [value, true] : value;
742};
743`
744