// Signature = Parameters [ Result ] . // Result = Type | Parameters . // func (p *parser) parseSignature(recv *types.Var) *types.Signature { params, isVariadic := p.parseParameters() // optional result type var results []*types.Var if p.tok == '(' { var variadic bool results, variadic = p.parseParameters() if variadic { p.error("... not permitted on result type") } } return types.NewSignature(nil, recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) }
func (c *compiler) VisitFuncDecl(f *ast.FuncDecl) Value { fn := c.Resolve(f.Name).(*LLVMValue) attributes := parseAttributes(f.Doc) for _, attr := range attributes { attr.Apply(fn) } if f.Body == nil { return fn } var paramVars []*types.Var ftyp := fn.Type().(*types.Signature) if recv := ftyp.Recv(); recv != nil { paramVars = append(paramVars, recv) } if ftyp.Params != nil { ftyp.Params().ForEach(func(p *types.Var) { paramVars = append(paramVars, p) }) } paramVarsTuple := types.NewTuple(paramVars...) c.buildFunction(fn, nil, paramVarsTuple, ftyp.Results(), f.Body, ftyp.IsVariadic()) if f.Recv == nil && f.Name.Name == "init" { // Is it an 'init' function? Then record it. fnptr := llvm.ConstExtractValue(fn.value, []uint32{0}) c.initfuncs = append(c.initfuncs, fnptr) } return fn }
// makeLen returns the len builtin specialized to type func(T)int. func makeLen(T types.Type) *Builtin { lenParams := types.NewTuple(anonVar(T)) return &Builtin{ name: "len", sig: types.NewSignature(nil, nil, lenParams, lenResults, false), } }
// makeLen returns the len builtin specialized to type func(T)int. func makeLen(T types.Type) *Builtin { lenParams := types.NewTuple(newVar("", T)) return &Builtin{ object: lenObject, sig: types.NewSignature(nil, nil, lenParams, lenResults, false), } }
func (p *importer) tuple() *types.Tuple { vars := make([]*types.Var, p.int()) for i := range vars { vars[i] = p.param() } return types.NewTuple(vars...) }
func (tm *LLVMTypeMap) funcLLVMType(tstr string, f *types.Signature) llvm.Type { typ, ok := tm.types[tstr] if !ok { // If there's a receiver change the receiver to an // additional (first) parameter, and take the value of // the resulting signature instead. var param_types []llvm.Type if recv := f.Recv(); recv != nil { params := f.Params() paramvars := make([]*types.Var, int(params.Len()+1)) paramvars[0] = recv for i := 0; i < int(params.Len()); i++ { paramvars[i+1] = params.At(i) } params = types.NewTuple(paramvars...) f := types.NewSignature(nil, params, f.Results(), f.IsVariadic()) return tm.ToLLVM(f) } typ = llvm.GlobalContext().StructCreateNamed("") tm.types[tstr] = typ params := f.Params() nparams := int(params.Len()) for i := 0; i < nparams; i++ { typ := params.At(i).Type() if f.IsVariadic() && i == nparams-1 { typ = types.NewSlice(typ) } llvmtyp := tm.ToLLVM(typ) param_types = append(param_types, llvmtyp) } var return_type llvm.Type results := f.Results() switch nresults := int(results.Len()); nresults { case 0: return_type = llvm.VoidType() case 1: return_type = tm.ToLLVM(results.At(0).Type()) default: elements := make([]llvm.Type, nresults) for i := range elements { result := results.At(i) elements[i] = tm.ToLLVM(result.Type()) } return_type = llvm.StructType(elements, false) } fntyp := llvm.FunctionType(return_type, param_types, false) fnptrtyp := llvm.PointerType(fntyp, 0) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) elements := []llvm.Type{fnptrtyp, i8ptr} // func, closure typ.StructSetBody(elements, false) } return typ }
// changeRecv returns sig with Recv prepended to Params(). func changeRecv(sig *types.Signature) *types.Signature { params := sig.Params() n := params.Len() p2 := make([]*types.Var, n+1) p2[0] = sig.Recv() for i := 0; i < n; i++ { p2[i+1] = params.At(i) } return types.NewSignature(nil, nil, types.NewTuple(p2...), sig.Results(), sig.IsVariadic()) }
func (tm *llvmTypeMap) funcLLVMType(f *types.Signature, name string) llvm.Type { // If there's a receiver change the receiver to an // additional (first) parameter, and take the value of // the resulting signature instead. if recv := f.Recv(); recv != nil { params := f.Params() paramvars := make([]*types.Var, int(params.Len()+1)) paramvars[0] = recv for i := 0; i < int(params.Len()); i++ { paramvars[i+1] = params.At(i) } params = types.NewTuple(paramvars...) f := types.NewSignature(nil, nil, params, f.Results(), f.Variadic()) return tm.toLLVM(f, name) } if typ, ok := tm.types.At(f).(llvm.Type); ok { return typ } typ := llvm.GlobalContext().StructCreateNamed(name) tm.types.Set(f, typ) params := f.Params() param_types := make([]llvm.Type, params.Len()) for i := range param_types { llvmtyp := tm.ToLLVM(params.At(i).Type()) param_types[i] = llvmtyp } var return_type llvm.Type results := f.Results() switch nresults := int(results.Len()); nresults { case 0: return_type = llvm.VoidType() case 1: return_type = tm.ToLLVM(results.At(0).Type()) default: elements := make([]llvm.Type, nresults) for i := range elements { result := results.At(i) elements[i] = tm.ToLLVM(result.Type()) } return_type = llvm.StructType(elements, false) } fntyp := llvm.FunctionType(return_type, param_types, false) fnptrtyp := llvm.PointerType(fntyp, 0) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) elements := []llvm.Type{fnptrtyp, i8ptr} // func, closure typ.StructSetBody(elements, false) return typ }
// ResultList = Type | ParamList . func (p *parser) parseResultList(pkg *types.Package) *types.Tuple { switch p.tok { case '<': return types.NewTuple(types.NewParam(token.NoPos, pkg, "", p.parseType(pkg))) case '(': params, _ := p.parseParamList(pkg) return params default: return nil } }
// emitTypeTest emits to f a type test value,ok := x.(t) and returns // a (value, ok) tuple. x.Type() must be an interface. // func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value { a := &TypeAssert{ X: x, AssertedType: t, CommaOk: true, } a.setPos(pos) a.setType(types.NewTuple( types.NewVar(token.NoPos, nil, "value", t), varOk, )) return f.emit(a) }
// emitTypeTest emits to f a type test value,ok := x.(t) and returns // a (value, ok) tuple. x.Type() must be an interface. // func emitTypeTest(f *Function, x Value, t types.Type) Value { // TODO(adonovan): opt: simplify infallible tests as per // emitTypeAssert, and return (x, vTrue). // (Requires that exprN returns a slice of extracted values, // not a single Value of type *types.Results.) a := &TypeAssert{ X: x, AssertedType: t, CommaOk: true, } a.setType(types.NewTuple( types.NewVar(nil, "value", t), varOk, )) return f.emit(a) }
func (m *TypeMap) descriptorSignature(t *types.Signature, name string) TypeDebugDescriptor { // If there's a receiver change the receiver to an // additional (first) parameter, and take the value of // the resulting signature instead. if recv := t.Recv(); recv != nil { params := t.Params() paramvars := make([]*types.Var, int(params.Len()+1)) paramvars[0] = recv for i := 0; i < int(params.Len()); i++ { paramvars[i+1] = params.At(i) } params = types.NewTuple(paramvars...) t := types.NewSignature(nil, nil, params, t.Results(), t.Variadic()) return m.typeDebugDescriptor(t, name) } if dt, ok := m.m.At(t).(TypeDebugDescriptor); ok { return dt } var returnType DebugDescriptor var paramTypes []DebugDescriptor if results := t.Results(); results.Len() == 1 { returnType = m.TypeDebugDescriptor(results.At(0).Type()) } else if results != nil { fields := make([]DebugDescriptor, results.Len()) for i := range fields { fields[i] = m.TypeDebugDescriptor(results.At(i).Type()) } returnType = NewStructCompositeType(fields) } if params := t.Params(); params != nil && params.Len() > 0 { paramTypes = make([]DebugDescriptor, params.Len()) for i := range paramTypes { paramTypes[i] = m.TypeDebugDescriptor(params.At(i).Type()) } } ct := NewStructCompositeType([]DebugDescriptor{ NewSubroutineCompositeType(returnType, paramTypes), m.TypeDebugDescriptor(types.NewPointer(types.Typ[types.Uint8])), }) ct.Name = name m.m.Set(t, ct) return ct }
// ParamList = "(" [ { Parameter "," } Parameter ] ")" . func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) { var list []*types.Var isVariadic := false p.expect('(') for p.tok != ')' && p.tok != scanner.EOF { if len(list) > 0 { p.expect(',') } par, variadic := p.parseParam(pkg) list = append(list, par) if variadic { if isVariadic { p.error("... not on final argument") } isVariadic = true } } p.expect(')') return types.NewTuple(list...), isVariadic }
func (c *compiler) VisitFuncDecl(f *ast.FuncDecl) Value { fn := c.Resolve(f.Name).(*LLVMValue) attributes := parseAttributes(f.Doc) for _, attr := range attributes { attr.Apply(fn) } if f.Body == nil { return fn } var paramVars []*types.Var ftyp := fn.Type().(*types.Signature) if recv := ftyp.Recv(); recv != nil { paramVars = append(paramVars, recv) } if ftyp.Params() != nil { for i := 0; i < ftyp.Params().Len(); i++ { p := ftyp.Params().At(i) paramVars = append(paramVars, p) } } c.pushDebugContext(c.createFunctionMetadata(f, fn)) defer c.popDebugContext() c.setDebugLine(f.Pos()) paramVarsTuple := types.NewTuple(paramVars...) c.buildFunction(fn, nil, paramVarsTuple, ftyp.Results(), f.Body) if f.Recv == nil && f.Name.Name == "init" { // Is it an 'init' function? Then record it. fnptr := llvm.ConstExtractValue(fn.value, []uint32{0}) c.initfuncs = append(c.initfuncs, fnptr) } return fn }
// makeWrapper returns a synthetic method that delegates to the // declared method denoted by meth.Obj(), first performing any // necessary pointer indirections or field selections implied by meth. // // The resulting method's receiver type is meth.Recv(). // // This function is versatile but quite subtle! Consider the // following axes of variation when making changes: // - optional receiver indirection // - optional implicit field selections // - meth.Obj() may denote a concrete or an interface method // - the result may be a thunk or a wrapper. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func makeWrapper(prog *Program, meth *types.Selection) *Function { obj := meth.Obj().(*types.Func) // the declared function sig := meth.Type().(*types.Signature) // type of this wrapper var recv *types.Var // wrapper's receiver or thunk's params[0] name := obj.Name() var description string var start int // first regular param if meth.Kind() == types.MethodExpr { name += "$thunk" description = "thunk" recv = sig.Params().At(0) start = 1 } else { description = "wrapper" recv = sig.Recv() } description = fmt.Sprintf("%s for %s", description, meth.Obj()) if prog.mode&LogSource != 0 { defer logStack("make %s to (%s)", description, recv.Type())() } fn := &Function{ name: name, method: meth, object: obj, Signature: sig, Synthetic: description, Prog: prog, pos: obj.Pos(), } fn.startBody() fn.addSpilledParam(recv) createParams(fn, start) indices := meth.Index() var v Value = fn.Locals[0] // spilled receiver if isPointer(meth.Recv()) { v = emitLoad(fn, v) // For simple indirection wrappers, perform an informative nil-check: // "value method (T).f called using nil *T pointer" if len(indices) == 1 && !isPointer(recvType(obj)) { var c Call c.Call.Value = &Builtin{ name: "ssa:wrapnilchk", sig: types.NewSignature(nil, nil, types.NewTuple(anonVar(meth.Recv()), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(meth.Recv())), false), } c.Call.Args = []Value{ v, stringConst(deref(meth.Recv()).String()), stringConst(meth.Obj().Name()), } c.setType(v.Type()) v = fn.emit(&c) } } // Invariant: v is a pointer, either // value of *A receiver param, or // address of A spilled receiver. // We use pointer arithmetic (FieldAddr possibly followed by // Load) in preference to value extraction (Field possibly // preceded by Load). v = emitImplicitSelections(fn, v, indices[:len(indices)-1]) // Invariant: v is a pointer, either // value of implicit *C field, or // address of implicit C field. var c Call if r := recvType(obj); !isInterface(r) { // concrete method if !isPointer(r) { v = emitLoad(fn, v) } c.Call.Value = prog.declaredFunc(obj) c.Call.Args = append(c.Call.Args, v) } else { c.Call.Method = obj c.Call.Value = emitLoad(fn, v) } for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() return fn }
if call, ok := instr.(*Call); ok { if blt, ok := call.Call.Value.(*Builtin); ok { if blt.Name() == "recover" { return true } } } } } return false } // newVar creates a 'var' for use in a types.Tuple. func newVar(name string, typ types.Type) *types.Var { return types.NewParam(token.NoPos, nil, name, typ) } var ( lenObject = types.Universe.Lookup("len").(*types.Builtin) lenResults = types.NewTuple(newVar("", tInt)) ) // makeLen returns the len builtin specialized to type func(T)int. func makeLen(T types.Type) *Builtin { lenParams := types.NewTuple(newVar("", T)) return &Builtin{ object: lenObject, sig: types.NewSignature(nil, nil, lenParams, lenResults, false), } }
// prepareCall returns the evaluated function and arguments. // // For builtins that may not be used in go/defer, prepareCall // will emits inline code. In this case, prepareCall returns // nil for fn and args, and returns a non-nil value for result. func (fr *frame) prepareCall(instr ssa.CallInstruction) (fn *LLVMValue, args []*LLVMValue, result *LLVMValue) { call := instr.Common() args = make([]*LLVMValue, len(call.Args)) for i, arg := range call.Args { args[i] = fr.value(arg) } if call.IsInvoke() { fn := fr.interfaceMethod(fr.value(call.Value), call.Method) return fn, args, nil } switch v := call.Value.(type) { case *ssa.Builtin: // handled below case *ssa.Function: // Function handled specially; value() will convert // a function to one with a context argument. fn = fr.resolveFunction(v) pair := llvm.ConstNull(fr.llvmtypes.ToLLVM(fn.Type())) pair = llvm.ConstInsertValue(pair, fn.LLVMValue(), []uint32{0}) fn = fr.NewValue(pair, fn.Type()) return fn, args, nil default: fn = fr.value(call.Value) return fn, args, nil } // Builtins may only be used in calls (i.e. can't be assigned), // and only print[ln], panic and recover may be used in go/defer. builtin := call.Value.(*ssa.Builtin) switch builtin.Name() { case "print", "println": // print/println generates a call-site specific anonymous // function to print the values. It's not inline because // print/println may be deferred. params := make([]*types.Var, len(call.Args)) for i, arg := range call.Args { // make sure to use args[i].Type(), not call.Args[i].Type(), // as the evaluated expression converts untyped. params[i] = types.NewParam(arg.Pos(), nil, arg.Name(), args[i].Type()) } sig := types.NewSignature(nil, nil, types.NewTuple(params...), nil, false) llfntyp := fr.llvmtypes.ToLLVM(sig) llfnptr := llvm.AddFunction(fr.module.Module, "", llfntyp.StructElementTypes()[0].ElementType()) currBlock := fr.builder.GetInsertBlock() entry := llvm.AddBasicBlock(llfnptr, "entry") fr.builder.SetInsertPointAtEnd(entry) internalArgs := make([]Value, len(args)) for i, arg := range args { internalArgs[i] = fr.NewValue(llfnptr.Param(i), arg.Type()) } fr.printValues(builtin.Name() == "println", internalArgs...) fr.builder.CreateRetVoid() fr.builder.SetInsertPointAtEnd(currBlock) return fr.NewValue(llfnptr, sig), args, nil case "panic": panic("TODO: panic") case "recover": // TODO(axw) determine number of frames to skip in pc check indirect := fr.NewValue(llvm.ConstNull(llvm.Int32Type()), types.Typ[types.Int32]) return fr.runtime.recover_, []*LLVMValue{indirect}, nil case "append": return nil, nil, fr.callAppend(args[0], args[1]) case "close": return fr.runtime.chanclose, args, nil case "cap": return nil, nil, fr.callCap(args[0]) case "len": return nil, nil, fr.callLen(args[0]) case "copy": return nil, nil, fr.callCopy(args[0], args[1]) case "delete": fr.callDelete(args[0], args[1]) return nil, nil, nil case "real": return nil, nil, args[0].extractComplexComponent(0) case "imag": return nil, nil, args[0].extractComplexComponent(1) case "complex": r := args[0].LLVMValue() i := args[1].LLVMValue() typ := instr.Value().Type() cmplx := llvm.Undef(fr.llvmtypes.ToLLVM(typ)) cmplx = fr.builder.CreateInsertValue(cmplx, r, 0, "") cmplx = fr.builder.CreateInsertValue(cmplx, i, 1, "") return nil, nil, fr.NewValue(cmplx, typ) default: panic("unimplemented: " + builtin.Name()) } }
func (c *funcContext) translateExpr(expr ast.Expr) *expression { exprType := c.p.info.Types[expr].Type if value := c.p.info.Types[expr].Value; value != nil { basic := types.Typ[types.String] if value.Kind() != exact.String { // workaround for bug in go/types basic = exprType.Underlying().(*types.Basic) } switch { case basic.Info()&types.IsBoolean != 0: return c.formatExpr("%s", strconv.FormatBool(exact.BoolVal(value))) case basic.Info()&types.IsInteger != 0: if is64Bit(basic) { d, _ := exact.Uint64Val(value) if basic.Kind() == types.Int64 { return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(int64(d)>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, _ := exact.Int64Val(value) return c.formatExpr("%s", strconv.FormatInt(d, 10)) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case basic.Info()&types.IsComplex != 0: r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.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 basic.Info()&types.IsString != 0: return c.formatExpr("%s", encodeString(exact.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { elements := make([]string, 0) i := 0 zero := c.zeroValue(elementType) for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, _ := exact.Int64Val(c.p.info.Types[kve.Key].Value) 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", c.zeroValue(t)) } zero := c.zeroValue(t.Elem()) 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: mapVar := c.newVariable("_map") keyVar := c.newVariable("_key") assignments := "" for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) assignments += c.formatExpr(`%s = %s, %s[%s] = { k: %s, v: %s }, `, keyVar, c.translateImplicitConversion(kve.Key, t.Key()), mapVar, c.makeKey(c.newIdent(keyVar, t.Key()), t.Key()), keyVar, c.translateImplicitConversion(kve.Value, t.Elem())).String() } return c.formatExpr("(%s = new $Map(), %s%s)", mapVar, assignments, mapVar) 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.translateImplicitConversion(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.zeroValue(t.Field(i).Type()) } 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.translateImplicitConversion(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: innerContext := c.p.analyzeFunction(exprType.(*types.Signature), e.Body) params, body := innerContext.translateFunction(e.Type, e.Body.List, c.allVars) 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.objectVars[obj]) } list := strings.Join(names, ", ") return c.formatExpr("(function(%s) { return function(%s) {\n%s%s}; })(%s)", list, strings.Join(params, ", "), string(body), strings.Repeat("\t", c.p.indentation), list) } return c.formatExpr("(function(%s) {\n%s%s})", strings.Join(params, ", "), string(body), strings.Repeat("\t", c.p.indentation)) case *ast.UnaryExpr: t := c.p.info.Types[e.X].Type switch e.Op { case token.AND: switch t.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } switch x := removeParens(e.X).(type) { case *ast.CompositeLit: return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.info.Types[e].Type)) case *ast.Ident: if obj := c.p.info.Uses[x]; c.p.escapingVars[obj] { return c.formatExpr("new %s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %s)", c.typeName(exprType), c.p.objectVars[obj]) } return c.formatExpr("new %s(function() { return %e; }, function($v) { %s })", c.typeName(exprType), x, c.translateAssign(x, "$v", exprType, false)) case *ast.SelectorExpr: newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.info.Types[x.X].Type), Sel: x.Sel} c.p.info.Selections[newSel] = c.p.info.Selections[x] return c.formatExpr("new %s(function() { return %e; }, function($v) { %s }, %e)", c.typeName(exprType), newSel, c.translateAssign(newSel, "$v", exprType, false), x.X) case *ast.IndexExpr: newIndex := &ast.IndexExpr{X: c.newIdent("this.$target", c.p.info.Types[x.X].Type), Index: x.Index} return c.formatExpr("new %s(function() { return %e; }, function($v) { %s }, %e)", c.typeName(exprType), newIndex, c.translateAssign(newIndex, "$v", exprType, false), x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ Fun: c.newIdent("$recv", types.NewSignature(nil, 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 basic.Info()&types.IsComplex != 0: return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case basic.Info()&types.IsUnsigned != 0: 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: x := c.translateExpr(e.X) if x.String() == "true" { return c.formatExpr("false") } if x.String() == "false" { return c.formatExpr("true") } return c.formatExpr("!%s", 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.info.Types[e.X].Type t2 := c.p.info.Types[e.Y].Type _, isInterface := t2.Underlying().(*types.Interface) if isInterface { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsNumeric != 0 { 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 basic.Info()&types.IsComplex != 0 { switch e.Op { case token.EQL: if basic.Kind() == types.Complex64 { return c.formatExpr("($float32IsEqual(%1r, %2r) && $float32IsEqual(%1i, %2i))", e.X, e.Y) } 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: if basic.Kind() == types.Float32 { return c.formatParenExpr("$float32IsEqual(%e, %e)", e.X, e.Y) } 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: if basic.Info()&types.IsInteger != 0 { return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) } return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.MUL: switch basic.Kind() { case types.Int32, types.Int: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >> 0) + (%1e << 16 >>> 16) * %2e) >> 0", e.X, e.Y) case types.Uint32, types.Uint, types.Uintptr: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >>> 0) + (%1e << 16 >>> 16) * %2e) >>> 0", e.X, e.Y) case types.Float32, types.Float64: return c.formatExpr("%e * %e", e.X, e.Y) default: return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) } case token.QUO: if basic.Info()&types.IsInteger != 0 { // cut off decimals shift := ">>" if basic.Info()&types.IsUnsigned != 0 { 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) } 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 && basic.Info()&types.IsUnsigned != 0 { op = ">>>" } if c.p.info.Types[e.Y].Value != nil { return c.fixNumber(c.formatExpr("%e %s %e", e.X, op, e.Y), basic) } if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 { return c.fixNumber(c.formatParenExpr("%e >> $min(%e, 31)", e.X, e.Y), basic) } y := c.newVariable("y") return c.fixNumber(c.formatExpr("(%s = %s, %s < 32 ? (%e %s %s) : 0)", y, c.translateImplicitConversion(e.Y, types.Typ[types.Uint]), y, e.X, op, y), basic) case token.AND, token.OR: if basic.Info()&types.IsUnsigned != 0 { 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.formatParenExpr("%e & ~%e", e.X, e.Y) 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: x := c.translateExpr(e.X) y := c.translateExpr(e.Y) if x.String() == "false" { return c.formatExpr("false") } return c.formatExpr("%s && %s", x, y) case token.LOR: x := c.translateExpr(e.X) y := c.translateExpr(e.Y) if x.String() == "true" { return c.formatExpr("true") } return c.formatExpr("%s || %s", x, 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: if isJsObject(t) { return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Pointer: xUnary, xIsUnary := e.X.(*ast.UnaryExpr) yUnary, yIsUnary := e.Y.(*ast.UnaryExpr) if xIsUnary && xUnary.Op == token.AND && yIsUnary && yUnary.Op == token.AND { xIndex, xIsIndex := xUnary.X.(*ast.IndexExpr) yIndex, yIsIndex := yUnary.X.(*ast.IndexExpr) if xIsIndex && yIsIndex { return c.formatExpr("$sliceIsEqual(%e, %f, %e, %f)", xIndex.X, xIndex.Index, yIndex.X, yIndex.Index) } } switch u.Elem().Underlying().(type) { case *types.Struct, *types.Interface: return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Array: return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) default: return c.formatExpr("$pointerIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } default: return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) } default: panic(e.Op) } case *ast.ParenExpr: x := c.translateExpr(e.X) if x.String() == "true" || x.String() == "false" { return x } return c.formatParenExpr("%s", x) case *ast.IndexExpr: switch t := c.p.info.Types[e.X].Type.Underlying().(type) { case *types.Array, *types.Pointer: if c.p.info.Types[e.Index].Value != nil { return c.formatExpr("%e[%f]", e.X, e.Index) } return c.formatExpr(`((%2f < 0 || %2f >= %1e.length) ? $throwRuntimeError("index out of range") : %1e[%2f])`, e.X, e.Index) case *types.Slice: return c.formatExpr(`((%2f < 0 || %2f >= %1e.$length) ? $throwRuntimeError("index out of range") : %1e.$array[%1e.$offset + %2f])`, e.X, e.Index) case *types.Map: key := c.makeKey(e.Index, t.Key()) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4s, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4s)`, 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.info.Types[e.X].Type.Underlying().(*types.Basic); isBasic && b.Info()&types.IsString != 0 { 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.info.Selections[e] if !ok { // qualified identifier obj := c.p.info.Uses[e.Sel] if isJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": return c.formatExpr("$global") case "This": if len(c.flattened) != 0 { return c.formatExpr("$this") } return c.formatExpr("this") case "Arguments": args := "arguments" if len(c.flattened) != 0 { args = "$args" } return c.formatExpr(`new ($sliceType(%s.Object))($global.Array.prototype.slice.call(%s, []))`, c.p.pkgVars["github.com/gopherjs/gopherjs/js"], args) case "Module": return c.formatExpr("$module") default: panic("Invalid js package object: " + obj.Name()) } } return c.formatExpr("%s", c.objectName(obj)) } parameterName := func(v *types.Var) string { if v.Anonymous() || v.Name() == "" { return c.newVariable("param") } return c.newVariable(v.Name()) } makeParametersList := func() []string { params := sel.Obj().Type().(*types.Signature).Params() names := make([]string, params.Len()) for i := 0; i < params.Len(); i++ { names[i] = parameterName(params.At(i)) } return names } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel) 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: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } parameters := makeParametersList() target := c.translateExpr(e.X) if isWrapped(sel.Recv()) { target = c.formatParenExpr("new %s(%s)", c.typeName(sel.Recv()), target) } recv := c.newVariable("_recv") return c.formatExpr("(%s = %s, function(%s) { $stackDepthOffset--; try { return %s.%s(%s); } finally { $stackDepthOffset++; } })", recv, target, strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", ")) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } recv := "recv" if isWrapped(sel.Recv()) { recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv())) } parameters := makeParametersList() return c.formatExpr("(function(%s) { $stackDepthOffset--; try { return %s.%s(%s); } finally { $stackDepthOffset++; } })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", ")) } panic("") case *ast.CallExpr: plainFun := e.Fun for { if p, isParen := plainFun.(*ast.ParenExpr); isParen { plainFun = p.X continue } break } var isType func(ast.Expr) bool isType = func(expr ast.Expr) bool { switch e := expr.(type) { case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType: return true case *ast.StarExpr: return isType(e.X) case *ast.Ident: _, ok := c.p.info.Uses[e].(*types.TypeName) return ok case *ast.SelectorExpr: _, ok := c.p.info.Uses[e.Sel].(*types.TypeName) return ok case *ast.ParenExpr: return isType(e.X) default: return false } } if isType(plainFun) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.info.Types[plainFun].Type)) } var fun *expression switch f := plainFun.(type) { case *ast.Ident: if o, ok := c.p.info.Uses[f].(*types.Builtin); ok { return c.translateBuiltin(o.Name(), e.Args, e.Ellipsis.IsValid(), exprType) } fun = c.translateExpr(plainFun) case *ast.SelectorExpr: sel, ok := c.p.info.Selections[f] if !ok { // qualified identifier obj := c.p.info.Uses[f.Sel] if isJsPackage(obj.Pkg()) { switch obj.Name() { case "InternalObject": return c.translateExpr(e.Args[0]) } } fun = c.translateExpr(f) break } externalizeExpr := func(e ast.Expr) string { t := c.p.info.Types[e].Type 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: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } methodName := sel.Obj().Name() if reservedKeywords[methodName] { methodName += "$" } recvType := sel.Recv() _, isPointer := recvType.Underlying().(*types.Pointer) methodsRecvType := sel.Obj().Type().(*types.Signature).Recv().Type() _, pointerExpected := methodsRecvType.(*types.Pointer) var recv *expression switch { case !isPointer && pointerExpected: recv = c.translateExpr(c.setType(&ast.UnaryExpr{Op: token.AND, X: f.X}, methodsRecvType)) default: recv = c.translateExpr(f.X) } for _, index := range sel.Index()[:len(sel.Index())-1] { if ptr, isPtr := recvType.(*types.Pointer); isPtr { recvType = ptr.Elem() } s := recvType.Underlying().(*types.Struct) recv = c.formatExpr("%s.%s", recv, fieldName(s, index)) recvType = s.Field(index).Type() } if isJsPackage(sel.Obj().Pkg()) { globalRef := func(id string) string { if recv.String() == "$global" && id[0] == '$' { 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 "Str": 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 case "IsUndefined": return c.formatParenExpr("%s === undefined", recv) case "IsNull": return c.formatParenExpr("%s === null", recv) default: panic("Invalid js package object: " + sel.Obj().Name()) } } if isWrapped(methodsRecvType) { fun = c.formatExpr("(new %s(%s)).%s", c.typeName(methodsRecvType), recv, methodName) break } fun = c.formatExpr("%s.%s", recv, methodName) case types.FieldVal: fields, jsTag := c.translateSelection(sel) if jsTag != "" { sig := sel.Type().(*types.Signature) return c.internalize(c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)), sig.Results().At(0).Type()) } fun = c.formatExpr("%e.%s", f.X, strings.Join(fields, ".")) case types.MethodExpr: fun = c.translateExpr(f) default: panic("") } default: fun = c.translateExpr(plainFun) } sig := c.p.info.Types[plainFun].Type.Underlying().(*types.Signature) if len(e.Args) == 1 { if tuple, isTuple := c.p.info.Types[e.Args[0]].Type.(*types.Tuple); isTuple { tupleVar := c.newVariable("_tuple") args := make([]ast.Expr, tuple.Len()) for i := range args { args[i] = c.newIdent(c.formatExpr("%s[%d]", tupleVar, i).String(), tuple.At(i).Type()) } return c.formatExpr("(%s = %e, %s(%s))", tupleVar, e.Args[0], fun, strings.Join(c.translateArgs(sig, args, false), ", ")) } } args := c.translateArgs(sig, e.Args, e.Ellipsis.IsValid()) if c.blocking[e] { resumeCase := c.caseCounter c.caseCounter++ returnVar := "$r" if sig.Results().Len() != 0 { returnVar = c.newVariable("_r") } c.Printf("%[1]s = %[2]s(%[3]s); /* */ $s = %[4]d; case %[4]d: if (%[1]s && %[1]s.constructor === Function) { %[1]s = %[1]s(); }", returnVar, fun, strings.Join(append(args, "true"), ", "), resumeCase) if sig.Results().Len() != 0 { return c.formatExpr("%s", returnVar) } return nil } return c.formatExpr("%s(%s)", fun, strings.Join(args, ", ")) case *ast.StarExpr: 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.info.Types[c2.Fun].Type, 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.info.Types[e.Type].Type check := "%1e !== null && " + c.typeCheck("%1e.constructor", t) valueSuffix := "" if _, isInterface := t.Underlying().(*types.Interface); !isInterface { valueSuffix = ".$val" } if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("("+check+" ? [%1e%2s, true] : [%3s, false])", e.X, valueSuffix, c.zeroValue(c.p.info.Types[e.Type].Type)) } return c.formatExpr("("+check+" ? %1e%2s : $typeAssertionFailed(%1e, %3s))", e.X, valueSuffix, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } obj := c.p.info.Defs[e] if obj == nil { obj = c.p.info.Uses[e] } switch o := obj.(type) { case *types.PkgName: return c.formatExpr("%s", c.p.pkgVars[o.Pkg().Path()]) 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: return c.formatExpr("%s", c.zeroValue(c.p.info.Types[e].Type)) default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *This: this := "this" if len(c.flattened) != 0 { this = "$this" } if isWrapped(c.p.info.Types[e].Type) { this += ".$val" } return c.formatExpr(this) case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
// CreateTestMainPackage creates and returns a synthetic "main" // package that runs all the tests of the supplied packages, similar // to the one that would be created by the 'go test' tool. // // It returns nil if the program contains no tests. // func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { pkgs, tests, benchmarks, examples := FindTests(pkgs) if len(pkgs) == 0 { return nil } testmain := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: types.NewPackage("testmain", "testmain"), } // Build package's init function. init := &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: testmain, Prog: prog, } init.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(init) } // Initialize packages to test. for _, pkg := range pkgs { var v Call v.Call.Value = pkg.init v.setType(types.NewTuple()) init.emit(&v) } init.emit(new(Return)) init.finishBody() testmain.init = init testmain.Object.MarkComplete() testmain.Members[init.name] = init main := &Function{ name: "main", Signature: new(types.Signature), Synthetic: "test main function", Prog: prog, Pkg: testmain, } main.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(main) } if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { testingMain := testingPkg.Func("Main") testingMainParams := testingMain.Signature.Params() // The generated code is as if compiled from this: // // func main() { // match := func(_, _ string) (bool, error) { return true, nil } // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} // benchmarks := []testing.InternalBenchmark{...} // examples := []testing.InternalExample{...} // testing.Main(match, tests, benchmarks, examples) // } matcher := &Function{ name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", parent: main, Pkg: testmain, Prog: prog, } main.AnonFuncs = append(main.AnonFuncs, matcher) matcher.startBody() matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) matcher.finishBody() // Emit call: testing.Main(matcher, tests, benchmarks, examples). var c Call c.Call.Value = testingMain c.Call.Args = []Value{ matcher, testMainSlice(main, tests, testingMainParams.At(1).Type()), testMainSlice(main, benchmarks, testingMainParams.At(2).Type()), testMainSlice(main, examples, testingMainParams.At(3).Type()), } emitTailCall(main, &c) } else { // The program does not import "testing", but FindTests // returned non-nil, which must mean there were Examples // but no Tests or Benchmarks. // We'll simply call them from testmain.main; this will // ensure they don't panic, but will not check any // "Output:" comments. for _, eg := range examples { var c Call c.Call.Value = eg c.setType(types.NewTuple()) main.emit(&c) } main.emit(&Return{}) main.currentBlock = nil } main.finishBody() testmain.Members["main"] = main if prog.mode&PrintPackages != 0 { printMu.Lock() testmain.WriteTo(os.Stdout) printMu.Unlock() } if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(testmain) } prog.packages[testmain.Object] = testmain return testmain }
func logStack(format string, args ...interface{}) func() { msg := fmt.Sprintf(format, args...) io.WriteString(os.Stderr, msg) io.WriteString(os.Stderr, "\n") return func() { io.WriteString(os.Stderr, msg) io.WriteString(os.Stderr, " end\n") } } // newVar creates a 'var' for use in a types.Tuple. func newVar(name string, typ types.Type) *types.Var { return types.NewParam(token.NoPos, nil, name, typ) } // anonVar creates an anonymous 'var' for use in a types.Tuple. func anonVar(typ types.Type) *types.Var { return newVar("", typ) } var lenResults = types.NewTuple(anonVar(tInt)) // makeLen returns the len builtin specialized to type func(T)int. func makeLen(T types.Type) *Builtin { lenParams := types.NewTuple(anonVar(T)) return &Builtin{ name: "len", sig: types.NewSignature(nil, nil, lenParams, lenResults, false), } }
func (c *compiler) VisitFuncLit(lit *ast.FuncLit) Value { ftyp := c.types.expr[lit].Type.(*types.Signature) // Walk the function literal, promoting stack vars not defined // in the function literal, and storing the ident's for non-const // values not declared in the function literal. // // (First, set a dummy "stack" value for the params and results.) var dummyfunc LLVMValue dummyfunc.stack = &dummyfunc paramVars := ftyp.Params() resultVars := ftyp.Results() c.functions.push(&function{ LLVMValue: &dummyfunc, results: resultVars, }) v := &identVisitor{compiler: c} ast.Walk(v, lit.Body) c.functions.pop() // Create closure by adding a context parameter to the function, // and bind it with the values of the stack vars found in the // step above. origfnpairtyp := c.types.ToLLVM(ftyp) fnpairtyp := origfnpairtyp fntyp := origfnpairtyp.StructElementTypes()[0].ElementType() if v.captures != nil { // Add the additional context param. ctxfields := make([]*types.Field, len(v.captures)) for i, capturevar := range v.captures { ctxfields[i] = &types.Field{ Type: types.NewPointer(capturevar.Type()), } } ctxtyp := types.NewPointer(types.NewStruct(ctxfields, nil)) llvmctxtyp := c.types.ToLLVM(ctxtyp) rettyp := fntyp.ReturnType() paramtyps := append([]llvm.Type{llvmctxtyp}, fntyp.ParamTypes()...) vararg := fntyp.IsFunctionVarArg() fntyp = llvm.FunctionType(rettyp, paramtyps, vararg) opaqueptrtyp := origfnpairtyp.StructElementTypes()[1] elttyps := []llvm.Type{llvm.PointerType(fntyp, 0), opaqueptrtyp} fnpairtyp = llvm.StructType(elttyps, false) } fnptr := llvm.AddFunction(c.module.Module, "", fntyp) fnvalue := llvm.ConstNull(fnpairtyp) fnvalue = llvm.ConstInsertValue(fnvalue, fnptr, []uint32{0}) currBlock := c.builder.GetInsertBlock() f := c.NewValue(fnvalue, ftyp) captureVars := types.NewTuple(v.captures...) c.buildFunction(f, captureVars, paramVars, resultVars, lit.Body, ftyp.IsVariadic()) // Closure? Bind values to a context block. if v.captures != nil { // Store the free variables in the heap allocated block. block := c.createTypeMalloc(fntyp.ParamTypes()[0].ElementType()) for i, contextvar := range v.captures { value := c.objectdata[contextvar].Value blockPtr := c.builder.CreateStructGEP(block, i, "") c.builder.CreateStore(value.pointer.LLVMValue(), blockPtr) } // Cast the function pointer type back to the original // type, without the context parameter. fnptr = llvm.ConstBitCast(fnptr, origfnpairtyp.StructElementTypes()[0]) fnvalue = llvm.Undef(origfnpairtyp) fnvalue = llvm.ConstInsertValue(fnvalue, fnptr, []uint32{0}) // Set the context value. i8ptr := llvm.PointerType(llvm.Int8Type(), 0) block = c.builder.CreateBitCast(block, i8ptr, "") fnvalue = c.builder.CreateInsertValue(fnvalue, block, 1, "") f.value = fnvalue } else { c.builder.SetInsertPointAtEnd(currBlock) } return f }
// builtinCallSignature returns a new Signature describing the // effective type of a builtin operator for the particular call e. // // This requires ad-hoc typing rules for all variadic (append, print, // println) and polymorphic (append, copy, delete, close) built-ins. // This logic could be part of the typechecker, and should arguably // be moved there and made accessible via an additional types.Context // callback. // // The returned Signature is degenerate and only intended for use by // emitCallArgs. // func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature { var params []*types.Var var isVariadic bool switch builtin := noparens(e.Fun).(*ast.Ident).Name; builtin { case "append": var t0, t1 types.Type t0 = info.TypeOf(e) // infer arg[0] type from result type if e.Ellipsis != 0 { // append([]T, []T) []T // append([]byte, string) []byte t1 = info.TypeOf(e.Args[1]) // no conversion } else { // append([]T, ...T) []T t1 = t0.Underlying().(*types.Slice).Elem() isVariadic = true } params = append(params, types.NewVar(nil, "", t0), types.NewVar(nil, "", t1)) case "print", "println": // print{,ln}(any, ...interface{}) isVariadic = true // Note, arg0 may have any type, not necessarily tEface. params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0])), types.NewVar(nil, "", tEface)) case "close": params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0]))) case "copy": // copy([]T, []T) int // Infer arg types from each other. Sleazy. var st *types.Slice if t, ok := info.TypeOf(e.Args[0]).Underlying().(*types.Slice); ok { st = t } else if t, ok := info.TypeOf(e.Args[1]).Underlying().(*types.Slice); ok { st = t } else { panic("cannot infer types in call to copy()") } stvar := types.NewVar(nil, "", st) params = append(params, stvar, stvar) case "delete": // delete(map[K]V, K) tmap := info.TypeOf(e.Args[0]) tkey := tmap.Underlying().(*types.Map).Key() params = append(params, types.NewVar(nil, "", tmap), types.NewVar(nil, "", tkey)) case "len", "cap": params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0]))) case "real", "imag": // Reverse conversion to "complex" case below. var argType types.Type switch info.TypeOf(e).(*types.Basic).Kind() { case types.UntypedFloat: argType = types.Typ[types.UntypedComplex] case types.Float64: argType = tComplex128 case types.Float32: argType = tComplex64 default: unreachable() } params = append(params, types.NewVar(nil, "", argType)) case "complex": var argType types.Type switch info.TypeOf(e).(*types.Basic).Kind() { case types.UntypedComplex: argType = types.Typ[types.UntypedFloat] case types.Complex128: argType = tFloat64 case types.Complex64: argType = tFloat32 default: unreachable() } v := types.NewVar(nil, "", argType) params = append(params, v, v) case "panic": params = append(params, types.NewVar(nil, "", tEface)) case "recover": // no params default: panic("unknown builtin: " + builtin) } return types.NewSignature(nil, types.NewTuple(params...), nil, isVariadic) }
func (c *funcContext) translateStmt(stmt ast.Stmt, label string) { c.WritePos(stmt.Pos()) switch s := stmt.(type) { case *ast.BlockStmt: c.printLabel(label) c.translateStmtList(s.List) case *ast.IfStmt: c.printLabel(label) if s.Init != nil { c.translateStmt(s.Init, "") } 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, c.translateExpr, nil, "", c.flattened[s]) case *ast.SwitchStmt: if s.Init != nil { c.translateStmt(s.Init, "") } translateCond := func(cond ast.Expr) *expression { return c.translateExpr(cond) } if s.Tag != nil { refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.Tag)) translateCond = func(cond ast.Expr) *expression { return c.translateExpr(&ast.BinaryExpr{ X: c.newIdent(refVar, c.p.info.Types[s.Tag].Type), 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, "") } var expr ast.Expr var typeSwitchVar string switch a := s.Assign.(type) { case *ast.AssignStmt: expr = a.Rhs[0].(*ast.TypeAssertExpr).X typeSwitchVar = c.newVariable(a.Lhs[0].(*ast.Ident).Name) for _, caseClause := range s.Body.List { c.p.objectVars[c.p.info.Implicits[caseClause]] = typeSwitchVar } case *ast.ExprStmt: expr = a.X.(*ast.TypeAssertExpr).X } refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(expr)) translateCond := func(cond ast.Expr) *expression { if types.Identical(c.p.info.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.info.Types[cond].Type)) } printCaseBodyPrefix := func(index int) { if typeSwitchVar == "" { return } value := refVar if conds := s.Body.List[index].(*ast.CaseClause).List; len(conds) == 1 { t := c.p.info.Types[conds[0]].Type if _, isInterface := t.Underlying().(*types.Interface); !isInterface && !types.Identical(t, types.Typ[types.UntypedNil]) { value += ".$val" } } c.Printf("%s = %s;", typeSwitchVar, value) } c.translateBranchingStmt(s.Body.List, true, translateCond, printCaseBodyPrefix, label, c.flattened[s]) case *ast.ForStmt: if s.Init != nil { c.translateStmt(s.Init, "") } cond := "true" if s.Cond != nil { cond = c.translateExpr(s.Cond).String() } c.translateLoopingStmt(cond, s.Body, nil, func() { if s.Post != nil { c.translateStmt(s.Post, "") } }, label, c.flattened[s]) case *ast.RangeStmt: refVar := c.newVariable("_ref") c.Printf("%s = %s;", refVar, c.translateExpr(s.X)) switch t := c.p.info.Types[s.X].Type.Underlying().(type) { case *types.Basic: iVar := c.newVariable("_i") c.Printf("%s = 0;", iVar) runeVar := c.newVariable("_rune") c.translateLoopingStmt(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(iVar+" < "+keysVar+".length", s.Body, func() { entryVar := c.newVariable("_entry") c.Printf("%s = %s[%s[%s]];", entryVar, refVar, keysVar, iVar) 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(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]) forStmt := &ast.ForStmt{ Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.AssignStmt{ Lhs: []ast.Expr{ s.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: s.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: c.printLabel(label) labelSuffix := "" data := c.flowDatas[""] if s.Label != nil { labelSuffix = " " + s.Label.Name data = c.flowDatas[s.Label.Name] } switch s.Tok { case token.BREAK: c.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", labelSuffix), fmt.Sprintf("$s = %d; continue;", data.endCase)) case token.CONTINUE: data.postStmt() c.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", labelSuffix), fmt.Sprintf("$s = %d; continue;", data.beginCase)) case token.GOTO: c.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("$s = %d; continue;", c.labelCases[s.Label.Name])) case token.FALLTHROUGH: // handled in CaseClause default: panic("Unhandled branch statment: " + s.Tok.String()) } case *ast.ReturnStmt: c.printLabel(label) 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, }, "") } results = c.resultNames } switch len(results) { case 0: c.Printf("return;") case 1: if c.sig.Results().Len() > 1 { c.Printf("return %s;", c.translateExpr(results[0])) return } v := c.translateImplicitConversion(results[0], c.sig.Results().At(0).Type()) c.delayedOutput = nil c.Printf("return %s;", v) default: values := make([]string, len(results)) for i, result := range results { values[i] = c.translateImplicitConversion(result, c.sig.Results().At(i).Type()).String() } c.delayedOutput = nil c.Printf("return [%s];", strings.Join(values, ", ")) } case *ast.DeferStmt: c.printLabel(label) isBuiltin := false isJs := false switch fun := s.Call.Fun.(type) { case *ast.Ident: var builtin *types.Builtin builtin, isBuiltin = c.p.info.Uses[fun].(*types.Builtin) if isBuiltin && builtin.Name() == "recover" { c.Printf("$deferred.push([$recover, []]);") return } case *ast.SelectorExpr: isJs = isJsPackage(c.p.info.Uses[fun.Sel].Pkg()) } if isBuiltin || isJs { args := make([]ast.Expr, len(s.Call.Args)) for i, arg := range s.Call.Args { args[i] = c.newIdent(c.newVariable("_arg"), c.p.info.Types[arg].Type) } call := c.translateExpr(&ast.CallExpr{ Fun: s.Call.Fun, Args: args, Ellipsis: s.Call.Ellipsis, }) c.Printf("$deferred.push([function(%s) { %s; }, [%s]]);", strings.Join(c.translateExprSlice(args, nil), ", "), call, strings.Join(c.translateExprSlice(s.Call.Args, nil), ", ")) return } sig := c.p.info.Types[s.Call.Fun].Type.Underlying().(*types.Signature) args := c.translateArgs(sig, s.Call.Args, s.Call.Ellipsis.IsValid()) if len(c.blocking) != 0 { args = append(args, "true") } c.Printf("$deferred.push([%s, [%s]]);", c.translateExpr(s.Call.Fun), strings.Join(args, ", ")) case *ast.AssignStmt: c.printLabel(label) if s.Tok != token.ASSIGN && s.Tok != token.DEFINE { var op token.Token switch s.Tok { case token.ADD_ASSIGN: op = token.ADD case token.SUB_ASSIGN: op = token.SUB case token.MUL_ASSIGN: op = token.MUL case token.QUO_ASSIGN: op = token.QUO case token.REM_ASSIGN: op = token.REM case token.AND_ASSIGN: op = token.AND case token.OR_ASSIGN: op = token.OR case token.XOR_ASSIGN: op = token.XOR case token.SHL_ASSIGN: op = token.SHL case token.SHR_ASSIGN: op = token.SHR case token.AND_NOT_ASSIGN: op = token.AND_NOT default: panic(s.Tok) } var parts []string lhs := s.Lhs[0] switch l := lhs.(type) { case *ast.IndexExpr: lhsVar := c.newVariable("_lhs") indexVar := c.newVariable("_index") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") parts = append(parts, indexVar+" = "+c.translateExpr(l.Index).String()+";") lhs = c.setType(&ast.IndexExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), Index: c.newIdent(indexVar, c.p.info.Types[l.Index].Type), }, c.p.info.Types[l].Type) case *ast.StarExpr: lhsVar := c.newVariable("_lhs") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") lhs = c.setType(&ast.StarExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), }, c.p.info.Types[l].Type) case *ast.SelectorExpr: v := hasCallVisitor{c.p.info, false} ast.Walk(&v, l.X) if v.hasCall { lhsVar := c.newVariable("_lhs") parts = append(parts, lhsVar+" = "+c.translateExpr(l.X).String()+";") lhs = c.setType(&ast.SelectorExpr{ X: c.newIdent(lhsVar, c.p.info.Types[l.X].Type), Sel: l.Sel, }, c.p.info.Types[l].Type) c.p.info.Selections[lhs.(*ast.SelectorExpr)] = c.p.info.Selections[l] } } lhsType := c.p.info.Types[s.Lhs[0]].Type parts = append(parts, c.translateAssign(lhs, c.translateExpr(c.setType(&ast.BinaryExpr{ X: lhs, Op: op, Y: c.setType(&ast.ParenExpr{X: s.Rhs[0]}, c.p.info.Types[s.Rhs[0]].Type), }, lhsType)).String(), lhsType, s.Tok == token.DEFINE)) c.Printf("%s", strings.Join(parts, " ")) return } if s.Tok == token.DEFINE { for _, lhs := range s.Lhs { if !isBlank(lhs) { obj := c.p.info.Defs[lhs.(*ast.Ident)] if obj == nil { obj = c.p.info.Uses[lhs.(*ast.Ident)] } c.setType(lhs, obj.Type()) } } } switch { case len(s.Lhs) == 1 && len(s.Rhs) == 1: lhs := removeParens(s.Lhs[0]) if isBlank(lhs) { v := hasCallVisitor{c.p.info, false} ast.Walk(&v, s.Rhs[0]) if v.hasCall { c.Printf("%s;", c.translateExpr(s.Rhs[0]).String()) } return } lhsType := c.p.info.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.info.Types[s.Rhs[0]].Type.(*types.Tuple) for i, lhs := range s.Lhs { lhs = removeParens(lhs) if !isBlank(lhs) { lhsType := c.p.info.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(removeParens(s.Lhs[i])) { v := hasCallVisitor{c.p.info, false} ast.Walk(&v, rhs) if v.hasCall { c.Printf("%s;", c.translateExpr(rhs).String()) } continue } lhsType := c.p.info.Types[s.Lhs[i]].Type parts = append(parts, c.translateAssignOfExpr(c.newIdent(tmpVars[i], c.p.info.Types[s.Lhs[i]].Type), rhs, lhsType, true)) } for i, lhs := range s.Lhs { lhs = removeParens(lhs) if !isBlank(lhs) { parts = append(parts, c.translateAssign(lhs, tmpVars[i], c.p.info.Types[lhs].Type, s.Tok == token.DEFINE)) } } c.Printf("%s", strings.Join(parts, " ")) default: panic("Invalid arity of AssignStmt.") } case *ast.IncDecStmt: t := c.p.info.Types[s.X].Type if iExpr, isIExpr := s.X.(*ast.IndexExpr); isIExpr { switch u := c.p.info.Types[iExpr.X].Type.Underlying().(type) { case *types.Array: t = u.Elem() case *types.Slice: t = u.Elem() case *types.Map: t = u.Elem() } } tok := token.ADD_ASSIGN if s.Tok == token.DEC { tok = token.SUB_ASSIGN } c.translateStmt(&ast.AssignStmt{ Lhs: []ast.Expr{s.X}, Tok: tok, Rhs: []ast.Expr{c.newInt(1, t)}, }, label) case *ast.DeclStmt: c.printLabel(label) 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.info.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, }, "") } case token.TYPE: for _, spec := range decl.Specs { o := c.p.info.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName) c.translateType(o, false) c.initType(o) } case token.CONST: // skip, constants are inlined } case *ast.ExprStmt: c.printLabel(label) expr := c.translateExpr(s.X) if expr != nil { c.Printf("%s;", expr) } case *ast.LabeledStmt: c.printLabel(label) c.translateStmt(s.Stmt, s.Label.Name) case *ast.GoStmt: c.printLabel(label) c.Printf("$go(%s, [%s]);", c.translateExpr(s.Call.Fun), strings.Join(c.translateArgs(c.p.info.Types[s.Call.Fun].Type.Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid()), ", ")) case *ast.SendStmt: chanType := c.p.info.Types[s.Chan].Type.Underlying().(*types.Chan) call := &ast.CallExpr{ Fun: c.newIdent("$send", types.NewSignature(nil, 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{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]", removeParens(comm.X).(*ast.UnaryExpr).X).String()) case *ast.AssignStmt: channels = append(channels, c.formatExpr("[%e]", 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)) } caseClauses = append(caseClauses, &ast.CaseClause{ List: []ast.Expr{c.newInt(i, types.Typ[types.Int])}, Body: clause.Body, }) flattened = flattened || c.flattened[clause] } selectCall := c.setType(&ast.CallExpr{ Fun: c.newIdent("$select", types.NewSignature(nil, 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.info.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}, "") default: c.translateStmt(&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}, "") } } } c.translateBranchingStmt(caseClauses, true, translateCond, printCaseBodyPrefix, label, flattened) case *ast.EmptyStmt: // skip default: panic(fmt.Sprintf("Unhandled statement: %T\n", s)) } }
// CreateTestMainPackage creates and returns a synthetic "main" // package that runs all the tests of the supplied packages, similar // to the one that would be created by the 'go test' tool. // // It returns nil if the program contains no tests. // func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { if len(pkgs) == 0 { return nil } testmain := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: types.NewPackage("testmain", "testmain"), } // Build package's init function. init := &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: testmain, Prog: prog, } init.startBody() // TODO(adonovan): use lexical order. var expfuncs []*Function // all exported functions of *_test.go in pkgs, unordered for _, pkg := range pkgs { if pkg.Prog != prog { panic("wrong Program") } // Initialize package to test. var v Call v.Call.Value = pkg.init v.setType(types.NewTuple()) init.emit(&v) // Enumerate its possible tests/benchmarks. for _, mem := range pkg.Members { if f, ok := mem.(*Function); ok && ast.IsExported(f.Name()) && strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") { expfuncs = append(expfuncs, f) } } } init.emit(new(Return)) init.finishBody() testmain.init = init testmain.Object.MarkComplete() testmain.Members[init.name] = init testingPkg := prog.ImportedPackage("testing") if testingPkg == nil { // If the program doesn't import "testing", it can't // contain any tests. // TODO(adonovan): but it might contain Examples. // Support them (by just calling them directly). return nil } testingMain := testingPkg.Func("Main") testingMainParams := testingMain.Signature.Params() // The generated code is as if compiled from this: // // func main() { // match := func(_, _ string) (bool, error) { return true, nil } // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} // benchmarks := []testing.InternalBenchmark{...} // examples := []testing.InternalExample{...} // testing.Main(match, tests, benchmarks, examples) // } main := &Function{ name: "main", Signature: new(types.Signature), Synthetic: "test main function", Prog: prog, Pkg: testmain, } matcher := &Function{ name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", parent: main, Pkg: testmain, Prog: prog, } main.AnonFuncs = append(main.AnonFuncs, matcher) matcher.startBody() matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) matcher.finishBody() main.startBody() var c Call c.Call.Value = testingMain tests := testMainSlice(main, expfuncs, "Test", testingMainParams.At(1).Type()) benchmarks := testMainSlice(main, expfuncs, "Benchmark", testingMainParams.At(2).Type()) examples := testMainSlice(main, expfuncs, "Example", testingMainParams.At(3).Type()) _, noTests := tests.(*Const) // i.e. nil slice _, noBenchmarks := benchmarks.(*Const) _, noExamples := examples.(*Const) if noTests && noBenchmarks && noExamples { return nil } c.Call.Args = []Value{matcher, tests, benchmarks, examples} // Emit: testing.Main(nil, tests, benchmarks, examples) emitTailCall(main, &c) main.finishBody() testmain.Members["main"] = main if prog.mode&LogPackages != 0 { testmain.WriteTo(os.Stderr) } if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(testmain) } prog.packages[testmain.Object] = testmain return testmain }