js实现canvas黑客帝国文字矩阵动画效果代码

代码语言:html

所属分类:动画

代码描述:js实现canvas黑客帝国文字矩阵动画效果代码

代码标签: 黑客帝国 文字 矩阵 动画 效果

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script>
        "use strict";
    (function() {
    
    Error.stackTraceLimit = Infinity;
    
    var $global, $module;
    if (typeof window !== "undefined") { /* web page */
      $global = window;
    } else if (typeof self !== "undefined") { /* web worker */
      $global = self;
    } else if (typeof global !== "undefined") { /* Node.js */
      $global = global;
      $global.require = require;
    } else { /* others (e.g. Nashorn) */
      $global = this;
    }
    
    if ($global === undefined || $global.Array === undefined) {
      throw new Error("no global object found");
    }
    if (typeof module !== "undefined") {
      $module = module;
    }
    
    var $packages = {}, $idCounter = 0;
    var $keys = function(m) { return m ? Object.keys(m) : []; };
    var $flushConsole = function() {};
    var $throwRuntimeError; /* set by package "runtime" */
    var $throwNilPointerError = function() { $throwRuntimeError("invalid memory address or nil pointer dereference"); };
    var $call = function(fn, rcvr, args) { return fn.apply(rcvr, args); };
    var $makeFunc = function(fn) { return function() { return $externalize(fn(this, new ($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments, []))), $emptyInterface); }; };
    var $unused = function(v) {};
    
    var $mapArray = function(array, f) {
      var newArray = new array.constructor(array.length);
      for (var i = 0; i < array.length; i++) {
        newArray[i] = f(array[i]);
      }
      return newArray;
    };
    
    var $methodVal = function(recv, name) {
      var vals = recv.$methodVals || {};
      recv.$methodVals = vals; /* noop for primitives */
      var f = vals[name];
      if (f !== undefined) {
        return f;
      }
      var method = recv[name];
      f = function() {
        $stackDepthOffset--;
        try {
          return method.apply(recv, arguments);
        } finally {
          $stackDepthOffset++;
        }
      };
      vals[name] = f;
      return f;
    };
    
    var $methodExpr = function(typ, name) {
      var method = typ.prototype[name];
      if (method.$expr === undefined) {
        method.$expr = function() {
          $stackDepthOffset--;
          try {
            if (typ.wrapped) {
              arguments[0] = new typ(arguments[0]);
            }
            return Function.call.apply(method, arguments);
          } finally {
            $stackDepthOffset++;
          }
        };
      }
      return method.$expr;
    };
    
    var $ifaceMethodExprs = {};
    var $ifaceMethodExpr = function(name) {
      var expr = $ifaceMethodExprs["$" + name];
      if (expr === undefined) {
        expr = $ifaceMethodExprs["$" + name] = function() {
          $stackDepthOffset--;
          try {
            return Function.call.apply(arguments[0][name], arguments);
          } finally {
            $stackDepthOffset++;
          }
        };
      }
      return expr;
    };
    
    var $subslice = function(slice, low, high, max) {
      if (low < 0 || high < low || max < high || high > slice.$capacity || max > slice.$capacity) {
        $throwRuntimeError("slice bounds out of range");
      }
      var s = new slice.constructor(slice.$array);
      s.$offset = slice.$offset + low;
      s.$length = slice.$length - low;
      s.$capacity = slice.$capacity - low;
      if (high !== undefined) {
        s.$length = high - low;
      }
      if (max !== undefined) {
        s.$capacity = max - low;
      }
      return s;
    };
    
    var $substring = function(str, low, high) {
      if (low < 0 || high < low || high > str.length) {
        $throwRuntimeError("slice bounds out of range");
      }
      return str.substring(low, high);
    };
    
    var $sliceToArray = function(slice) {
      if (slice.$array.constructor !== Array) {
        return slice.$array.subarray(slice.$offset, slice.$offset + slice.$length);
      }
      return slice.$array.slice(slice.$offset, slice.$offset + slice.$length);
    };
    
    var $decodeRune = function(str, pos) {
      var c0 = str.charCodeAt(pos);
    
      if (c0 < 0x80) {
        return [c0, 1];
      }
    
      if (c0 !== c0 || c0 < 0xC0) {
        return [0xFFFD, 1];
      }
    
      var c1 = str.charCodeAt(pos + 1);
      if (c1 !== c1 || c1 < 0x80 || 0xC0 <= c1) {
        return [0xFFFD, 1];
      }
    
      if (c0 < 0xE0) {
        var r = (c0 & 0x1F) << 6 | (c1 & 0x3F);
        if (r <= 0x7F) {
          return [0xFFFD, 1];
        }
        return [r, 2];
      }
    
      var c2 = str.charCodeAt(pos + 2);
      if (c2 !== c2 || c2 < 0x80 || 0xC0 <= c2) {
        return [0xFFFD, 1];
      }
    
      if (c0 < 0xF0) {
        var r = (c0 & 0x0F) << 12 | (c1 & 0x3F) << 6 | (c2 & 0x3F);
        if (r <= 0x7FF) {
          return [0xFFFD, 1];
        }
        if (0xD800 <= r && r <= 0xDFFF) {
          return [0xFFFD, 1];
        }
        return [r, 3];
      }
    
      var c3 = str.charCodeAt(pos + 3);
      if (c3 !== c3 || c3 < 0x80 || 0xC0 <= c3) {
        return [0xFFFD, 1];
      }
    
      if (c0 < 0xF8) {
        var r = (c0 & 0x07) << 18 | (c1 & 0x3F) << 12 | (c2 & 0x3F) << 6 | (c3 & 0x3F);
        if (r <= 0xFFFF || 0x10FFFF < r) {
          return [0xFFFD, 1];
        }
        return [r, 4];
      }
    
      return [0xFFFD, 1];
    };
    
    var $encodeRune = function(r) {
      if (r < 0 || r > 0x10FFFF || (0xD800 <= r && r <= 0xDFFF)) {
        r = 0xFFFD;
      }
      if (r <= 0x7F) {
        return String.fromCharCode(r);
      }
      if (r <= 0x7FF) {
        return String.fromCharCode(0xC0 | r >> 6, 0x80 | (r & 0x3F));
      }
      if (r <= 0xFFFF) {
        return String.fromCharCode(0xE0 | r >> 12, 0x80 | (r >> 6 & 0x3F), 0x80 | (r & 0x3F));
      }
      return String.fromCharCode(0xF0 | r >> 18, 0x80 | (r >> 12 & 0x3F), 0x80 | (r >> 6 & 0x3F), 0x80 | (r & 0x3F));
    };
    
    var $stringToBytes = function(str) {
      var array = new Uint8Array(str.length);
      for (var i = 0; i < str.length; i++) {
        array[i] = str.charCodeAt(i);
      }
      return array;
    };
    
    var $bytesToString = function(slice) {
      if (slice.$length === 0) {
        return "";
      }
      var str = "";
      for (var i = 0; i < slice.$length; i += 10000) {
        str += String.fromCharCode.apply(undefined, slice.$array.subarray(slice.$offset + i, slice.$offset + Math.min(slice.$length, i + 10000)));
      }
      return str;
    };
    
    var $stringToRunes = function(str) {
      var array = new Int32Array(str.length);
      var rune, j = 0;
      for (var i = 0; i < str.length; i += rune[1], j++) {
        rune = $decodeRune(str, i);
        array[j] = rune[0];
      }
      return array.subarray(0, j);
    };
    
    var $runesToString = function(slice) {
      if (slice.$length === 0) {
        return "";
      }
      var str = "";
      for (var i = 0; i < slice.$length; i++) {
        str += $encodeRune(slice.$array[slice.$offset + i]);
      }
      return str;
    };
    
    var $copyString = function(dst, src) {
      var n = Math.min(src.length, dst.$length);
      for (var i = 0; i < n; i++) {
        dst.$array[dst.$offset + i] = src.charCodeAt(i);
      }
      return n;
    };
    
    var $copySlice = function(dst, src) {
      var n = Math.min(src.$length, dst.$length);
      $copyArray(dst.$array, src.$array, dst.$offset, src.$offset, n, dst.constructor.elem);
      return n;
    };
    
    var $copyArray = function(dst, src, dstOffset, srcOffset, n, elem) {
      if (n === 0 || (dst === src && dstOffset === srcOffset)) {
        return;
      }
    
      if (src.subarray) {
        dst.set(src.subarray(srcOffset, srcOffset + n), dstOffset);
        return;
      }
    
      switch (elem.kind) {
      case $kindArray:
      case $kindStruct:
        if (dst === src && dstOffset > srcOffset) {
          for (var i = n - 1; i >= 0; i--) {
            elem.copy(dst[dstOffset + i], src[srcOffset + i]);
          }
          return;
        }
        for (var i = 0; i < n; i++) {
          elem.copy(dst[dstOffset + i], src[srcOffset + i]);
        }
        return;
      }
    
      if (dst === src && dstOffset > srcOffset) {
        for (var i = n - 1; i >= 0; i--) {
          dst[dstOffset + i] = src[srcOffset + i];
        }
        return;
      }
      for (var i = 0; i < n; i++) {
        dst[dstOffset + i] = src[srcOffset + i];
      }
    };
    
    var $clone = function(src, type) {
      var clone = type.zero();
      type.copy(clone, src);
      return clone;
    };
    
    var $pointerOfStructConversion = function(obj, type) {
      if(obj.$proxies === undefined) {
        obj.$proxies = {};
        obj.$proxies[obj.constructor.string] = obj;
      }
      var proxy = obj.$proxies[type.string];
      if (proxy === undefined) {
        var properties = {};
        for (var i = 0; i < type.elem.fields.length; i++) {
          (function(fieldProp) {
            properties[fieldProp] = {
              get: function() { return obj[fieldProp]; },
              set: function(value) { obj[fieldProp] = value; }
            };
          })(type.elem.fields[i].prop);
        }
        proxy = Object.create(type.prototype, properties);
        proxy.$val = proxy;
        obj.$proxies[type.string] = proxy;
        proxy.$proxies = obj.$proxies;
      }
      return proxy;
    };
    
    var $append = function(slice) {
      return $internalAppend(slice, arguments, 1, arguments.length - 1);
    };
    
    var $appendSlice = function(slice, toAppend) {
      if (toAppend.constructor === String) {
        var bytes = $stringToBytes(toAppend);
        return $internalAppend(slice, bytes, 0, bytes.length);
      }
      return $internalAppend(slice, toAppend.$array, toAppend.$offset, toAppend.$length);
    };
    
    var $internalAppend = function(slice, array, offset, length) {
      if (length === 0) {
        return slice;
      }
    
      var newArray = slice.$array;
      var newOffset = slice.$offset;
      var newLength = slice.$length + length;
      var newCapacity = slice.$capacity;
    
      if (newLength > newCapacity) {
        newOffset = 0;
        newCapacity = Math.max(newLength, slice.$capacity < 1024 ? slice.$capacity * 2 : Math.floor(slice.$capacity * 5 / 4));
    
        if (slice.$array.constructor === Array) {
          newArray = slice.$array.slice(slice.$offset, slice.$offset + slice.$length);
          newArray.length = newCapacity;
          var zero = slice.constructor.elem.zero;
          for (var i = slice.$length; i < newCapacity; i++) {
            newArray[i] = zero();
          }
        } else {
          newArray = new slice.$array.constructor(newCapacity);
          newArray.set(slice.$array.subarray(slice.$offset, slice.$offset + slice.$length));
        }
      }
    
      $copyArray(newArray, array, newOffset + slice.$length, offset, length, slice.constructor.elem);
    
      var newSlice = new slice.constructor(newArray);
      newSlice.$offset = newOffset;
      newSlice.$length = newLength;
      newSlice.$capacity = newCapacity;
      return newSlice;
    };
    
    var $equal = function(a, b, type) {
      if (type === $jsObjectPtr) {
        return a === b;
      }
      switch (type.kind) {
      case $kindComplex64:
      case $kindComplex128:
        return a.$real === b.$real && a.$imag === b.$imag;
      case $kindInt64:
      case $kindUint64:
        return a.$high === b.$high && a.$low === b.$low;
      case $kindArray:
        if (a.length !== b.length) {
          return false;
        }
        for (var i = 0; i < a.length; i++) {
          if (!$equal(a[i], b[i], type.elem)) {
            return false;
          }
        }
        return true;
      case $kindStruct:
        for (var i = 0; i < type.fields.length; i++) {
          var f = type.fields[i];
          if (!$equal(a[f.prop], b[f.prop], f.typ)) {
            return false;
          }
        }
        return true;
      case $kindInterface:
        return $interfaceIsEqual(a, b);
      default:
        return a === b;
      }
    };
    
    var $interfaceIsEqual = function(a, b) {
      if (a === $ifaceNil || b === $ifaceNil) {
        return a === b;
      }
      if (a.constructor !== b.constructor) {
        return false;
      }
      if (a.constructor === $jsObjectPtr) {
        return a.object === b.object;
      }
      if (!a.constructor.comparable) {
        $throwRuntimeError("comparing uncomparable type " + a.constructor.string);
      }
      return $equal(a.$val, b.$val, a.constructor);
    };
    
    var $min = Math.min;
    var $mod = function(x, y) { return x % y; };
    var $parseInt = parseInt;
    var $parseFloat = function(f) {
      if (f !== undefined && f !== null && f.constructor === Number) {
        return f;
      }
      return parseFloat(f);
    };
    
    var $froundBuf = new Float32Array(1);
    var $fround = Math.fround || function(f) {
      $froundBuf[0] = f;
      return $froundBuf[0];
    };
    
    var $imul = Math.imul || function(a, b) {
      var ah = (a >>> 16) & 0xffff;
      var al = a & 0xffff;
      var bh = (b >>> 16) & 0xffff;
      var bl = b & 0xffff;
      return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) >> 0);
    };
    
    var $floatKey = function(f) {
      if (f !== f) {
        $idCounter++;
        return "NaN$" + $idCounter;
      }
      return String(f);
    };
    
    var $flatten64 = function(x) {
      return x.$high * 4294967296 + x.$low;
    };
    
    var $shiftLeft64 = function(x, y) {
      if (y === 0) {
        return x;
      }
      if (y < 32) {
        return new x.constructor(x.$high << y | x.$low >>> (32 - y), (x.$low << y) >>> 0);
      }
      if (y < 64) {
        return new x.constructor(x.$low << (y - 32), 0);
      }
      return new x.constructor(0, 0);
    };
    
    var $shiftRightInt64 = function(x, y) {
      if (y === 0) {
        return x;
      }
      if (y < 32) {
        return new x.constructor(x.$high >> y, (x.$low >>> y | x.$high << (32 - y)) >>> 0);
      }
      if (y < 64) {
        return new x.constructor(x.$high >> 31, (x.$high >> (y - 32)) >>> 0);
      }
      if (x.$high < 0) {
        return new x.constructor(-1, 4294967295);
      }
      return new x.constructor(0, 0);
    };
    
    var $shiftRightUint64 = function(x, y) {
      if (y === 0) {
        return x;
      }
      if (y < 32) {
        return new x.constructor(x.$high >>> y, (x.$low >>> y | x.$high << (32 - y)) >>> 0);
      }
      if (y < 64) {
        return new x.constructor(0, x.$high >>> (y - 32));
      }
      return new x.constructor(0, 0);
    };
    
    var $mul64 = function(x, y) {
      var high = 0, low = 0;
      if ((y.$low & 1) !== 0) {
        high = x.$high;
        low = x.$low;
      }
      for (var i = 1; i < 32; i++) {
        if ((y.$low & 1<<i) !== 0) {
          high += x.$high << i | x.$low >>> (32 - i);
          low += (x.$low << i) >>> 0;
        }
      }
      for (var i = 0; i < 32; i++) {
        if ((y.$high & 1<<i) !== 0) {
          high += x.$low << i;
        }
      }
      return new x.constructor(high, low);
    };
    
    var $div64 = function(x, y, returnRemainder) {
      if (y.$high === 0 && y.$low === 0) {
        $throwRuntimeError("integer divide by zero");
      }
    
      var s = 1;
      var rs = 1;
    
      var xHigh = x.$high;
      var xLow = x.$low;
      if (xHigh < 0) {
        s = -1;
        rs = -1;
        xHigh = -xHigh;
        if (xLow !== 0) {
          xHigh--;
          xLow = 4294967296 - xLow;
        }
      }
    
      var yHigh = y.$high;
      var yLow = y.$low;
      if (y.$high < 0) {
        s *= -1;
        yHigh = -yHigh;
        if (yLow !== 0) {
          yHigh--;
          yLow = 4294967296 - yLow;
        }
      }
    
      var high = 0, low = 0, n = 0;
      while (yHigh < 2147483648 && ((xHigh > yHigh) || (xHigh === yHigh && xLow > yLow))) {
        yHigh = (yHigh << 1 | yLow >>> 31) >>> 0;
        yLow = (yLow << 1) >>> 0;
        n++;
      }
      for (var i = 0; i <= n; i++) {
        high = high << 1 | low >>> 31;
        low = (low << 1) >>> 0;
        if ((xHigh > yHigh) || (xHigh === yHigh && xLow >= yLow)) {
          xHigh = xHigh - yHigh;
          xLow = xLow - yLow;
          if (xLow < 0) {
            xHigh--;
            xLow += 4294967296;
          }
          low++;
          if (low === 4294967296) {
            high++;
            low = 0;
          }
        }
        yLow = (yLow >>> 1 | yHigh << (32 - 1)) >>> 0;
        yHigh = yHigh >>> 1;
      }
    
      if (returnRemainder) {
        return new x.constructor(xHigh * rs, xLow * rs);
      }
      return new x.constructor(high * s, low * s);
    };
    
    var $divComplex = function(n, d) {
      var ninf = n.$real === Infinity || n.$real === -Infinity || n.$imag === Infinity || n.$imag === -Infinity;
      var dinf = d.$real === Infinity || d.$real === -Infinity || d.$imag === Infinity || d.$imag === -Infinity;
      var nnan = !ninf && (n.$real !== n.$real || n.$imag !== n.$imag);
      var dnan = !dinf && (d.$real !== d.$real || d.$imag !== d.$imag);
      if(nnan || dnan) {
        return new n.constructor(NaN, NaN);
      }
      if (ninf && !dinf) {
        return new n.constructor(Infinity, Infinity);
      }
      if (!ninf && dinf) {
        return new n.constructor(0, 0);
      }
      if (d.$real === 0 && d.$imag === 0) {
        if (n.$real === 0 && n.$imag === 0) {
          return new n.constructor(NaN, NaN);
        }
        return new n.constructor(Infinity, Infinity);
      }
      var a = Math.abs(d.$real);
      var b = Math.abs(d.$imag);
      if (a <= b) {
        var ratio = d.$real / d.$imag;
        var denom = d.$real * ratio + d.$imag;
        return new n.constructor((n.$real * ratio + n.$imag) / denom, (n.$imag * ratio - n.$real) / denom);
      }
      var ratio = d.$imag / d.$real;
      var denom = d.$imag * ratio + d.$real;
      return new n.constructor((n.$imag * ratio + n.$real) / denom, (n.$imag - n.$real * ratio) / denom);
    };
    
    var $kindBool = 1;
    var $kindInt = 2;
    var $kindInt8 = 3;
    var $kindInt16 = 4;
    var $kindInt32 = 5;
    var $kindInt64 = 6;
    var $kindUint = 7;
    var $kindUint8 = 8;
    var $kindUint16 = 9;
    var $kindUint32 = 10;
    var $kindUint64 = 11;
    var $kindUintptr = 12;
    var $kindFloat32 = 13;
    var $kindFloat64 = 14;
    var $kindComplex64 = 15;
    var $kindComplex128 = 16;
    var $kindArray = 17;
    var $kindChan = 18;
    var $kindFunc = 19;
    var $kindInterface = 20;
    var $kindMap = 21;
    var $kindPtr = 22;
    var $kindSlice = 23;
    var $kindString = 24;
    var $kindStruct = 25;
    var $kindUnsafePointer = 26;
    
    var $methodSynthesizers = [];
    var $addMethodSynthesizer = function(f) {
      if ($methodSynthesizers === null) {
        f();
        return;
      }
      $methodSynthesizers.push(f);
    };
    var $synthesizeMethods = function() {
      $methodSynthesizers.forEach(function(f) { f(); });
      $methodSynthesizers = null;
    };
    
    var $ifaceKeyFor = function(x) {
      if (x === $ifaceNil) {
        return 'nil';
      }
      var c = x.constructor;
      return c.string + '$' + c.keyFor(x.$val);
    };
    
    var $identity = function(x) { return x; };
    
    var $typeIDCounter = 0;
    
    var $idKey = function(x) {
      if (x.$id === undefined) {
        $idCounter++;
        x.$id = $idCounter;
      }
      return String(x.$id);
    };
    
    var $newType = function(size, kind, string, named, pkg, exported, constructor) {
      var typ;
      switch(kind) {
      case $kindBool:
      case $kindInt:
      case $kindInt8:
      case $kindInt16:
      case $kindInt32:
      case $kindUint:
      case $kindUint8:
      case $kindUint16:
      case $kindUint32:
      case $kindUintptr:
      case $kindUnsafePointer:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.keyFor = $identity;
        break;
    
      case $kindString:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.keyFor = function(x) { return "$" + x; };
        break;
    
      case $kindFloat32:
      case $kindFloat64:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.keyFor = function(x) { return $floatKey(x); };
        break;
    
      case $kindInt64:
        typ = function(high, low) {
          this.$high = (high + Math.floor(Math.ceil(low) / 4294967296)) >> 0;
          this.$low = low >>> 0;
          this.$val = this;
        };
        typ.keyFor = function(x) { return x.$high + "$" + x.$low; };
        break;
    
      case $kindUint64:
        typ = function(high, low) {
          this.$high = (high + Math.floor(Math.ceil(low) / 4294967296)) >>> 0;
          this.$low = low >>> 0;
          this.$val = this;
        };
        typ.keyFor = function(x) { return x.$high + "$" + x.$low; };
        break;
    
      case $kindComplex64:
        typ = function(real, imag) {
          this.$real = $fround(real);
          this.$imag = $fround(imag);
          this.$val = this;
        };
        typ.keyFor = function(x) { return x.$real + "$" + x.$imag; };
        break;
    
      case $kindComplex128:
        typ = function(real, imag) {
          this.$real = real;
          this.$imag = imag;
          this.$val = this;
        };
        typ.keyFor = function(x) { return x.$real + "$" + x.$imag; };
        break;
    
      case $kindArray:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.ptr = $newType(4, $kindPtr, "*" + string, false, "", false, function(array) {
          this.$get = function() { return array; };
          this.$set = function(v) { typ.copy(this, v); };
          this.$val = array;
        });
        typ.init = function(elem, len) {
          typ.elem = elem;
          typ.len = len;
          typ.comparable = elem.comparable;
          typ.keyFor = function(x) {
            return Array.prototype.join.call($mapArray(x, function(e) {
              return String(elem.keyFor(e)).replace(/\\/g, "\\\\").replace(/\$/g, "\\$");
            }), "$");
          };
          typ.copy = function(dst, src) {
            $copyArray(dst, src, 0, 0, src.length, elem);
          };
          typ.ptr.init(typ);
          Object.defineProperty(typ.ptr.nil, "nilCheck", { get: $throwNilPointerError });
        };
        break;
    
      case $kindChan:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.keyFor = $idKey;
        typ.init = function(elem, sendOnly, recvOnly) {
          typ.elem = elem;
          typ.sendOnly = sendOnly;
          typ.recvOnly = recvOnly;
        };
        break;
    
      case $kindFunc:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.init = function(params, results, variadic) {
          typ.params = params;
          typ.results = results;
          typ.variadic = variadic;
          typ.comparable = false;
        };
        break;
    
      case $kindInterface:
        typ = { implementedBy: {}, missingMethodFor: {} };
        typ.keyFor = $ifaceKeyFor;
        typ.init = function(methods) {
          typ.methods = methods;
          methods.forEach(function(m) {
            $ifaceNil[m.prop] = $throwNilPointerError;
          });
        };
        break;
    
      case $kindMap:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.init = function(key, elem) {
          typ.key = key;
          typ.elem = elem;
          typ.comparable = false;
        };
        break;
    
      case $kindPtr:
        typ = constructor || function(getter, setter, target) {
          this.$get = getter;
          this.$set = setter;
          this.$target = target;
          this.$val = this;
        };
        typ.keyFor = $idKey;
        typ.init = function(elem) {
          typ.elem = elem;
          typ.wrapped = (elem.kind === $kindArray);
          typ.nil = new typ($throwNilPointerError, $throwNilPointerError);
        };
        break;
    
      case $kindSlice:
        typ = function(array) {
          if (array.constructor !== typ.nativeArray) {
            array = new typ.nativeArray(array);
          }
          this.$array = array;
          this.$offset = 0;
          this.$length = array.length;
          this.$capacity = array.length;
          this.$val = this;
        };
        typ.init = function(elem) {
          typ.elem = elem;
          typ.comparable = false;
          typ.nativeArray = $nativeArray(elem.kind);
          typ.nil = new typ([]);
        };
        break;
    
      case $kindStruct:
        typ = function(v) { this.$val = v; };
        typ.wrapped = true;
        typ.ptr = $newType(4, $kindPtr, "*" + string, false, pkg, exported, constructor);
        typ.ptr.elem = typ;
        typ.ptr.prototype.$get = function() { return this; };
        typ.ptr.prototype.$set = function(v) { typ.copy(this, v); };
        typ.init = function(pkgPath, fields) {
          typ.pkgPath = pkgPath;
          typ.fields = fields;
          fields.forEach(function(f) {
            if (!f.typ.comparable) {
              typ.comparable = false;
            }
          });
          typ.keyFor = function(x) {
            var val = x.$val;
            return $mapArray(fields, function(f) {
              return String(f.typ.keyFor(val[f.prop])).replace(/\\/g, "\\\\").replace(/\$/g, "\\$");
            }).join("$");
          };
          typ.copy = function(dst, src) {
            for (var i = 0; i < fields.length; i++) {
              var f = fields[i];
              switch (f.typ.kind) {
              case $kindArray:
              case $kindStruct:
                f.typ.copy(dst[f.prop], src[f.prop]);
                continue;
              default:
                dst[f.prop] = src[f.prop];
                continue;
              }
            }
          };
          /* nil value */
          var properties = {};
          fields.forEach(function(f) {
            properties[f.prop] = { get: $throwNilPointerError, set: $throwNilPointerError };
          });
          typ.ptr.nil = Object.create(constructor.prototype, properties);
          typ.ptr.nil.$val = typ.ptr.nil;
          /* methods for embedded fields */
          $addMethodSynthesizer(function() {
            var synthesizeMethod = function(target, m, f) {
              if (target.prototype[m.prop] !== undefined) { return; }
              target.prototype[m.prop] = function() {
                var v = this.$val[f.prop];
                if (f.typ === $jsObjectPtr) {
                  v = new $jsObjectPtr(v);
                }
                if (v.$val === undefined) {
                  v = new f.typ(v);
                }
                return v[m.prop].apply(v, arguments);
              };
            };
            fields.forEach(function(f) {
              if (f.anonymous) {
                $methodSet(f.typ).forEach(function(m) {
                  synthesizeMethod(typ, m, f);
                  synthesizeMethod(typ.ptr, m, f);
                });
                $methodSet($ptrType(f.typ)).forEach(function(m) {
                  synthesizeMethod(typ.ptr, m, f);
                });
              }
            });
          });
        };
        break;
    
      default:
        $panic(new $String("invalid kind: " + kind));
      }
    
      switch (kind) {
      case $kindBool:
      case $kindMap:
        typ.zero = function() { return false; };
        break;
    
      case $kindInt:
      case $kindInt8:
      case $kindInt16:
      case $kindInt32:
      case $kindUint:
      case $kindUint8 :
      case $kindUint16:
      case $kindUint32:
      case $kindUintptr:
      case $kindUnsafePointer:
      case $kindFloat32:
      case $kindFloat64:
        typ.zero = function() { return 0; };
        break;
    
      case $kindString:
        typ.zero = function() { return ""; };
        break;
    
      case $kindInt64:
      case $kindUint64:
      case $kindComplex64:
      case $kindComplex128:
        var zero = new typ(0, 0);
        typ.zero = function() { return zero; };
        break;
    
      case $kindPtr:
      case $kindSlice:
        typ.zero = function() { return typ.nil; };
        break;
    
      case $kindChan:
        typ.zero = function() { return $chanNil; };
        break;
    
      case $kindFunc:
        typ.zero = function() { return $throwNilPointerError; };
        break;
    
      case $kindInterface:
        typ.zero = function() { return $ifaceNil; };
        break;
    
      case $kindArray:
        typ.zero = function() {
          var arrayClass = $nativeArray(typ.elem.kind);
          if (arrayClass !== Array) {
            return new arrayClass(typ.len);
          }
          var array = new Array(typ.len);
          for (var i = 0; i < typ.len; i++) {
            array[i] = typ.elem.zero();
          }
          return array;
        };
        break;
    
      case $kindStruct:
        typ.zero = function() { return new typ.ptr(); };
        break;
    
      default:
        $panic(new $String("invalid kind: " + kind));
      }
    
      typ.id = $typeIDCounter;
      $typeIDCounter++;
      typ.size = size;
      typ.kind = kind;
      typ.string = string;
      typ.named = named;
      typ.pkg = pkg;
      typ.exported = exported;
      typ.methods = [];
      typ.methodSetCache = null;
      typ.comparable = true;
      return typ;
    };
    
    var $methodSet = function(typ) {
      if (typ.methodSetCache !== null) {
        return typ.methodSetCache;
      }
      var base = {};
    
      var isPtr = (typ.kind === $kindPtr);
      if (isPtr && typ.elem.kind === $kindInterface) {
        typ.methodSetCache = [];
        return [];
      }
    
      var current = [{typ: isPtr ? typ.elem : typ, indirect: isPtr}];
    
      var seen = {};
    
      while (current.length > 0) {
        var next = [];
        var mset = [];
    
        current.forEach(function(e) {
          if (seen[e.typ.string]) {
            return;
          }
          seen[e.typ.string] = true;
    
          if (e.typ.named) {
            mset = mset.concat(e.typ.methods);
            if (e.indirect) {
              mset = mset.concat($ptrType(e.typ).methods);
            }
          }
    
          switch (e.typ.kind) {
          case $kindStruct:
            e.typ.fields.forEach(function(f) {
              if (f.anonymous) {
                var fTyp = f.typ;
                var fIsPtr = (fTyp.kind === $kindPtr);
                next.push({typ: fIsPtr ? fTyp.elem : fTyp, indirect: e.indirect || fIsPtr});
              }
            });
            break;
    
          case $kindInterface:
            mset = mset.concat(e.typ.methods);
            break;
          }
        });
    
        mset.forEach(function(m) {
          if (base[m.name] === undefined) {
            base[m.name] = m;
          }
        });
    
        current = next;
      }
    
      typ.methodSetCache = [];
      Object.keys(base).sort().forEach(function(name) {
        typ.methodSetCache.push(base[name]);
      });
      return typ.methodSetCache;
    };
    
    var $Bool          = $newType( 1, $kindBool,          "bool",           true, "", false, null);
    var $Int           = $newType( 4, $kindInt,           "int",            true, "", false, null);
    var $Int8          = $newType( 1, $kindInt8,          "int8",           true, "", false, null);
    var $Int16         = $newType( 2, $kindInt16,         "int16",          true, "", false, null);
    var $Int32         = $newType( 4, $kindInt32,         "int32",          true, "", false, null);
    var $Int64         = $newType( 8, $kindInt64,         "int64",          true, "", false, null);
    var $Uint          = $newType( 4, $kindUint,          "uint",           true, "", false, null);
    var $Uint8         = $newType( 1, $kindUint8,         "uint8",          true, "", false, null);
    var $Uint16        = $newType( 2, $kindUint16,        "uint16",         true, "", false, null);
    var $Uint32        = $newType( 4, $kindUint32,        "uint32",         true, "", false, null);
    var $Uint64        = $newType( 8, $kindUint64,        "uint64",         true, "", false, null);
    var $Uintptr       = $newType( 4, $kindUintptr,       "uintptr",        true, "", false, null);
    var $Float32       = $newType( 4, $kindFloat32,       "float32",        true, "", false, null);
    var $Float64       = $newType( 8, $kindFloat64,       "float64",        true, "", false, null);
    var $Complex64     = $newType( 8, $kindComplex64,     "complex64",      true, "", false, null);
    var $Complex128    = $newType(16, $kindComplex128,    "complex128",     true, "", false, null);
    var $String        = $newType( 8, $kindString,        "string",         true, "", false, null);
    var $UnsafePointer = $newType( 4, $kindUnsafePointer, "unsafe.Pointer", true, "", false, null);
    
    var $nativeArray = function(elemKind) {
      switch (elemKind) {
      case $kindInt:
        return Int32Array;
      case $kindInt8:
        return Int8Array;
      case $kindInt16:
        return Int16Array;
      case $kindInt32:
        return Int32Array;
      case $kindUint:
        return Uint32Array;
      case $kindUint8:
        return Uint8Array;
      case $kindUint16:
        return Uint16Array;
      case $kindUint32:
        return Uint32Array;
      case $kindUintptr:
        return Uint32Array;
      case $kindFloat32:
        return Float32Array;
      case $kindFloat64:
        return Float64Array;
      default:
        return Array;
      }
    };
    var $toNativeArray = function(elemKind, array) {
      var nativeArray = $nativeArray(elemKind);
      if (nativeArray === Array) {
        return array;
      }
      return new nativeArray(array);
    };
    var $arrayTypes = {};
    var $arrayType = function(elem, len) {
      var typeKey = elem.id + "$" + len;
      var typ = $arrayTypes[typeKey];
      if (typ === undefined) {
        typ = $newType(12, $kindArray, "[" + len + "]" + elem.string, false, "", false, null);
        $arrayTypes[typeKey] = typ;
        typ.init(elem, len);
      }
      return typ;
    };
    
    var $chanType = function(elem, sendOnly, recvOnly) {
      var string = (recvOnly ? "<-" : "") + "chan" + (sendOnly ? "<- " : " ") + elem.string;
      var field = sendOnly ? "SendChan" : (recvOnly ? "RecvChan" : "Chan");
      var typ = elem[field];
      if (typ === undefined) {
        typ = $newType(4, $kindChan, string, false, "", false, null);
        elem[field] = typ;
        typ.init(elem, sendOnly, recvOnly);
      }
      return typ;
    };
    var $Chan = function(elem, capacity) {
      if (capacity < 0 || capacity > 2147483647) {
        $throwRuntimeError("makechan: size out of range");
      }
      this.$elem = elem;
      this.$capacity = capacity;
      this.$buffer = [];
      this.$sendQueue = [];
      this.$recvQueue = [];
      this.$closed = false;
    };
    var $chanNil = new $Chan(null, 0);
    $chanNil.$sendQueue = $chanNil.$recvQueue = { length: 0, push: function() {}, shift: function() { return undefined; }, indexOf: function() { return -1; } };
    
    var $funcTypes = {};
    var $funcType = function(params, results, variadic) {
      var typeKey = $mapArray(params, function(p) { return p.id; }).join(",") + "$" + $mapArray(results, function(r) { return r.id; }).join(",") + "$" + variadic;
      var typ = $funcTypes[typeKey];
      if (typ === undefined) {
        var paramTypes = $mapArray(params, function(p) { return p.string; });
        if (variadic) {
          paramTypes[paramTypes.length - 1] = "..." + paramTypes[paramTypes.length - 1].substr(2);
        }
        var string = "func(" + paramTypes.join(", ") + ")";
        if (results.length === 1) {
          string += " " + results[0].string;
        } else if (results.length > 1) {
          string += " (" + $mapArray(results, function(r) { return r.string; }).join(", ") + ")";
        }
        typ = $newType(4, $kindFunc, string, false, "", false, null);
        $funcTypes[typeKey] = typ;
        typ.init(params, results, variadic);
      }
      return typ;
    };
    
    var $interfaceTypes = {};
    var $interfaceType = function(methods) {
      var typeKey = $mapArray(methods, function(m) { return m.pkg + "," + m.name + "," + m.typ.id; }).join("$");
      var typ = $interfaceTypes[typeKey];
      if (typ === undefined) {
        var string = "interface {}";
        if (methods.length !== 0) {
          string = "interface { " + $mapArray(methods, function(m) {
            return (m.pkg !== "" ? m.pkg + "." : "") + m.name + m.typ.string.substr(4);
          }).join("; ") + " }";
        }
        typ = $newType(8, $kindInterface, string, false, "", false, null);
        $interfaceTypes[typeKey] = typ;
        typ.init(methods);
      }
      return typ;
    };
    var $emptyInterface = $interfaceType([]);
    var $ifaceNil = {};
    var $error = $newType(8, $kindInterface, "error", true, "", false, null);
    $error.init([{prop: "Error", name: "Error", pkg: "", typ: $funcType([], [$String], false)}]);
    
    var $mapTypes = {};
    var $mapType = function(key, elem) {
      var typeKey = key.id + "$" + elem.id;
      var typ = $mapTypes[typeKey];
      if (typ === undefined) {
        typ = $newType(4, $kindMap, "map[" + key.string + "]" + elem.string, false, "", false, null);
        $mapTypes[typeKey] = typ;
        typ.init(key, elem);
      }
      return typ;
    };
    var $makeMap = function(keyForFunc, entries) {
      var m = {};
      for (var i = 0; i < entries.length; i++) {
        var e = entries[i];
        m[keyForFunc(e.k)] = e;
      }
      return m;
    };
    
    var $ptrType = function(elem) {
      var typ = elem.ptr;
      if (typ === undefined) {
        typ = $newType(4, $kindPtr, "*" + elem.string, false, "", elem.exported, null);
        elem.ptr = typ;
        typ.init(elem);
      }
      return typ;
    };
    
    var $newDataPointer = function(data, constructor) {
      if (constructor.elem.kind === $kindStruct) {
        return data;
      }
      return new constructor(function() { return data; }, function(v) { data = v; });
    };
    
    var $indexPtr = function(array, index, constructor) {
      array.$ptr = array.$ptr || {};
      return array.$ptr[index] || (array.$ptr[index] = new constructor(function() { return array[index]; }, function(v) { array[index] = v; }));
    };
    
    var $sliceType = function(elem) {
      var typ = elem.slice;
      if (typ === undefined) {
        typ = $newType(12, $kindSlice, "[]" + elem.string, false, "", false, null);
        elem.slice = typ;
        typ.init(elem);
      }
      return typ;
    };
    var $makeSlice = function(typ, length, capacity) {
      capacity = capacity || length;
      if (length < 0 || length > 2147483647) {
        $throwRuntimeError("makeslice: len out of range");
      }
      if (capacity < 0 || capacity < length || capacity > 2147483647) {
        $throwRuntimeError("makeslice: cap out of range");
      }
      var array = new typ.nativeArray(capacity);
      if (typ.nativeArray === Array) {
        for (var i = 0; i < capacity; i++) {
          array[i] = typ.elem.zero();
        }
      }
      var slice = new typ(array);
      slice.$length = length;
      return slice;
    };
    
    var $structTypes = {};
    var $structType = function(pkgPath, fields) {
      var typeKey = $mapArray(fields, function(f) { return f.name + "," + f.typ.id + "," + f.tag; }).join("$");
      var typ = $structTypes[typeKey];
      if (typ === undefined) {
        var string = "struct { " + $mapArray(fields, function(f) {
          return f.name + " " + f.typ.string + (f.tag !== "" ? (" \"" + f.tag.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"") : "");
        }).join("; ") + " }";
        if (fields.length === 0) {
          string = "struct {}";
        }
        typ = $newType(0, $kindStruct, string, false, "", false, function() {
          this.$val = this;
          for (var i = 0; i < fields.length; i++) {
            var f = fields[i];
            var arg = arguments[i];
            this[f.prop] = arg !== undefined ? arg : f.typ.zero();
          }
        });
        $structTypes[typeKey] = typ;
        typ.init(pkgPath, fields);
      }
      return typ;
    };
    
    var $assertType = function(value, type, returnTuple) {
      var isInterface = (type.kind === $kindInterface), ok, missingMethod = "";
      if (value === $ifaceNil) {
        ok = false;
      } else if (!isInterface) {
        ok = value.constructor === type;
      } else {
        var valueTypeString = value.constructor.string;
        ok = type.implementedBy[valueTypeString];
        if (ok === undefined) {
          ok = true;
          var valueMethodSet = $methodSet(value.constructor);
          var interfaceMethods = type.methods;
          for (var i = 0; i < interfaceMethods.length; i++) {
            var tm = interfaceMethods[i];
            var found = false;
            for (var j = 0; j < valueMethodSet.length; j++) {
              var vm = valueMethodSet[j];
              if (vm.name === tm.name && vm.pkg === tm.pkg && vm.typ === tm.typ) {
                found = true;
                break;
              }
            }
            if (!found) {
              ok = false;
              type.missingMethodFor[valueTypeString] = tm.name;
              break;
            }
          }
          type.implementedBy[valueTypeString] = ok;
        }
        if (!ok) {
          missingMethod = type.missingMethodFor[valueTypeString];
        }
      }
    
      if (!ok) {
        if (returnTuple) {
          return [type.zero(), false];
        }
        $panic(new $packages["runtime"].TypeAssertionError.ptr("", (value === $ifaceNil ? "" : value.constructor.string), type.string, missingMethod));
      }
    
      if (!isInterface) {
        value = value.$val;
      }
      if (type === $jsObjectPtr) {
        value = value.object;
      }
      return returnTuple ? [value, true] : value;
    };
    
    var $stackDepthOffset = 0;
    var $getStackDepth = function() {
      var err = new Error();
      if (err.stack === undefined) {
        return undefined;
      }
      return $stackDepthOffset + err.stack.split("\n").length;
    };
    
    var $panicStackDepth = null, $panicValue;
    var $callDeferred = function(deferred, jsErr, fromPanic) {
      if (!fromPanic && deferred !== null && deferred.index >= $curGoroutine.deferStack.length) {
        throw jsErr;
      }
      if (jsErr !== null) {
        var newErr = null;
        try {
          $curGoroutine.deferStack.push(deferred);
          $panic(new $jsErrorPtr(jsErr));
        } catch (err) {
          newErr = err;
        }
        $curGoroutine.deferStack.pop();
        $callDeferred(deferred, newErr);
        return;
      }
      if ($curGoroutine.asleep) {
        return;
      }
    
      $stackDepthOffset--;
      var outerPanicStackDepth = $panicStackDepth;
      var outerPanicValue = $panicValue;
    
      var localPanicValue = $curGoroutine.panicStack.pop();
      if (localPanicValue !== undefined) {
        $panicStackDepth = $getStackDepth();
        $panicValue = localPanicValue;
      }
    
      try {
        while (true) {
          if (deferred === null) {
            deferred = $curGoroutine.deferStack[$curGoroutine.deferStack.length - 1];
            if (deferred === undefined) {
              /* The panic reached the top of the stack. Clear it and throw it as a JavaScript error. */
              $panicStackDepth = null;
              if (localPanicValue.Object instanceof Error) {
                throw localPanicValue.Object;
              }
              var msg;
              if (localPanicValue.constructor === $String) {
                msg = localPanicValue.$val;
              } else if (localPanicValue.Error !== undefined) {
                msg = localPanicValue.Error();
              } else if (localPanicValue.String !== undefined) {
                msg = localPanicValue.String();
              } else {
                msg = localPanicValue;
              }
              throw new Error(msg);
            }
          }
          var call = deferred.pop();
          if (call === undefined) {
            $curGoroutine.deferStack.pop();
            if (localPanicValue !== undefined) {
              deferred = null;
              continue;
            }
            return;
          }
          var r = call[0].apply(call[2], call[1]);
          if (r && r.$blk !== undefined) {
            deferred.push([r.$blk, [], r]);
            if (fromPanic) {
              throw null;
            }
            return;
          }
    
          if (localPanicValue !== undefined && $panicStackDepth === null) {
            throw null; /* error was recovered */
          }
        }
      } finally {
        if (localPanicValue !== undefined) {
          if ($panicStackDepth !== null) {
            $curGoroutine.panicStack.push(localPanicValue);
          }
          $panicStackDepth = outerPanicStackDepth;
          $panicValue = outerPanicValue;
        }
        $stackDepthOffset++;
      }
    };
    
    var $panic = function(value) {
      $curGoroutine.panicStack.push(value);
      $callDeferred(null, null, true);
    };
    var $recover = function() {
      if ($panicStackDepth === null || ($panicStackDepth !== undefined && $panicStackDepth !== $getStackDepth() - 2)) {
        return $ifaceNil;
      }
      $panicStackDepth = null;
      return $panicValue;
    };
    var $throw = function(err) { throw err; };
    
    var $noGoroutine = { asleep: false, exit: false, deferStack: [], panicStack: [] };
    var $curGoroutine = $noGoroutine, $totalGoroutines = 0, $awakeGoroutines = 0, $checkForDeadlock = true;
    var $mainFinished = false;
    var $go = function(fun, args) {
      $totalGoroutines++;
      $awakeGoroutines++;
      var $goroutine = function() {
        try {
          $curGoroutine = $goroutine;
          var r = fun.apply(undefined, args);
          if (r && r.$blk !== undefined) {
            fun = function() { return r.$blk(); };
            args = [];
            return;
          }
          $goroutine.exit = true;
        } catch (err) {
          if (!$goroutine.exit) {
            throw err;
          }
        } finally {
          $curGoroutine = $noGoroutine;
          if ($goroutine.exit) { /* also set by runtime.Goexit() */
            $totalGoroutines--;
            $goroutine.asleep = true;
          }
          if ($goroutine.asleep) {
            $awakeGoroutines--;
            if (!$mainFinished && $awakeGoroutines === 0 && $checkForDeadlock) {
              console.error("fatal error: all goroutines are asleep - deadlock!");
              if ($global.process !== undefined) {
                $global.process.exit(2);
              }
            }
          }
        }
      };
      $goroutine.asleep = false;
      $goroutine.exit = false;
      $goroutine.deferStack = [];
      $goroutine.panicStack = [];
      $schedule($goroutine);
    };
    
    var $scheduled = [];
    var $runScheduled = function() {
      try {
        var r;
        while ((r = $scheduled.shift()) !== undefined) {
          r();
        }
      } finally {
        if ($scheduled.length > 0) {
          setTimeout($runScheduled, 0);
        }
      }
    };
    
    var $schedule = function(goroutine) {
      if (goroutine.asleep) {
        goroutine.asleep = false;
        $awakeGoroutines++;
      }
      $scheduled.push(goroutine);
      if ($curGoroutine === $noGoroutine) {
        $runScheduled();
      }
    };
    
    var $setTimeout = function(f, t) {
      $awakeGoroutines++;
      return setTimeout(function() {
        $awakeGoroutines--;
        f();
      }, t);
    };
    
    var $block = function() {
      if ($curGoroutine === $noGoroutine) {
        $throwRuntimeError("cannot block in JavaScript callback, fix by wrapping code in goroutine");
      }
      $curGoroutine.asleep = true;
    };
    
    var $send = function(chan, value) {
      if (chan.$closed) {
        $throwRuntimeError("send on closed channel");
      }
      var queuedRecv = chan.$recvQueue.shift();
      if (queuedRecv !== undefined) {
        queuedRecv([value, true]);
        return;
      }
      if (chan.$buffer.length < chan.$capacity) {
        chan.$buffer.push(value);
        return;
      }
    
      var thisGoroutine = $curGoroutine;
      var closedDuringSend;
      chan.$sendQueue.push(function(closed) {
        closedDuringSend = closed;
        $schedule(thisGoroutine);
        return value;
      });
      $block();
      return {
        $blk: function() {
          if (closedDuringSend) {
            $throwRuntimeError("send on closed channel");
          }
        }
      };
    };
    var $recv = function(chan) {
      var queuedSend = chan.$sendQueue.shift();
      if (queuedSend !== undefined) {
        chan.$buffer.push(queuedSend(false));
      }
      var bufferedValue = chan.$buffer.shift();
      if (bufferedValue !== undefined) {
        return [bufferedValue, true];
      }
      if (chan.$closed) {
        return [chan.$elem.zero(), false];
      }
    
      var thisGoroutine = $curGoroutine;
      var f = { $blk: function() { return this.value; } };
      var queueEntry = function(v) {
        f.value = v;
        $schedule(thisGoroutine);
      };
      chan.$recvQueue.push(queueEntry);
      $block();
      return f;
    };
    var $close = function(chan) {
      if (chan.$closed) {
        $throwRuntimeError("close of closed channel");
      }
      chan.$closed = true;
      while (true) {
        var queuedSend = chan.$sendQueue.shift();
        if (queuedSend === undefined) {
          break;
        }
        queuedSend(true); /* will panic */
      }
      while (true) {
        var queuedRecv = chan.$recvQueue.shift();
        if (queuedRecv === undefined) {
          break;
        }
        queuedRecv([chan.$elem.zero(), false]);
      }
    };
    var $select = function(comms) {
      var ready = [];
      var selection = -1;
      for (var i = 0; i < comms.length; i++) {
        var comm = comms[i];
        var chan = comm[0];
        switch (comm.length) {
        case 0: /* default */
          selection = i;
          break;
        case 1: /* recv */
          if (chan.$sendQueue.length !== 0 || chan.$buffer.length !== 0 || chan.$closed) {
            ready.push(i);
          }
          break;
        case 2: /* send */
          if (chan.$closed) {
            $throwRuntimeError("send on closed channel");
          }
          if (chan.$recvQueue.length !== 0 || chan.$buffer.length < chan.$capacity) {
            ready.push(i);
          }
          break;
        }
      }
    
      if (ready.length !== 0) {
        selection = ready[Math.floor(Math.random() * ready.length)];
      }
      if (selection !== -1) {
        var comm = comms[selection];
        switch (comm.length) {
        case 0: /* default */
          return [selection];
        case 1: /* recv */
          return [selection, $recv(comm[0])];
        case 2: /* send */
          $send(comm[0], comm[1]);
          return [selection];
        }
      }
    
      var entries = [];
      var thisGoroutine = $curGoroutine;
      var f = { $blk: function() { return this.selection; } };
      var removeFromQueues = function() {
        for (var i = 0; i < entries.length; i++) {
          var entry = entries[i];
          var queue = entry[0];
          var index = queue.indexOf(entry[1]);
          if (index !== -1) {
            queue.splice(index, 1);
          }
        }
      };
      for (var i = 0; i < comms.length; i++) {
        (function(i) {
          var comm = comms[i];
          switch (comm.length) {
          case 1: /* recv */
            var queueEntry = function(value) {
              f.selection = [i, value];
              removeFromQueues();
              $schedule(thisGoroutine);
            };
            entries.push([comm[0].$recvQueue, queueEntry]);
            comm[0].$recvQueue.push(queueEntry);
            break;
          case 2: /* send */
            var queueEntry = function() {
              if (comm[0].$closed) {
                $throwRuntimeError("send on closed channel");
              }
              f.selection = [i];
              removeFromQueues();
              $schedule(thisGoroutine);
              return comm[1];
            };
            entries.push([comm[0].$sendQueue, queueEntry]);
            comm[0].$sendQueue.push(queueEntry);
            break;
          }
        })(i);
      }
      $block();
      return f;
    };
    
    var $jsObjectPtr, $jsErrorPtr;
    
    var $needsExternalization = function(t) {
      switch (t.kind) {
        case $kindBool:
        case $kindInt:
        case $kindInt8:
        case $kindInt16:
        case $kindInt32:
        case $kindUint:
        case $kindUint8:
        case $kindUint16:
        case $kindUint32:
        case $kindUintptr:
        case $kindFloat32:
        case $kindFloat64:
          return false;
        default:
          return t !== $jsObjectPtr;
      }
    };
    
    var $externalize = function(v, t) {
      if (t === $jsObjectPtr) {
        return v;
      }
      switch (t.kind) {
      case $kindBool:
      case $kindInt:
      case $kindInt8:
      case $kindInt16:
      case $kindInt32:
      case $kindUint:
      case $kindUint8:
      case $kindUint16:
      case $kindUint32:
      case $kindUintptr:
      case $kindFloat32:
      case $kindFloat64:
        return v;
      case $kindInt64:
      case $kindUint64:
        return $flatten64(v);
      case $kindArray:
        if ($needsExternalization(t.elem)) {
          return $mapArray(v, function(e) { return $externalize(e, t.elem); });
        }
        return v;
      case $kindFunc:
        return $externalizeFunction(v, t, false);
      case $kindInterface:
        if (v === $ifaceNil) {
          return null;
        }
        if (v.constructor === $jsObjectPtr) {
          return v.$val.object;
        }
        return $externalize(v.$val, v.constructor);
      case $kindMap:
        var m = {};
        var keys = $keys(v);
        for (var i = 0; i < keys.length; i++) {
          var entry = v[keys[i]];
          m[$externalize(entry.k, t.key)] = $externalize(entry.v, t.elem);
        }
        return m;
      case $kindPtr:
        if (v === t.nil) {
          return null;
        }
        return $externalize(v.$get(), t.elem);
      case $kindSlice:
        if ($needsExternalization(t.elem)) {
          return $mapArray($sliceToArray(v), function(e) { return $externalize(e, t.elem); });
        }
        return $sliceToArray(v);
      case $kindString:
        if ($isASCII(v)) {
          return v;
        }
        var s = "", r;
        for (var i = 0; i < v.length; i += r[1]) {
          r = $decodeRune(v, i);
          var c = r[0];
          if (.........完整代码请登录后点击上方下载按钮下载查看

网友评论0