Exemplo n.º 1
0
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named type.
//
func isValuePreserving(ut_src, ut_dst types.Type) bool {
	// Identical underlying types?
	if types.IsIdentical(ut_dst, ut_src) {
		return true
	}

	switch ut_dst.(type) {
	case *types.Chan:
		// Conversion between channel types?
		_, ok := ut_src.(*types.Chan)
		return ok

	case *types.Pointer:
		// Conversion between pointers with identical base types?
		_, ok := ut_src.(*types.Pointer)
		return ok

	case *types.Signature:
		// Conversion between f(T) function and (T) func f() method?
		// TODO(adonovan): is this sound?  Discuss with gri.
		_, ok := ut_src.(*types.Signature)
		return ok
	}
	return false
}
Exemplo n.º 2
0
func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) {
	// The prefix of all assertions messages.
	prefix := fmt.Sprintf("VarValue(%s @ L%d)",
		obj, prog.Fset.Position(ref[0].Pos()).Line)

	v := prog.VarValue(obj, pkg, ref)

	// Kind is the concrete type of the ssa Value.
	gotKind := "nil"
	if v != nil {
		gotKind = fmt.Sprintf("%T", v)[len("*ssa."):]
	}

	// fmt.Printf("%s = %v (kind %q; expect %q) addr=%t\n", prefix, v, gotKind, expKind, wantAddr) // debugging

	// Check the kinds match.
	// "nil" indicates expected failure (e.g. optimized away).
	if expKind != gotKind {
		t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind)
	}

	// Check the types match.
	// If wantAddr, the expected type is the object's address.
	if v != nil {
		expType := obj.Type()
		if wantAddr {
			expType = types.NewPointer(expType)
		}
		if !types.IsIdentical(v.Type(), expType) {
			t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType)
		}
	}
}
Exemplo n.º 3
0
// Set sets the map entry for key to val,
// and returns the previous entry, if any.
func (m *M) Set(key types.Type, value interface{}) (prev interface{}) {
	if m.table != nil {
		hash := m.hasher.Hash(key)
		bucket := m.table[hash]
		var hole *entry
		for i, e := range bucket {
			if e.key == nil {
				hole = &bucket[i]
			} else if types.IsIdentical(key, e.key) {
				prev = e.value
				bucket[i].value = value
				return
			}
		}

		if hole != nil {
			*hole = entry{key, value} // overwrite deleted entry
		} else {
			m.table[hash] = append(bucket, entry{key, value})
		}
	} else {
		if m.hasher.memo == nil {
			m.hasher = MakeHasher()
		}
		hash := m.hasher.Hash(key)
		m.table = map[uint32][]entry{hash: {entry{key, value}}}
	}

	m.length++
	return
}
Exemplo n.º 4
0
// typeAssert checks whether dynamic type of itf is instr.AssertedType.
// It returns the extracted value on success, and panics on failure,
// unless instr.CommaOk, in which case it always returns a "value,ok" tuple.
//
func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
	var v value
	err := ""
	if itf.t == nil {
		err = fmt.Sprintf("interface conversion: interface is nil, not %s", instr.AssertedType)

	} else if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok {
		v = itf
		err = checkInterface(i, idst, itf)

	} else if types.IsIdentical(itf.t, instr.AssertedType) {
		v = copyVal(itf.v) // extract value

	} else {
		err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType)
	}

	if err != "" {
		if !instr.CommaOk {
			panic(err)
		}
		return tuple{zero(instr.AssertedType), false}
	}
	if instr.CommaOk {
		return tuple{v, true}
	}
	return v
}
Exemplo n.º 5
0
func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) {
	tIface, _ := c.typ.Underlying().(*types.Interface)

	for ifaceObj := range delta {
		tDyn, v, indirect := a.taggedValue(ifaceObj)
		if tDyn == nil {
			panic("not a tagged value")
		}
		if indirect {
			// TODO(adonovan): we'll need to implement this
			// when we start creating indirect tagged objects.
			panic("indirect tagged object")
		}

		if tIface != nil {
			if types.IsAssignableTo(tDyn, tIface) {
				if a.addLabel(c.dst, ifaceObj) {
					a.addWork(c.dst)
				}
			}
		} else {
			if types.IsIdentical(tDyn, c.typ) {
				// Copy entire payload to dst.
				//
				// TODO(adonovan): opt: if tConc is
				// nonpointerlike we can skip this
				// entire constraint, perhaps.  We
				// only care about pointers among the
				// fields.
				a.onlineCopyN(c.dst, v, a.sizeof(tDyn))
			}
		}
	}
}
Exemplo n.º 6
0
// emitConv emits to f code to convert Value val to exactly type typ,
// and returns the converted value.  Implicit conversions are required
// by language assignability rules in assignments, parameter passing,
// etc.
//
func emitConv(f *Function, val Value, typ types.Type) Value {
	t_src := val.Type()

	// Identical types?  Conversion is a no-op.
	if types.IsIdentical(t_src, typ) {
		return val
	}

	ut_dst := typ.Underlying()
	ut_src := t_src.Underlying()

	// Just a change of type, but not value or representation?
	if isValuePreserving(ut_src, ut_dst) {
		c := &ChangeType{X: val}
		c.setType(typ)
		return f.emit(c)
	}

	// Conversion to, or construction of a value of, an interface type?
	if _, ok := ut_dst.(*types.Interface); ok {

		// Assignment from one interface type to another?
		if _, ok := ut_src.(*types.Interface); ok {
			return emitTypeAssert(f, val, typ)
		}

		// Untyped nil literal?  Return interface-typed nil literal.
		if ut_src == tUntypedNil {
			return nilLiteral(typ)
		}

		// Convert (non-nil) "untyped" literals to their default type.
		// TODO(gri): expose types.isUntyped().
		if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
			val = emitConv(f, val, DefaultType(ut_src))
		}

		mi := &MakeInterface{
			X:       val,
			Methods: f.Prog.MethodSet(t_src),
		}
		mi.setType(typ)
		return f.emit(mi)
	}

	// Conversion of a literal to a non-interface type results in
	// a new literal of the destination type and (initially) the
	// same abstract value.  We don't compute the representation
	// change yet; this defers the point at which the number of
	// possible representations explodes.
	if l, ok := val.(*Literal); ok {
		return newLiteral(l.Value, typ)
	}

	// A representation-changing conversion.
	c := &Convert{X: val}
	c.setType(typ)
	return f.emit(c)
}
Exemplo n.º 7
0
func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) {
	if !types.IsIdentical(x, y) {
		t.Errorf("%s: not equal: %s, %s", comment, x, y)
	}
	if x == y {
		t.Errorf("%s: identical: %p, %p", comment, x, y)
	}
}
Exemplo n.º 8
0
// At returns the map entry for the given key.
// The result is nil if the entry is not present.
//
func (m *M) At(key types.Type) interface{} {
	if m != nil && m.table != nil {
		for _, e := range m.table[m.hasher.Hash(key)] {
			if e.key != nil && types.IsIdentical(key, e.key) {
				return e.value
			}
		}
	}
	return nil
}
Exemplo n.º 9
0
// testMainSlice emits to fn code to construct a slice of type slice
// (one of []testing.Internal{Test,Benchmark,Example}) for all
// functions in this package whose name starts with prefix (one of
// "Test", "Benchmark" or "Example") and whose type is appropriate.
// It returns the slice value.
//
func testMainSlice(fn *Function, prefix string, slice types.Type) Value {
	tElem := slice.(*types.Slice).Elem()
	tFunc := tElem.Underlying().(*types.Struct).Field(1).Type()

	var testfuncs []*Function
	for name, mem := range fn.Pkg.Members {
		if fn, ok := mem.(*Function); ok && isTest(name, prefix) && types.IsIdentical(fn.Signature, tFunc) {
			testfuncs = append(testfuncs, fn)
		}
	}
	if testfuncs == nil {
		return nilConst(slice)
	}

	tString := types.Typ[types.String]
	tPtrString := types.NewPointer(tString)
	tPtrElem := types.NewPointer(tElem)
	tPtrFunc := types.NewPointer(tFunc)

	// Emit: array = new [n]testing.InternalTest
	tArray := types.NewArray(tElem, int64(len(testfuncs)))
	array := emitNew(fn, tArray, token.NoPos)
	array.Comment = "test main"
	for i, testfunc := range testfuncs {
		// Emit: pitem = &array[i]
		ia := &IndexAddr{X: array, Index: intConst(int64(i))}
		ia.setType(tPtrElem)
		pitem := fn.emit(ia)

		// Emit: pname = &pitem.Name
		fa := &FieldAddr{X: pitem, Field: 0} // .Name
		fa.setType(tPtrString)
		pname := fn.emit(fa)

		// Emit: *pname = "testfunc"
		emitStore(fn, pname, NewConst(exact.MakeString(testfunc.Name()), tString))

		// Emit: pfunc = &pitem.F
		fa = &FieldAddr{X: pitem, Field: 1} // .F
		fa.setType(tPtrFunc)
		pfunc := fn.emit(fa)

		// Emit: *pfunc = testfunc
		emitStore(fn, pfunc, testfunc)
	}

	// Emit: slice array[:]
	sl := &Slice{X: array}
	sl.setType(slice)
	return fn.emit(sl)
}
Exemplo n.º 10
0
// Delete removes the entry with the given key, if any.
// It returns true if the entry was found.
//
func (m *M) Delete(key types.Type) bool {
	if m != nil && m.table != nil {
		hash := m.hasher.Hash(key)
		bucket := m.table[hash]
		for i, e := range bucket {
			if e.key != nil && types.IsIdentical(key, e.key) {
				// We can't compact the bucket as it
				// would disturb iterators.
				bucket[i] = entry{}
				m.length--
				return true
			}
		}
	}
	return false
}
Exemplo n.º 11
0
func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) {
	fn := prog.FuncValue(obj)
	// fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging
	if fn == nil {
		t.Errorf("FuncValue(%s) == nil", obj)
		return
	}
	if fnobj := fn.Object(); fnobj != obj {
		t.Errorf("FuncValue(%s).Object() == %s; value was %s",
			obj, fnobj, fn.Name())
		return
	}
	if !types.IsIdentical(fn.Type(), obj.Type()) {
		t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type())
		return
	}
}
Exemplo n.º 12
0
func (c *rVSetBytesConstraint) solve(a *analysis, _ *node, delta nodeset) {
	for vObj := range delta {
		tDyn, slice, indirect := a.taggedValue(vObj)
		if indirect {
			// TODO(adonovan): we'll need to implement this
			// when we start creating indirect tagged objects.
			panic("indirect tagged object")
		}

		tSlice, ok := tDyn.Underlying().(*types.Slice)
		if ok && types.IsIdentical(tSlice.Elem(), types.Typ[types.Uint8]) {
			if a.onlineCopy(slice, c.x) {
				a.addWork(slice)
			}
		}
	}
}
Exemplo n.º 13
0
// emitTypeAssert emits to f a type assertion value := x.(t) and
// returns the value.  x.Type() must be an interface.
//
func emitTypeAssert(f *Function, x Value, t types.Type) Value {
	// Simplify infallible assertions.
	txi := x.Type().Underlying().(*types.Interface)
	if ti, ok := t.Underlying().(*types.Interface); ok {
		if types.IsIdentical(ti, txi) {
			return x
		}
		if isSuperinterface(ti, txi) {
			c := &ChangeInterface{X: x}
			c.setType(t)
			return f.emit(c)
		}
	}

	a := &TypeAssert{X: x, AssertedType: t}
	a.setType(t)
	return f.emit(a)
}
Exemplo n.º 14
0
// isErrorMethodCall reports whether the call is of a method with signature
//	func Error() string
// where "string" is the universe's string type. We know the method is called "Error".
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
	typ := f.pkg.types[call]
	if typ != nil {
		// We know it's called "Error", so just check the function signature.
		return types.IsIdentical(f.pkg.types[call.Fun], stringerMethodType)
	}
	// Without types, we can still check by hand.
	// Is it a selector expression? Otherwise it's a function call, not a method call.
	sel, ok := call.Fun.(*ast.SelectorExpr)
	if !ok {
		return false
	}
	// The package is type-checked, so if there are no arguments, we're done.
	if len(call.Args) > 0 {
		return false
	}
	// Check the type of the method declaration
	typ = f.pkg.types[sel]
	if typ == nil {
		return false
	}
	// The type must be a signature, but be sure for safety.
	sig, ok := typ.(*types.Signature)
	if !ok {
		return false
	}
	// There must be a receiver for it to be a method call. Otherwise it is
	// a function, not something that satisfies the error interface.
	if sig.Recv() == nil {
		return false
	}
	// There must be no arguments. Already verified by type checking, but be thorough.
	if sig.Params().Len() > 0 {
		return false
	}
	// Finally the real questions.
	// There must be one result.
	if sig.Results().Len() != 1 {
		return false
	}
	// It must have return type "string" from the universe.
	return sig.Results().At(0).Type() == types.Typ[types.String]
}
Exemplo n.º 15
0
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) {
	c := prog.ConstValue(obj)
	// fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging
	if c == nil {
		t.Errorf("ConstValue(%s) == nil", obj)
		return
	}
	if !types.IsIdentical(c.Type(), obj.Type()) {
		t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
		return
	}
	if obj.Name() != "nil" {
		if !exact.Compare(c.Value, token.EQL, obj.Val()) {
			t.Errorf("ConstValue(%s).Value (%s) != %s",
				obj, c.Value, obj.Val())
			return
		}
	}
}
Exemplo n.º 16
0
// isSuperinterface returns true if x is a superinterface of y,
// i.e.  x's methods are a subset of y's.
//
func isSuperinterface(x, y *types.Interface) bool {
	if y.NumMethods() < x.NumMethods() {
		return false
	}
	// TODO(adonovan): opt: this is quadratic.
outer:
	for i, n := 0, x.NumMethods(); i < n; i++ {
		xm := x.Method(i)
		for j, m := 0, y.NumMethods(); j < m; j++ {
			ym := y.Method(j)
			if MakeId(xm.Name(), xm.Pkg()) == MakeId(ym.Name(), ym.Pkg()) {
				if !types.IsIdentical(xm.Type(), ym.Type()) {
					return false // common name but conflicting types
				}
				continue outer
			}
		}
		return false // y doesn't have this method
	}
	return true
}
Exemplo n.º 17
0
// emitCompare emits to f code compute the boolean result of
// comparison comparison 'x op y'.
//
func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
	xt := x.Type().Underlying()
	yt := y.Type().Underlying()

	// Special case to optimise a tagless SwitchStmt so that
	// these are equivalent
	//   switch { case e: ...}
	//   switch true { case e: ... }
	//   if e==true { ... }
	// even in the case when e's type is an interface.
	// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
	if x == vTrue && op == token.EQL {
		if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
			return y
		}
	}

	if types.IsIdentical(xt, yt) {
		// no conversion necessary
	} else if _, ok := xt.(*types.Interface); ok {
		y = emitConv(f, y, x.Type())
	} else if _, ok := yt.(*types.Interface); ok {
		x = emitConv(f, x, y.Type())
	} else if _, ok := x.(*Const); ok {
		x = emitConv(f, x, y.Type())
	} else if _, ok := y.(*Const); ok {
		y = emitConv(f, y, x.Type())
	} else {
		// other cases, e.g. channels.  No-op.
	}

	v := &BinOp{
		Op: op,
		X:  x,
		Y:  y,
	}
	v.setPos(pos)
	v.setType(tBool)
	return f.emit(v)
}
Exemplo n.º 18
0
// checkShadowing checks whether the identifier shadows an identifier in an outer scope.
func (f *File) checkShadowing(ident *ast.Ident) {
	obj := f.pkg.idents[ident]
	if obj == nil {
		return
	}
	// obj.Parent.Parent is the surrounding scope. If we can find another declaration
	// starting from there, we have a shadowed variable.
	shadowed := obj.Parent().Parent().LookupParent(obj.Name())
	if shadowed == nil {
		return
	}
	// Don't complain if it's shadowing a universe-declared variable; that's fine.
	if shadowed.Parent() == types.Universe {
		return
	}
	if *strictShadowing {
		// The shadowed variable must appear before this one to be an instance of shadowing.
		if shadowed.Pos() > ident.Pos() {
			return
		}
	} else {
		// Don't complain if the span of validity of the shadowed variable doesn't include
		// the shadowing variable.
		span, ok := f.pkg.spans[shadowed]
		if !ok {
			f.Badf(ident.Pos(), "internal error: no range for %s", ident.Name)
			return
		}
		if !span.contains(ident.Pos()) {
			return
		}
	}
	// Don't complain if the types differ: that implies the programmer really wants two variables.
	if types.IsIdentical(obj.Type(), shadowed.Type()) {
		f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos()))
	}
}
Exemplo n.º 19
0
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named type.
//
func isValuePreserving(ut_src, ut_dst types.Type) bool {
	// Identical underlying types?
	if types.IsIdentical(ut_dst, ut_src) {
		return true
	}

	switch ut_dst.(type) {
	case *types.Chan:
		// Conversion between channel types?
		_, ok := ut_src.(*types.Chan)
		return ok

	case *types.Pointer:
		// Conversion between pointers with identical base types?
		_, ok := ut_src.(*types.Pointer)
		return ok

	case *types.Signature:
		// Conversion from (T) func f() method to f(T) function?
		_, ok := ut_src.(*types.Signature)
		return ok
	}
	return false
}
Exemplo n.º 20
0
func (v *LLVMValue) Convert(dsttyp types.Type) Value {
	b := v.compiler.builder

	// If it's a stack allocated value, we'll want to compare the
	// value type, not the pointer type.
	srctyp := v.typ

	// Get the underlying type, if any.
	origdsttyp := dsttyp
	dsttyp = dsttyp.Underlying()
	srctyp = srctyp.Underlying()

	// Identical (underlying) types? Just swap in the destination type.
	if types.IsIdentical(srctyp, dsttyp) {
		// A method converted to a function type without the
		// receiver is where we convert a "method value" into a
		// function.
		if srctyp, ok := srctyp.(*types.Signature); ok && srctyp.Recv() != nil {
			if dsttyp, ok := dsttyp.(*types.Signature); ok && dsttyp.Recv() == nil {
				return v.convertMethodValue(origdsttyp)
			}
		}

		// TODO avoid load here by reusing pointer value, if exists.
		return v.compiler.NewValue(v.LLVMValue(), origdsttyp)
	}

	// Both pointer types with identical underlying types? Same as above.
	if srctyp, ok := srctyp.(*types.Pointer); ok {
		if dsttyp, ok := dsttyp.(*types.Pointer); ok {
			srctyp := srctyp.Elem().Underlying()
			dsttyp := dsttyp.Elem().Underlying()
			if types.IsIdentical(srctyp, dsttyp) {
				return v.compiler.NewValue(v.LLVMValue(), origdsttyp)
			}
		}
	}

	// Convert from an interface type.
	if _, isinterface := srctyp.(*types.Interface); isinterface {
		if interface_, isinterface := dsttyp.(*types.Interface); isinterface {
			return v.mustConvertI2I(interface_)
		} else {
			return v.mustConvertI2V(origdsttyp)
		}
	}

	// Converting to an interface type.
	if interface_, isinterface := dsttyp.(*types.Interface); isinterface {
		return v.convertV2I(interface_)
	}

	byteslice := types.NewSlice(types.Typ[types.Byte])
	runeslice := types.NewSlice(types.Typ[types.Rune])

	// string ->
	if isString(srctyp) {
		// (untyped) string -> string
		// XXX should untyped strings be able to escape go/types?
		if isString(dsttyp) {
			return v.compiler.NewValue(v.LLVMValue(), origdsttyp)
		}

		// string -> []byte
		if types.IsIdentical(dsttyp, byteslice) {
			c := v.compiler
			value := v.LLVMValue()
			strdata := c.builder.CreateExtractValue(value, 0, "")
			strlen := c.builder.CreateExtractValue(value, 1, "")

			// Data must be copied, to prevent changes in
			// the byte slice from mutating the string.
			newdata := c.builder.CreateArrayMalloc(strdata.Type().ElementType(), strlen, "")
			memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)")
			c.builder.CreateCall(memcpy, []llvm.Value{
				c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""),
				c.builder.CreatePtrToInt(strdata, c.target.IntPtrType(), ""),
				strlen,
			}, "")
			strdata = newdata

			struct_ := llvm.Undef(c.types.ToLLVM(byteslice))
			struct_ = c.builder.CreateInsertValue(struct_, strdata, 0, "")
			struct_ = c.builder.CreateInsertValue(struct_, strlen, 1, "")
			struct_ = c.builder.CreateInsertValue(struct_, strlen, 2, "")
			return c.NewValue(struct_, byteslice)
		}

		// string -> []rune
		if types.IsIdentical(dsttyp, runeslice) {
			return v.stringToRuneSlice()
		}
	}

	// []byte -> string
	if types.IsIdentical(srctyp, byteslice) && isString(dsttyp) {
		c := v.compiler
		value := v.LLVMValue()
		data := c.builder.CreateExtractValue(value, 0, "")
		len := c.builder.CreateExtractValue(value, 1, "")

		// Data must be copied, to prevent changes in
		// the byte slice from mutating the string.
		newdata := c.builder.CreateArrayMalloc(data.Type().ElementType(), len, "")
		memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)")
		c.builder.CreateCall(memcpy, []llvm.Value{
			c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""),
			c.builder.CreatePtrToInt(data, c.target.IntPtrType(), ""),
			len,
		}, "")
		data = newdata

		struct_ := llvm.Undef(c.types.ToLLVM(types.Typ[types.String]))
		struct_ = c.builder.CreateInsertValue(struct_, data, 0, "")
		struct_ = c.builder.CreateInsertValue(struct_, len, 1, "")
		return c.NewValue(struct_, types.Typ[types.String])
	}

	// []rune -> string
	if types.IsIdentical(srctyp, runeslice) && isString(dsttyp) {
		return v.runeSliceToString()
	}

	// rune -> string
	if isString(dsttyp) && isInteger(srctyp) {
		return v.runeToString()
	}

	// TODO other special conversions?
	llvm_type := v.compiler.types.ToLLVM(dsttyp)

	// Unsafe pointer conversions.
	if dsttyp == types.Typ[types.UnsafePointer] { // X -> unsafe.Pointer
		if _, isptr := srctyp.(*types.Pointer); isptr {
			value := b.CreatePtrToInt(v.LLVMValue(), llvm_type, "")
			return v.compiler.NewValue(value, origdsttyp)
		} else if srctyp == types.Typ[types.Uintptr] {
			return v.compiler.NewValue(v.LLVMValue(), origdsttyp)
		}
	} else if srctyp == types.Typ[types.UnsafePointer] { // unsafe.Pointer -> X
		if _, isptr := dsttyp.(*types.Pointer); isptr {
			value := b.CreateIntToPtr(v.LLVMValue(), llvm_type, "")
			return v.compiler.NewValue(value, origdsttyp)
		} else if dsttyp == types.Typ[types.Uintptr] {
			return v.compiler.NewValue(v.LLVMValue(), origdsttyp)
		}
	}

	lv := v.LLVMValue()
	srcType := lv.Type()
	switch srcType.TypeKind() {
	case llvm.IntegerTypeKind:
		switch llvm_type.TypeKind() {
		case llvm.IntegerTypeKind:
			srcBits := srcType.IntTypeWidth()
			dstBits := llvm_type.IntTypeWidth()
			delta := srcBits - dstBits
			switch {
			case delta < 0:
				// TODO check if (un)signed, use S/ZExt accordingly.
				lv = b.CreateZExt(lv, llvm_type, "")
			case delta > 0:
				lv = b.CreateTrunc(lv, llvm_type, "")
			}
			return v.compiler.NewValue(lv, origdsttyp)
		case llvm.FloatTypeKind, llvm.DoubleTypeKind:
			if !isUnsigned(v.Type()) {
				lv = b.CreateSIToFP(lv, llvm_type, "")
			} else {
				lv = b.CreateUIToFP(lv, llvm_type, "")
			}
			return v.compiler.NewValue(lv, origdsttyp)
		}
	case llvm.DoubleTypeKind:
		switch llvm_type.TypeKind() {
		case llvm.FloatTypeKind:
			lv = b.CreateFPTrunc(lv, llvm_type, "")
			return v.compiler.NewValue(lv, origdsttyp)
		case llvm.IntegerTypeKind:
			if !isUnsigned(dsttyp) {
				lv = b.CreateFPToSI(lv, llvm_type, "")
			} else {
				lv = b.CreateFPToUI(lv, llvm_type, "")
			}
			return v.compiler.NewValue(lv, origdsttyp)
		}
	case llvm.FloatTypeKind:
		switch llvm_type.TypeKind() {
		case llvm.DoubleTypeKind:
			lv = b.CreateFPExt(lv, llvm_type, "")
			return v.compiler.NewValue(lv, origdsttyp)
		case llvm.IntegerTypeKind:
			if !isUnsigned(dsttyp) {
				lv = b.CreateFPToSI(lv, llvm_type, "")
			} else {
				lv = b.CreateFPToUI(lv, llvm_type, "")
			}
			return v.compiler.NewValue(lv, origdsttyp)
		}
	}

	// Complex -> complex. Complexes are only convertible to other
	// complexes, contant conversions aside. So we can just check the
	// source type here; given that the types are not identical
	// (checked above), we can assume the destination type is the alternate
	// complex type.
	if isComplex(srctyp) {
		var fpcast func(*Builder, llvm.Value, llvm.Type, string) llvm.Value
		var fptype llvm.Type
		if srctyp == types.Typ[types.Complex64] {
			fpcast = (*Builder).CreateFPExt
			fptype = llvm.DoubleType()
		} else {
			fpcast = (*Builder).CreateFPTrunc
			fptype = llvm.FloatType()
		}
		if fpcast != nil {
			realv := b.CreateExtractValue(lv, 0, "")
			imagv := b.CreateExtractValue(lv, 1, "")
			realv = fpcast(b, realv, fptype, "")
			imagv = fpcast(b, imagv, fptype, "")
			lv = llvm.Undef(v.compiler.types.ToLLVM(dsttyp))
			lv = b.CreateInsertValue(lv, realv, 0, "")
			lv = b.CreateInsertValue(lv, imagv, 1, "")
			return v.compiler.NewValue(lv, origdsttyp)
		}
	}

	srcstr := v.compiler.types.TypeString(v.typ)
	dststr := v.compiler.types.TypeString(origdsttyp)
	panic(fmt.Sprintf("unimplemented conversion: %s -> %s", srcstr, dststr))
}
Exemplo n.º 21
0
// nil-tolerant variant of types.IsIdentical.
func sameType(x, y types.Type) bool {
	if x == nil {
		return y == nil
	}
	return y != nil && types.IsIdentical(x, y)
}
Exemplo n.º 22
0
func (x rtype) eq(_ types.Type, y interface{}) bool {
	return types.IsIdentical(x.t, y.(rtype).t)
}
Exemplo n.º 23
0
// Implements displays the "implements" relation as it pertains to the
// selected type.
//
func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
	// Find the selected type.
	// TODO(adonovan): fix: make it work on qualified Idents too.
	path, action := findInterestingNode(qpos.info, qpos.path)
	if action != actionType {
		return nil, fmt.Errorf("no type here")
	}
	T := qpos.info.TypeOf(path[0].(ast.Expr))
	if T == nil {
		return nil, fmt.Errorf("no type here")
	}

	// Find all named types, even local types (which can have
	// methods via promotion) and the built-in "error".
	//
	// TODO(adonovan): include all packages in PTA scope too?
	// i.e. don't reduceScope?
	//
	var allNamed []types.Type
	for _, info := range o.typeInfo {
		for id, obj := range info.Objects {
			if obj, ok := obj.(*types.TypeName); ok && obj.Pos() == id.Pos() {
				allNamed = append(allNamed, obj.Type())
			}
		}
	}
	allNamed = append(allNamed, types.Universe.Lookup("error").Type())

	// Test each named type.
	var to, from, fromPtr []types.Type
	for _, U := range allNamed {
		if isInterface(T) {
			if T.MethodSet().Len() == 0 {
				continue // empty interface
			}
			if isInterface(U) {
				if U.MethodSet().Len() == 0 {
					continue // empty interface
				}

				// T interface, U interface
				if !types.IsIdentical(T, U) {
					if types.IsAssignableTo(U, T) {
						to = append(to, U)
					}
					if types.IsAssignableTo(T, U) {
						from = append(from, U)
					}
				}
			} else {
				// T interface, U concrete
				if types.IsAssignableTo(U, T) {
					to = append(to, U)
				} else if pU := types.NewPointer(U); types.IsAssignableTo(pU, T) {
					to = append(to, pU)
				}
			}
		} else if isInterface(U) {
			if U.MethodSet().Len() == 0 {
				continue // empty interface
			}

			// T concrete, U interface
			if types.IsAssignableTo(T, U) {
				from = append(from, U)
			} else if pT := types.NewPointer(T); types.IsAssignableTo(pT, U) {
				fromPtr = append(fromPtr, U)
			}
		}
	}

	var pos interface{} = qpos
	if nt, ok := deref(T).(*types.Named); ok {
		pos = nt.Obj()
	}

	// Sort types (arbitrarily) to ensure test nondeterminism.
	sort.Sort(typesByString(to))
	sort.Sort(typesByString(from))
	sort.Sort(typesByString(fromPtr))

	return &implementsResult{T, pos, to, from, fromPtr}, nil
}
Exemplo n.º 24
0
func returnsError(f *ssa.Function) bool {
	results := f.Signature.Results()
	n := results.Len()
	return n > 0 && types.IsIdentical(results.At(n-1).Type(), errorType)
}
Exemplo n.º 25
0
func describeValue(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeValueResult, error) {
	var expr ast.Expr
	var obj types.Object
	switch n := path[0].(type) {
	case *ast.ValueSpec:
		// ambiguous ValueSpec containing multiple names
		return nil, fmt.Errorf("multiple value specification")
	case *ast.Ident:
		obj = qpos.info.ObjectOf(n)
		expr = n
	case ast.Expr:
		expr = n
	default:
		// Is this reachable?
		return nil, fmt.Errorf("unexpected AST for expr: %T", n)
	}

	typ := qpos.info.TypeOf(expr)
	constVal := qpos.info.ValueOf(expr)

	// From this point on, we cannot fail with an error.
	// Failure to run the pointer analysis will be reported later.
	//
	// Our disposition to pointer analysis may be one of the following:
	// - ok:    ssa.Value was const or func.
	// - error: no ssa.Value for expr (e.g. trivially dead code)
	// - ok:    ssa.Value is non-pointerlike
	// - error: no Pointer for ssa.Value (e.g. analytically unreachable)
	// - ok:    Pointer has empty points-to set
	// - ok:    Pointer has non-empty points-to set
	// ptaErr is non-nil only in the "error:" cases.

	var ptaErr error
	var ptrs []pointerResult

	// Only run pointer analysis on pointerlike expression types.
	if pointer.CanPoint(typ) {
		// Determine the ssa.Value for the expression.
		var value ssa.Value
		if obj != nil {
			// def/ref of func/var/const object
			value, ptaErr = ssaValueForIdent(o.prog, qpos.info, obj, path)
		} else {
			// any other expression
			if qpos.info.ValueOf(path[0].(ast.Expr)) == nil { // non-constant?
				value, ptaErr = ssaValueForExpr(o.prog, qpos.info, path)
			}
		}
		if value != nil {
			// TODO(adonovan): IsIdentical may be too strict;
			// perhaps we need is-assignable or even
			// has-same-underlying-representation?
			indirect := types.IsIdentical(types.NewPointer(typ), value.Type())

			ptrs, ptaErr = describePointer(o, value, indirect)
		}
	}

	return &describeValueResult{
		qpos:     qpos,
		expr:     expr,
		typ:      typ,
		constVal: constVal,
		obj:      obj,
		ptaErr:   ptaErr,
		ptrs:     ptrs,
	}, nil
}
Exemplo n.º 26
0
func (c *PkgContext) translateExpr(expr ast.Expr) string {
	exprType := c.info.Types[expr]
	if value, valueFound := c.info.Values[expr]; valueFound {
		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 strconv.FormatBool(exact.BoolVal(value))
		case basic.Info()&types.IsInteger != 0:
			if is64Bit(basic) {
				d, _ := exact.Uint64Val(value)
				return fmt.Sprintf("new %s(%d, %d)", c.typeName(exprType), d>>32, d&(1<<32-1))
			}
			d, _ := exact.Int64Val(value)
			return strconv.FormatInt(d, 10)
		case basic.Info()&types.IsFloat != 0:
			f, _ := exact.Float64Val(value)
			return 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 fmt.Sprintf("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:
			buffer := bytes.NewBuffer(nil)
			for _, r := range []byte(exact.StringVal(value)) {
				switch r {
				case '\b':
					buffer.WriteString(`\b`)
				case '\f':
					buffer.WriteString(`\f`)
				case '\n':
					buffer.WriteString(`\n`)
				case '\r':
					buffer.WriteString(`\r`)
				case '\t':
					buffer.WriteString(`\t`)
				case '\v':
					buffer.WriteString(`\v`)
				case '"':
					buffer.WriteString(`\"`)
				case '\\':
					buffer.WriteString(`\\`)
				default:
					if r < 0x20 || r > 0x7E {
						fmt.Fprintf(buffer, `\x%02X`, r)
						continue
					}
					buffer.WriteByte(r)
				}
			}
			return `"` + buffer.String() + `"`
		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.info.Values[kve.Key])
					i = int(key)
					element = kve.Value
				}
				for len(elements) <= i {
					elements = append(elements, zero)
				}
				elements[i] = c.translateExprToType(element, elementType)
				i++
			}
			return elements
		}

		switch t := exprType.Underlying().(type) {
		case *types.Array:
			elements := collectIndexedElements(t.Elem())
			if len(elements) != 0 {
				zero := c.zeroValue(t.Elem())
				for len(elements) < int(t.Len()) {
					elements = append(elements, zero)
				}
				return createListComposite(t.Elem(), elements)
			}
			return fmt.Sprintf("Go$makeArray(%s, %d, function() { return %s; })", toArrayType(t.Elem()), t.Len(), c.zeroValue(t.Elem()))
		case *types.Slice:
			return fmt.Sprintf("new %s(%s)", c.typeName(exprType), createListComposite(t.Elem(), collectIndexedElements(t.Elem())))
		case *types.Map:
			elements := make([]string, len(e.Elts)*2)
			for i, element := range e.Elts {
				kve := element.(*ast.KeyValueExpr)
				elements[i*2] = c.translateExprToType(kve.Key, t.Key())
				elements[i*2+1] = c.translateExprToType(kve.Value, t.Elem())
			}
			return fmt.Sprintf("new %s([%s])", c.typeName(exprType), strings.Join(elements, ", "))
		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.translateExprToType(element, t.Field(i).Type())
				}
			}
			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.translateExprToType(kve.Value, t.Field(j).Type())
							break
						}
					}
				}
			}
			if named, isNamed := exprType.(*types.Named); isNamed {
				return fmt.Sprintf("new %s(%s)", c.objectName(named.Obj()), strings.Join(elements, ", "))
			}
			structVar := c.newVariable("_struct")
			c.translateTypeSpec(&ast.TypeSpec{
				Name: c.newIdent(structVar, t),
				Type: e.Type,
			})
			return fmt.Sprintf("new %s(%s)", structVar, strings.Join(elements, ", "))
		default:
			panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t))
		}

	case *ast.FuncLit:
		return strings.TrimSpace(string(c.CatchOutput(func() {
			c.newScope(func() {
				params := c.translateParams(e.Type)
				closurePrefix := "("
				closureSuffix := ")"
				if len(c.escapingVars) != 0 {
					list := strings.Join(c.escapingVars, ", ")
					closurePrefix = "(function(" + list + ") { return "
					closureSuffix = "; })(" + list + ")"
				}
				c.Printf("%sfunction(%s) {", closurePrefix, strings.Join(params, ", "))
				c.Indent(func() {
					c.translateFunctionBody(e.Body.List, exprType.(*types.Signature))
				})
				c.Printf("}%s", closureSuffix)
			})
		})))

	case *ast.UnaryExpr:
		switch e.Op {
		case token.AND:
			switch c.info.Types[e.X].Underlying().(type) {
			case *types.Struct, *types.Array:
				return c.translateExpr(e.X)
			default:
				if _, isComposite := e.X.(*ast.CompositeLit); isComposite {
					return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.translateExpr(e.X), c.typeName(c.info.Types[e]))
				}
				closurePrefix := ""
				closureSuffix := ""
				if len(c.escapingVars) != 0 {
					list := strings.Join(c.escapingVars, ", ")
					closurePrefix = "(function(" + list + ") { return "
					closureSuffix = "; })(" + list + ")"
				}
				vVar := c.newVariable("v")
				return fmt.Sprintf("%snew %s(function() { return %s; }, function(%s) { %s; })%s", closurePrefix, c.typeName(exprType), c.translateExpr(e.X), vVar, c.translateAssign(e.X, vVar), closureSuffix)
			}
		case token.ARROW:
			return "undefined"
		}

		t := c.info.Types[e.X]
		basic := t.Underlying().(*types.Basic)
		op := e.Op.String()
		switch e.Op {
		case token.ADD:
			return c.translateExpr(e.X)
		case token.SUB:
			if is64Bit(basic) {
				x := c.newVariable("x")
				return fmt.Sprintf("(%s = %s, new %s(-%s.high, -%s.low))", x, c.translateExpr(e.X), c.typeName(t), x, x)
			}
			if basic.Info()&types.IsComplex != 0 {
				x := c.newVariable("x")
				return fmt.Sprintf("(%s = %s, new %s(-%s.real, -%s.imag))", x, c.translateExpr(e.X), c.typeName(t), x, x)
			}
		case token.XOR:
			if is64Bit(basic) {
				x := c.newVariable("x")
				return fmt.Sprintf("(%s = %s, new %s(~%s.high, ~%s.low >>> 0))", x, c.translateExpr(e.X), c.typeName(t), x, x)
			}
			op = "~"
		}
		return fixNumber(fmt.Sprintf("%s%s", op, c.translateExpr(e.X)), basic)

	case *ast.BinaryExpr:
		if e.Op == token.NEQ {
			return fmt.Sprintf("!(%s)", c.translateExpr(&ast.BinaryExpr{
				X:  e.X,
				Op: token.EQL,
				Y:  e.Y,
			}))
		}

		t := c.info.Types[e.X]
		t2 := c.info.Types[e.Y]
		_, 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) {
				var expr string
				switch e.Op {
				case token.MUL:
					return fmt.Sprintf("Go$mul64(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y))
				case token.QUO:
					return fmt.Sprintf("Go$div64(%s, %s, false)", c.translateExpr(e.X), c.translateExpr(e.Y))
				case token.REM:
					return fmt.Sprintf("Go$div64(%s, %s, true)", c.translateExpr(e.X), c.translateExpr(e.Y))
				case token.SHL:
					return fmt.Sprintf("Go$shiftLeft64(%s, %s)", c.translateExpr(e.X), c.flatten64(e.Y))
				case token.SHR:
					return fmt.Sprintf("Go$shiftRight%s(%s, %s)", toJavaScriptType(basic), c.translateExpr(e.X), c.flatten64(e.Y))
				case token.EQL:
					expr = "x.high === y.high && x.low === y.low"
				case token.LSS:
					expr = "x.high < y.high || (x.high === y.high && x.low < y.low)"
				case token.LEQ:
					expr = "x.high < y.high || (x.high === y.high && x.low <= y.low)"
				case token.GTR:
					expr = "x.high > y.high || (x.high === y.high && x.low > y.low)"
				case token.GEQ:
					expr = "x.high > y.high || (x.high === y.high && x.low >= y.low)"
				case token.ADD, token.SUB:
					expr = fmt.Sprintf("new %s(x.high %s y.high, x.low %s y.low)", c.typeName(t), e.Op, e.Op)
				case token.AND, token.OR, token.XOR:
					expr = fmt.Sprintf("new %s(x.high %s y.high, (x.low %s y.low) >>> 0)", c.typeName(t), e.Op, e.Op)
				case token.AND_NOT:
					expr = fmt.Sprintf("new %s(x.high &~ y.high, (x.low &~ y.low) >>> 0)", c.typeName(t))
				default:
					panic(e.Op)
				}
				x := c.newVariable("x")
				y := c.newVariable("y")
				expr = strings.Replace(expr, "x.", x+".", -1)
				expr = strings.Replace(expr, "y.", y+".", -1)
				return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr)
			}

			if basic.Info()&types.IsComplex != 0 {
				var expr string
				switch e.Op {
				case token.EQL:
					expr = "x.real === y.real && x.imag === y.imag"
				case token.ADD, token.SUB:
					expr = fmt.Sprintf("new %s(x.real %s y.real, x.imag %s y.imag)", c.typeName(t), e.Op, e.Op)
				case token.MUL:
					expr = fmt.Sprintf("new %s(x.real * y.real - x.imag * y.imag, x.real * y.imag + x.imag * y.real)", c.typeName(t))
				case token.QUO:
					return fmt.Sprintf("Go$divComplex(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y))
				default:
					panic(e.Op)
				}
				x := c.newVariable("x")
				y := c.newVariable("y")
				expr = strings.Replace(expr, "x.", x+".", -1)
				expr = strings.Replace(expr, "y.", y+".", -1)
				return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr)
			}

			switch e.Op {
			case token.EQL:
				return fmt.Sprintf("%s === %s", c.translateExpr(e.X), c.translateExpr(e.Y))
			case token.LSS, token.LEQ, token.GTR, token.GEQ:
				return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y))
			case token.ADD, token.SUB:
				return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic)
			case token.MUL:
				if basic.Kind() == types.Int32 {
					x := c.newVariable("x")
					y := c.newVariable("y")
					return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >> 0) + (%s << 16 >>> 16) * %s) >> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y)
				}
				if basic.Kind() == types.Uint32 || basic.Kind() == types.Uintptr {
					x := c.newVariable("x")
					y := c.newVariable("y")
					return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >>> 0) + (%s << 16 >>> 16) * %s) >>> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y)
				}
				return fixNumber(fmt.Sprintf("%s * %s", c.translateExpr(e.X), c.translateExpr(e.Y)), basic)
			case token.QUO:
				value := fmt.Sprintf("%s / %s", c.translateExpr(e.X), c.translateExpr(e.Y))
				if basic.Info()&types.IsInteger != 0 {
					value = "(Go$obj = " + value + `, (Go$obj === Go$obj && Go$obj !== 1/0 && Go$obj !== -1/0) ? Go$obj : Go$throwRuntimeError("integer divide by zero"))`
				}
				switch basic.Kind() {
				case types.Int, types.Uint:
					return "(" + value + " >> 0)" // cut off decimals
				default:
					return fixNumber(value, basic)
				}
			case token.REM:
				return fmt.Sprintf(`(Go$obj = %s %% %s, Go$obj === Go$obj ? Go$obj : Go$throwRuntimeError("integer divide by zero"))`, c.translateExpr(e.X), c.translateExpr(e.Y))
			case token.SHL, token.SHR:
				op := e.Op.String()
				if e.Op == token.SHR && basic.Info()&types.IsUnsigned != 0 {
					op = ">>>"
				}
				if c.info.Values[e.Y] != nil {
					return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), op, c.translateExpr(e.Y)), basic)
				}
				if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 {
					return fixNumber(fmt.Sprintf("(%s >> Go$min(%s, 31))", c.translateExpr(e.X), c.translateExpr(e.Y)), basic)
				}
				y := c.newVariable("y")
				return fixNumber(fmt.Sprintf("(%s = %s, %s < 32 ? (%s %s %s) : 0)", y, c.translateExprToType(e.Y, types.Typ[types.Uint]), y, c.translateExpr(e.X), op, y), basic)
			case token.AND, token.OR, token.XOR:
				return fixNumber(fmt.Sprintf("(%s %s %s)", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic)
			case token.AND_NOT:
				return fixNumber(fmt.Sprintf("(%s &~ %s)", c.translateExpr(e.X), c.translateExpr(e.Y)), basic)
			default:
				panic(e.Op)
			}
		}

		switch e.Op {
		case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ, token.LAND, token.LOR:
			return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y))
		case token.EQL:
			switch u := t.Underlying().(type) {
			case *types.Struct:
				x := c.newVariable("x")
				y := c.newVariable("y")
				var conds []string
				for i := 0; i < u.NumFields(); i++ {
					field := u.Field(i)
					if field.Name() == "_" {
						continue
					}
					conds = append(conds, c.translateExpr(&ast.BinaryExpr{
						X:  c.newIdent(x+"."+field.Name(), field.Type()),
						Op: token.EQL,
						Y:  c.newIdent(y+"."+field.Name(), field.Type()),
					}))
				}
				if len(conds) == 0 {
					conds = []string{"true"}
				}
				return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), strings.Join(conds, " && "))
			case *types.Interface:
				return fmt.Sprintf("Go$interfaceIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t))
			case *types.Array:
				return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y))
			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 fmt.Sprintf("Go$sliceIsEqual(%s, %s, %s, %s)", c.translateExpr(xIndex.X), c.flatten64(xIndex.Index), c.translateExpr(yIndex.X), c.flatten64(yIndex.Index))
					}
				}
				switch u.Elem().Underlying().(type) {
				case *types.Struct, *types.Interface:
					return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t)
				case *types.Array:
					return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t))
				default:
					return fmt.Sprintf("Go$pointerIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t))
				}
			default:
				return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t)
			}
		default:
			panic(e.Op)
		}

	case *ast.ParenExpr:
		return fmt.Sprintf("(%s)", c.translateExpr(e.X))

	case *ast.IndexExpr:
		xType := c.info.Types[e.X]
		if ptr, isPointer := xType.(*types.Pointer); isPointer {
			xType = ptr.Elem()
		}
		switch t := xType.Underlying().(type) {
		case *types.Array:
			return fmt.Sprintf("%s[%s]", c.translateExpr(e.X), c.flatten64(e.Index))
		case *types.Slice:
			sliceVar := c.newVariable("_slice")
			indexVar := c.newVariable("_index")
			return fmt.Sprintf(`(%s = %s, %s = %s, (%s >= 0 && %s < %s.length) ? %s.array[%s.offset + %s] : Go$throwRuntimeError("index out of range"))`, sliceVar, c.translateExpr(e.X), indexVar, c.flatten64(e.Index), indexVar, indexVar, sliceVar, sliceVar, sliceVar, indexVar)
		case *types.Map:
			key := c.makeKey(e.Index, t.Key())
			if _, isTuple := exprType.(*types.Tuple); isTuple {
				return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? [Go$obj.v, true] : [%s, false])`, c.translateExpr(e.X), key, c.zeroValue(t.Elem()))
			}
			return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? Go$obj.v : %s)`, c.translateExpr(e.X), key, c.zeroValue(t.Elem()))
		case *types.Basic:
			return fmt.Sprintf("%s.charCodeAt(%s)", c.translateExpr(e.X), c.flatten64(e.Index))
		default:
			panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t))
		}

	case *ast.SliceExpr:
		b, isBasic := c.info.Types[e.X].(*types.Basic)
		isString := isBasic && b.Info()&types.IsString != 0
		slice := c.translateExprToType(e.X, exprType)
		if e.High == nil {
			if e.Low == nil {
				return slice
			}
			if isString {
				return fmt.Sprintf("%s.substring(%s)", slice, c.flatten64(e.Low))
			}
			return fmt.Sprintf("Go$subslice(%s, %s)", slice, c.flatten64(e.Low))
		}
		low := "0"
		if e.Low != nil {
			low = c.flatten64(e.Low)
		}
		if isString {
			return fmt.Sprintf("%s.substring(%s, %s)", slice, low, c.flatten64(e.High))
		}
		if e.Max != nil {
			return fmt.Sprintf("Go$subslice(%s, %s, %s, %s)", slice, low, c.flatten64(e.High), c.flatten64(e.Max))
		}
		return fmt.Sprintf("Go$subslice(%s, %s, %s)", slice, low, c.flatten64(e.High))

	case *ast.SelectorExpr:
		sel := c.info.Selections[e]
		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:
			return c.translateExpr(e.X) + "." + translateSelection(sel)
		case types.MethodVal:
			parameters := makeParametersList()
			recv := c.newVariable("_recv")
			return fmt.Sprintf("(%s = %s, function(%s) { return %s.%s(%s); })", recv, c.translateExpr(e.X), strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", "))
		case types.MethodExpr:
			recv := "recv"
			if isWrapped(sel.Recv()) {
				recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv()))
			}
			parameters := makeParametersList()
			return fmt.Sprintf("(function(%s) { return %s.%s(%s); })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", "))
		case types.PackageObj:
			return fmt.Sprintf("%s.%s", c.translateExpr(e.X), e.Sel.Name)
		}
		panic("")

	case *ast.CallExpr:
		plainFun := e.Fun
		for {
			if p, isParen := plainFun.(*ast.ParenExpr); isParen {
				plainFun = p.X
				continue
			}
			break
		}

		switch f := plainFun.(type) {
		case *ast.Ident:
			switch o := c.info.Objects[f].(type) {
			case *types.Builtin:
				switch o.Name() {
				case "new":
					t := c.info.Types[e].(*types.Pointer)
					if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Uintptr]) {
						return "new Uint8Array(8)"
					}
					switch t.Elem().Underlying().(type) {
					case *types.Struct, *types.Array:
						return c.zeroValue(t.Elem())
					default:
						return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.zeroValue(t.Elem()), c.typeName(t))
					}
				case "make":
					switch t2 := c.info.Types[e.Args[0]].Underlying().(type) {
					case *types.Slice:
						if len(e.Args) == 3 {
							return fmt.Sprintf("Go$subslice(new %s(Go$makeArray(%s, %s, function() { return %s; })), 0, %s)", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[2], types.Typ[types.Int]), c.zeroValue(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int]))
						}
						return fmt.Sprintf("new %s(Go$makeArray(%s, %s, function() { return %s; }))", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int]), c.zeroValue(t2.Elem()))
					default:
						args := []string{"undefined"}
						for _, arg := range e.Args[1:] {
							args = append(args, c.translateExpr(arg))
						}
						return fmt.Sprintf("new %s(%s)", c.typeName(c.info.Types[e.Args[0]]), strings.Join(args, ", "))
					}
				case "len":
					arg := c.translateExpr(e.Args[0])
					switch argType := c.info.Types[e.Args[0]].Underlying().(type) {
					case *types.Basic, *types.Slice:
						return arg + ".length"
					case *types.Pointer:
						return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len())
					case *types.Map:
						return fmt.Sprintf("(Go$obj = %s, Go$obj !== null ? Go$keys(Go$obj).length : 0)", arg)
					case *types.Chan:
						return "0"
					// length of array is constant
					default:
						panic(fmt.Sprintf("Unhandled len type: %T\n", argType))
					}
				case "cap":
					arg := c.translateExpr(e.Args[0])
					switch argType := c.info.Types[e.Args[0]].Underlying().(type) {
					case *types.Slice:
						return arg + ".capacity"
					case *types.Pointer:
						return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len())
					case *types.Chan:
						return "0"
					// capacity of array is constant
					default:
						panic(fmt.Sprintf("Unhandled cap type: %T\n", argType))
					}
				case "panic":
					return fmt.Sprintf("throw new Go$Panic(%s)", c.translateExprToType(e.Args[0], types.NewInterface(nil, nil)))
				case "append":
					if e.Ellipsis.IsValid() {
						return fmt.Sprintf("Go$append(%s, %s)", c.translateExpr(e.Args[0]), c.translateExprToType(e.Args[1], exprType))
					}
					sliceType := exprType.Underlying().(*types.Slice)
					toAppend := createListComposite(sliceType.Elem(), c.translateExprSlice(e.Args[1:], sliceType.Elem()))
					return fmt.Sprintf("Go$append(%s, new %s(%s))", c.translateExpr(e.Args[0]), c.typeName(exprType), toAppend)
				case "delete":
					return fmt.Sprintf(`delete (%s || Go$Map.Go$nil)[%s]`, c.translateExpr(e.Args[0]), c.makeKey(e.Args[1], c.info.Types[e.Args[0]].Underlying().(*types.Map).Key()))
				case "copy":
					return fmt.Sprintf("Go$copy(%s, %s)", c.translateExprToType(e.Args[0], types.NewSlice(types.Typ[types.Byte])), c.translateExprToType(e.Args[1], types.NewSlice(types.Typ[types.Byte])))
				case "print", "println":
					return fmt.Sprintf("console.log(%s)", strings.Join(c.translateExprSlice(e.Args, nil), ", "))
				case "complex":
					return fmt.Sprintf("new %s(%s, %s)", c.typeName(c.info.Types[e]), c.translateExpr(e.Args[0]), c.translateExpr(e.Args[1]))
				case "real":
					return c.translateExpr(e.Args[0]) + ".real"
				case "imag":
					return c.translateExpr(e.Args[0]) + ".imag"
				case "recover":
					return "Go$recover()"
				case "close":
					return `Go$throwRuntimeError("not supported by GopherJS: close")`
				default:
					panic(fmt.Sprintf("Unhandled builtin: %s\n", o.Name()))
				}
			case *types.TypeName: // conversion
				if basic, isBasic := o.Type().Underlying().(*types.Basic); isBasic && !types.IsIdentical(c.info.Types[e.Args[0]], types.Typ[types.UnsafePointer]) {
					return fixNumber(c.translateExprToType(e.Args[0], o.Type()), basic)
				}
				return c.translateExprToType(e.Args[0], o.Type())
			}
		case *ast.FuncType: // conversion
			return c.translateExprToType(e.Args[0], c.info.Types[plainFun])
		}

		funType := c.info.Types[plainFun]
		sig, isSig := funType.Underlying().(*types.Signature)
		if !isSig { // conversion
			if c.pkg.Path() == "reflect" {
				if call, isCall := e.Args[0].(*ast.CallExpr); isCall && types.IsIdentical(c.info.Types[call.Fun], types.Typ[types.UnsafePointer]) {
					if named, isNamed := funType.(*types.Pointer).Elem().(*types.Named); isNamed {
						return c.translateExpr(call.Args[0]) + "." + named.Obj().Name() // unsafe conversion
					}
				}
			}
			return c.translateExprToType(e.Args[0], funType)
		}

		var fun string
		switch f := plainFun.(type) {
		case *ast.SelectorExpr:
			sel := c.info.Selections[f]

			switch sel.Kind() {
			case types.MethodVal:
				methodsRecvType := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type()
				_, pointerExpected := methodsRecvType.(*types.Pointer)
				_, isPointer := sel.Recv().Underlying().(*types.Pointer)
				_, isStruct := sel.Recv().Underlying().(*types.Struct)
				_, isArray := sel.Recv().Underlying().(*types.Array)
				if pointerExpected && !isPointer && !isStruct && !isArray {
					target := c.translateExpr(f.X)
					vVar := c.newVariable("v")
					fun = fmt.Sprintf("(new %s(function() { return %s; }, function(%s) { %s = %s; })).%s", c.typeName(methodsRecvType), target, vVar, target, vVar, f.Sel.Name)
					break
				}
				if isWrapped(sel.Recv()) {
					fun = fmt.Sprintf("(new %s(%s)).%s", c.typeName(sel.Recv()), c.translateExpr(f.X), f.Sel.Name)
					break
				}
				fun = fmt.Sprintf("%s.%s", c.translateExpr(f.X), f.Sel.Name)
			case types.FieldVal, types.MethodExpr, types.PackageObj:
				fun = c.translateExpr(f)
			default:
				panic("")
			}
		default:
			fun = c.translateExpr(plainFun)
		}
		if len(e.Args) == 1 {
			if tuple, isTuple := c.info.Types[e.Args[0]].(*types.Tuple); isTuple {
				args := make([]ast.Expr, tuple.Len())
				for i := range args {
					args[i] = c.newIdent(fmt.Sprintf("Go$tuple[%d]", i), tuple.At(i).Type())
				}
				return fmt.Sprintf("(Go$tuple = %s, %s(%s))", c.translateExpr(e.Args[0]), fun, c.translateArgs(sig, args, false))
			}
		}
		return fmt.Sprintf("%s(%s)", fun, c.translateArgs(sig, e.Args, e.Ellipsis.IsValid()))

	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.IsIdentical(c.info.Types[c2.Fun], types.Typ[types.UnsafePointer]) {
				if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND {
					return c.translateExpr(unary.X) // unsafe conversion
				}
			}
		}
		switch exprType.Underlying().(type) {
		case *types.Struct, *types.Array:
			return c.translateExpr(e.X)
		}
		return c.translateExpr(e.X) + ".Go$get()"

	case *ast.TypeAssertExpr:
		if e.Type == nil {
			return c.translateExpr(e.X)
		}
		t := c.info.Types[e.Type]
		check := "Go$obj !== null && " + c.typeCheck("Go$obj.constructor", t)
		value := "Go$obj"
		if _, isInterface := t.Underlying().(*types.Interface); !isInterface {
			value += ".Go$val"
		}
		if _, isTuple := exprType.(*types.Tuple); isTuple {
			return fmt.Sprintf("(Go$obj = %s, %s ? [%s, true] : [%s, false])", c.translateExpr(e.X), check, value, c.zeroValue(c.info.Types[e.Type]))
		}
		return fmt.Sprintf("(Go$obj = %s, %s ? %s : Go$typeAssertionFailed(Go$obj))", c.translateExpr(e.X), check, value)

	case *ast.Ident:
		if e.Name == "_" {
			panic("Tried to translate underscore identifier.")
		}
		switch o := c.info.Objects[e].(type) {
		case *types.PkgName:
			return c.pkgVars[o.Pkg().Path()]
		case *types.Var, *types.Const:
			return c.objectName(o)
		case *types.Func:
			return c.objectName(o)
		case *types.TypeName:
			return c.typeName(o.Type())
		case *types.Nil:
			return c.zeroValue(c.info.Types[e])
		case nil:
			return e.Name
		default:
			panic(fmt.Sprintf("Unhandled object: %T\n", o))
		}

	case nil:
		return ""

	default:
		panic(fmt.Sprintf("Unhandled expression: %T\n", e))

	}
}
Exemplo n.º 27
0
// Ensure that, in debug mode, we can determine the ssa.Value
// corresponding to every ast.Expr.
func TestValueForExpr(t *testing.T) {
	imp := importer.New(new(importer.Config)) // (uses GCImporter)
	f, err := parser.ParseFile(imp.Fset, "testdata/valueforexpr.go", nil, parser.ParseComments)
	if err != nil {
		t.Error(err)
		return
	}

	mainInfo := imp.CreatePackage("main", f)

	prog := ssa.NewProgram(imp.Fset, 0)
	if err := prog.CreatePackages(imp); err != nil {
		t.Error(err)
		return
	}
	mainPkg := prog.Package(mainInfo.Pkg)
	mainPkg.SetDebugMode(true)
	mainPkg.Build()

	if false {
		// debugging
		for _, mem := range mainPkg.Members {
			if fn, ok := mem.(*ssa.Function); ok {
				fn.DumpTo(os.Stderr)
			}
		}
	}

	// Find the actual AST node for each canonical position.
	parenExprByPos := make(map[token.Pos]*ast.ParenExpr)
	ast.Inspect(f, func(n ast.Node) bool {
		if n != nil {
			if e, ok := n.(*ast.ParenExpr); ok {
				parenExprByPos[e.Pos()] = e
			}
		}
		return true
	})

	// Find all annotations of form /*@kind*/.
	for _, c := range f.Comments {
		text := strings.TrimSpace(c.Text())
		if text == "" || text[0] != '@' {
			continue
		}
		text = text[1:]
		pos := c.End() + 1
		position := imp.Fset.Position(pos)
		var e ast.Expr
		if target := parenExprByPos[pos]; target == nil {
			t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text)
			continue
		} else {
			e = target.X
		}

		path, _ := importer.PathEnclosingInterval(f, pos, pos)
		if path == nil {
			t.Errorf("%s: can't find AST path from root to comment: %s", position, text)
			continue
		}

		fn := ssa.EnclosingFunction(mainPkg, path)
		if fn == nil {
			t.Errorf("%s: can't find enclosing function", position)
			continue
		}

		v, gotAddr := fn.ValueForExpr(e) // (may be nil)
		got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")
		if want := text; got != want {
			t.Errorf("%s: got value %q, want %q", position, got, want)
		}
		if v != nil {
			T := v.Type()
			if gotAddr {
				T = T.Underlying().(*types.Pointer).Elem() // deref
			}
			if !types.IsIdentical(T, mainInfo.TypeOf(e)) {
				t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T)
			}
		}
	}
}
Exemplo n.º 28
0
func (c *PkgContext) translateExprToType(expr ast.Expr, desiredType types.Type) string {
	if desiredType == nil {
		return c.translateExpr(expr)
	}
	if expr == nil {
		return c.zeroValue(desiredType)
	}

	exprType := c.info.Types[expr]

	// TODO should be fixed in go/types
	if _, isSlice := exprType.(*types.Slice); isSlice {
		constValue := c.info.Values[expr]
		if constValue != nil && constValue.Kind() == exact.String {
			exprType = types.Typ[types.String]
			c.info.Types[expr] = exprType
		}
	}

	basicExprType, isBasicExpr := exprType.Underlying().(*types.Basic)
	if isBasicExpr && basicExprType.Kind() == types.UntypedNil {
		return c.zeroValue(desiredType)
	}

	switch t := desiredType.Underlying().(type) {
	case *types.Basic:
		switch {
		case t.Info()&types.IsInteger != 0:
			switch {
			case is64Bit(t):
				switch {
				case !is64Bit(basicExprType):
					return fmt.Sprintf("new %s(0, %s)", c.typeName(desiredType), c.translateExpr(expr))
				case !types.IsIdentical(exprType, desiredType):
					return fmt.Sprintf("(Go$obj = %s, new %s(Go$obj.high, Go$obj.low))", c.translateExpr(expr), c.typeName(desiredType))
				}
			case is64Bit(basicExprType):
				return fmt.Sprintf("(Go$obj = %s, Go$obj.low + ((Go$obj.high >> 31) * 4294967296))", c.translateExpr(expr))
			case basicExprType.Info()&types.IsFloat != 0:
				return fmt.Sprintf("(%s >> 0)", c.translateExpr(expr))
			default:
				return c.translateExpr(expr)
			}
		case t.Info()&types.IsFloat != 0:
			return c.flatten64(expr)
		case t.Info()&types.IsString != 0:
			value := c.translateExpr(expr)
			switch et := exprType.Underlying().(type) {
			case *types.Basic:
				if is64Bit(et) {
					value = fmt.Sprintf("%s.low", value)
				}
				if et.Info()&types.IsNumeric != 0 {
					return fmt.Sprintf("Go$encodeRune(%s)", value)
				}
				return value
			case *types.Slice:
				if types.IsIdentical(et.Elem().Underlying(), types.Typ[types.Rune]) {
					return fmt.Sprintf("Go$runesToString(%s)", value)
				}
				return fmt.Sprintf("Go$bytesToString(%s)", value)
			default:
				panic(fmt.Sprintf("Unhandled conversion: %v\n", et))
			}
		case t.Kind() == types.UnsafePointer:
			if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND {
				if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr {
					return fmt.Sprintf("Go$sliceToArray(%s)", c.translateExprToType(indexExpr.X, types.NewSlice(nil)))
				}
				if ident, isIdent := unary.X.(*ast.Ident); isIdent && ident.Name == "_zero" {
					return "new Uint8Array(0)"
				}
			}
			if ptr, isPtr := c.info.Types[expr].(*types.Pointer); isPtr {
				if s, isStruct := ptr.Elem().Underlying().(*types.Struct); isStruct {
					array := c.newVariable("_array")
					target := c.newVariable("_struct")
					c.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s))
					c.Delayed(func() {
						c.Printf("%s = %s;", target, c.translateExpr(expr))
						c.loadStruct(array, target, s)
					})
					return array
				}
			}
		}

	case *types.Slice:
		switch et := exprType.Underlying().(type) {
		case *types.Basic:
			if et.Info()&types.IsString != 0 {
				if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Rune]) {
					return fmt.Sprintf("new %s(Go$stringToRunes(%s))", c.typeName(desiredType), c.translateExpr(expr))
				}
				return fmt.Sprintf("new %s(Go$stringToBytes(%s))", c.typeName(desiredType), c.translateExpr(expr))
			}
		case *types.Array, *types.Pointer:
			return fmt.Sprintf("new %s(%s)", c.typeName(desiredType), c.translateExpr(expr))
		}
		_, desiredIsNamed := desiredType.(*types.Named)
		if desiredIsNamed && !types.IsIdentical(exprType, desiredType) {
			return fmt.Sprintf("(Go$obj = %s, Go$subslice(new %s(Go$obj.array), Go$obj.offset, Go$obj.offset + Go$obj.length))", c.translateExpr(expr), c.typeName(desiredType))
		}
		return c.translateExpr(expr)

	case *types.Interface:
		if isWrapped(exprType) {
			return fmt.Sprintf("new %s(%s)", c.typeName(exprType), c.translateExpr(expr))
		}
		if _, isStruct := exprType.Underlying().(*types.Struct); isStruct {
			return fmt.Sprintf("(Go$obj = %s, new Go$obj.constructor.Go$NonPointer(Go$obj))", c.translateExpr(expr))
		}

	case *types.Pointer:
		n, isNamed := t.Elem().(*types.Named)
		s, isStruct := t.Elem().Underlying().(*types.Struct)

		if isStruct && types.IsIdentical(exprType, types.Typ[types.UnsafePointer]) {
			array := c.newVariable("_array")
			target := c.newVariable("_struct")
			c.Printf("%s = %s;", array, c.translateExpr(expr))
			c.Printf("%s = %s;", target, c.zeroValue(t.Elem()))
			c.loadStruct(array, target, s)
			return target
		}

		if isNamed && !types.IsIdentical(exprType, desiredType) {
			if isStruct {
				return c.clone(c.translateExpr(expr), t.Elem())
			}
			return fmt.Sprintf("(Go$obj = %s, new %s.Go$Pointer(Go$obj.Go$get, Go$obj.Go$set))", c.translateExpr(expr), c.typeName(n))
		}

	case *types.Struct, *types.Array:
		if _, isComposite := expr.(*ast.CompositeLit); !isComposite {
			return c.clone(c.translateExpr(expr), desiredType)
		}

	case *types.Chan, *types.Map, *types.Signature:
		// no converion

	default:
		panic(fmt.Sprintf("Unhandled conversion: %v\n", t))
	}

	return c.translateExpr(expr)
}
Exemplo n.º 29
0
// matchArgTypeInternal is the internal version of matchArgType. It carries a map
// remembering what types are in progress so we don't recur when faced with recursive
// types or mutually recursive types.
func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
	// %v, %T accept any argument type.
	if t == anyType {
		return true
	}
	if typ == nil {
		// external call
		typ = f.pkg.types[arg]
		if typ == nil {
			return true // probably a type check problem
		}
	}
	// If the type implements fmt.Formatter, we have nothing to check.
	// But (see issue 6259) that's not easy to verify, so instead we see
	// if its method set contains a Format function. We could do better,
	// even now, but we don't need to be 100% accurate. Wait for 6259 to
	// be fixed instead. TODO.
	if hasMethod(typ, "Format") {
		return true
	}
	// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
	if t&argString != 0 {
		if types.Implements(typ, errorType, false) || types.Implements(typ, stringerType, false) {
			return true
		}
	}

	typ = typ.Underlying()
	if inProgress[typ] {
		// We're already looking at this type. The call that started it will take care of it.
		return true
	}
	inProgress[typ] = true

	switch typ := typ.(type) {
	case *types.Signature:
		return t&argPointer != 0

	case *types.Map:
		// Recur: map[int]int matches %d.
		return t&argPointer != 0 ||
			(f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress))

	case *types.Chan:
		return t&argPointer != 0

	case *types.Array:
		// Same as slice.
		if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
			return true // %s matches []byte
		}
		// Recur: []int matches %d.
		return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress)

	case *types.Slice:
		// Same as array.
		if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
			return true // %s matches []byte
		}
		// Recur: []int matches %d. But watch out for
		//	type T []T
		// If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
		return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)

	case *types.Pointer:
		// Ugly, but dealing with an edge case: a known pointer to an invalid type,
		// probably something from a failed import.
		if typ.Elem().String() == "invalid type" {
			if *verbose {
				f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg))
			}
			return true // special case
		}
		// If it's actually a pointer with %p, it prints as one.
		if t == argPointer {
			return true
		}
		// If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct.
		if str, ok := typ.Elem().Underlying().(*types.Struct); ok {
			return f.matchStructArgType(t, str, arg, inProgress)
		}
		// The rest can print with %p as pointers, or as integers with %x etc.
		return t&(argInt|argPointer) != 0

	case *types.Struct:
		return f.matchStructArgType(t, typ, arg, inProgress)

	case *types.Interface:
		// If the static type of the argument is empty interface, there's little we can do.
		// Example:
		//	func f(x interface{}) { fmt.Printf("%s", x) }
		// Whether x is valid for %s depends on the type of the argument to f. One day
		// we will be able to do better. For now, we assume that empty interface is OK
		// but non-empty interfaces, with Stringer and Error handled above, are errors.
		return typ.NumMethods() == 0

	case *types.Basic:
		switch typ.Kind() {
		case types.UntypedBool,
			types.Bool:
			return t&argBool != 0

		case types.UntypedInt,
			types.Int,
			types.Int8,
			types.Int16,
			types.Int32,
			types.Int64,
			types.Uint,
			types.Uint8,
			types.Uint16,
			types.Uint32,
			types.Uint64,
			types.Uintptr:
			return t&argInt != 0

		case types.UntypedFloat,
			types.Float32,
			types.Float64:
			return t&argFloat != 0

		case types.UntypedComplex,
			types.Complex64,
			types.Complex128:
			return t&argComplex != 0

		case types.UntypedString,
			types.String:
			return t&argString != 0

		case types.UnsafePointer:
			return t&(argPointer|argInt) != 0

		case types.UntypedRune:
			return t&(argInt|argRune) != 0

		case types.UntypedNil:
			return t&argPointer != 0 // TODO?

		case types.Invalid:
			if *verbose {
				f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg))
			}
			return true // Probably a type check problem.
		}
		panic("unreachable")
	}

	return false
}
Exemplo n.º 30
0
// peers enumerates, for a given channel send (or receive) operation,
// the set of possible receives (or sends) that correspond to it.
//
// TODO(adonovan): support reflect.{Select,Recv,Send}.
// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
// or the implicit receive in "for v := range ch".
//
func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
	arrowPos := findArrow(qpos)
	if arrowPos == token.NoPos {
		return nil, fmt.Errorf("there is no send/receive here")
	}

	buildSSA(o)

	var queryOp chanOp // the originating send or receive operation
	var ops []chanOp   // all sends/receives of opposite direction

	// Look at all send/receive instructions in the whole ssa.Program.
	// Build a list of those of same type to query.
	allFuncs := ssautil.AllFunctions(o.prog)
	for fn := range allFuncs {
		for _, b := range fn.Blocks {
			for _, instr := range b.Instrs {
				for _, op := range chanOps(instr) {
					ops = append(ops, op)
					if op.pos == arrowPos {
						queryOp = op // we found the query op
					}
				}
			}
		}
	}
	if queryOp.ch == nil {
		return nil, fmt.Errorf("ssa.Instruction for send/receive not found")
	}

	// Discard operations of wrong channel element type.
	// Build set of channel ssa.Values as query to pointer analysis.
	// We compare channels by element types, not channel types, to
	// ignore both directionality and type names.
	queryType := queryOp.ch.Type()
	queryElemType := queryType.Underlying().(*types.Chan).Elem()
	o.ptaConfig.AddQuery(queryOp.ch)
	i := 0
	for _, op := range ops {
		if types.IsIdentical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
			o.ptaConfig.AddQuery(op.ch)
			ops[i] = op
			i++
		}
	}
	ops = ops[:i]

	// Run the pointer analysis.
	ptares := ptrAnalysis(o)

	// Combine the PT sets from all contexts.
	queryChanPts := pointer.PointsToCombined(ptares.Queries[queryOp.ch])

	// Ascertain which make(chan) labels the query's channel can alias.
	var makes []token.Pos
	for _, label := range queryChanPts.Labels() {
		makes = append(makes, label.Pos())
	}
	sort.Sort(byPos(makes))

	// Ascertain which send/receive operations can alias the same make(chan) labels.
	var sends, receives []token.Pos
	for _, op := range ops {
		for _, ptr := range ptares.Queries[op.ch] {
			if ptr.PointsTo().Intersects(queryChanPts) {
				if op.dir == types.SendOnly {
					sends = append(sends, op.pos)
				} else {
					receives = append(receives, op.pos)
				}
			}
		}
	}
	sort.Sort(byPos(sends))
	sort.Sort(byPos(receives))

	return &peersResult{
		queryPos:  arrowPos,
		queryType: queryType,
		makes:     makes,
		sends:     sends,
		receives:  receives,
	}, nil
}