// computeTrackBits sets a.track to the necessary 'track' bits for the pointer queries. func (a *analysis) computeTrackBits() { var queryTypes []types.Type for v := range a.config.Queries { queryTypes = append(queryTypes, v.Type()) } for v := range a.config.IndirectQueries { queryTypes = append(queryTypes, mustDeref(v.Type())) } for _, t := range queryTypes { switch t.Underlying().(type) { case *types.Chan: a.track |= trackChan case *types.Map: a.track |= trackMap case *types.Pointer: a.track |= trackPtr case *types.Slice: a.track |= trackSlice case *types.Interface: a.track = trackAll return } if rVObj := a.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) { a.track = trackAll return } } }
// containsAllIdsOf reports whether the method identifiers of T are a // superset of those in U. If U belongs to an interface type, the // result is equal to types.Assignable(T, U), but is cheaper to compute. // // TODO(gri): make this a method of *types.MethodSet. // func containsAllIdsOf(T, U *types.MethodSet) bool { t, tlen := 0, T.Len() u, ulen := 0, U.Len() for t < tlen && u < ulen { tMeth := T.At(t).Obj() uMeth := U.At(u).Obj() tId := tMeth.Id() uId := uMeth.Id() if tId > uId { // U has a method T lacks: fail. return false } if tId < uId { // T has a method U lacks: ignore it. t++ continue } // U and T both have a method of this Id. Check types. if !types.Identical(tMeth.Type(), uMeth.Type()) { return false // type mismatch } u++ t++ } return u == ulen }
// Set sets the map entry for key to val, // and returns the previous entry, if any. func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) { if m.table != nil { hash := m.hasher.Hash(key) bucket := m.table[hash] var hole *entry for i, e := range bucket { if e.key == nil { hole = &bucket[i] } else if types.Identical(key, e.key) { prev = e.value bucket[i].value = value return } } if hole != nil { *hole = entry{key, value} // overwrite deleted entry } else { m.table[hash] = append(bucket, entry{key, value}) } } else { if m.hasher.memo == nil { m.hasher = MakeHasher() } hash := m.hasher.Hash(key) m.table = map[uint32][]entry{hash: {entry{key, value}}} } m.length++ return }
func (c *funcContext) translateImplicitConversion(expr ast.Expr, desiredType types.Type) *expression { if desiredType == nil { return c.translateExpr(expr) } exprType := c.p.TypeOf(expr) if types.Identical(exprType, desiredType) { return c.translateExpr(expr) } basicExprType, isBasicExpr := exprType.Underlying().(*types.Basic) if isBasicExpr && basicExprType.Kind() == types.UntypedNil { return c.formatExpr("%e", c.zeroValue(desiredType)) } switch desiredType.Underlying().(type) { case *types.Slice: return c.formatExpr("$subslice(new %1s(%2e.$array), %2e.$offset, %2e.$offset + %2e.$length)", c.typeName(desiredType), expr) case *types.Interface: if typesutil.IsJsObject(exprType) { // wrap JS object into js.Object struct when converting to interface return c.formatExpr("new $jsObjectPtr(%e)", expr) } if isWrapped(exprType) { return c.formatExpr("new %s(%e)", c.typeName(exprType), expr) } if _, isStruct := exprType.Underlying().(*types.Struct); isStruct { return c.formatExpr("new %1e.constructor.elem(%1e)", expr) } } return c.translateExpr(expr) }
// assign records pairs of distinct types that are related by // assignability, where the left-hand side is an interface and both // sides have methods. // // It should be called for all assignability checks, type assertions, // explicit conversions and comparisons between two types, unless the // types are uninteresting (e.g. lhs is a concrete type, or the empty // interface; rhs has no methods). // func (f *Finder) assign(lhs, rhs types.Type) { if types.Identical(lhs, rhs) { return } if !isInterface(lhs) { return } if f.msetcache.MethodSet(lhs).Len() == 0 { return } if f.msetcache.MethodSet(rhs).Len() == 0 { return } // canonicalize types lhsc, ok := f.canon.At(lhs).(types.Type) if !ok { lhsc = lhs f.canon.Set(lhs, lhsc) } rhsc, ok := f.canon.At(rhs).(types.Type) if !ok { rhsc = rhs f.canon.Set(rhs, rhsc) } // record the pair f.Result[Constraint{lhsc, rhsc}] = true }
// typeAssert checks whether dynamic type of itf is instr.AssertedType. // It returns the extracted value on success, and panics on failure, // unless instr.CommaOk, in which case it always returns a "value,ok" tuple. // func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value { var v value err := "" if itf.t == nil { err = fmt.Sprintf("interface conversion: interface is nil, not %s", instr.AssertedType) } else if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok { v = itf err = checkInterface(i, idst, itf) } else if types.Identical(itf.t, instr.AssertedType) { v = copyVal(itf.v) // extract value } else { err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType) } if err != "" { if !instr.CommaOk { panic(err) } return tuple{zero(instr.AssertedType), false} } if instr.CommaOk { return tuple{v, true} } return v }
func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { if !types.Identical(x, y) { t.Errorf("%s: not equal: %s, %s", comment, x, y) } if x == y { t.Errorf("%s: identical: %v, %v", comment, x, y) } }
// At returns the map entry for the given key. // The result is nil if the entry is not present. // func (m *Map) At(key types.Type) interface{} { if m != nil && m.table != nil { for _, e := range m.table[m.hasher.Hash(key)] { if e.key != nil && types.Identical(key, e.key) { return e.value } } } return nil }
// findAssignments returns the set of types to or from which type T is // assigned in the program syntax. func (r *renamer) findAssignments(T types.Type) map[types.Type]bool { if r.satisfyConstraints == nil { // Compute on demand: it's expensive. var f satisfy.Finder for _, info := range r.packages { f.Find(&info.Info, info.Files) } r.satisfyConstraints = f.Result } result := make(map[types.Type]bool) for key := range r.satisfyConstraints { // key = (lhs, rhs) where lhs is always an interface. if types.Identical(key.RHS, T) { result[key.LHS] = true } if isInterface(T) && types.Identical(key.LHS, T) { // must check both sides result[key.RHS] = true } } return result }
func hasType(pkg *grinder.Package, fn *ast.FuncDecl, edit *grinder.EditBuffer, x, v ast.Expr) bool { // Does x (by itself) default to v's type? // Find the scope in which x appears. xScope := pkg.Info.Scopes[fn.Type] ast.Inspect(fn.Body, func(z ast.Node) bool { if z == nil { return false } if x.Pos() < z.Pos() || z.End() <= x.Pos() { return false } scope := pkg.Info.Scopes[z] if scope != nil { xScope = scope } return true }) xs := edit.TextAt(x.Pos(), x.End()) xt, err := types.Eval(pkg.FileSet, pkg.Types, xScope.Pos(), xs) if err != nil { return false } vt := pkg.Info.Types[v] if types.Identical(xt.Type, vt.Type) { return true } // Might be untyped. vb, ok1 := vt.Type.(*types.Basic) xb, ok2 := xt.Type.(*types.Basic) if ok1 && ok2 { switch xb.Kind() { case types.UntypedInt: return vb.Kind() == types.Int case types.UntypedBool: return vb.Kind() == types.Bool case types.UntypedRune: return vb.Kind() == types.Rune case types.UntypedFloat: return vb.Kind() == types.Float64 case types.UntypedComplex: return vb.Kind() == types.Complex128 case types.UntypedString: return vb.Kind() == types.String } } return false }
// Delete removes the entry with the given key, if any. // It returns true if the entry was found. // func (m *Map) Delete(key types.Type) bool { if m != nil && m.table != nil { hash := m.hasher.Hash(key) bucket := m.table[hash] for i, e := range bucket { if e.key != nil && types.Identical(key, e.key) { // We can't compact the bucket as it // would disturb iterators. bucket[i] = entry{} m.length-- return true } } } return false }
// isErrorMethodCall reports whether the call is of a method with signature // func Error() string // where "string" is the universe's string type. We know the method is called "Error". func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { typ := f.pkg.types[call].Type if typ != nil { // We know it's called "Error", so just check the function signature // (stringerType has exactly one method, String). if stringerType != nil && stringerType.NumMethods() == 1 { return types.Identical(f.pkg.types[call.Fun].Type, stringerType.Method(0).Type()) } } // Without types, we can still check by hand. // Is it a selector expression? Otherwise it's a function call, not a method call. sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return false } // The package is type-checked, so if there are no arguments, we're done. if len(call.Args) > 0 { return false } // Check the type of the method declaration typ = f.pkg.types[sel].Type if typ == nil { return false } // The type must be a signature, but be sure for safety. sig, ok := typ.(*types.Signature) if !ok { return false } // There must be a receiver for it to be a method call. Otherwise it is // a function, not something that satisfies the error interface. if sig.Recv() == nil { return false } // There must be no arguments. Already verified by type checking, but be thorough. if sig.Params().Len() > 0 { return false } // Finally the real questions. // There must be one result. if sig.Results().Len() != 1 { return false } // It must have return type "string" from the universe. return sig.Results().At(0).Type() == types.Typ[types.String] }
// assign records pairs of distinct types that are related by // assignability, where the left-hand side is an interface and both // sides have methods. // // It should be called for all assignability checks, type assertions, // explicit conversions and comparisons between two types, unless the // types are uninteresting (e.g. lhs is a concrete type, or the empty // interface; rhs has no methods). // func (f *Finder) assign(lhs, rhs types.Type) { if types.Identical(lhs, rhs) { return } if !isInterface(lhs) { return } if f.msetcache.MethodSet(lhs).Len() == 0 { return } if f.msetcache.MethodSet(rhs).Len() == 0 { return } // record the pair f.Result[Constraint{lhs, rhs}] = true }
// isValuePreserving returns true if a conversion from ut_src to // ut_dst is value-preserving, i.e. just a change of type. // Precondition: neither argument is a named type. // func isValuePreserving(ut_src, ut_dst types.Type) bool { // Identical underlying types? if types.Identical(ut_dst, ut_src) { return true } switch ut_dst.(type) { case *types.Chan: // Conversion between channel types? _, ok := ut_src.(*types.Chan) return ok case *types.Pointer: // Conversion between pointers with identical base types? _, ok := ut_src.(*types.Pointer) return ok } return false }
func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) { fn := prog.FuncValue(obj) // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging if fn == nil { if obj.Name() != "interfaceMethod" { t.Errorf("FuncValue(%s) == nil", obj) } return } if fnobj := fn.Object(); fnobj != obj { t.Errorf("FuncValue(%s).Object() == %s; value was %s", obj, fnobj, fn.Name()) return } if !types.Identical(fn.Type(), obj.Type()) { t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type()) return } }
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) { c := prog.ConstValue(obj) // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging if c == nil { t.Errorf("ConstValue(%s) == nil", obj) return } if !types.Identical(c.Type(), obj.Type()) { t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type()) return } if obj.Name() != "nil" { if !exact.Compare(c.Value, token.EQL, obj.Val()) { t.Errorf("ConstValue(%s).Value (%s) != %s", obj, c.Value, obj.Val()) return } } }
// checkShadowing checks whether the identifier shadows an identifier in an outer scope. func checkShadowing(f *File, ident *ast.Ident) { if ident.Name == "_" { // Can't shadow the blank identifier. return } obj := f.pkg.defs[ident] if obj == nil { return } // obj.Parent.Parent is the surrounding scope. If we can find another declaration // starting from there, we have a shadowed identifier. _, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos()) if shadowed == nil { return } // Don't complain if it's shadowing a universe-declared identifier; that's fine. if shadowed.Parent() == types.Universe { return } if *strictShadowing { // The shadowed identifier must appear before this one to be an instance of shadowing. if shadowed.Pos() > ident.Pos() { return } } else { // Don't complain if the span of validity of the shadowed identifier doesn't include // the shadowing identifier. span, ok := f.pkg.spans[shadowed] if !ok { f.Badf(ident.Pos(), "internal error: no range for %s", ident.Name) return } if !span.contains(ident.Pos()) { return } } // Don't complain if the types differ: that implies the programmer really wants two different things. if types.Identical(obj.Type(), shadowed.Type()) { f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos())) } }
// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil. func findVisibleErrs(prog *ssa.Program, qpos *QueryPos) map[*ssa.Global]ssa.Value { globals := make(map[*ssa.Global]ssa.Value) for _, pkg := range prog.AllPackages() { for _, mem := range pkg.Members { gbl, ok := mem.(*ssa.Global) if !ok { continue } gbltype := gbl.Type() // globals are always pointers if !types.Identical(deref(gbltype), builtinErrorType) { continue } if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) { continue } globals[gbl] = nil } } return globals }
// emitCompare emits to f code compute the boolean result of // comparison comparison 'x op y'. // func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value { xt := x.Type().Underlying() yt := y.Type().Underlying() // Special case to optimise a tagless SwitchStmt so that // these are equivalent // switch { case e: ...} // switch true { case e: ... } // if e==true { ... } // even in the case when e's type is an interface. // TODO(adonovan): opt: generalise to x==true, false!=y, etc. if x == vTrue && op == token.EQL { if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 { return y } } if types.Identical(xt, yt) { // no conversion necessary } else if _, ok := xt.(*types.Interface); ok { y = emitConv(f, y, x.Type()) } else if _, ok := yt.(*types.Interface); ok { x = emitConv(f, x, y.Type()) } else if _, ok := x.(*Const); ok { x = emitConv(f, x, y.Type()) } else if _, ok := y.(*Const); ok { y = emitConv(f, y, x.Type()) } else { // other cases, e.g. channels. No-op. } v := &BinOp{ Op: op, X: x, Y: y, } v.setPos(pos) v.setType(tBool) return f.emit(v) }
func checkUnusedResult(f *File, n ast.Node) { call, ok := unparen(n.(*ast.ExprStmt).X).(*ast.CallExpr) if !ok { return // not a call statement } fun := unparen(call.Fun) if f.pkg.types[fun].IsType() { return // a conversion, not a call } selector, ok := fun.(*ast.SelectorExpr) if !ok { return // neither a method call nor a qualified ident } sel, ok := f.pkg.selectors[selector] if ok && sel.Kind() == types.MethodVal { // method (e.g. foo.String()) obj := sel.Obj().(*types.Func) sig := sel.Type().(*types.Signature) if types.Identical(sig, sigNoArgsStringResult) { if unusedStringMethods[obj.Name()] { f.Badf(call.Lparen, "result of (%s).%s call not used", sig.Recv().Type(), obj.Name()) } } } else if !ok { // package-qualified function (e.g. fmt.Errorf) obj, _ := f.pkg.uses[selector.Sel] if obj, ok := obj.(*types.Func); ok { qname := obj.Pkg().Path() + "." + obj.Name() if unusedFuncs[qname] { f.Badf(call.Lparen, "result of %v call not used", qname) } } } }
func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) { // The prefix of all assertions messages. prefix := fmt.Sprintf("VarValue(%s @ L%d)", obj, prog.Fset.Position(ref[0].Pos()).Line) v, gotAddr := prog.VarValue(obj, pkg, ref) // Kind is the concrete type of the ssa Value. gotKind := "nil" if v != nil { gotKind = fmt.Sprintf("%T", v)[len("*ssa."):] } // fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging // Check the kinds match. // "nil" indicates expected failure (e.g. optimized away). if expKind != gotKind { t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind) } // Check the types match. // If wantAddr, the expected type is the object's address. if v != nil { expType := obj.Type() if wantAddr { expType = types.NewPointer(expType) if !gotAddr { t.Errorf("%s: got value, want address", prefix) } } else if gotAddr { t.Errorf("%s: got address, want value", prefix) } if !types.Identical(v.Type(), expType) { t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType) } } }
// matchType reports whether the two type ASTs denote identical types. func (tr *Transformer) matchType(x, y ast.Expr) bool { tx := tr.info.Types[x].Type ty := tr.info.Types[y].Type return types.Identical(tx, ty) }
// lintVarDecls examines variable declarations. It complains about declarations with // redundant LHS types that can be inferred from the RHS. func (f *file) lintVarDecls() { var lastGen *ast.GenDecl // last GenDecl entered. f.walk(func(node ast.Node) bool { switch v := node.(type) { case *ast.GenDecl: if v.Tok != token.CONST && v.Tok != token.VAR { return false } lastGen = v return true case *ast.ValueSpec: if lastGen.Tok == token.CONST { return false } if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 { return false } rhs := v.Values[0] // An underscore var appears in a common idiom for compile-time interface satisfaction, // as in "var _ Interface = (*Concrete)(nil)". if isIdent(v.Names[0], "_") { return false } // If the RHS is a zero value, suggest dropping it. zero := false if lit, ok := rhs.(*ast.BasicLit); ok { zero = zeroLiteral[lit.Value] } else if isIdent(rhs, "nil") { zero = true } if zero { f.errorf(rhs, 0.9, category("zero-value"), "should drop = %s from declaration of var %s; it is the zero value", f.render(rhs), v.Names[0]) return false } lhsTyp := f.pkg.typeOf(v.Type) rhsTyp := f.pkg.typeOf(rhs) if lhsTyp != nil && rhsTyp != nil && !types.Identical(lhsTyp, rhsTyp) { // Assignment to a different type is not redundant. return false } // The next three conditions are for suppressing the warning in situations // where we were unable to typecheck. // If the LHS type is an interface, don't warn, since it is probably a // concrete type on the RHS. Note that our feeble lexical check here // will only pick up interface{} and other literal interface types; // that covers most of the cases we care to exclude right now. if _, ok := v.Type.(*ast.InterfaceType); ok { return false } // If the RHS is an untyped const, only warn if the LHS type is its default type. if defType, ok := f.isUntypedConst(rhs); ok && !isIdent(v.Type, defType) { return false } // If the LHS is a known weaker type, and we couldn't type check both sides, // don't warn. if lhsTyp == nil || rhsTyp == nil { if knownWeakerTypes[f.render(v.Type)] { return false } } f.errorf(v.Type, 0.8, category("type-inference"), "should omit type %s from declaration of var %s; it will be inferred from the right-hand side", f.render(v.Type), v.Names[0]) return false } return true }) }
func (x rtype) eq(_ types.Type, y interface{}) bool { return types.Identical(x.t, y.(rtype).t) }
// nil-tolerant variant of types.Identical. func sameType(x, y types.Type) bool { if x == nil { return y == nil } return y != nil && types.Identical(x, y) }
func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) { c.SetPos(stmt.Pos()) stmt = filter.IncDecStmt(stmt, c.p.Info) stmt = filter.Assign(stmt, c.p.Info) switch s := stmt.(type) { case *ast.BlockStmt: c.translateStmtList(s.List) case *ast.IfStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } var caseClauses []ast.Stmt ifStmt := s for { caseClauses = append(caseClauses, &ast.CaseClause{List: []ast.Expr{ifStmt.Cond}, Body: ifStmt.Body.List}) switch elseStmt := ifStmt.Else.(type) { case *ast.IfStmt: if elseStmt.Init != nil { caseClauses = append(caseClauses, &ast.CaseClause{List: nil, Body: []ast.Stmt{elseStmt}}) break } ifStmt = elseStmt continue case *ast.BlockStmt: caseClauses = append(caseClauses, &ast.CaseClause{List: nil, Body: elseStmt.List}) case *ast.EmptyStmt, nil: // no else clause default: panic(fmt.Sprintf("Unhandled else: %T\n", elseStmt)) } break } c.translateBranchingStmt(caseClauses, false, nil, nil, nil, c.Flattened[s]) case *ast.SwitchStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } tag := s.Tag if tag == nil { tag = ast.NewIdent("true") c.p.Types[tag] = types.TypeAndValue{Type: types.Typ[types.Bool], Value: exact.MakeBool(true)} } if c.p.Types[tag].Value == nil { refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(tag)) tag = c.newIdent(refVar, c.p.Types[tag].Type) } translateCond := func(cond ast.Expr) *expression { return c.translateExpr(&ast.BinaryExpr{ X: tag, Op: token.EQL, Y: cond, }) } c.translateBranchingStmt(s.Body.List, true, translateCond, nil, label, c.Flattened[s]) case *ast.TypeSwitchStmt: if s.Init != nil { c.translateStmt(s.Init, nil) } refVar := c.newVariable("_ref") var expr ast.Expr var printCaseBodyPrefix func(index int) switch a := s.Assign.(type) { case *ast.AssignStmt: expr = a.Rhs[0].(*ast.TypeAssertExpr).X printCaseBodyPrefix = func(index int) { value := refVar caseClause := s.Body.List[index].(*ast.CaseClause) if len(caseClause.List) == 1 { t := c.p.Types[caseClause.List[0]].Type if _, isInterface := t.Underlying().(*types.Interface); !isInterface && !types.Identical(t, types.Typ[types.UntypedNil]) { value += ".$val" } } c.Printf("%s = %s;", c.objectName(c.p.Implicits[caseClause]), value) } 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.Types[cond].Type, types.Typ[types.UntypedNil]) { return c.formatExpr("%s === $ifaceNil", refVar) } return c.formatExpr("$assertType(%s, %s, true)[1]", refVar, c.typeName(c.p.Types[cond].Type)) } c.translateBranchingStmt(s.Body.List, true, translateCond, printCaseBodyPrefix, 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.Types[s.X].Type.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, iVar, types.Typ[types.Int], s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, 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, entryVar+".k", t.Key(), s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, 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, iVar, types.Typ[types.Int], s.Tok == token.DEFINE)) } if !isBlank(s.Value) { c.Printf("%s", c.translateAssign(s.Value, c.translateImplicitConversion(c.setType(&ast.IndexExpr{ X: c.newIdent(refVar, t), Index: c.newIdent(iVar, types.Typ[types.Int]), }, elemType), elemType).String(), 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 } c.Printf("return%s;", c.translateResults(results)) 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.Types[s.Call.Fun].Type.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.Types[arg].Type) } 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) } if s.Tok == token.DEFINE { for _, lhs := range s.Lhs { if !isBlank(lhs) { obj := c.p.Defs[lhs.(*ast.Ident)] if obj == nil { obj = c.p.Uses[lhs.(*ast.Ident)] } c.setType(lhs, obj.Type()) } } } 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]).String()) } return } lhsType := c.p.Types[s.Lhs[0]].Type c.Printf("%s", c.translateAssignOfExpr(lhs, s.Rhs[0], lhsType, s.Tok == token.DEFINE)) case len(s.Lhs) > 1 && len(s.Rhs) == 1: tupleVar := c.newVariable("_tuple") out := tupleVar + " = " + c.translateExpr(s.Rhs[0]).String() + ";" tuple := c.p.Types[s.Rhs[0]].Type.(*types.Tuple) for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { lhsType := c.p.Types[s.Lhs[i]].Type out += " " + c.translateAssignOfExpr(lhs, c.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), lhsType, s.Tok == token.DEFINE) } } c.Printf("%s", out) case len(s.Lhs) == len(s.Rhs): tmpVars := make([]string, len(s.Rhs)) var parts []string 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).String()) } continue } lhsType := c.p.Types[s.Lhs[i]].Type parts = append(parts, c.translateAssignOfExpr(c.newIdent(tmpVars[i], c.p.Types[s.Lhs[i]].Type), rhs, lhsType, true)) } for i, lhs := range s.Lhs { lhs = astutil.RemoveParens(lhs) if !isBlank(lhs) { t := c.p.Types[lhs].Type parts = append(parts, c.translateAssignOfExpr(lhs, c.newIdent(tmpVars[i], t), t, s.Tok == token.DEFINE)) } } c.Printf("%s", strings.Join(parts, " ")) 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 isTuple := false if len(rhs) == 1 { _, isTuple = c.p.Types[rhs[0]].Type.(*types.Tuple) } for len(rhs) < len(lhs) && !isTuple { rhs = append(rhs, nil) } 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.Types[s.Call.Fun].Type.Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid(), false), ", ")) case *ast.SendStmt: chanType := c.p.Types[s.Chan].Type.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, s.Value}, } c.Blocking[call] = true c.translateStmt(&ast.ExprStmt{X: call}, label) case *ast.SelectStmt: var channels []string var caseClauses []ast.Stmt flattened := false hasDefault := false for i, s := range s.Body.List { clause := s.(*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: channels = append(channels, c.formatExpr("[%e, %e]", comm.Chan, comm.Value).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: exact.MakeInt64(int64(i))} caseClauses = append(caseClauses, &ast.CaseClause{ List: []ast.Expr{indexLit}, Body: 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 selectionVar := c.newVariable("_selection") c.Printf("%s = %s;", selectionVar, c.translateExpr(selectCall)) translateCond := func(cond ast.Expr) *expression { return c.formatExpr("%s[0] === %e", selectionVar, cond) } printCaseBodyPrefix := func(index int) { if assign, ok := s.Body.List[index].(*ast.CommClause).Comm.(*ast.AssignStmt); ok { switch rhsType := c.p.Types[assign.Rhs[0]].Type.(type) { case *types.Tuple: c.translateStmt(&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1]", rhsType)}, Tok: assign.Tok}, nil) default: c.translateStmt(&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}, nil) } } } c.translateBranchingStmt(caseClauses, true, translateCond, printCaseBodyPrefix, label, flattened) case *ast.EmptyStmt: // skip default: panic(fmt.Sprintf("Unhandled statement: %T\n", s)) } }
// Like isTest, but checks the signature too. func isTestSig(f *Function, prefix string, sig *types.Signature) bool { return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig) }
// emitConv emits to f code to convert Value val to exactly type typ, // and returns the converted value. Implicit conversions are required // by language assignability rules in assignments, parameter passing, // etc. Conversions cannot fail dynamically. // func emitConv(f *Function, val Value, typ types.Type) Value { t_src := val.Type() // Identical types? Conversion is a no-op. if types.Identical(t_src, typ) { return val } ut_dst := typ.Underlying() ut_src := t_src.Underlying() // Just a change of type, but not value or representation? if isValuePreserving(ut_src, ut_dst) { c := &ChangeType{X: val} c.setType(typ) return f.emit(c) } // Conversion to, or construction of a value of, an interface type? if _, ok := ut_dst.(*types.Interface); ok { // Assignment from one interface type to another? if _, ok := ut_src.(*types.Interface); ok { c := &ChangeInterface{X: val} c.setType(typ) return f.emit(c) } // Untyped nil constant? Return interface-typed nil constant. if ut_src == tUntypedNil { return nilConst(typ) } // Convert (non-nil) "untyped" literals to their default type. if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 { val = emitConv(f, val, DefaultType(ut_src)) } f.Pkg.needMethodsOf(val.Type()) mi := &MakeInterface{X: val} mi.setType(typ) return f.emit(mi) } // Conversion of a compile-time constant value? if c, ok := val.(*Const); ok { if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() { // Conversion of a compile-time constant to // another constant type results in a new // constant of the destination type and // (initially) the same abstract value. // We don't truncate the value yet. return NewConst(c.Value, typ) } // We're converting from constant to non-constant type, // e.g. string -> []byte/[]rune. } // A representation-changing conversion? // At least one of {ut_src,ut_dst} must be *Basic. // (The other may be []byte or []rune.) _, ok1 := ut_src.(*types.Basic) _, ok2 := ut_dst.(*types.Basic) if ok1 || ok2 { c := &Convert{X: val} c.setType(typ) return f.emit(c) } panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ)) }
func (c *funcContext) translateBuiltin(name string, sig *types.Signature, args []ast.Expr, ellipsis bool) *expression { switch name { case "new": t := sig.Results().At(0).Type().(*types.Pointer) if c.p.Pkg.Path() == "syscall" && types.Identical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { return c.formatExpr("new Uint8Array(8)") } switch t.Elem().Underlying().(type) { case *types.Struct, *types.Array: return c.formatExpr("%e", c.zeroValue(t.Elem())) default: return c.formatExpr("$newDataPointer(%e, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch argType := c.p.TypeOf(args[0]).Underlying().(type) { case *types.Slice: t := c.typeName(c.p.TypeOf(args[0])) if len(args) == 3 { return c.formatExpr("$makeSlice(%s, %f, %f)", t, args[1], args[2]) } return c.formatExpr("$makeSlice(%s, %f)", t, args[1]) case *types.Map: return c.formatExpr("{}") case *types.Chan: length := "0" if len(args) == 2 { length = c.translateExpr(args[1]).String() } return c.formatExpr("new %s(%s)", c.typeName(c.p.TypeOf(args[0])), length) default: panic(fmt.Sprintf("Unhandled make type: %T\n", argType)) } case "len": switch argType := c.p.TypeOf(args[0]).Underlying().(type) { case *types.Basic: return c.formatExpr("%e.length", args[0]) case *types.Slice: return c.formatExpr("%e.$length", args[0]) case *types.Pointer: return c.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) case *types.Map: return c.formatExpr("$keys(%e).length", args[0]) case *types.Chan: return c.formatExpr("%e.$buffer.length", args[0]) // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": switch argType := c.p.TypeOf(args[0]).Underlying().(type) { case *types.Slice, *types.Chan: return c.formatExpr("%e.$capacity", args[0]) case *types.Pointer: return c.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len()) // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": return c.formatExpr("$panic(%s)", c.translateImplicitConversion(args[0], types.NewInterface(nil, nil))) case "append": if ellipsis || len(args) == 1 { argStr := c.translateArgs(sig, args, ellipsis, false) return c.formatExpr("$appendSlice(%s, %s)", argStr[0], argStr[1]) } sliceType := sig.Results().At(0).Type().Underlying().(*types.Slice) return c.formatExpr("$append(%e, %s)", args[0], strings.Join(c.translateExprSlice(args[1:], sliceType.Elem()), ", ")) case "delete": keyType := c.p.TypeOf(args[0]).Underlying().(*types.Map).Key() return c.formatExpr(`delete %e[%s.keyFor(%s)]`, args[0], c.typeName(keyType), c.translateImplicitConversion(args[1], keyType)) case "copy": if basic, isBasic := c.p.TypeOf(args[1]).Underlying().(*types.Basic); isBasic && isString(basic) { return c.formatExpr("$copyString(%e, %e)", args[0], args[1]) } return c.formatExpr("$copySlice(%e, %e)", args[0], args[1]) case "print", "println": return c.formatExpr("console.log(%s)", strings.Join(c.translateExprSlice(args, nil), ", ")) case "complex": argStr := c.translateArgs(sig, args, ellipsis, false) return c.formatExpr("new %s(%s, %s)", c.typeName(sig.Results().At(0).Type()), argStr[0], argStr[1]) case "real": return c.formatExpr("%e.$real", args[0]) case "imag": return c.formatExpr("%e.$imag", args[0]) case "recover": return c.formatExpr("$recover()") case "close": return c.formatExpr(`$close(%e)`, args[0]) default: panic(fmt.Sprintf("Unhandled builtin: %s\n", name)) } }
func (c *funcContext) translateConversion(expr ast.Expr, desiredType types.Type) *expression { exprType := c.p.TypeOf(expr) if types.Identical(exprType, desiredType) { return c.translateExpr(expr) } if c.p.Pkg.Path() == "reflect" { if call, isCall := expr.(*ast.CallExpr); isCall && types.Identical(c.p.TypeOf(call.Fun), types.Typ[types.UnsafePointer]) { if ptr, isPtr := desiredType.(*types.Pointer); isPtr { if named, isNamed := ptr.Elem().(*types.Named); isNamed { switch named.Obj().Name() { case "arrayType", "chanType", "funcType", "interfaceType", "mapType", "ptrType", "sliceType", "structType": return c.formatExpr("%e.kindType", call.Args[0]) // unsafe conversion default: return c.translateExpr(expr) } } } } } switch t := desiredType.Underlying().(type) { case *types.Basic: switch { case isInteger(t): basicExprType := exprType.Underlying().(*types.Basic) switch { case is64Bit(t): if !is64Bit(basicExprType) { if basicExprType.Kind() == types.Uintptr { // this might be an Object returned from reflect.Value.Pointer() return c.formatExpr("new %1s(0, %2e.constructor === Number ? %2e : 1)", c.typeName(desiredType), expr) } return c.formatExpr("new %s(0, %e)", c.typeName(desiredType), expr) } return c.formatExpr("new %1s(%2h, %2l)", c.typeName(desiredType), expr) case is64Bit(basicExprType): if !isUnsigned(t) && !isUnsigned(basicExprType) { return c.fixNumber(c.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), t) } return c.fixNumber(c.formatExpr("%s.$low", c.translateExpr(expr)), t) case isFloat(basicExprType): return c.formatParenExpr("%e >> 0", expr) case types.Identical(exprType, types.Typ[types.UnsafePointer]): return c.translateExpr(expr) default: return c.fixNumber(c.translateExpr(expr), t) } case isFloat(t): if t.Kind() == types.Float32 && exprType.Underlying().(*types.Basic).Kind() == types.Float64 { return c.formatExpr("$fround(%e)", expr) } return c.formatExpr("%f", expr) case isComplex(t): return c.formatExpr("new %1s(%2r, %2i)", c.typeName(desiredType), expr) case isString(t): value := c.translateExpr(expr) switch et := exprType.Underlying().(type) { case *types.Basic: if is64Bit(et) { value = c.formatExpr("%s.$low", value) } if isNumeric(et) { return c.formatExpr("$encodeRune(%s)", value) } return value case *types.Slice: if types.Identical(et.Elem().Underlying(), types.Typ[types.Rune]) { return c.formatExpr("$runesToString(%s)", value) } return c.formatExpr("$bytesToString(%s)", value) default: panic(fmt.Sprintf("Unhandled conversion: %v\n", et)) } case t.Kind() == types.UnsafePointer: if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND { if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr { return c.formatExpr("$sliceToArray(%s)", c.translateConversionToSlice(indexExpr.X, types.NewSlice(types.Typ[types.Uint8]))) } if ident, isIdent := unary.X.(*ast.Ident); isIdent && ident.Name == "_zero" { return c.formatExpr("new Uint8Array(0)") } } if ptr, isPtr := c.p.TypeOf(expr).(*types.Pointer); c.p.Pkg.Path() == "syscall" && isPtr { if s, isStruct := ptr.Elem().Underlying().(*types.Struct); isStruct { array := c.newVariable("_array") target := c.newVariable("_struct") c.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s)) c.Delayed(func() { c.Printf("%s = %s, %s;", target, c.translateExpr(expr), c.loadStruct(array, target, s)) }) return c.formatExpr("%s", array) } } if call, ok := expr.(*ast.CallExpr); ok { if id, ok := call.Fun.(*ast.Ident); ok && id.Name == "new" { return c.formatExpr("new Uint8Array(%d)", int(sizes32.Sizeof(c.p.TypeOf(call.Args[0])))) } } } case *types.Slice: switch et := exprType.Underlying().(type) { case *types.Basic: if isString(et) { if types.Identical(t.Elem().Underlying(), types.Typ[types.Rune]) { return c.formatExpr("new %s($stringToRunes(%e))", c.typeName(desiredType), expr) } return c.formatExpr("new %s($stringToBytes(%e))", c.typeName(desiredType), expr) } case *types.Array, *types.Pointer: return c.formatExpr("new %s(%e)", c.typeName(desiredType), expr) } case *types.Pointer: if s, isStruct := t.Elem().Underlying().(*types.Struct); isStruct { if c.p.Pkg.Path() == "syscall" && types.Identical(exprType, types.Typ[types.UnsafePointer]) { array := c.newVariable("_array") target := c.newVariable("_struct") return c.formatExpr("(%s = %e, %s = %e, %s, %s)", array, expr, target, c.zeroValue(t.Elem()), c.loadStruct(array, target, s), target) } return c.formatExpr("$pointerOfStructConversion(%e, %s)", expr, c.typeName(t)) } if !types.Identical(exprType, types.Typ[types.UnsafePointer]) { exprTypeElem := exprType.Underlying().(*types.Pointer).Elem() ptrVar := c.newVariable("_ptr") getterConv := c.translateConversion(c.setType(&ast.StarExpr{X: c.newIdent(ptrVar, exprType)}, exprTypeElem), t.Elem()) setterConv := c.translateConversion(c.newIdent("$v", t.Elem()), exprTypeElem) return c.formatExpr("(%1s = %2e, new %3s(function() { return %4s; }, function($v) { %1s.$set(%5s); }, %1s.$target))", ptrVar, expr, c.typeName(desiredType), getterConv, setterConv) } case *types.Interface: if types.Identical(exprType, types.Typ[types.UnsafePointer]) { return c.translateExpr(expr) } } return c.translateImplicitConversionWithCloning(expr, desiredType) }