func (c *simplifyContext) newIdent(t types.Type) *ast.Ident { c.varCounter++ id := ast.NewIdent(fmt.Sprintf("_%d", c.varCounter)) c.info.Types[id] = types.TypeAndValue{Type: t} // TODO remove? c.info.Uses[id] = types.NewVar(token.NoPos, nil, id.Name, t) return id }
func NewIdent(name string, t types.Type, info *types.Info, pkg *types.Package) *ast.Ident { ident := ast.NewIdent(name) info.Types[ident] = types.TypeAndValue{Type: t} obj := types.NewVar(0, pkg, name, t) info.Uses[ident] = obj return ident }
func (p *importer) obj(tag int) { switch tag { case constTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) val := p.value() p.declare(types.NewConst(pos, pkg, name, typ, val)) case typeTag: _ = p.typ(nil) case varTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) p.declare(types.NewVar(pos, pkg, name, typ)) case funcTag: pos := p.pos() pkg, name := p.qualifiedName() params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) p.declare(types.NewFunc(pos, pkg, name, sig)) default: panic(fmt.Sprintf("unexpected object tag %d", tag)) } }
func (p *importer) param(named bool) (*types.Var, bool) { t := p.typ(nil) td, isddd := t.(*dddSlice) if isddd { t = types.NewSlice(td.elem) } var pkg *types.Package var name string if named { name = p.string() if name == "" { panic("expected named parameter") } if name != "_" { pkg = p.pkg() } if i := strings.Index(name, "ยท"); i > 0 { name = name[:i] // cut off gc-specific parameter numbering } } // read and discard compiler-specific info p.string() return types.NewVar(token.NoPos, pkg, name, t), isddd }
func (c *funcContext) loadStruct(array, target string, s *types.Struct) string { view := c.newVariable("_view") code := fmt.Sprintf("%s = new DataView(%s.buffer, %s.byteOffset)", view, array, array) var fields []*types.Var var collectFields func(s *types.Struct, path string) collectFields = func(s *types.Struct, path string) { for i := 0; i < s.NumFields(); i++ { field := s.Field(i) if fs, isStruct := field.Type().Underlying().(*types.Struct); isStruct { collectFields(fs, path+"."+fieldName(s, i)) continue } fields = append(fields, types.NewVar(0, nil, path+"."+fieldName(s, i), field.Type())) } } collectFields(s, target) offsets := sizes32.Offsetsof(fields) for i, field := range fields { switch t := field.Type().Underlying().(type) { case *types.Basic: if isNumeric(t) { if is64Bit(t) { code += fmt.Sprintf(", %s = new %s(%s.getUint32(%d, true), %s.getUint32(%d, true))", field.Name(), c.typeName(field.Type()), view, offsets[i]+4, view, offsets[i]) break } code += fmt.Sprintf(", %s = %s.get%s(%d, true)", field.Name(), view, toJavaScriptType(t), offsets[i]) } case *types.Array: code += fmt.Sprintf(`, %s = new ($nativeArray(%s))(%s.buffer, $min(%s.byteOffset + %d, %s.buffer.byteLength))`, field.Name(), typeKind(t.Elem()), array, array, offsets[i], array) } } return code }
func (c *funcContext) newIdent(name string, t types.Type) *ast.Ident { ident := ast.NewIdent(name) c.setType(ident, t) obj := types.NewVar(0, c.p.Pkg, name, t) c.p.Uses[ident] = obj c.p.objectNames[obj] = name return ident }
// newMethod creates a new method of the specified name, package and receiver type. func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function { // TODO(adonovan): fix: hack: currently the only part of Signature // that is needed is the "pointerness" of Recv.Type, and for // now, we'll set it to always be false since we're only // concerned with rtype. Encapsulate this better. sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", recvType), nil, nil, false) fn := pkg.Prog.NewFunction(name, sig, "fake reflect method") fn.Pkg = pkg return fn }
func (p *importer) obj(tag int) { switch tag { case constTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) val := p.value() p.declare(types.NewConst(pos, pkg, name, typ, val)) case typeTag: p.typ(nil) case varTag: pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) p.declare(types.NewVar(pos, pkg, name, typ)) case funcTag: pos := p.pos() pkg, name := p.qualifiedName() params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) p.declare(types.NewFunc(pos, pkg, name, sig)) case aliasTag: pos := p.pos() name := p.string() var orig types.Object if pkg, name := p.qualifiedName(); pkg != nil { orig = pkg.Scope().Lookup(name) } // Alias-related code. Keep for now. _ = pos _ = name _ = orig // p.declare(types.NewAlias(pos, p.pkgList[0], name, orig)) default: errorf("unexpected object tag %d", tag) } }
func (p *importer) param(named bool) (*types.Var, bool) { t := p.typ(nil) td, isddd := t.(*dddSlice) if isddd { t = types.NewSlice(td.elem) } var name string if named { name = p.string() if name == "" { panic("expected named parameter") } } // read and discard compiler-specific info p.string() return types.NewVar(token.NoPos, nil, name, t), isddd }
func (p *importer) obj(pkg *types.Package) { var obj types.Object switch tag := p.int(); tag { case constTag: obj = types.NewConst(token.NoPos, pkg, p.string(), p.typ(), p.value()) case typeTag: // type object is added to scope via respective named type _ = p.typ().(*types.Named) return case varTag: obj = types.NewVar(token.NoPos, pkg, p.string(), p.typ()) case funcTag: obj = types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature)) default: panic(fmt.Sprintf("unexpected object tag %d", tag)) } if alt := pkg.Scope().Insert(obj); alt != nil { panic(fmt.Sprintf("%s already declared", alt.Name())) } }
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . // func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { _, name := p.parseName(nil, false) // remove gc-specific parameter numbering if i := strings.Index(name, "ยท"); i >= 0 { name = name[:i] } if p.tok == '.' { p.expectSpecial("...") isVariadic = true } typ := p.parseType(nil) if isVariadic { typ = types.NewSlice(typ) } // ignore argument tag (e.g. "noescape") if p.tok == scanner.String { p.next() } // TODO(gri) should we provide a package? par = types.NewVar(token.NoPos, nil, name, typ) return }
// appendComponentsRecursive implements componentsOfType. // Recursion is required to correct handle structs and arrays, // which can contain arbitrary other types. func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component { s := t.String() size := int(arch.sizes.Sizeof(t)) kind := asmKindForType(t, size) cc = append(cc, newComponent(suffix, kind, s, off, size, suffix)) switch kind { case 8: if arch.ptrSize() == 4 { w1, w2 := "lo", "hi" if arch.bigEndian { w1, w2 = w2, w1 } cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix)) cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix)) } case asmEmptyInterface: cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize()), "interface type", off, arch.ptrSize(), suffix)) cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize()), "interface data", off+arch.ptrSize(), arch.ptrSize(), suffix)) case asmInterface: cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize()), "interface itable", off, arch.ptrSize(), suffix)) cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize()), "interface data", off+arch.ptrSize(), arch.ptrSize(), suffix)) case asmSlice: cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize()), "slice base", off, arch.ptrSize(), suffix)) cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize()), "slice len", off+arch.ptrSize(), arch.intSize(), suffix)) cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize()), "slice cap", off+arch.ptrSize()+arch.intSize(), arch.intSize(), suffix)) case asmString: cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize()), "string base", off, arch.ptrSize(), suffix)) cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize()), "string len", off+arch.ptrSize(), arch.intSize(), suffix)) case asmComplex: fsize := size / 2 cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix)) cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix)) case asmStruct: tu := t.Underlying().(*types.Struct) fields := make([]*types.Var, tu.NumFields()) for i := 0; i < tu.NumFields(); i++ { fields[i] = tu.Field(i) } offsets := arch.sizes.Offsetsof(fields) for i, f := range fields { cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i])) } case asmArray: tu := t.Underlying().(*types.Array) elem := tu.Elem() // Calculate offset of each element array. fields := []*types.Var{ types.NewVar(token.NoPos, nil, "fake0", elem), types.NewVar(token.NoPos, nil, "fake1", elem), } offsets := arch.sizes.Offsetsof(fields) elemoff := int(offsets[1]) for i := 0; i < int(tu.Len()); i++ { cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff) } } return cc }
// BImportData imports a package from the serialized package data // and returns the number of bytes consumed and a reference to the package. // If data is obviously malformed, an error is returned but in // general it is not recommended to call BImportData on untrusted data. func BImportData(imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { p := importer{ imports: imports, data: data, } p.buf = p.bufarray[:] // read low-level encoding format switch format := p.byte(); format { case 'c': // compact format - nothing to do case 'd': p.debugFormat = true default: return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format) } // --- generic export data --- if v := p.string(); v != "v0" { return p.read, nil, fmt.Errorf("unknown version: %s", v) } // populate typList with predeclared "known" types p.typList = append(p.typList, predeclared...) // read package data // TODO(gri) clean this up i := p.tagOrIndex() if i != packageTag { panic(fmt.Sprintf("package tag expected, got %d", i)) } name := p.string() if s := p.string(); s != "" { panic(fmt.Sprintf("empty path expected, got %s", s)) } pkg := p.imports[path] if pkg == nil { pkg = types.NewPackage(path, name) p.imports[path] = pkg } p.pkgList = append(p.pkgList, pkg) if debug && p.pkgList[0] != pkg { panic("imported packaged not found in pkgList[0]") } // read compiler-specific flags p.string() // discard // read consts for i := p.int(); i > 0; i-- { name := p.string() typ := p.typ(nil) val := p.value() p.declare(types.NewConst(token.NoPos, pkg, name, typ, val)) } // read vars for i := p.int(); i > 0; i-- { name := p.string() typ := p.typ(nil) p.declare(types.NewVar(token.NoPos, pkg, name, typ)) } // read funcs for i := p.int(); i > 0; i-- { name := p.string() sig := p.typ(nil).(*types.Signature) p.int() // read and discard index of inlined function body p.declare(types.NewFunc(token.NoPos, pkg, name, sig)) } // read types for i := p.int(); i > 0; i-- { // name is parsed as part of named type and the // type object is added to scope via respective // named type _ = p.typ(nil).(*types.Named) } // ignore compiler-specific import data // complete interfaces for _, typ := range p.typList { if it, ok := typ.(*types.Interface); ok { it.Complete() } } // record all referenced packages as imports list := append(([]*types.Package)(nil), p.pkgList[1:]...) sort.Sort(byPath(list)) pkg.SetImports(list) // package was imported completely and without errors pkg.MarkComplete() return p.read, pkg, nil }
// VarDecl = "var" ExportedName Type . // func (p *parser) parseVarDecl() { p.expectKeyword("var") pkg, name := p.parseExportedName() typ := p.parseType(pkg) pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ)) }
func (c *funcContext) translateExpr(expr ast.Expr) *expression { exprType := c.p.TypeOf(expr) if value := c.p.Types[expr].Value; value != nil { basic := exprType.Underlying().(*types.Basic) switch { case isBoolean(basic): return c.formatExpr("%s", strconv.FormatBool(constant.BoolVal(value))) case isInteger(basic): if is64Bit(basic) { if basic.Kind() == types.Int64 { d, ok := constant.Int64Val(constant.ToInt(value)) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10)) } d, ok := constant.Uint64Val(constant.ToInt(value)) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, ok := constant.Int64Val(constant.ToInt(value)) if !ok { panic("could not get exact int") } return c.formatExpr("%s", strconv.FormatInt(d, 10)) case isFloat(basic): f, _ := constant.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case isComplex(basic): r, _ := constant.Float64Val(constant.Real(value)) i, _ := constant.Float64Val(constant.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case isString(basic): return c.formatExpr("%s", encodeString(constant.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } var obj types.Object switch e := expr.(type) { case *ast.SelectorExpr: obj = c.p.Uses[e.Sel] case *ast.Ident: obj = c.p.Defs[e] if obj == nil { obj = c.p.Uses[e] } } if obj != nil && typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": return c.formatExpr("$global") case "Module": return c.formatExpr("$module") case "Undefined": return c.formatExpr("undefined") } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { var elements []string i := 0 zero := c.translateExpr(c.zeroValue(elementType)).String() for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, ok := constant.Int64Val(constant.ToInt(c.p.Types[kve.Key].Value)) if !ok { panic("could not get exact int") } i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateImplicitConversionWithCloning(element, elementType).String() i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) == 0 { return c.formatExpr("%s.zero()", c.typeName(t)) } zero := c.translateExpr(c.zeroValue(t.Elem())).String() for len(elements) < int(t.Len()) { elements = append(elements, zero) } return c.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) case *types.Slice: return c.formatExpr("new %s([%s])", c.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) case *types.Map: entries := make([]string, len(e.Elts)) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) entries[i] = fmt.Sprintf("{ k: %s, v: %s }", c.translateImplicitConversionWithCloning(kve.Key, t.Key()), c.translateImplicitConversionWithCloning(kve.Value, t.Elem())) } return c.formatExpr("$makeMap(%s.keyFor, [%s])", c.typeName(t.Key()), strings.Join(entries, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.translateExpr(c.zeroValue(t.Field(i).Type())).String() } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String() break } } } } return c.formatExpr("new %s.ptr(%s)", c.typeName(exprType), strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: _, fun := translateFunction(e.Type, nil, e.Body, c, exprType.(*types.Signature), c.p.FuncLitInfos[e], "") if len(c.p.escapingVars) != 0 { names := make([]string, 0, len(c.p.escapingVars)) for obj := range c.p.escapingVars { names = append(names, c.p.objectNames[obj]) } sort.Strings(names) list := strings.Join(names, ", ") return c.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list) } return c.formatExpr("(%s)", fun) case *ast.UnaryExpr: t := c.p.TypeOf(e.X) switch e.Op { case token.AND: if typesutil.IsJsObject(exprType) { return c.formatExpr("%e.object", e.X) } switch t.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } switch x := astutil.RemoveParens(e.X).(type) { case *ast.CompositeLit: return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.TypeOf(e))) case *ast.Ident: obj := c.p.Uses[x].(*types.Var) if c.p.escapingVars[obj] { return c.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", c.p.objectNames[obj], c.typeName(exprType)) } return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) case *ast.SelectorExpr: sel, ok := c.p.SelectionOf(x) if !ok { // qualified identifier obj := c.p.Uses[x.Sel].(*types.Var) return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) } newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.TypeOf(x.X)), Sel: x.Sel} c.setType(newSel, exprType) c.p.additionalSelections[newSel] = sel return c.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, c.typeName(exprType), newSel, c.translateAssign(newSel, c.newIdent("$v", exprType), false)) case *ast.IndexExpr: if _, ok := c.p.TypeOf(x.X).Underlying().(*types.Slice); ok { return c.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, c.typeName(exprType)) } return c.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, c.typeName(exprType)) case *ast.StarExpr: return c.translateExpr(x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ Fun: c.newIdent("$recv", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)), Args: []ast.Expr{e.X}, } c.Blocking[call] = true if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("%e", call) } return c.formatExpr("%e[0]", call) } basic := t.Underlying().(*types.Basic) switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: switch { case is64Bit(basic): return c.formatExpr("new %1s(-%2h, -%2l)", c.typeName(t), e.X) case isComplex(basic): return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case isUnsigned(basic): return c.fixNumber(c.formatExpr("-%e", e.X), basic) default: return c.formatExpr("-%e", e.X) } case token.XOR: if is64Bit(basic) { return c.formatExpr("new %1s(~%2h, ~%2l >>> 0)", c.typeName(t), e.X) } return c.fixNumber(c.formatExpr("~%e", e.X), basic) case token.NOT: return c.formatExpr("!%e", e.X) default: panic(e.Op) } case *ast.BinaryExpr: if e.Op == token.NEQ { return c.formatExpr("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.p.TypeOf(e.X) t2 := c.p.TypeOf(e.Y) _, isInterface := t2.Underlying().(*types.Interface) if isInterface || types.Identical(t, types.Typ[types.UntypedNil]) { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && isNumeric(basic) { if is64Bit(basic) { switch e.Op { case token.MUL: return c.formatExpr("$mul64(%e, %e)", e.X, e.Y) case token.QUO: return c.formatExpr("$div64(%e, %e, false)", e.X, e.Y) case token.REM: return c.formatExpr("$div64(%e, %e, true)", e.X, e.Y) case token.SHL: return c.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y) case token.SHR: return c.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y) case token.EQL: return c.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y) case token.LSS: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y) case token.LEQ: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y) case token.GTR: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y) case token.GEQ: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, c.typeName(t), e.Op) case token.AND, token.OR, token.XOR: return c.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, c.typeName(t), e.Op) case token.AND_NOT: return c.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, c.typeName(t)) default: panic(e.Op) } } if isComplex(basic) { switch e.Op { case token.EQL: return c.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, c.typeName(t), e.Op) case token.MUL: return c.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, c.typeName(t)) case token.QUO: return c.formatExpr("$divComplex(%e, %e)", e.X, e.Y) default: panic(e.Op) } } switch e.Op { case token.EQL: return c.formatParenExpr("%e === %e", e.X, e.Y) case token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.ADD, token.SUB: return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) case token.MUL: switch basic.Kind() { case types.Int32, types.Int: return c.formatParenExpr("$imul(%e, %e)", e.X, e.Y) case types.Uint32, types.Uintptr: return c.formatParenExpr("$imul(%e, %e) >>> 0", e.X, e.Y) } return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) case token.QUO: if isInteger(basic) { // cut off decimals shift := ">>" if isUnsigned(basic) { shift = ">>>" } return c.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_q"), e.X, e.Y, shift) } if basic.Kind() == types.Float32 { return c.fixNumber(c.formatExpr("%e / %e", e.X, e.Y), basic) } return c.formatExpr("%e / %e", e.X, e.Y) case token.REM: return c.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_r"), e.X, e.Y) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && isUnsigned(basic) { op = ">>>" } if v := c.p.Types[e.Y].Value; v != nil { i, _ := constant.Uint64Val(constant.ToInt(v)) if i >= 32 { return c.formatExpr("0") } return c.fixNumber(c.formatExpr("%e %s %s", e.X, op, strconv.FormatUint(i, 10)), basic) } if e.Op == token.SHR && !isUnsigned(basic) { return c.fixNumber(c.formatParenExpr("%e >> $min(%f, 31)", e.X, e.Y), basic) } y := c.newVariable("y") return c.fixNumber(c.formatExpr("(%s = %f, %s < 32 ? (%e %s %s) : 0)", y, e.Y, y, e.X, op, y), basic) case token.AND, token.OR: if isUnsigned(basic) { return c.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y) } return c.formatParenExpr("%e %t %e", e.X, e.Op, e.Y) case token.AND_NOT: return c.fixNumber(c.formatParenExpr("%e & ~%e", e.X, e.Y), basic) case token.XOR: return c.fixNumber(c.formatParenExpr("%e ^ %e", e.X, e.Y), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.LAND: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e && %e", e.X, e.Y) case token.LOR: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (%s) { %s = true; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e || %e", e.X, e.Y) case token.EQL: switch u := t.Underlying().(type) { case *types.Array, *types.Struct: return c.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, c.typeName(t)) case *types.Interface: return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Pointer: if _, ok := u.Elem().Underlying().(*types.Array); ok { return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) } case *types.Basic: if isBoolean(u) { if b, ok := analysis.BoolValue(e.X, c.p.Info.Info); ok && b { return c.translateExpr(e.Y) } if b, ok := analysis.BoolValue(e.Y, c.p.Info.Info); ok && b { return c.translateExpr(e.X) } } } return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) default: panic(e.Op) } case *ast.ParenExpr: return c.formatParenExpr("%e", e.X) case *ast.IndexExpr: switch t := c.p.TypeOf(e.X).Underlying().(type) { case *types.Array, *types.Pointer: pattern := rangeCheck("%1e[%2f]", c.p.Types[e.Index].Value != nil, true) if _, ok := t.(*types.Pointer); ok { // check pointer for nix (attribute getter causes a panic) pattern = `(%1e.nilCheck, ` + pattern + `)` } return c.formatExpr(pattern, e.X, e.Index) case *types.Slice: return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", c.p.Types[e.Index].Value != nil, false), e.X, e.Index) case *types.Map: if typesutil.IsJsObject(c.p.TypeOf(e.Index)) { c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"}) } key := fmt.Sprintf("%s.keyFor(%s)", c.typeName(t.Key()), c.translateImplicitConversion(e.Index, t.Key())) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4e, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4e)`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) case *types.Basic: return c.formatExpr("%e.charCodeAt(%f)", e.X, e.Index) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: if b, isBasic := c.p.TypeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) { switch { case e.Low == nil && e.High == nil: return c.translateExpr(e.X) case e.Low == nil: return c.formatExpr("%e.substring(0, %f)", e.X, e.High) case e.High == nil: return c.formatExpr("%e.substring(%f)", e.X, e.Low) default: return c.formatExpr("%e.substring(%f, %f)", e.X, e.Low, e.High) } } slice := c.translateConversionToSlice(e.X, exprType) switch { case e.Low == nil && e.High == nil: return c.formatExpr("%s", slice) case e.Low == nil: if e.Max != nil { return c.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) } return c.formatExpr("$subslice(%s, 0, %f)", slice, e.High) case e.High == nil: return c.formatExpr("$subslice(%s, %f)", slice, e.Low) default: if e.Max != nil { return c.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) } return c.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High) } case *ast.SelectorExpr: sel, ok := c.p.SelectionOf(e) if !ok { // qualified identifier return c.formatExpr("%s", c.objectName(obj)) } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel, e.Pos()) if jsTag != "" { if _, ok := sel.Type().(*types.Signature); ok { return c.formatExpr("$internalize(%1e.%2s.%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), jsTag, c.typeName(sel.Type())) } return c.internalize(c.formatExpr("%e.%s.%s", e.X, strings.Join(fields, "."), jsTag), sel.Type()) } return c.formatExpr("%e.%s", e.X, strings.Join(fields, ".")) case types.MethodVal: return c.formatExpr(`$methodVal(%s, "%s")`, c.makeReceiver(e), sel.Obj().(*types.Func).Name()) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } if _, ok := sel.Recv().Underlying().(*types.Interface); ok { return c.formatExpr(`$ifaceMethodExpr("%s")`, sel.Obj().(*types.Func).Name()) } return c.formatExpr(`$methodExpr(%s, "%s")`, c.typeName(sel.Recv()), sel.Obj().(*types.Func).Name()) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } case *ast.CallExpr: plainFun := astutil.RemoveParens(e.Fun) if astutil.IsTypeExpr(plainFun, c.p.Info.Info) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.TypeOf(plainFun))) } sig := c.p.TypeOf(plainFun).Underlying().(*types.Signature) switch f := plainFun.(type) { case *ast.Ident: obj := c.p.Uses[f] if o, ok := obj.(*types.Builtin); ok { return c.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid()) } if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" { return c.translateExpr(e.Args[0]) } return c.translateCall(e, sig, c.translateExpr(f)) case *ast.SelectorExpr: sel, ok := c.p.SelectionOf(f) if !ok { // qualified identifier obj := c.p.Uses[f.Sel] if typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Debugger": return c.formatExpr("debugger") case "InternalObject": return c.translateExpr(e.Args[0]) } } return c.translateCall(e, sig, c.translateExpr(f)) } externalizeExpr := func(e ast.Expr) string { t := c.p.TypeOf(e) if types.Identical(t, types.Typ[types.UntypedNil]) { return "null" } return c.externalize(c.translateExpr(e).String(), t) } externalizeArgs := func(args []ast.Expr) string { s := make([]string, len(args)) for i, arg := range args { s[i] = externalizeExpr(arg) } return strings.Join(s, ", ") } switch sel.Kind() { case types.MethodVal: recv := c.makeReceiver(f) declaredFuncRecv := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type() if typesutil.IsJsObject(declaredFuncRecv) { globalRef := func(id string) string { if recv.String() == "$global" && id[0] == '$' && len(id) > 1 { return id } return recv.String() + "." + id } switch sel.Obj().Name() { case "Get": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s", globalRef(id)) } return c.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0]) case "Set": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Delete": return c.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0]) case "Length": return c.formatExpr("$parseInt(%s.length)", recv) case "Index": return c.formatExpr("%s[%e]", recv, e.Args[0]) case "SetIndex": return c.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Call": if id, ok := c.identifierConstant(e.Args[0]); ok { if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:])) } if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:])) case "Invoke": if e.Ellipsis.IsValid() { return c.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("%s(%s)", recv, externalizeArgs(e.Args)) case "New": if e.Ellipsis.IsValid() { return c.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args)) case "Bool": return c.internalize(recv, types.Typ[types.Bool]) case "String": return c.internalize(recv, types.Typ[types.String]) case "Int": return c.internalize(recv, types.Typ[types.Int]) case "Int64": return c.internalize(recv, types.Typ[types.Int64]) case "Uint64": return c.internalize(recv, types.Typ[types.Uint64]) case "Float": return c.internalize(recv, types.Typ[types.Float64]) case "Interface": return c.internalize(recv, types.NewInterface(nil, nil)) case "Unsafe": return recv default: panic("Invalid js package object: " + sel.Obj().Name()) } } methodName := sel.Obj().Name() if reservedKeywords[methodName] { methodName += "$" } return c.translateCall(e, sig, c.formatExpr("%s.%s", recv, methodName)) case types.FieldVal: fields, jsTag := c.translateSelection(sel, f.Pos()) if jsTag != "" { call := c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)) switch sig.Results().Len() { case 0: return call case 1: return c.internalize(call, sig.Results().At(0).Type()) default: c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"}) } } return c.translateCall(e, sig, c.formatExpr("%e.%s", f.X, strings.Join(fields, "."))) case types.MethodExpr: return c.translateCall(e, sig, c.translateExpr(f)) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } default: return c.translateCall(e, sig, c.translateExpr(plainFun)) } case *ast.StarExpr: if typesutil.IsJsObject(c.p.TypeOf(e.X)) { return c.formatExpr("new $jsObjectPtr(%e)", e.X) } if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(c.p.TypeOf(c2.Fun), types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.formatExpr("%e.$get()", e.X) case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.p.TypeOf(e.Type) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("$assertType(%e, %s, true)", e.X, c.typeName(t)) } return c.formatExpr("$assertType(%e, %s)", e.X, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := obj.(type) { case *types.Var, *types.Const: return c.formatExpr("%s", c.objectName(o)) case *types.Func: return c.formatExpr("%s", c.objectName(o)) case *types.TypeName: return c.formatExpr("%s", c.typeName(o.Type())) case *types.Nil: if typesutil.IsJsObject(exprType) { return c.formatExpr("null") } switch t := exprType.Underlying().(type) { case *types.Basic: if t.Kind() != types.UnsafePointer { panic("unexpected basic type") } return c.formatExpr("0") case *types.Slice, *types.Pointer: return c.formatExpr("%s.nil", c.typeName(exprType)) case *types.Chan: return c.formatExpr("$chanNil") case *types.Map: return c.formatExpr("false") case *types.Interface: return c.formatExpr("$ifaceNil") case *types.Signature: return c.formatExpr("$throwNilPointerError") default: panic(fmt.Sprintf("unexpected type: %T", t)) } default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *this: if isWrapped(c.p.TypeOf(e)) { return c.formatExpr("this.$val") } return c.formatExpr("this") case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
func (p *importer) param() *types.Var { return types.NewVar(token.NoPos, nil, p.string(), p.typ()) }
func buildSSA(fn *gst.FuncDecl, fnType *types.Func, log bool) (ssafn *ssa.Func, ok bool) { // HACK, hardcoded arch := "amd64" signature, ok := fnType.Type().(*types.Signature) if signature == nil || !ok { fmt.Println("Error: function type isn't signature") return nil, false } if signature.Results().Len() > 1 { fmt.Println("Multiple return values unsupported") return nil, false } var e ssaExport var s state e.log = log link := obj.Link{} s.ctx = Ctx{nil} //Ctx{fnInfo} s.fnDecl = nil s.fnType = nil s.fnInfo = nil s.config = ssa.NewConfig(arch, &e, &link, false) s.f = s.config.NewFunc() s.f.Name = fnType.Name() s.f.Entry = s.f.NewBlock(ssa.BlockPlain) s.scanBlocksGst(fn.Body) if len(s.blocks) < 1 { panic("no blocks found, need at least one block per function") } s.f.Entry = s.blocks[0].b s.startBlock(s.f.Entry) // Allocate starting values s.labels = map[string]*ssaLabel{} s.labeledNodes = map[ast.Node]*ssaLabel{} s.startmem = s.entryNewValue0(ssa.OpInitMem, ssa.TypeMem) s.sp = s.entryNewValue0(ssa.OpSP, Typ[types.Uintptr]) // TODO: use generic pointer type (unsafe.Pointer?) instead s.sb = s.entryNewValue0(ssa.OpSB, Typ[types.Uintptr]) s.vars = map[ssaVar]*ssa.Value{} s.vars[&memVar] = s.startmem //s.varsyms = map[*Node]interface{}{} // Generate addresses of local declarations s.decladdrs = map[ssaVar]*ssa.Value{} vars := []ssaVar{} //getVars(s.ctx, fn, fnType) for _, v := range vars { switch v.Class() { case PPARAM: // aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n}) // s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) case PAUTO | PHEAP: // TODO this looks wrong for PAUTO|PHEAP, no vardef, but also no definition // aux := s.lookupSymbol(n, &ssa.AutoSymbol{Typ: n.Type, Node: n}) // s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp) case PPARAM | PHEAP, PPARAMOUT | PHEAP: // This ends up wrong, have to do it at the PARAM node instead. case PAUTO, PPARAMOUT: // processed at each use, to prevent Addr coming // before the decl. case PFUNC: // local function - already handled by frontend default: str := "" if v.Class()&PHEAP != 0 { str = ",heap" } s.Unimplementedf("local variable with class %s%s unimplemented", v.Class(), str) } } //fpVar := types.NewVar(0, fnType.Pkg(), ".fp", Typ[types.Int32].Type) fpVar := types.NewVar(0, nil, ".fp", Typ[types.Int32].Type) nodfp := &ssaParam{v: fpVar, ctx: s.ctx} // nodfp is a special argument which is the function's FP. aux := &ssa.ArgSymbol{Typ: Typ[types.Uintptr], Node: nodfp} s.decladdrs[nodfp] = s.entryNewValue1A(ssa.OpAddr, Typ[types.Uintptr], aux, s.sp) s.processBlocks() // Link up variable uses to variable definitions s.linkForwardReferences() //fmt.Println("f:", f) ssa.Compile(s.f) return s.f, true }
func (c *simplifyContext) simplifyStmt(stmts *[]ast.Stmt, s ast.Stmt) { if s == nil { return } switch s := s.(type) { case *ast.ExprStmt: *stmts = append(*stmts, &ast.ExprStmt{ X: c.simplifyExpr2(stmts, s.X, true), }) case *ast.BlockStmt: *stmts = append(*stmts, c.simplifyBlock(s)) case *ast.LabeledStmt: c.simplifyStmt(stmts, s.Stmt) (*stmts)[len(*stmts)-1] = &ast.LabeledStmt{ Label: s.Label, Colon: s.Colon, Stmt: (*stmts)[len(*stmts)-1], } case *ast.AssignStmt: lhs := make([]ast.Expr, len(s.Lhs)) for i, x := range s.Lhs { lhs[i] = c.simplifyExpr(stmts, x) } rhs := make([]ast.Expr, len(s.Rhs)) for i, x := range s.Rhs { rhs[i] = c.simplifyExpr2(stmts, x, true) } *stmts = append(*stmts, &ast.AssignStmt{ Lhs: lhs, Tok: s.Tok, TokPos: s.TokPos, Rhs: rhs, }) case *ast.DeclStmt: *stmts = append(*stmts, &ast.DeclStmt{ Decl: c.simplifyGenDecl(stmts, s.Decl.(*ast.GenDecl)), }) case *ast.IfStmt: if s.Init != nil { block := &ast.BlockStmt{} *stmts = append(*stmts, block) stmts = &block.List c.simplifyStmt(stmts, s.Init) } newS := &ast.IfStmt{ If: s.If, Cond: c.simplifyExpr(stmts, s.Cond), Body: c.simplifyBlock(s.Body), Else: c.toElseBranch(c.simplifyToStmtList(s.Else), c.info.Scopes[s.Else]), } c.info.Scopes[newS] = c.info.Scopes[s] *stmts = append(*stmts, newS) case *ast.SwitchStmt: c.simplifySwitch(stmts, s) case *ast.TypeSwitchStmt: if s.Init != nil { block := &ast.BlockStmt{} *stmts = append(*stmts, block) stmts = &block.List c.simplifyStmt(stmts, s.Init) } var assign ast.Stmt switch a := s.Assign.(type) { case *ast.ExprStmt: ta := a.X.(*ast.TypeAssertExpr) assign = &ast.ExprStmt{ X: &ast.TypeAssertExpr{ X: c.simplifyExpr(stmts, ta.X), Lparen: ta.Lparen, Type: ta.Type, Rparen: ta.Rparen, }, } case *ast.AssignStmt: ta := a.Rhs[0].(*ast.TypeAssertExpr) assign = &ast.AssignStmt{ Lhs: a.Lhs, Tok: a.Tok, TokPos: a.TokPos, Rhs: []ast.Expr{ &ast.TypeAssertExpr{ X: c.simplifyExpr(stmts, ta.X), Lparen: ta.Lparen, Type: ta.Type, Rparen: ta.Rparen, }, }, } default: panic("unexpected type switch assign") } clauses := make([]ast.Stmt, len(s.Body.List)) for i, ccs := range s.Body.List { cc := ccs.(*ast.CaseClause) newClause := &ast.CaseClause{ Case: cc.Case, List: cc.List, Colon: cc.Colon, Body: c.simplifyStmtList(cc.Body), } if implicit, ok := c.info.Implicits[cc]; ok { c.info.Implicits[newClause] = implicit } clauses[i] = newClause } newS := &ast.TypeSwitchStmt{ Switch: s.Switch, Assign: assign, Body: &ast.BlockStmt{ List: clauses, }, } c.info.Scopes[newS] = c.info.Scopes[s] *stmts = append(*stmts, newS) case *ast.ForStmt: newS := &ast.ForStmt{ For: s.For, Init: s.Init, Cond: s.Cond, Post: s.Post, Body: c.simplifyBlock(s.Body), } c.info.Scopes[newS] = c.info.Scopes[s] *stmts = append(*stmts, newS) // case *ast.ForStmt: // c.simplifyStmt(stmts, s.Init) // var condStmts []ast.Stmt // cond := c.newVar(&condStmts, s.Cond) // bodyStmts := s.Body.List // if len(condStmts) != 0 { // bodyStmts = append(append(condStmts, &ast.IfStmt{ // Cond: &ast.UnaryExpr{ // Op: token.NOT, // X: cond, // }, // Body: &ast.BlockStmt{ // List: []ast.Stmt{&ast.BranchStmt{ // Tok: token.BREAK, // }}, // }, // }), bodyStmts...) // cond = nil // } // *stmts = append(*stmts, &ast.ForStmt{ // For: s.For, // Cond: cond, // Post: s.Post, // Body: &ast.BlockStmt{ // List: bodyStmts, // }, // }) case *ast.RangeStmt: var newS ast.Stmt switch t := c.info.TypeOf(s.X).Underlying().(type) { case *types.Chan: key := s.Key tok := s.Tok if key == nil { key = ast.NewIdent("_") tok = token.DEFINE } okVar := c.newIdent(types.Typ[types.Bool]) if s.Tok == token.ASSIGN { *stmts = append(*stmts, &ast.DeclStmt{ Decl: &ast.GenDecl{ Tok: token.VAR, Specs: []ast.Spec{&ast.ValueSpec{ Names: []*ast.Ident{okVar}, Type: ast.NewIdent("bool"), }}, }, }) } newS = &ast.ForStmt{ For: s.For, Body: &ast.BlockStmt{ Lbrace: s.Body.Lbrace, List: append([]ast.Stmt{ &ast.AssignStmt{ Lhs: []ast.Expr{key, okVar}, TokPos: s.TokPos, Tok: tok, Rhs: []ast.Expr{c.setType(&ast.UnaryExpr{ Op: token.ARROW, X: c.newVar(stmts, s.X), }, types.NewTuple( types.NewVar(token.NoPos, nil, "", t.Elem()), types.NewVar(token.NoPos, nil, "", types.Typ[types.Bool]), ))}, }, &ast.IfStmt{ Cond: c.setType(&ast.UnaryExpr{ Op: token.NOT, X: okVar, }, types.Typ[types.Bool]), Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.BranchStmt{Tok: token.BREAK}, }, }, }, }, c.simplifyStmtList(s.Body.List)...), Rbrace: s.Body.Rbrace, }, } default: newS = &ast.RangeStmt{ For: s.For, Key: s.Key, Value: s.Value, TokPos: s.TokPos, Tok: s.Tok, X: s.X, Body: c.simplifyBlock(s.Body), } } c.info.Scopes[newS] = c.info.Scopes[s] *stmts = append(*stmts, newS) case *ast.IncDecStmt: *stmts = append(*stmts, &ast.IncDecStmt{ X: c.simplifyExpr(stmts, s.X), TokPos: s.TokPos, Tok: s.Tok, }) case *ast.GoStmt: *stmts = append(*stmts, &ast.GoStmt{ Go: s.Go, Call: c.simplifyCall(stmts, s.Call), }) case *ast.SelectStmt: clauses := make([]ast.Stmt, len(s.Body.List)) for i, entry := range s.Body.List { cc := entry.(*ast.CommClause) var newComm ast.Stmt var bodyPrefix []ast.Stmt switch comm := cc.Comm.(type) { case *ast.ExprStmt: recv := comm.X.(*ast.UnaryExpr) if recv.Op != token.ARROW { panic("unexpected comm clause") } newComm = &ast.ExprStmt{ X: &ast.UnaryExpr{ Op: token.ARROW, OpPos: recv.OpPos, X: c.simplifyExpr(stmts, recv.X), }, } case *ast.AssignStmt: recv := comm.Rhs[0].(*ast.UnaryExpr) if recv.Op != token.ARROW { panic("unexpected comm clause") } simplifyLhs := false for _, x := range comm.Lhs { if c.simplifyCalls && ContainsCall(x) { simplifyLhs = true } } lhs := comm.Lhs tok := comm.Tok if simplifyLhs { for i, x := range lhs { id := c.newIdent(c.info.TypeOf(x)) bodyPrefix = append(bodyPrefix, simpleAssign(c.simplifyExpr(&bodyPrefix, x), comm.Tok, id)) lhs[i] = id } tok = token.DEFINE } newComm = &ast.AssignStmt{ Lhs: lhs, Tok: tok, Rhs: []ast.Expr{c.simplifyExpr(stmts, recv)}, } case *ast.SendStmt: newComm = &ast.SendStmt{ Chan: c.simplifyExpr(stmts, comm.Chan), Arrow: comm.Arrow, Value: c.simplifyExpr(stmts, comm.Value), } case nil: newComm = nil default: panic("unexpected comm clause") } newCC := &ast.CommClause{ Case: cc.Case, Comm: newComm, Colon: cc.Colon, Body: append(bodyPrefix, c.simplifyStmtList(cc.Body)...), } c.info.Scopes[newCC] = c.info.Scopes[cc] clauses[i] = newCC } *stmts = append(*stmts, &ast.SelectStmt{ Select: s.Select, Body: &ast.BlockStmt{ List: clauses, }, }) case *ast.DeferStmt: *stmts = append(*stmts, &ast.DeferStmt{ Defer: s.Defer, Call: c.simplifyCall(stmts, s.Call), }) case *ast.SendStmt: *stmts = append(*stmts, &ast.SendStmt{ Chan: c.simplifyExpr(stmts, s.Chan), Arrow: s.Arrow, Value: c.simplifyExpr(stmts, s.Value), }) case *ast.ReturnStmt: *stmts = append(*stmts, &ast.ReturnStmt{ Return: s.Return, Results: c.simplifyExprList(stmts, s.Results), }) default: *stmts = append(*stmts, s) } }
"comma-separated list of functions whose results must be used") var unusedStringMethodsFlag = flag.String("unusedstringmethods", "Error,String", "comma-separated list of names of methods of type func() string whose results must be used") func init() { register("unusedresult", "check for unused result of calls to functions in -unusedfuncs list and methods in -unusedstringmethods list", checkUnusedResult, exprStmt) } // func() string var sigNoArgsStringResult = types.NewSignature(nil, nil, nil, types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])), false) var unusedFuncs = make(map[string]bool) var unusedStringMethods = make(map[string]bool) func initUnusedFlags() { commaSplit := func(s string, m map[string]bool) { if s != "" { for _, name := range strings.Split(s, ",") { if len(name) == 0 { flag.Usage() } m[name] = true } }
// Var = Name Type . func (p *parser) parseVar(pkg *types.Package) *types.Var { name := p.parseName() return types.NewVar(token.NoPos, pkg, name, p.parseType(pkg)) }
func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { c.SetPos(stmt.Pos()) stmt = filter.IncDecStmt(stmt, c.p.Info.Info) stmt = filter.Assign(stmt, c.p.Info.Info, c.p.Info.Pkg) switch s := stmt.(type) { case *ast.BlockStmt: c.translateStmtList(s.List) case *ast.IfStmt: var caseClauses []*ast.CaseClause ifStmt := s for { if ifStmt.Init != nil { panic("simplification error") } caseClauses = append(caseClauses, &ast.CaseClause{List: []ast.Expr{ifStmt.Cond}, Body: ifStmt.Body.List}) elseStmt, ok := ifStmt.Else.(*ast.IfStmt) if !ok { break } ifStmt = elseStmt } var defaultClause *ast.CaseClause if block, ok := ifStmt.Else.(*ast.BlockStmt); ok { defaultClause = &ast.CaseClause{Body: block.List} } c.translateBranchingStmt(caseClauses, defaultClause, false, c.translateExpr, nil, c.Flattened[s]) case *ast.SwitchStmt: if s.Init != nil || s.Tag != nil || len(s.Body.List) != 1 { panic("simplification error") } clause := s.Body.List[0].(*ast.CaseClause) if len(clause.List) != 0 { panic("simplification error") } prevFlowData := c.flowDatas[nil] data := &flowData{ postStmt: prevFlowData.postStmt, // for "continue" of outer loop beginCase: prevFlowData.beginCase, // same } c.flowDatas[nil] = data c.flowDatas[label] = data defer func() { delete(c.flowDatas, label) c.flowDatas[nil] = prevFlowData }() if c.Flattened[s] { data.endCase = c.caseCounter c.caseCounter++ c.Indent(func() { c.translateStmtList(clause.Body) }) c.Printf("case %d:", data.endCase) return } if label != nil || analysis.HasBreak(clause) { if label != nil { c.Printf("%s:", label.Name()) } c.Printf("switch (0) { default:") c.Indent(func() { c.translateStmtList(clause.Body) }) c.Printf("}") return } c.translateStmtList(clause.Body) case *ast.TypeSwitchStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } refVar := c.newVariable("_ref") var expr ast.Expr switch a := s.Assign.(type) { case *ast.AssignStmt: expr = a.Rhs[0].(*ast.TypeAssertExpr).X case *ast.ExprStmt: expr = a.X.(*ast.TypeAssertExpr).X } c.Printf("%s = %s;", refVar, c.translateExpr(expr)) translateCond := func(cond ast.Expr) *expression { if types.Identical(c.p.TypeOf(cond), types.Typ[types.UntypedNil]) { return c.formatExpr("%s === $ifaceNil", refVar) } return c.formatExpr("$assertType(%s, %s, true)[1]", refVar, c.typeName(c.p.TypeOf(cond))) } var caseClauses []*ast.CaseClause var defaultClause *ast.CaseClause for _, cc := range s.Body.List { clause := cc.(*ast.CaseClause) var bodyPrefix []ast.Stmt if implicit := c.p.Implicits[clause]; implicit != nil { value := refVar if _, isInterface := implicit.Type().Underlying().(*types.Interface); !isInterface { value += ".$val" } bodyPrefix = []ast.Stmt{&ast.AssignStmt{ Lhs: []ast.Expr{c.newIdent(c.objectName(implicit), implicit.Type())}, Tok: token.DEFINE, Rhs: []ast.Expr{c.newIdent(value, implicit.Type())}, }} } c := &ast.CaseClause{ List: clause.List, Body: append(bodyPrefix, clause.Body...), } if len(c.List) == 0 { defaultClause = c continue } caseClauses = append(caseClauses, c) } c.translateBranchingStmt(caseClauses, defaultClause, true, translateCond, label, c.Flattened[s]) case *ast.ForStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } cond := func() string { if s.Cond == nil { return "true" } return c.translateExpr(s.Cond).String() } c.translateLoopingStmt(cond, s.Body, nil, func() { if s.Post != nil { c.translateStmt(s.Post, nil) } }, label, c.Flattened[s]) case *ast.RangeStmt: refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.X)) switch t := c.p.TypeOf(s.X).Underlying().(type) { case *types.Basic: iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) runeVar := c.newVariable("_rune") c.translateLoopingStmt(func() string { return iVar + " < " + refVar + ".length" }, s.Body, func() { c.Printf("%s = $decodeRune(%s, %s);", runeVar, refVar, iVar) if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.newIdent(runeVar+"[0]", types.Typ[types.Rune]), s.Tok == token.DEFINE)) } }, func() { c.Printf("%s += %s[1];", iVar, runeVar) }, label, c.Flattened[s]) case *types.Map: iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) keysVar := c.newVariable("_keys") c.Printf("%s = $keys(%s);", keysVar, refVar) c.translateLoopingStmt(func() string { return iVar + " < " + keysVar + ".length" }, s.Body, func() { entryVar := c.newVariable("_entry") c.Printf("%s = %s[%s[%s]];", entryVar, refVar, keysVar, iVar) c.translateStmt(&ast.IfStmt{ Cond: c.newIdent(entryVar+" === undefined", types.Typ[types.Bool]), Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.CONTINUE}}}, }, nil) if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, c.newIdent(entryVar+".k", t.Key()), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.newIdent(entryVar+".v", t.Elem()), s.Tok == token.DEFINE)) } }, func() { c.Printf("%s++;", iVar) }, label, c.Flattened[s]) case *types.Array, *types.Pointer, *types.Slice: var length string var elemType types.Type switch t2 := t.(type) { case *types.Array: length = fmt.Sprintf("%d", t2.Len()) elemType = t2.Elem() case *types.Pointer: length = fmt.Sprintf("%d", t2.Elem().Underlying().(*types.Array).Len()) elemType = t2.Elem().Underlying().(*types.Array).Elem() case *types.Slice: length = refVar + ".$length" elemType = t2.Elem() } iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) c.translateLoopingStmt(func() string { return iVar + " < " + length }, s.Body, func() { if !isBlank(s.Key) { c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.setType(&ast.IndexExpr{ X: c.newIdent(refVar, t), Index: c.newIdent(iVar, types.Typ[types.Int]), }, elemType), s.Tok == token.DEFINE)) } }, func() { c.Printf("%s++;", iVar) }, label, c.Flattened[s]) case *types.Chan: okVar := c.newIdent(c.newVariable("_ok"), types.Typ[types.Bool]) key := s.Key tok := s.Tok if key == nil { key = ast.NewIdent("_") tok = token.ASSIGN } forStmt := &ast.ForStmt{ Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.AssignStmt{ Lhs: []ast.Expr{ key, okVar, }, Rhs: []ast.Expr{ c.setType(&ast.UnaryExpr{X: c.newIdent(refVar, t), Op: token.ARROW}, types.NewTuple(types.NewVar(0, nil, "", t.Elem()), types.NewVar(0, nil, "", types.Typ[types.Bool]))), }, Tok: tok, }, &ast.IfStmt{ Cond: &ast.UnaryExpr{X: okVar, Op: token.NOT}, Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.BREAK}}}, }, s.Body, }, }, } c.Flattened[forStmt] = true c.translateStmt(forStmt, label) default: panic("") } case *ast.BranchStmt: normalLabel := "" blockingLabel := "" data := c.flowDatas[nil] if s.Label != nil { normalLabel = " " + s.Label.Name blockingLabel = " s" // use explicit label "s", because surrounding loop may not be flattened data = c.flowDatas[c.p.Uses[s.Label].(*types.Label)] } switch s.Tok { case token.BREAK: c.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.endCase, blockingLabel)) case token.CONTINUE: data.postStmt() c.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.beginCase, blockingLabel)) case token.GOTO: c.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("$s = %d; continue;", c.labelCase(c.p.Uses[s.Label].(*types.Label)))) case token.FALLTHROUGH: // handled in CaseClause default: panic("Unhandled branch statment: " + s.Tok.String()) } case *ast.ReturnStmt: results := s.Results if c.resultNames != nil { if len(s.Results) != 0 { c.translateStmt(&ast.AssignStmt{ Lhs: c.resultNames, Tok: token.ASSIGN, Rhs: s.Results, }, nil) } results = c.resultNames } rVal := c.translateResults(results) if c.Flattened[s] { resumeCase := c.caseCounter c.caseCounter++ c.Printf("/* */ $s = %[1]d; case %[1]d:", resumeCase) } c.Printf("return%s;", rVal) case *ast.DeferStmt: isBuiltin := false isJs := false switch fun := s.Call.Fun.(type) { case *ast.Ident: var builtin *types.Builtin builtin, isBuiltin = c.p.Uses[fun].(*types.Builtin) if isBuiltin && builtin.Name() == "recover" { c.Printf("$deferred.push([$recover, []]);") return } case *ast.SelectorExpr: isJs = typesutil.IsJsPackage(c.p.Uses[fun.Sel].Pkg()) } sig := c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature) args := c.translateArgs(sig, s.Call.Args, s.Call.Ellipsis.IsValid(), true) if isBuiltin || isJs { vars := make([]string, len(s.Call.Args)) callArgs := make([]ast.Expr, len(s.Call.Args)) for i, arg := range s.Call.Args { v := c.newVariable("_arg") vars[i] = v callArgs[i] = c.newIdent(v, c.p.TypeOf(arg)) } call := c.translateExpr(&ast.CallExpr{ Fun: s.Call.Fun, Args: callArgs, Ellipsis: s.Call.Ellipsis, }) c.Printf("$deferred.push([function(%s) { %s; }, [%s]]);", strings.Join(vars, ", "), call, strings.Join(args, ", ")) return } c.Printf("$deferred.push([%s, [%s]]);", c.translateExpr(s.Call.Fun), strings.Join(args, ", ")) case *ast.AssignStmt: if s.Tok != token.ASSIGN && s.Tok != token.DEFINE { panic(s.Tok) } switch { case len(s.Lhs) == 1 && len(s.Rhs) == 1: lhs := astutil.RemoveParens(s.Lhs[0]) if isBlank(lhs) { if analysis.HasSideEffect(s.Rhs[0], c.p.Info.Info) { c.Printf("%s;", c.translateExpr(s.Rhs[0])) } return } c.Printf("%s", c.translateAssign(lhs, s.Rhs[0], s.Tok == token.DEFINE)) case len(s.Lhs) > 1 && len(s.Rhs) == 1: tupleVar := c.newVariable("_tuple") c.Printf("%s = %s;", tupleVar, c.translateExpr(s.Rhs[0])) tuple := c.p.TypeOf(s.Rhs[0]).(*types.Tuple) for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { c.Printf("%s", c.translateAssign(lhs, c.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), s.Tok == token.DEFINE)) } } case len(s.Lhs) == len(s.Rhs): tmpVars := make([]string, len(s.Rhs)) for i, rhs := range s.Rhs { tmpVars[i] = c.newVariable("_tmp") if isBlank(astutil.RemoveParens(s.Lhs[i])) { if analysis.HasSideEffect(rhs, c.p.Info.Info) { c.Printf("%s;", c.translateExpr(rhs)) } continue } c.Printf("%s", c.translateAssign(c.newIdent(tmpVars[i], c.p.TypeOf(s.Lhs[i])), rhs, true)) } for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { c.Printf("%s", c.translateAssign(lhs, c.newIdent(tmpVars[i], c.p.TypeOf(lhs)), s.Tok == token.DEFINE)) } } default: panic("Invalid arity of AssignStmt.") } case *ast.DeclStmt: decl := s.Decl.(*ast.GenDecl) switch decl.Tok { case token.VAR: for _, spec := range s.Decl.(*ast.GenDecl).Specs { valueSpec := spec.(*ast.ValueSpec) lhs := make([]ast.Expr, len(valueSpec.Names)) for i, name := range valueSpec.Names { lhs[i] = name } rhs := valueSpec.Values if len(rhs) == 0 { rhs = make([]ast.Expr, len(lhs)) for i, e := range lhs { rhs[i] = c.zeroValue(c.p.TypeOf(e)) } } c.translateStmt(&ast.AssignStmt{ Lhs: lhs, Tok: token.DEFINE, Rhs: rhs, }, nil) } case token.TYPE: for _, spec := range decl.Specs { o := c.p.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) c.p.typeNames = append(c.p.typeNames, o) c.p.objectNames[o] = c.newVariableWithLevel(o.Name(), true) c.p.dependencies[o] = true } case token.CONST: // skip, constants are inlined } case *ast.ExprStmt: expr := c.translateExpr(s.X) if expr != nil && expr.String() != "" { c.Printf("%s;", expr) } case *ast.LabeledStmt: label := c.p.Defs[s.Label].(*types.Label) if c.GotoLabel[label] { c.PrintCond(false, s.Label.Name+":", fmt.Sprintf("case %d:", c.labelCase(label))) } c.translateStmt(s.Stmt, label) case *ast.GoStmt: c.Printf("$go(%s, [%s]);", c.translateExpr(s.Call.Fun), strings.Join(c.translateArgs(c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid(), true), ", ")) case *ast.SendStmt: chanType := c.p.TypeOf(s.Chan).Underlying().(*types.Chan) call := &ast.CallExpr{ Fun: c.newIdent("$send", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", chanType), types.NewVar(0, nil, "", chanType.Elem())), nil, false)), Args: []ast.Expr{s.Chan, c.newIdent(c.translateImplicitConversionWithCloning(s.Value, chanType.Elem()).String(), chanType.Elem())}, } c.Blocking[call] = true c.translateStmt(&ast.ExprStmt{X: call}, label) case *ast.SelectStmt: selectionVar := c.newVariable("_selection") var channels []string var caseClauses []*ast.CaseClause flattened := false hasDefault := false for i, cc := range s.Body.List { clause := cc.(*ast.CommClause) switch comm := clause.Comm.(type) { case nil: channels = append(channels, "[]") hasDefault = true case *ast.ExprStmt: channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.X).(*ast.UnaryExpr).X).String()) case *ast.AssignStmt: channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.Rhs[0]).(*ast.UnaryExpr).X).String()) case *ast.SendStmt: chanType := c.p.TypeOf(comm.Chan).Underlying().(*types.Chan) channels = append(channels, c.formatExpr("[%e, %s]", comm.Chan, c.translateImplicitConversionWithCloning(comm.Value, chanType.Elem())).String()) default: panic(fmt.Sprintf("unhandled: %T", comm)) } indexLit := &ast.BasicLit{Kind: token.INT} c.p.Types[indexLit] = types.TypeAndValue{Type: types.Typ[types.Int], Value: constant.MakeInt64(int64(i))} var bodyPrefix []ast.Stmt if assign, ok := clause.Comm.(*ast.AssignStmt); ok { switch rhsType := c.p.TypeOf(assign.Rhs[0]).(type) { case *types.Tuple: bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1]", rhsType)}, Tok: assign.Tok}} default: bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}} } } caseClauses = append(caseClauses, &ast.CaseClause{ List: []ast.Expr{indexLit}, Body: append(bodyPrefix, clause.Body...), }) flattened = flattened || c.Flattened[clause] } selectCall := c.setType(&ast.CallExpr{ Fun: c.newIdent("$select", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", types.NewInterface(nil, nil))), types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), false)), Args: []ast.Expr{c.newIdent(fmt.Sprintf("[%s]", strings.Join(channels, ", ")), types.NewInterface(nil, nil))}, }, types.Typ[types.Int]) c.Blocking[selectCall] = !hasDefault c.Printf("%s = %s;", selectionVar, c.translateExpr(selectCall)) if len(caseClauses) != 0 { translateCond := func(cond ast.Expr) *expression { return c.formatExpr("%s[0] === %e", selectionVar, cond) } c.translateBranchingStmt(caseClauses, nil, true, translateCond, label, flattened) } case *ast.EmptyStmt: // skip default: panic(fmt.Sprintf("Unhandled statement: %T\n", s)) } }