// 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)
}
Exemple #2
0
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
}
Exemple #3
0
// 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),
	}
}
Exemple #4
0
// 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),
	}
}
Exemple #5
0
func (p *importer) tuple() *types.Tuple {
	vars := make([]*types.Var, p.int())
	for i := range vars {
		vars[i] = p.param()
	}
	return types.NewTuple(vars...)
}
Exemple #6
0
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())
}
Exemple #8
0
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
}
Exemple #9
0
// 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
	}
}
Exemple #10
0
// 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)
}
Exemple #11
0
// 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)
}
Exemple #12
0
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
}
Exemple #13
0
// 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
}
Exemple #14
0
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
}
Exemple #15
0
// 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
}
Exemple #16
0
			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),
	}
}
Exemple #17
0
Fichier : ssa.go Projet : pcc/llgo
// 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())
	}
}
Exemple #18
0
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))

	}
}
Exemple #19
0
// 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
}
Exemple #20
0
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),
	}
}
Exemple #21
0
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
}
Exemple #22
0
// 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)
}
Exemple #23
0
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))

	}
}
Exemple #24
0
// 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
}