1
0
mirror of https://github.com/golang/go synced 2024-11-23 03:20:03 -07:00

Merge branch 'master' of git://github.com/golang/go into fix-fmt-formatting

This commit is contained in:
alex-semenyuk 2020-04-20 23:23:13 +03:00
commit 6a8dcb8c54
21 changed files with 604 additions and 252 deletions

View File

@ -10,7 +10,7 @@ if(! test -f make.rc){
exit wrongdir
}
. ./make.rc --no-banner
. ./make.rc --no-banner $*
bind -b $GOROOT/bin /bin
./run.rc --no-rebuild
$GOTOOLDIR/dist banner # print build info

View File

@ -586,7 +586,7 @@ s%~ %%g
*/
func symfmt(b *bytes.Buffer, s *types.Sym, flag FmtFlag, mode fmtMode) {
if s.Pkg != nil && flag&FmtShort == 0 {
if flag&FmtShort == 0 {
switch mode {
case FErr: // This is for the user
if s.Pkg == builtinpkg || s.Pkg == localpkg {

View File

@ -45,6 +45,14 @@ func isRuntimePkg(p *types.Pkg) bool {
return p.Path == "runtime"
}
// isReflectPkg reports whether p is package reflect.
func isReflectPkg(p *types.Pkg) bool {
if p == localpkg {
return myimportpath == "reflect"
}
return p.Path == "reflect"
}
// The Class of a variable/function describes the "storage class"
// of a variable or function. During parsing, storage classes are
// called declaration contexts.

View File

@ -585,7 +585,7 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node {
case OCALLMETH:
// Prevent inlining some reflect.Value methods when using checkptr,
// even when package reflect was compiled without it (#35073).
if s := n.Left.Sym; Debug_checkptr != 0 && s.Pkg.Path == "reflect" && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
if s := n.Left.Sym; Debug_checkptr != 0 && isReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
return n
}
}

View File

@ -365,11 +365,12 @@ func stringsym(pos src.XPos, s string) (data *obj.LSym) {
var slicebytes_gen int
func slicebytes(nam *Node, s string, len int) {
func slicebytes(nam *Node, s string) {
slicebytes_gen++
symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen)
sym := localpkg.Lookup(symname)
sym.Def = asTypesNode(newname(sym))
symnode := newname(sym)
sym.Def = asTypesNode(symnode)
lsym := sym.Linksym()
off := dsname(lsym, 0, s, nam.Pos, "slice")
@ -378,11 +379,7 @@ func slicebytes(nam *Node, s string, len int) {
if nam.Op != ONAME {
Fatalf("slicebytes %v", nam)
}
nsym := nam.Sym.Linksym()
off = int(nam.Xoffset)
off = dsymptr(nsym, off, lsym, 0)
off = duintptr(nsym, off, uint64(len))
duintptr(nsym, off, uint64(len))
slicesym(nam, symnode, int64(len(s)))
}
func dsname(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
@ -430,69 +427,86 @@ func slicesym(n, arr *Node, lencap int64) {
s.WriteInt(Ctxt, base+sliceCapOffset, Widthptr, lencap)
}
func gdata(nam *Node, nr *Node, wid int) {
if nam.Op != ONAME {
Fatalf("gdata nam op %v", nam.Op)
// addrsym writes the static address of a to n. a must be an ONAME.
// Neither n nor a is modified.
func addrsym(n, a *Node) {
if n.Op != ONAME {
Fatalf("addrsym n op %v", n.Op)
}
if nam.Sym == nil {
Fatalf("gdata nil nam sym")
if n.Sym == nil {
Fatalf("addrsym nil n sym")
}
s := nam.Sym.Linksym()
if a.Op != ONAME {
Fatalf("addrsym a op %v", a.Op)
}
s := n.Sym.Linksym()
s.WriteAddr(Ctxt, n.Xoffset, Widthptr, a.Sym.Linksym(), a.Xoffset)
}
switch nr.Op {
case OLITERAL:
switch u := nr.Val().U.(type) {
// pfuncsym writes the static address of f to n. f must be a global function.
// Neither n nor f is modified.
func pfuncsym(n, f *Node) {
if n.Op != ONAME {
Fatalf("pfuncsym n op %v", n.Op)
}
if n.Sym == nil {
Fatalf("pfuncsym nil n sym")
}
if f.Class() != PFUNC {
Fatalf("pfuncsym class not PFUNC %d", f.Class())
}
s := n.Sym.Linksym()
s.WriteAddr(Ctxt, n.Xoffset, Widthptr, funcsym(f.Sym).Linksym(), f.Xoffset)
}
// litsym writes the static literal c to n.
// Neither n nor c is modified.
func litsym(n, c *Node, wid int) {
if n.Op != ONAME {
Fatalf("litsym n op %v", n.Op)
}
if c.Op != OLITERAL {
Fatalf("litsym c op %v", c.Op)
}
if n.Sym == nil {
Fatalf("litsym nil n sym")
}
s := n.Sym.Linksym()
switch u := c.Val().U.(type) {
case bool:
i := int64(obj.Bool2int(u))
s.WriteInt(Ctxt, nam.Xoffset, wid, i)
s.WriteInt(Ctxt, n.Xoffset, wid, i)
case *Mpint:
s.WriteInt(Ctxt, nam.Xoffset, wid, u.Int64())
s.WriteInt(Ctxt, n.Xoffset, wid, u.Int64())
case *Mpflt:
f := u.Float64()
switch nam.Type.Etype {
switch n.Type.Etype {
case TFLOAT32:
s.WriteFloat32(Ctxt, nam.Xoffset, float32(f))
s.WriteFloat32(Ctxt, n.Xoffset, float32(f))
case TFLOAT64:
s.WriteFloat64(Ctxt, nam.Xoffset, f)
s.WriteFloat64(Ctxt, n.Xoffset, f)
}
case *Mpcplx:
r := u.Real.Float64()
i := u.Imag.Float64()
switch nam.Type.Etype {
switch n.Type.Etype {
case TCOMPLEX64:
s.WriteFloat32(Ctxt, nam.Xoffset, float32(r))
s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i))
s.WriteFloat32(Ctxt, n.Xoffset, float32(r))
s.WriteFloat32(Ctxt, n.Xoffset+4, float32(i))
case TCOMPLEX128:
s.WriteFloat64(Ctxt, nam.Xoffset, r)
s.WriteFloat64(Ctxt, nam.Xoffset+8, i)
s.WriteFloat64(Ctxt, n.Xoffset, r)
s.WriteFloat64(Ctxt, n.Xoffset+8, i)
}
case string:
symdata := stringsym(nam.Pos, u)
s.WriteAddr(Ctxt, nam.Xoffset, Widthptr, symdata, 0)
s.WriteInt(Ctxt, nam.Xoffset+int64(Widthptr), Widthptr, int64(len(u)))
symdata := stringsym(n.Pos, u)
s.WriteAddr(Ctxt, n.Xoffset, Widthptr, symdata, 0)
s.WriteInt(Ctxt, n.Xoffset+int64(Widthptr), Widthptr, int64(len(u)))
default:
Fatalf("gdata unhandled OLITERAL %v", nr)
}
case OADDR:
if nr.Left.Op != ONAME {
Fatalf("gdata ADDR left op %v", nr.Left.Op)
}
to := nr.Left
s.WriteAddr(Ctxt, nam.Xoffset, wid, to.Sym.Linksym(), to.Xoffset)
case ONAME:
if nr.Class() != PFUNC {
Fatalf("gdata NAME not PFUNC %d", nr.Class())
}
s.WriteAddr(Ctxt, nam.Xoffset, wid, funcsym(nr.Sym).Linksym(), nr.Xoffset)
default:
Fatalf("gdata unhandled op %v %v\n", nr, nr.Op)
Fatalf("litsym unhandled OLITERAL %v", c)
}
}

View File

@ -71,7 +71,7 @@ func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
return false
}
if r.Class() == PFUNC {
gdata(l, r, Widthptr)
pfuncsym(l, r)
return true
}
if r.Class() != PEXTERN || r.Sym.Pkg != localpkg {
@ -107,13 +107,12 @@ func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
if isZero(r) {
return true
}
gdata(l, r, int(l.Type.Width))
litsym(l, r, int(l.Type.Width))
return true
case OADDR:
switch r.Left.Op {
case ONAME:
gdata(l, r, int(l.Type.Width))
if a := r.Left; a.Op == ONAME {
addrsym(l, a)
return true
}
@ -121,7 +120,7 @@ func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
switch r.Left.Op {
case OARRAYLIT, OSLICELIT, OSTRUCTLIT, OMAPLIT:
// copy pointer
gdata(l, nod(OADDR, s.inittemps[r], nil), int(l.Type.Width))
addrsym(l, s.inittemps[r])
return true
}
@ -140,7 +139,7 @@ func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
n.Xoffset = l.Xoffset + e.Xoffset
n.Type = e.Expr.Type
if e.Expr.Op == OLITERAL {
gdata(n, e.Expr, int(n.Type.Width))
litsym(n, e.Expr, int(n.Type.Width))
continue
}
ll := n.sepcopy()
@ -175,15 +174,13 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
if isZero(r) {
return true
}
gdata(l, r, int(l.Type.Width))
litsym(l, r, int(l.Type.Width))
return true
case OADDR:
var nam Node
if stataddr(&nam, r.Left) {
n := *r
n.Left = &nam
gdata(l, &n, int(l.Type.Width))
addrsym(l, &nam)
return true
}
fallthrough
@ -195,7 +192,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
a := staticname(r.Left.Type)
s.inittemps[r] = a
gdata(l, nod(OADDR, a, nil), int(l.Type.Width))
addrsym(l, a)
// Init underlying literal.
if !s.staticassign(a, r.Left) {
@ -208,7 +205,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
case OSTR2BYTES:
if l.Class() == PEXTERN && r.Left.Op == OLITERAL {
sval := strlit(r.Left)
slicebytes(l, sval, len(sval))
slicebytes(l, sval)
return true
}
@ -235,7 +232,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
n.Xoffset = l.Xoffset + e.Xoffset
n.Type = e.Expr.Type
if e.Expr.Op == OLITERAL {
gdata(n, e.Expr, int(n.Type.Width))
litsym(n, e.Expr, int(n.Type.Width))
continue
}
setlineno(e.Expr)
@ -257,7 +254,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
}
// Closures with no captured variables are globals,
// so the assignment can be done at link time.
gdata(l, r.Func.Closure.Func.Nname, Widthptr)
pfuncsym(l, r.Func.Closure.Func.Nname)
return true
}
closuredebugruntimecheck(r)
@ -291,7 +288,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
n := l.copy()
// Emit itab, advance offset.
gdata(n, itab, Widthptr)
addrsym(n, itab.Left) // itab is an OADDR node
n.Xoffset += int64(Widthptr)
// Emit data.
@ -314,9 +311,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
if !s.staticassign(a, val) {
s.append(nod(OAS, a, val))
}
ptr := nod(OADDR, a, nil)
n.Type = types.NewPtr(val.Type)
gdata(n, ptr, Widthptr)
addrsym(n, a)
}
return true
@ -1157,10 +1152,10 @@ func genAsStatic(as *Node) {
switch {
case as.Right.Op == OLITERAL:
litsym(&nam, as.Right, int(as.Right.Type.Width))
case as.Right.Op == ONAME && as.Right.Class() == PFUNC:
pfuncsym(&nam, as.Right)
default:
Fatalf("genAsStatic: rhs %v", as.Right)
}
gdata(&nam, as.Right, int(as.Right.Type.Width))
}

View File

@ -394,7 +394,7 @@ func buildssa(fn *Node, worker int) *ssa.Func {
// For this value, AuxInt is initialized to zero by default
startDeferBits := s.entryNewValue0(ssa.OpConst8, types.Types[TUINT8])
s.vars[&deferBitsVar] = startDeferBits
s.deferBitsAddr = s.addr(deferBitsTemp, false)
s.deferBitsAddr = s.addr(deferBitsTemp)
s.store(types.Types[TUINT8], s.deferBitsAddr, startDeferBits)
// Make sure that the deferBits stack slot is kept alive (for use
// by panics) and stores to deferBits are not eliminated, even if
@ -1246,7 +1246,7 @@ func (s *state) stmt(n *Node) {
if rhs == nil {
r = nil // Signal assign to use OpZero.
} else {
r = s.addr(rhs, false)
r = s.addr(rhs)
}
} else {
if rhs == nil {
@ -2008,10 +2008,10 @@ func (s *state) expr(n *Node) *ssa.Value {
if s.canSSA(n) {
return s.variable(n, n.Type)
}
addr := s.addr(n, false)
addr := s.addr(n)
return s.load(n.Type, addr)
case OCLOSUREVAR:
addr := s.addr(n, false)
addr := s.addr(n)
return s.load(n.Type, addr)
case OLITERAL:
switch u := n.Val().U.(type) {
@ -2542,14 +2542,14 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.expr(n.Left)
case OADDR:
return s.addr(n.Left, n.Bounded())
return s.addr(n.Left)
case ORESULT:
addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
return s.load(n.Type, addr)
case ODEREF:
p := s.exprPtr(n.Left, false, n.Pos)
p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
return s.load(n.Type, p)
case ODOT:
@ -2567,14 +2567,14 @@ func (s *state) expr(n *Node) *ssa.Value {
// prevents false memory dependencies in race/msan
// instrumentation.
if islvalue(n) && !s.canSSA(n) {
p := s.addr(n, false)
p := s.addr(n)
return s.load(n.Type, p)
}
v := s.expr(n.Left)
return s.newValue1I(ssa.OpStructSelect, n.Type, int64(fieldIdx(n)), v)
case ODOTPTR:
p := s.exprPtr(n.Left, false, n.Pos)
p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
p = s.newValue1I(ssa.OpOffPtr, types.NewPtr(n.Type), n.Xoffset, p)
return s.load(n.Type, p)
@ -2600,7 +2600,7 @@ func (s *state) expr(n *Node) *ssa.Value {
}
return s.load(types.Types[TUINT8], ptr)
case n.Left.Type.IsSlice():
p := s.addr(n, false)
p := s.addr(n)
return s.load(n.Left.Type.Elem(), p)
case n.Left.Type.IsArray():
if canSSAType(n.Left.Type) {
@ -2620,7 +2620,7 @@ func (s *state) expr(n *Node) *ssa.Value {
s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded()) // checks i == 0
return s.newValue1I(ssa.OpArraySelect, n.Type, 0, a)
}
p := s.addr(n, false)
p := s.addr(n)
return s.load(n.Left.Type.Elem(), p)
default:
s.Fatalf("bad type for index %v", n.Left.Type)
@ -2786,7 +2786,7 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
var slice, addr *ssa.Value
if inplace {
addr = s.addr(sn, false)
addr = s.addr(sn)
slice = s.load(n.Type, addr)
} else {
slice = s.expr(sn)
@ -2867,7 +2867,7 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
if canSSAType(n.Type) {
args = append(args, argRec{v: s.expr(n), store: true})
} else {
v := s.addr(n, false)
v := s.addr(n)
args = append(args, argRec{v: v})
}
}
@ -3038,7 +3038,7 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask)
}
// Left is not ssa-able. Compute its address.
addr := s.addr(left, false)
addr := s.addr(left)
if isReflectHeaderDataField(left) {
// Package unsafe's documentation says storing pointers into
// reflect.SliceHeader and reflect.StringHeader's Data fields
@ -4215,7 +4215,7 @@ func (s *state) openDeferSave(n *Node, t *types.Type, val *ssa.Value) *ssa.Value
argTemp.Name.SetNeedzero(true)
}
if !canSSA {
a := s.addr(n, false)
a := s.addr(n)
s.move(t, addrArgTemp, a)
return addrArgTemp
}
@ -4387,7 +4387,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
d := tempAt(n.Pos, s.curfn, t)
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem())
addr := s.addr(d, false)
addr := s.addr(d)
// Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer.
// 0: siz
@ -4578,9 +4578,7 @@ func etypesign(e types.EType) int8 {
// addr converts the address of the expression n to SSA, adds it to s and returns the SSA result.
// The value that the returned Value represents is guaranteed to be non-nil.
// If bounded is true then this address does not require a nil check for its operand
// even if that would otherwise be implied.
func (s *state) addr(n *Node, bounded bool) *ssa.Value {
func (s *state) addr(n *Node) *ssa.Value {
if n.Op != ONAME {
s.pushLine(n.Pos)
defer s.popLine()
@ -4633,25 +4631,25 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
p := s.newValue1(ssa.OpSlicePtr, t, a)
return s.newValue2(ssa.OpPtrIndex, t, p, i)
} else { // array
a := s.addr(n.Left, bounded)
a := s.addr(n.Left)
i := s.expr(n.Right)
len := s.constInt(types.Types[TINT], n.Left.Type.NumElem())
i = s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded())
return s.newValue2(ssa.OpPtrIndex, types.NewPtr(n.Left.Type.Elem()), a, i)
}
case ODEREF:
return s.exprPtr(n.Left, bounded, n.Pos)
return s.exprPtr(n.Left, n.Bounded(), n.Pos)
case ODOT:
p := s.addr(n.Left, bounded)
p := s.addr(n.Left)
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
case ODOTPTR:
p := s.exprPtr(n.Left, bounded, n.Pos)
p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
case OCLOSUREVAR:
return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr))
case OCONVNOP:
addr := s.addr(n.Left, bounded)
addr := s.addr(n.Left)
return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
case OCALLFUNC, OCALLINTER, OCALLMETH:
return s.call(n, callNormal)
@ -5076,7 +5074,7 @@ func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off in
}
if !canSSAType(t) {
a := s.addr(n, false)
a := s.addr(n)
s.move(t, addr, a)
return
}
@ -5630,7 +5628,7 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
// TODO: get rid of some of these temporaries.
tmp = tempAt(n.Pos, s.curfn, n.Type)
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, tmp, s.mem())
addr = s.addr(tmp, false)
addr = s.addr(tmp)
}
cond := s.newValue2(ssa.OpEqPtr, types.Types[TBOOL], itab, targetITab)

View File

@ -1913,10 +1913,10 @@ func ifaceData(pos src.XPos, n *Node, t *types.Type) *Node {
return ptr
}
ptr.Type = types.NewPtr(t)
ptr.SetBounded(true)
ptr.SetTypecheck(1)
ind := nodl(pos, ODEREF, ptr, nil)
ind.Type = t
ind.SetTypecheck(1)
ind.SetBounded(true)
return ind
}

View File

@ -188,15 +188,39 @@ func (n *Node) SetImplicit(b bool) { n.flags.set(nodeImplicit, b) }
func (n *Node) SetIsDDD(b bool) { n.flags.set(nodeIsDDD, b) }
func (n *Node) SetDiag(b bool) { n.flags.set(nodeDiag, b) }
func (n *Node) SetColas(b bool) { n.flags.set(nodeColas, b) }
func (n *Node) SetNonNil(b bool) { n.flags.set(nodeNonNil, b) }
func (n *Node) SetTransient(b bool) { n.flags.set(nodeTransient, b) }
func (n *Node) SetBounded(b bool) { n.flags.set(nodeBounded, b) }
func (n *Node) SetHasCall(b bool) { n.flags.set(nodeHasCall, b) }
func (n *Node) SetLikely(b bool) { n.flags.set(nodeLikely, b) }
func (n *Node) SetHasVal(b bool) { n.flags.set(nodeHasVal, b) }
func (n *Node) SetHasOpt(b bool) { n.flags.set(nodeHasOpt, b) }
func (n *Node) SetEmbedded(b bool) { n.flags.set(nodeEmbedded, b) }
// MarkNonNil marks a pointer n as being guaranteed non-nil,
// on all code paths, at all times.
// During conversion to SSA, non-nil pointers won't have nil checks
// inserted before dereferencing. See state.exprPtr.
func (n *Node) MarkNonNil() {
if !n.Type.IsPtr() && !n.Type.IsUnsafePtr() {
Fatalf("MarkNonNil(%v), type %v", n, n.Type)
}
n.flags.set(nodeNonNil, true)
}
// SetBounded indicates whether operation n does not need safety checks.
// When n is an index or slice operation, n does not need bounds checks.
// When n is a dereferencing operation, n does not need nil checks.
func (n *Node) SetBounded(b bool) {
switch n.Op {
case OINDEX, OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
// No bounds checks needed.
case ODOTPTR, ODEREF:
// No nil check needed.
default:
Fatalf("SetBounded(%v)", n)
}
n.flags.set(nodeBounded, b)
}
// MarkReadonly indicates that n is an ONAME with readonly contents.
func (n *Node) MarkReadonly() {
if n.Op != ONAME {

View File

@ -457,7 +457,7 @@ func walkexpr(n *Node, init *Nodes) *Node {
nn := nod(ODEREF, n.Name.Param.Heapaddr, nil)
nn = typecheck(nn, ctxExpr)
nn = walkexpr(nn, init)
nn.Left.SetNonNil(true)
nn.Left.MarkNonNil()
return nn
}
@ -762,7 +762,7 @@ opswitch:
if !a.isBlank() {
var_ := temp(types.NewPtr(t.Elem()))
var_.SetTypecheck(1)
var_.SetNonNil(true) // mapaccess always returns a non-nil pointer
var_.MarkNonNil() // mapaccess always returns a non-nil pointer
n.List.SetFirst(var_)
n = walkexpr(n, init)
init.Append(n)
@ -1103,7 +1103,7 @@ opswitch:
}
}
n.Type = types.NewPtr(t.Elem())
n.SetNonNil(true) // mapaccess1* and mapassign always return non-nil pointers.
n.MarkNonNil() // mapaccess1* and mapassign always return non-nil pointers.
n = nod(ODEREF, n, nil)
n.Type = t.Elem()
n.SetTypecheck(1)
@ -1382,7 +1382,7 @@ opswitch:
fn := syslook(fnname)
m.Left = mkcall1(fn, types.Types[TUNSAFEPTR], init, typename(t.Elem()), conv(len, argtype), conv(cap, argtype))
m.Left.SetNonNil(true)
m.Left.MarkNonNil()
m.List.Set2(conv(len, types.Types[TINT]), conv(cap, types.Types[TINT]))
m = typecheck(m, ctxExpr)
@ -1952,7 +1952,7 @@ func callnew(t *types.Type) *Node {
n := nod(ONEWOBJ, typename(t), nil)
n.Type = types.NewPtr(t)
n.SetTypecheck(1)
n.SetNonNil(true)
n.MarkNonNil()
return n
}
@ -3659,7 +3659,7 @@ func usemethod(n *Node) {
// Note: Don't rely on res0.Type.String() since its formatting depends on multiple factors
// (including global variables such as numImports - was issue #19028).
// Also need to check for reflect package itself (see Issue #38515).
if s := res0.Type.Sym; s != nil && s.Name == "Method" && s.Pkg != nil && (s.Pkg.Path == "reflect" || s.Pkg == localpkg && myimportpath == "reflect") {
if s := res0.Type.Sym; s != nil && s.Name == "Method" && isReflectPkg(s.Pkg) {
Curfn.Func.SetReflectMethod(true)
}
}

View File

@ -20,6 +20,7 @@ const (
fuseTypePlain fuseType = 1 << iota
fuseTypeIf
fuseTypeIntInRange
fuseTypeShortCircuit
)
// fuse simplifies control flow by joining basic blocks.
@ -38,6 +39,9 @@ func fuse(f *Func, typ fuseType) {
if typ&fuseTypePlain != 0 {
changed = fuseBlockPlain(b) || changed
}
if typ&fuseTypeShortCircuit != 0 {
changed = shortcircuitBlock(b) || changed
}
}
if changed {
f.invalidateCFG()

View File

@ -58,20 +58,7 @@ func shortcircuit(f *Func) {
// if v goto t else u
// We can redirect p to go directly to t instead of b.
// (If v is not live after b).
for changed := true; changed; {
changed = false
for i := len(f.Blocks) - 1; i >= 0; i-- {
b := f.Blocks[i]
if fuseBlockPlain(b) {
changed = true
continue
}
changed = shortcircuitBlock(b) || changed
}
if changed {
f.invalidateCFG()
}
}
fuse(f, fuseTypePlain|fuseTypeShortCircuit)
}
// shortcircuitBlock checks for a CFG in which an If block

View File

@ -13,6 +13,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"fmt"
"io"
"log"
"path/filepath"
"sort"
@ -262,13 +263,13 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
fmt.Fprintf(ctxt.Bso, "\n")
if s.Type == objabi.STEXT {
for p := s.Func.Text; p != nil; p = p.Link {
var s string
fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
if ctxt.Debugasm > 1 {
s = p.String()
io.WriteString(ctxt.Bso, p.String())
} else {
s = p.InnermostString()
p.InnermostString(ctxt.Bso)
}
fmt.Fprintf(ctxt.Bso, "\t%#04x %s\n", uint(int(p.Pc)), s)
fmt.Fprintln(ctxt.Bso)
}
}
for i := 0; i < len(s.P); i += 16 {
@ -283,11 +284,11 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
fmt.Fprintf(ctxt.Bso, " ")
for j = i; j < i+16 && j < len(s.P); j++ {
c := int(s.P[j])
b := byte('.')
if ' ' <= c && c <= 0x7e {
fmt.Fprintf(ctxt.Bso, "%c", c)
} else {
fmt.Fprintf(ctxt.Bso, ".")
b = byte(c)
}
ctxt.Bso.WriteByte(b)
}
fmt.Fprintf(ctxt.Bso, "\n")

View File

@ -8,6 +8,7 @@ import (
"bytes"
"cmd/internal/objabi"
"fmt"
"io"
"strings"
)
@ -17,8 +18,8 @@ const REG_NONE = 0
func (p *Prog) Line() string {
return p.Ctxt.OutermostPos(p.Pos).Format(false, true)
}
func (p *Prog) InnermostLine() string {
return p.Ctxt.InnermostPos(p.Pos).Format(false, true)
func (p *Prog) InnermostLine(w io.Writer) {
p.Ctxt.InnermostPos(p.Pos).WriteTo(w, false, true)
}
// InnermostLineNumber returns a string containing the line number for the
@ -121,45 +122,61 @@ func (p *Prog) String() string {
return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())
}
func (p *Prog) InnermostString() string {
func (p *Prog) InnermostString(w io.Writer) {
if p == nil {
return "<nil Prog>"
io.WriteString(w, "<nil Prog>")
return
}
if p.Ctxt == nil {
return "<Prog without ctxt>"
io.WriteString(w, "<Prog without ctxt>")
return
}
return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.InnermostLine(), p.InstructionString())
fmt.Fprintf(w, "%.5d (", p.Pc)
p.InnermostLine(w)
io.WriteString(w, ")\t")
p.WriteInstructionString(w)
}
// InstructionString returns a string representation of the instruction without preceding
// program counter or file and line number.
func (p *Prog) InstructionString() string {
buf := new(bytes.Buffer)
p.WriteInstructionString(buf)
return buf.String()
}
// WriteInstructionString writes a string representation of the instruction without preceding
// program counter or file and line number.
func (p *Prog) WriteInstructionString(w io.Writer) {
if p == nil {
return "<nil Prog>"
io.WriteString(w, "<nil Prog>")
return
}
if p.Ctxt == nil {
return "<Prog without ctxt>"
io.WriteString(w, "<Prog without ctxt>")
return
}
sc := CConv(p.Scond)
var buf bytes.Buffer
fmt.Fprintf(&buf, "%v%s", p.As, sc)
io.WriteString(w, p.As.String())
io.WriteString(w, sc)
sep := "\t"
if p.From.Type != TYPE_NONE {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From))
io.WriteString(w, sep)
WriteDconv(w, p, &p.From)
sep = ", "
}
if p.Reg != REG_NONE {
// Should not happen but might as well show it if it does.
fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.Reg)))
fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg)))
sep = ", "
}
for i := range p.RestArgs {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.RestArgs[i]))
io.WriteString(w, sep)
WriteDconv(w, p, &p.RestArgs[i])
sep = ", "
}
@ -170,17 +187,17 @@ func (p *Prog) InstructionString() string {
// TEXT foo(SB), $0
s := p.From.Sym.Attribute.TextAttrString()
if s != "" {
fmt.Fprintf(&buf, "%s%s", sep, s)
fmt.Fprintf(w, "%s%s", sep, s)
sep = ", "
}
}
if p.To.Type != TYPE_NONE {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To))
io.WriteString(w, sep)
WriteDconv(w, p, &p.To)
}
if p.RegTo2 != REG_NONE {
fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2)))
fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
}
return buf.String()
}
func (ctxt *Link) NewProg() *Prog {
@ -194,16 +211,20 @@ func (ctxt *Link) CanReuseProgs() bool {
}
func Dconv(p *Prog, a *Addr) string {
var str string
buf := new(bytes.Buffer)
WriteDconv(buf, p, a)
return buf.String()
}
func WriteDconv(w io.Writer, p *Prog, a *Addr) {
switch a.Type {
default:
str = fmt.Sprintf("type=%d", a.Type)
fmt.Fprintf(w, "type=%d", a.Type)
case TYPE_NONE:
str = ""
if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), Rconv(int(a.Reg)))
a.WriteNameTo(w)
fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))
}
case TYPE_REG:
@ -212,71 +233,75 @@ func Dconv(p *Prog, a *Addr) string {
// where the $1 is included in the p->to Addr.
// Move into a new field.
if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg)))
break
fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))
return
}
str = Rconv(int(a.Reg))
if a.Name != NAME_NONE || a.Sym != nil {
str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg)))
a.WriteNameTo(w)
fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))
} else {
io.WriteString(w, Rconv(int(a.Reg)))
}
if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&
a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {
str += fmt.Sprintf("[%d]", a.Index)
fmt.Fprintf(w, "[%d]", a.Index)
}
case TYPE_BRANCH:
if a.Sym != nil {
str = fmt.Sprintf("%s(SB)", a.Sym.Name)
fmt.Fprintf(w, "%s(SB)", a.Sym.Name)
} else if p != nil && p.Pcond != nil {
str = fmt.Sprint(p.Pcond.Pc)
fmt.Fprint(w, p.Pcond.Pc)
} else if a.Val != nil {
str = fmt.Sprint(a.Val.(*Prog).Pc)
fmt.Fprint(w, a.Val.(*Prog).Pc)
} else {
str = fmt.Sprintf("%d(PC)", a.Offset)
fmt.Fprintf(w, "%d(PC)", a.Offset)
}
case TYPE_INDIR:
str = fmt.Sprintf("*%s", Mconv(a))
io.WriteString(w, "*")
a.WriteNameTo(w)
case TYPE_MEM:
str = Mconv(a)
a.WriteNameTo(w)
if a.Index != REG_NONE {
if a.Scale == 0 {
// arm64 shifted or extended register offset, scale = 0.
str += fmt.Sprintf("(%v)", Rconv(int(a.Index)))
fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))
} else {
str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
}
}
case TYPE_CONST:
io.WriteString(w, "$")
a.WriteNameTo(w)
if a.Reg != 0 {
str = fmt.Sprintf("$%v(%v)", Mconv(a), Rconv(int(a.Reg)))
} else {
str = fmt.Sprintf("$%v", Mconv(a))
fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
}
case TYPE_TEXTSIZE:
if a.Val.(int32) == objabi.ArgsSizeUnknown {
str = fmt.Sprintf("$%d", a.Offset)
fmt.Fprintf(w, "$%d", a.Offset)
} else {
str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))
}
case TYPE_FCONST:
str = fmt.Sprintf("%.17g", a.Val.(float64))
str := fmt.Sprintf("%.17g", a.Val.(float64))
// Make sure 1 prints as 1.0
if !strings.ContainsAny(str, ".e") {
str += ".0"
}
str = fmt.Sprintf("$(%s)", str)
fmt.Fprintf(w, "$(%s)", str)
case TYPE_SCONST:
str = fmt.Sprintf("$%q", a.Val.(string))
fmt.Fprintf(w, "$%q", a.Val.(string))
case TYPE_ADDR:
str = fmt.Sprintf("$%s", Mconv(a))
io.WriteString(w, "$")
a.WriteNameTo(w)
case TYPE_SHIFT:
v := int(a.Offset)
@ -285,49 +310,45 @@ func Dconv(p *Prog, a *Addr) string {
case "arm":
op := ops[((v>>5)&3)<<1:]
if v&(1<<4) != 0 {
str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
} else {
str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
}
if a.Reg != 0 {
str += fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
}
case "arm64":
op := ops[((v>>22)&3)<<1:]
r := (v >> 16) & 31
str = fmt.Sprintf("%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
default:
panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)
}
case TYPE_REGREG:
str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
case TYPE_REGREG2:
str = fmt.Sprintf("%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
case TYPE_REGLIST:
str = RLconv(a.Offset)
io.WriteString(w, RLconv(a.Offset))
}
return str
}
func Mconv(a *Addr) string {
var str string
func (a *Addr) WriteNameTo(w io.Writer) {
switch a.Name {
default:
str = fmt.Sprintf("name=%d", a.Name)
fmt.Fprintf(w, "name=%d", a.Name)
case NAME_NONE:
switch {
case a.Reg == REG_NONE:
str = fmt.Sprint(a.Offset)
fmt.Fprint(w, a.Offset)
case a.Offset == 0:
str = fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
case a.Offset != 0:
str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg)))
fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg)))
}
// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
@ -337,9 +358,9 @@ func Mconv(a *Addr) string {
reg = Rconv(int(a.Reg))
}
if a.Sym != nil {
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
} else {
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
}
case NAME_GOTREF:
@ -348,9 +369,9 @@ func Mconv(a *Addr) string {
reg = Rconv(int(a.Reg))
}
if a.Sym != nil {
str = fmt.Sprintf("%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
} else {
str = fmt.Sprintf("%s@GOT(%s)", offConv(a.Offset), reg)
fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)
}
case NAME_STATIC:
@ -359,9 +380,9 @@ func Mconv(a *Addr) string {
reg = Rconv(int(a.Reg))
}
if a.Sym != nil {
str = fmt.Sprintf("%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
} else {
str = fmt.Sprintf("<>%s(%s)", offConv(a.Offset), reg)
fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)
}
case NAME_AUTO:
@ -370,9 +391,9 @@ func Mconv(a *Addr) string {
reg = Rconv(int(a.Reg))
}
if a.Sym != nil {
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
} else {
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
}
case NAME_PARAM:
@ -381,9 +402,9 @@ func Mconv(a *Addr) string {
reg = Rconv(int(a.Reg))
}
if a.Sym != nil {
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
} else {
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
}
case NAME_TOCREF:
reg := "SB"
@ -391,13 +412,11 @@ func Mconv(a *Addr) string {
reg = Rconv(int(a.Reg))
}
if a.Sym != nil {
str = fmt.Sprintf("%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
} else {
str = fmt.Sprintf("%s(%s)", offConv(a.Offset), reg)
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
}
}
return str
}
func offConv(off int64) string {

View File

@ -7,8 +7,9 @@
package src
import (
"bytes"
"fmt"
"strconv"
"io"
)
// A Pos encodes a source position consisting of a (line, column) number pair
@ -129,13 +130,22 @@ func (p Pos) String() string {
// shown as well, as in "filename:line[origfile:origline:origcolumn] if
// showOrig is set.
func (p Pos) Format(showCol, showOrig bool) string {
buf := new(bytes.Buffer)
p.WriteTo(buf, showCol, showOrig)
return buf.String()
}
// WriteTo a position to w, formatted as Format does.
func (p Pos) WriteTo(w io.Writer, showCol, showOrig bool) {
if !p.IsKnown() {
return "<unknown line number>"
io.WriteString(w, "<unknown line number>")
return
}
if b := p.base; b == b.Pos().base {
// base is file base (incl. nil)
return format(p.Filename(), p.Line(), p.Col(), showCol)
format(w, p.Filename(), p.Line(), p.Col(), showCol)
return
}
// base is relative
@ -146,22 +156,32 @@ func (p Pos) Format(showCol, showOrig bool) string {
// that's provided via a line directive).
// TODO(gri) This may not be true if we have an inlining base.
// We may want to differentiate at some point.
s := format(p.RelFilename(), p.RelLine(), p.RelCol(), showCol)
format(w, p.RelFilename(), p.RelLine(), p.RelCol(), showCol)
if showOrig {
s += "[" + format(p.Filename(), p.Line(), p.Col(), showCol) + "]"
io.WriteString(w, "[")
format(w, p.Filename(), p.Line(), p.Col(), showCol)
io.WriteString(w, "]")
}
return s
}
// format formats a (filename, line, col) tuple as "filename:line" (showCol
// is false or col == 0) or "filename:line:column" (showCol is true and col != 0).
func format(filename string, line, col uint, showCol bool) string {
s := filename + ":" + strconv.FormatUint(uint64(line), 10)
func format(w io.Writer, filename string, line, col uint, showCol bool) {
io.WriteString(w, filename)
io.WriteString(w, ":")
fmt.Fprint(w, line)
// col == 0 and col == colMax are interpreted as unknown column values
if showCol && 0 < col && col < colMax {
s += ":" + strconv.FormatUint(uint64(col), 10)
io.WriteString(w, ":")
fmt.Fprint(w, col)
}
return s
}
// formatstr wraps format to return a string.
func formatstr(filename string, line, col uint, showCol bool) string {
buf := new(bytes.Buffer)
format(buf, filename, line, col, showCol)
return buf.String()
}
// ----------------------------------------------------------------------------

View File

@ -147,7 +147,7 @@ func TestLico(t *testing.T) {
{makeLico(lineMax+1, colMax+1), fmt.Sprintf(":%d", lineMax), lineMax, 0},
} {
x := test.x
if got := format("", x.Line(), x.Col(), true); got != test.string {
if got := formatstr("", x.Line(), x.Col(), true); got != test.string {
t.Errorf("%s: got %q", test.string, got)
}
}
@ -179,7 +179,7 @@ func TestIsStmt(t *testing.T) {
{makeLico(lineMax+1, colMax+1).withNotStmt(), fmt.Sprintf(":%d", lineMax) + not, lineMax, 0},
} {
x := test.x
if got := format("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d", x.IsStmt()); got != test.string {
if got := formatstr("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d", x.IsStmt()); got != test.string {
t.Errorf("%s: got %q", test.string, got)
}
}
@ -219,7 +219,7 @@ func TestLogue(t *testing.T) {
{makeLico(lineMax, 1).withXlogue(PosEpilogueBegin), fmt.Sprintf(":%d:1", lineMax) + defs + epi, lineMax, 1},
} {
x := test.x
if got := format("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d:%d", x.IsStmt(), x.Xlogue()); got != test.string {
if got := formatstr("", x.Line(), x.Col(), true) + fmt.Sprintf(":%d:%d", x.IsStmt(), x.Xlogue()); got != test.string {
t.Errorf("%d: %s: got %q", i, test.string, got)
}
}

View File

@ -390,6 +390,7 @@ func setStrictFakeConnClose(t *testing.T) {
func (c *fakeConn) ResetSession(ctx context.Context) error {
c.dirtySession = false
c.currTx = nil
if c.isBad() {
return driver.ErrBadConn
}
@ -734,6 +735,9 @@ var hookExecBadConn func() bool
func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
panic("Using ExecContext")
}
var errFakeConnSessionDirty = errors.New("fakedb: session is dirty")
func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
if s.panic == "Exec" {
panic(s.panic)
@ -746,7 +750,7 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d
return nil, driver.ErrBadConn
}
if s.c.isDirtyAndMark() {
return nil, errors.New("fakedb: session is dirty")
return nil, errFakeConnSessionDirty
}
err := checkSubsetTypes(s.c.db.allowAny, args)
@ -860,7 +864,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
return nil, driver.ErrBadConn
}
if s.c.isDirtyAndMark() {
return nil, errors.New("fakedb: session is dirty")
return nil, errFakeConnSessionDirty
}
err := checkSubsetTypes(s.c.db.allowAny, args)
@ -893,6 +897,37 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
}
}
}
if s.table == "tx_status" && s.colName[0] == "tx_status" {
txStatus := "autocommit"
if s.c.currTx != nil {
txStatus = "transaction"
}
cursor := &rowsCursor{
parentMem: s.c,
posRow: -1,
rows: [][]*row{
[]*row{
{
cols: []interface{}{
txStatus,
},
},
},
},
cols: [][]string{
[]string{
"tx_status",
},
},
colType: [][]string{
[]string{
"string",
},
},
errPos: -1,
}
return cursor, nil
}
t.mu.Lock()

View File

@ -1261,7 +1261,13 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
if !ok {
return nil, errDBClosed
}
if ret.err == nil && ret.conn.expired(lifetime) {
// Only check if the connection is expired if the strategy is cachedOrNewConns.
// If we require a new connection, just re-use the connection without looking
// at the expiry time. If it is expired, it will be checked when it is placed
// back into the connection pool.
// This prioritizes giving a valid connection to a client over the exact connection
// lifetime, which could expire exactly after this point anyway.
if strategy == cachedOrNewConn && ret.err == nil && ret.conn.expired(lifetime) {
ret.conn.Close()
return nil, driver.ErrBadConn
}
@ -1338,11 +1344,16 @@ func (db *DB) putConn(dc *driverConn, err error, resetSession bool) {
}
db.mu.Lock()
if !dc.inUse {
db.mu.Unlock()
if debugGetPut {
fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", dc, stack(), db.lastPut[dc])
}
panic("sql: connection returned that was never out")
}
if err != driver.ErrBadConn && dc.expired(db.maxLifetime) {
err = driver.ErrBadConn
}
if debugGetPut {
db.lastPut[dc] = stack()
}
@ -1735,7 +1746,11 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra
// beginDC starts a transaction. The provided dc must be valid and ready to use.
func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error), opts *TxOptions) (tx *Tx, err error) {
var txi driver.Tx
keepConnOnRollback := false
withLock(dc, func() {
_, hasSessionResetter := dc.ci.(driver.SessionResetter)
_, hasConnectionValidator := dc.ci.(driver.Validator)
keepConnOnRollback = hasSessionResetter && hasConnectionValidator
txi, err = ctxDriverBegin(ctx, opts, dc.ci)
})
if err != nil {
@ -1752,6 +1767,7 @@ func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error),
releaseConn: release,
txi: txi,
cancel: cancel,
keepConnOnRollback: keepConnOnRollback,
ctx: ctx,
}
go tx.awaitDone()
@ -2014,6 +2030,11 @@ type Tx struct {
// Use atomic operations on value when checking value.
done int32
// keepConnOnRollback is true if the driver knows
// how to reset the connection's session and if need be discard
// the connection.
keepConnOnRollback bool
// All Stmts prepared for this transaction. These will be closed after the
// transaction has been committed or rolled back.
stmts struct {
@ -2039,7 +2060,10 @@ func (tx *Tx) awaitDone() {
// transaction is closed and the resources are released. This
// rollback does nothing if the transaction has already been
// committed or rolled back.
tx.rollback(true)
// Do not discard the connection if the connection knows
// how to reset the session.
discardConnection := !tx.keepConnOnRollback
tx.rollback(discardConnection)
}
func (tx *Tx) isDone() bool {
@ -2050,14 +2074,10 @@ func (tx *Tx) isDone() bool {
// that has already been committed or rolled back.
var ErrTxDone = errors.New("sql: transaction has already been committed or rolled back")
// close returns the connection to the pool and
// must only be called by Tx.rollback or Tx.Commit.
func (tx *Tx) close(err error) {
tx.cancel()
tx.closemu.Lock()
defer tx.closemu.Unlock()
// closeLocked returns the connection to the pool and
// must only be called by Tx.rollback or Tx.Commit while
// closemu is Locked and tx already canceled.
func (tx *Tx) closeLocked(err error) {
tx.releaseConn(err)
tx.dc = nil
tx.txi = nil
@ -2124,6 +2144,15 @@ func (tx *Tx) Commit() error {
if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
return ErrTxDone
}
// Cancel the Tx to release any active R-closemu locks.
// This is safe to do because tx.done has already transitioned
// from 0 to 1. Hold the W-closemu lock prior to rollback
// to ensure no other connection has an active query.
tx.cancel()
tx.closemu.Lock()
defer tx.closemu.Unlock()
var err error
withLock(tx.dc, func() {
err = tx.txi.Commit()
@ -2131,16 +2160,31 @@ func (tx *Tx) Commit() error {
if err != driver.ErrBadConn {
tx.closePrepared()
}
tx.close(err)
tx.closeLocked(err)
return err
}
var rollbackHook func()
// rollback aborts the transaction and optionally forces the pool to discard
// the connection.
func (tx *Tx) rollback(discardConn bool) error {
if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
return ErrTxDone
}
if rollbackHook != nil {
rollbackHook()
}
// Cancel the Tx to release any active R-closemu locks.
// This is safe to do because tx.done has already transitioned
// from 0 to 1. Hold the W-closemu lock prior to rollback
// to ensure no other connection has an active query.
tx.cancel()
tx.closemu.Lock()
defer tx.closemu.Unlock()
var err error
withLock(tx.dc, func() {
err = tx.txi.Rollback()
@ -2151,7 +2195,7 @@ func (tx *Tx) rollback(discardConn bool) error {
if discardConn {
err = driver.ErrBadConn
}
tx.close(err)
tx.closeLocked(err)
return err
}

View File

@ -80,6 +80,11 @@ func newTestDBConnector(t testing.TB, fc *fakeConnector, name string) *DB {
exec(t, db, "CREATE|magicquery|op=string,millis=int32")
exec(t, db, "INSERT|magicquery|op=sleep,millis=10")
}
if name == "tx_status" {
// Magic table name and column, known by fakedb_test.go.
exec(t, db, "CREATE|tx_status|tx_status=string")
exec(t, db, "INSERT|tx_status|tx_status=invalid")
}
return db
}
@ -437,6 +442,7 @@ func TestTxContextWait(t *testing.T) {
}
t.Fatal(err)
}
tx.keepConnOnRollback = false
// This will trigger the *fakeConn.Prepare method which will take time
// performing the query. The ctxDriverPrepare func will check the context
@ -449,6 +455,35 @@ func TestTxContextWait(t *testing.T) {
waitForFree(t, db, 5*time.Second, 0)
}
// TestTxContextWaitNoDiscard is the same as TestTxContextWait, but should not discard
// the final connection.
func TestTxContextWaitNoDiscard(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Millisecond)
defer cancel()
tx, err := db.BeginTx(ctx, nil)
if err != nil {
// Guard against the context being canceled before BeginTx completes.
if err == context.DeadlineExceeded {
t.Skip("tx context canceled prior to first use")
}
t.Fatal(err)
}
// This will trigger the *fakeConn.Prepare method which will take time
// performing the query. The ctxDriverPrepare func will check the context
// after this and close the rows and return an error.
_, err = tx.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
if err != context.DeadlineExceeded {
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
}
waitForFree(t, db, 5*time.Second, 1)
}
// TestUnsupportedOptions checks that the database fails when a driver that
// doesn't implement ConnBeginTx is used with non-default options and an
// un-cancellable context.
@ -2707,6 +2742,159 @@ func TestManyErrBadConn(t *testing.T) {
}
}
// Issue 34755: Ensure that a Tx cannot commit after a rollback.
func TestTxCannotCommitAfterRollback(t *testing.T) {
db := newTestDB(t, "tx_status")
defer closeDB(t, db)
// First check query reporting is correct.
var txStatus string
err := db.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus)
if err != nil {
t.Fatal(err)
}
if g, w := txStatus, "autocommit"; g != w {
t.Fatalf("tx_status=%q, wanted %q", g, w)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tx, err := db.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
// Ignore dirty session for this test.
// A failing test should trigger the dirty session flag as well,
// but that isn't exactly what this should test for.
tx.txi.(*fakeTx).c.skipDirtySession = true
defer tx.Rollback()
err = tx.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus)
if err != nil {
t.Fatal(err)
}
if g, w := txStatus, "transaction"; g != w {
t.Fatalf("tx_status=%q, wanted %q", g, w)
}
// 1. Begin a transaction.
// 2. (A) Start a query, (B) begin Tx rollback through a ctx cancel.
// 3. Check if 2.A has committed in Tx (pass) or outside of Tx (fail).
sendQuery := make(chan struct{})
hookTxGrabConn = func() {
cancel()
<-sendQuery
}
rollbackHook = func() {
close(sendQuery)
}
defer func() {
hookTxGrabConn = nil
rollbackHook = nil
}()
err = tx.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus)
if err != nil {
// A failure here would be expected if skipDirtySession was not set to true above.
t.Fatal(err)
}
if g, w := txStatus, "transaction"; g != w {
t.Fatalf("tx_status=%q, wanted %q", g, w)
}
}
// Issue32530 encounters an issue where a connection may
// expire right after it comes out of a used connection pool
// even when a new connection is requested.
func TestConnExpiresFreshOutOfPool(t *testing.T) {
execCases := []struct {
expired bool
badReset bool
}{
{false, false},
{true, false},
{false, true},
}
t0 := time.Unix(1000000, 0)
offset := time.Duration(0)
offsetMu := sync.RWMutex{}
nowFunc = func() time.Time {
offsetMu.RLock()
defer offsetMu.RUnlock()
return t0.Add(offset)
}
defer func() { nowFunc = time.Now }()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db := newTestDB(t, "magicquery")
defer closeDB(t, db)
db.SetMaxOpenConns(1)
for _, ec := range execCases {
ec := ec
name := fmt.Sprintf("expired=%t,badReset=%t", ec.expired, ec.badReset)
t.Run(name, func(t *testing.T) {
db.clearAllConns(t)
db.SetMaxIdleConns(1)
db.SetConnMaxLifetime(10 * time.Second)
conn, err := db.conn(ctx, alwaysNewConn)
if err != nil {
t.Fatal(err)
}
afterPutConn := make(chan struct{})
waitingForConn := make(chan struct{})
go func() {
conn, err := db.conn(ctx, alwaysNewConn)
if err != nil {
t.Fatal(err)
}
db.putConn(conn, err, false)
close(afterPutConn)
}()
go func() {
for {
db.mu.Lock()
ct := len(db.connRequests)
db.mu.Unlock()
if ct > 0 {
close(waitingForConn)
return
}
time.Sleep(10 * time.Millisecond)
}
}()
<-waitingForConn
offsetMu.Lock()
if ec.expired {
offset = 11 * time.Second
} else {
offset = time.Duration(0)
}
offsetMu.Unlock()
conn.ci.(*fakeConn).stickyBad = ec.badReset
db.putConn(conn, err, true)
<-afterPutConn
})
}
}
// TestIssue20575 ensures the Rows from query does not block
// closing a transaction. Ensure Rows is closed while closing a trasaction.
func TestIssue20575(t *testing.T) {

View File

@ -451,7 +451,7 @@ func testDWARF(t *testing.T, linktype int) {
}
offset := uintptr(addr) - imageBase
if offset != uintptr(wantoffset) {
t.Fatal("Runtime offset (0x%x) did "+
t.Fatalf("Runtime offset (0x%x) did "+
"not match dwarf offset "+
"(0x%x)", wantoffset, offset)
}

View File

@ -81,6 +81,7 @@ import (
"sort"
"strings"
"sync"
"sync/atomic"
"text/tabwriter"
"time"
"unsafe"
@ -714,10 +715,22 @@ func (p runtimeProfile) Stack(i int) []uintptr { return p[i].Stack() }
var cpu struct {
sync.Mutex
profiling bool
profiling uint32 // bool, accessed atomically
done chan bool
}
func cpuProfiling() bool {
return atomic.LoadUint32(&cpu.profiling) != 0
}
func setCPUProfiling(b bool) {
if b {
atomic.StoreUint32(&cpu.profiling, 1)
} else {
atomic.StoreUint32(&cpu.profiling, 0)
}
}
// StartCPUProfile enables CPU profiling for the current process.
// While profiling, the profile will be buffered and written to w.
// StartCPUProfile returns an error if profiling is already enabled.
@ -747,10 +760,10 @@ func StartCPUProfile(w io.Writer) error {
cpu.done = make(chan bool)
}
// Double-check.
if cpu.profiling {
if cpuProfiling() {
return fmt.Errorf("cpu profiling already in use")
}
cpu.profiling = true
setCPUProfiling(true)
runtime.SetCPUProfileRate(hz)
go profileWriter(w)
return nil
@ -767,7 +780,9 @@ func profileWriter(w io.Writer) {
b := newProfileBuilder(w)
var err error
for {
if cpuProfiling() {
time.Sleep(100 * time.Millisecond)
}
data, tags, eof := readProfile()
if e := b.addCPUData(data, tags); e != nil && err == nil {
err = e
@ -792,10 +807,10 @@ func StopCPUProfile() {
cpu.Lock()
defer cpu.Unlock()
if !cpu.profiling {
if !cpuProfiling() {
return
}
cpu.profiling = false
setCPUProfiling(false)
runtime.SetCPUProfileRate(0)
<-cpu.done
}