github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/compiler/prelude/types.go (about)

     1  package prelude
     2  
     3  const types = `
     4  var $kindBool = 1;
     5  var $kindInt = 2;
     6  var $kindInt8 = 3;
     7  var $kindInt16 = 4;
     8  var $kindInt32 = 5;
     9  var $kindInt64 = 6;
    10  var $kindUint = 7;
    11  var $kindUint8 = 8;
    12  var $kindUint16 = 9;
    13  var $kindUint32 = 10;
    14  var $kindUint64 = 11;
    15  var $kindUintptr = 12;
    16  var $kindFloat32 = 13;
    17  var $kindFloat64 = 14;
    18  var $kindComplex64 = 15;
    19  var $kindComplex128 = 16;
    20  var $kindArray = 17;
    21  var $kindChan = 18;
    22  var $kindFunc = 19;
    23  var $kindInterface = 20;
    24  var $kindMap = 21;
    25  var $kindPtr = 22;
    26  var $kindSlice = 23;
    27  var $kindString = 24;
    28  var $kindStruct = 25;
    29  var $kindUnsafePointer = 26;
    30  
    31  var $methodSynthesizers = [];
    32  var $addMethodSynthesizer = function(f) {
    33    if ($methodSynthesizers === null) {
    34      f();
    35      return;
    36    }
    37    $methodSynthesizers.push(f);
    38  };
    39  var $synthesizeMethods = function() {
    40    $methodSynthesizers.forEach(function(f) { f(); });
    41    $methodSynthesizers = null;
    42  };
    43  
    44  var $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  
    52  var $identity = function(x) { return x; };
    53  
    54  var $typeIDCounter = 0;
    55  
    56  var $idKey = function(x) {
    57    if (x.$id === undefined) {
    58      $idCounter++;
    59      x.$id = $idCounter;
    60    }
    61    return String(x.$id);
    62  };
    63  
    64  var $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.embedded) {
   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  
   396  var $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.embedded) {
   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  
   462  var $Bool          = $newType( 1, $kindBool,          "bool",           true, "", false, null);
   463  var $Int           = $newType( 4, $kindInt,           "int",            true, "", false, null);
   464  var $Int8          = $newType( 1, $kindInt8,          "int8",           true, "", false, null);
   465  var $Int16         = $newType( 2, $kindInt16,         "int16",          true, "", false, null);
   466  var $Int32         = $newType( 4, $kindInt32,         "int32",          true, "", false, null);
   467  var $Int64         = $newType( 8, $kindInt64,         "int64",          true, "", false, null);
   468  var $Uint          = $newType( 4, $kindUint,          "uint",           true, "", false, null);
   469  var $Uint8         = $newType( 1, $kindUint8,         "uint8",          true, "", false, null);
   470  var $Uint16        = $newType( 2, $kindUint16,        "uint16",         true, "", false, null);
   471  var $Uint32        = $newType( 4, $kindUint32,        "uint32",         true, "", false, null);
   472  var $Uint64        = $newType( 8, $kindUint64,        "uint64",         true, "", false, null);
   473  var $Uintptr       = $newType( 4, $kindUintptr,       "uintptr",        true, "", false, null);
   474  var $Float32       = $newType( 4, $kindFloat32,       "float32",        true, "", false, null);
   475  var $Float64       = $newType( 8, $kindFloat64,       "float64",        true, "", false, null);
   476  var $Complex64     = $newType( 8, $kindComplex64,     "complex64",      true, "", false, null);
   477  var $Complex128    = $newType(16, $kindComplex128,    "complex128",     true, "", false, null);
   478  var $String        = $newType( 8, $kindString,        "string",         true, "", false, null);
   479  var $UnsafePointer = $newType( 4, $kindUnsafePointer, "unsafe.Pointer", true, "", false, null);
   480  
   481  var $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  };
   509  var $toNativeArray = function(elemKind, array) {
   510    var nativeArray = $nativeArray(elemKind);
   511    if (nativeArray === Array) {
   512      return array;
   513    }
   514    return new nativeArray(array);
   515  };
   516  var $arrayTypes = {};
   517  var $arrayType = function(elem, len) {
   518    var typeKey = elem.id + "$" + len;
   519    var typ = $arrayTypes[typeKey];
   520    if (typ === undefined) {
   521      typ = $newType(elem.size*len, $kindArray, "[" + len + "]" + elem.string, false, "", false, null);
   522      $arrayTypes[typeKey] = typ;
   523      typ.init(elem, len);
   524    }
   525    return typ;
   526  };
   527  
   528  var $chanType = function(elem, sendOnly, recvOnly) {
   529    var string = (recvOnly ? "<-" : "") + "chan" + (sendOnly ? "<- " : " ")
   530    if (!sendOnly && !recvOnly && (elem.string[0] == "<")) {
   531      string += "(" + elem.string + ")";
   532    } else {
   533      string += elem.string;
   534    }
   535    var field = sendOnly ? "SendChan" : (recvOnly ? "RecvChan" : "Chan");
   536    var typ = elem[field];
   537    if (typ === undefined) {
   538      typ = $newType(4, $kindChan, string, false, "", false, null);
   539      elem[field] = typ;
   540      typ.init(elem, sendOnly, recvOnly);
   541    }
   542    return typ;
   543  };
   544  var $Chan = function(elem, capacity) {
   545    if (capacity < 0 || capacity > 2147483647) {
   546      $throwRuntimeError("makechan: size out of range");
   547    }
   548    this.$elem = elem;
   549    this.$capacity = capacity;
   550    this.$buffer = [];
   551    this.$sendQueue = [];
   552    this.$recvQueue = [];
   553    this.$closed = false;
   554  };
   555  var $chanNil = new $Chan(null, 0);
   556  $chanNil.$sendQueue = $chanNil.$recvQueue = { length: 0, push: function() {}, shift: function() { return undefined; }, indexOf: function() { return -1; } };
   557  
   558  var $funcTypes = {};
   559  var $funcType = function(params, results, variadic) {
   560    var typeKey = $mapArray(params, function(p) { return p.id; }).join(",") + "$" + $mapArray(results, function(r) { return r.id; }).join(",") + "$" + variadic;
   561    var typ = $funcTypes[typeKey];
   562    if (typ === undefined) {
   563      var paramTypes = $mapArray(params, function(p) { return p.string; });
   564      if (variadic) {
   565        paramTypes[paramTypes.length - 1] = "..." + paramTypes[paramTypes.length - 1].substr(2);
   566      }
   567      var string = "func(" + paramTypes.join(", ") + ")";
   568      if (results.length === 1) {
   569        string += " " + results[0].string;
   570      } else if (results.length > 1) {
   571        string += " (" + $mapArray(results, function(r) { return r.string; }).join(", ") + ")";
   572      }
   573      typ = $newType(4, $kindFunc, string, false, "", false, null);
   574      $funcTypes[typeKey] = typ;
   575      typ.init(params, results, variadic);
   576    }
   577    return typ;
   578  };
   579  
   580  var $interfaceTypes = {};
   581  var $interfaceType = function(methods) {
   582    var typeKey = $mapArray(methods, function(m) { return m.pkg + "," + m.name + "," + m.typ.id; }).join("$");
   583    var typ = $interfaceTypes[typeKey];
   584    if (typ === undefined) {
   585      var string = "interface {}";
   586      if (methods.length !== 0) {
   587        string = "interface { " + $mapArray(methods, function(m) {
   588          return (m.pkg !== "" ? m.pkg + "." : "") + m.name + m.typ.string.substr(4);
   589        }).join("; ") + " }";
   590      }
   591      typ = $newType(8, $kindInterface, string, false, "", false, null);
   592      $interfaceTypes[typeKey] = typ;
   593      typ.init(methods);
   594    }
   595    return typ;
   596  };
   597  var $emptyInterface = $interfaceType([]);
   598  var $ifaceNil = {};
   599  var $error = $newType(8, $kindInterface, "error", true, "", false, null);
   600  $error.init([{prop: "Error", name: "Error", pkg: "", typ: $funcType([], [$String], false)}]);
   601  
   602  var $mapTypes = {};
   603  var $mapType = function(key, elem) {
   604    var typeKey = key.id + "$" + elem.id;
   605    var typ = $mapTypes[typeKey];
   606    if (typ === undefined) {
   607      typ = $newType(4, $kindMap, "map[" + key.string + "]" + elem.string, false, "", false, null);
   608      $mapTypes[typeKey] = typ;
   609      typ.init(key, elem);
   610    }
   611    return typ;
   612  };
   613  var $makeMap = function(keyForFunc, entries) {
   614    var m = {};
   615    for (var i = 0; i < entries.length; i++) {
   616      var e = entries[i];
   617      m[keyForFunc(e.k)] = e;
   618    }
   619    return m;
   620  };
   621  
   622  var $ptrType = function(elem) {
   623    var typ = elem.ptr;
   624    if (typ === undefined) {
   625      typ = $newType(4, $kindPtr, "*" + elem.string, false, "", elem.exported, null);
   626      elem.ptr = typ;
   627      typ.init(elem);
   628    }
   629    return typ;
   630  };
   631  
   632  var $newDataPointer = function(data, constructor) {
   633    if (constructor.elem.kind === $kindStruct) {
   634      return data;
   635    }
   636    return new constructor(function() { return data; }, function(v) { data = v; });
   637  };
   638  
   639  var $indexPtr = function(array, index, constructor) {
   640    array.$ptr = array.$ptr || {};
   641    return array.$ptr[index] || (array.$ptr[index] = new constructor(function() { return array[index]; }, function(v) { array[index] = v; }));
   642  };
   643  
   644  var $sliceType = function(elem) {
   645    var typ = elem.slice;
   646    if (typ === undefined) {
   647      typ = $newType(12, $kindSlice, "[]" + elem.string, false, "", false, null);
   648      elem.slice = typ;
   649      typ.init(elem);
   650    }
   651    return typ;
   652  };
   653  var $makeSlice = function(typ, length, capacity) {
   654    capacity = capacity || length;
   655    if (length < 0 || length > 2147483647) {
   656      $throwRuntimeError("makeslice: len out of range");
   657    }
   658    if (capacity < 0 || capacity < length || capacity > 2147483647) {
   659      $throwRuntimeError("makeslice: cap out of range");
   660    }
   661    var array = new typ.nativeArray(capacity);
   662    if (typ.nativeArray === Array) {
   663      for (var i = 0; i < capacity; i++) {
   664        array[i] = typ.elem.zero();
   665      }
   666    }
   667    var slice = new typ(array);
   668    slice.$length = length;
   669    return slice;
   670  };
   671  
   672  var $structTypes = {};
   673  var $structType = function(pkgPath, fields) {
   674    var typeKey = $mapArray(fields, function(f) { return f.name + "," + f.typ.id + "," + f.tag; }).join("$");
   675    var typ = $structTypes[typeKey];
   676    if (typ === undefined) {
   677      var string = "struct { " + $mapArray(fields, function(f) {
   678      var str = f.typ.string + (f.tag !== "" ? (" \"" + f.tag.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"") : "");
   679      if (f.embedded) {
   680        return str
   681      }
   682      return f.name + " " + str
   683      }).join("; ") + " }";
   684      if (fields.length === 0) {
   685        string = "struct {}";
   686      }
   687      typ = $newType(0, $kindStruct, string, false, "", false, function() {
   688        this.$val = this;
   689        for (var i = 0; i < fields.length; i++) {
   690          var f = fields[i];
   691          var arg = arguments[i];
   692          this[f.prop] = arg !== undefined ? arg : f.typ.zero();
   693        }
   694      });
   695      $structTypes[typeKey] = typ;
   696      typ.init(pkgPath, fields);
   697    }
   698    return typ;
   699  };
   700  
   701  var $assertType = function(value, type, returnTuple) {
   702    var isInterface = (type.kind === $kindInterface), ok, missingMethod = "";
   703    if (value === $ifaceNil) {
   704      ok = false;
   705    } else if (!isInterface) {
   706      ok = value.constructor === type;
   707    } else {
   708      var valueTypeString = value.constructor.string;
   709      ok = type.implementedBy[valueTypeString];
   710      if (ok === undefined) {
   711        ok = true;
   712        var valueMethodSet = $methodSet(value.constructor);
   713        var interfaceMethods = type.methods;
   714        for (var i = 0; i < interfaceMethods.length; i++) {
   715          var tm = interfaceMethods[i];
   716          var found = false;
   717          for (var j = 0; j < valueMethodSet.length; j++) {
   718            var vm = valueMethodSet[j];
   719            if (vm.name === tm.name && vm.pkg === tm.pkg && vm.typ === tm.typ) {
   720              found = true;
   721              break;
   722            }
   723          }
   724          if (!found) {
   725            ok = false;
   726            type.missingMethodFor[valueTypeString] = tm.name;
   727            break;
   728          }
   729        }
   730        type.implementedBy[valueTypeString] = ok;
   731      }
   732      if (!ok) {
   733        missingMethod = type.missingMethodFor[valueTypeString];
   734      }
   735    }
   736  
   737    if (!ok) {
   738      if (returnTuple) {
   739        return [type.zero(), false];
   740      }
   741      $panic(new $packages["runtime"].TypeAssertionError.ptr(
   742        $packages["runtime"]._type.ptr.nil,
   743        (value === $ifaceNil ? $packages["runtime"]._type.ptr.nil : new $packages["runtime"]._type.ptr(value.constructor.string)),
   744        new $packages["runtime"]._type.ptr(type.string),
   745        missingMethod));
   746    }
   747  
   748    if (!isInterface) {
   749      value = value.$val;
   750    }
   751    if (type === $jsObjectPtr) {
   752      value = value.object;
   753    }
   754    return returnTuple ? [value, true] : value;
   755  };
   756  `