github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/prelude/types.js (about)

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