mirror of
https://github.com/golang/go
synced 2024-11-23 00:30:07 -07:00
Merge branch 'master' of git://github.com/golang/go into fix-fmt-formatting
This commit is contained in:
commit
6a8dcb8c54
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
case bool:
|
||||
i := int64(obj.Bool2int(u))
|
||||
s.WriteInt(Ctxt, nam.Xoffset, wid, i)
|
||||
// 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)
|
||||
}
|
||||
|
||||
case *Mpint:
|
||||
s.WriteInt(Ctxt, nam.Xoffset, wid, u.Int64())
|
||||
// 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, n.Xoffset, wid, i)
|
||||
|
||||
case *Mpflt:
|
||||
f := u.Float64()
|
||||
switch nam.Type.Etype {
|
||||
case TFLOAT32:
|
||||
s.WriteFloat32(Ctxt, nam.Xoffset, float32(f))
|
||||
case TFLOAT64:
|
||||
s.WriteFloat64(Ctxt, nam.Xoffset, f)
|
||||
}
|
||||
case *Mpint:
|
||||
s.WriteInt(Ctxt, n.Xoffset, wid, u.Int64())
|
||||
|
||||
case *Mpcplx:
|
||||
r := u.Real.Float64()
|
||||
i := u.Imag.Float64()
|
||||
switch nam.Type.Etype {
|
||||
case TCOMPLEX64:
|
||||
s.WriteFloat32(Ctxt, nam.Xoffset, float32(r))
|
||||
s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i))
|
||||
case TCOMPLEX128:
|
||||
s.WriteFloat64(Ctxt, nam.Xoffset, r)
|
||||
s.WriteFloat64(Ctxt, nam.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)))
|
||||
|
||||
default:
|
||||
Fatalf("gdata unhandled OLITERAL %v", nr)
|
||||
case *Mpflt:
|
||||
f := u.Float64()
|
||||
switch n.Type.Etype {
|
||||
case TFLOAT32:
|
||||
s.WriteFloat32(Ctxt, n.Xoffset, float32(f))
|
||||
case TFLOAT64:
|
||||
s.WriteFloat64(Ctxt, n.Xoffset, f)
|
||||
}
|
||||
|
||||
case OADDR:
|
||||
if nr.Left.Op != ONAME {
|
||||
Fatalf("gdata ADDR left op %v", nr.Left.Op)
|
||||
case *Mpcplx:
|
||||
r := u.Real.Float64()
|
||||
i := u.Imag.Float64()
|
||||
switch n.Type.Etype {
|
||||
case TCOMPLEX64:
|
||||
s.WriteFloat32(Ctxt, n.Xoffset, float32(r))
|
||||
s.WriteFloat32(Ctxt, n.Xoffset+4, float32(i))
|
||||
case TCOMPLEX128:
|
||||
s.WriteFloat64(Ctxt, n.Xoffset, r)
|
||||
s.WriteFloat64(Ctxt, n.Xoffset+8, i)
|
||||
}
|
||||
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)
|
||||
case string:
|
||||
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 op %v %v\n", nr, nr.Op)
|
||||
Fatalf("litsym unhandled OLITERAL %v", c)
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 {
|
||||
@ -1747,12 +1762,13 @@ func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error),
|
||||
// The cancel function in Tx will be called after done is set to true.
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
tx = &Tx{
|
||||
db: db,
|
||||
dc: dc,
|
||||
releaseConn: release,
|
||||
txi: txi,
|
||||
cancel: cancel,
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
dc: dc,
|
||||
releaseConn: release,
|
||||
txi: txi,
|
||||
cancel: cancel,
|
||||
keepConnOnRollback: keepConnOnRollback,
|
||||
ctx: ctx,
|
||||
}
|
||||
go tx.awaitDone()
|
||||
return tx, nil
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user