| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- package vrp
- import (
- "fmt"
- "go/token"
- "go/types"
- "honnef.co/go/tools/ssa"
- )
- type StringInterval struct {
- Length IntInterval
- }
- func (s StringInterval) Union(other Range) Range {
- i, ok := other.(StringInterval)
- if !ok {
- i = StringInterval{EmptyIntInterval}
- }
- if s.Length.Empty() || !s.Length.IsKnown() {
- return i
- }
- if i.Length.Empty() || !i.Length.IsKnown() {
- return s
- }
- return StringInterval{
- Length: s.Length.Union(i.Length).(IntInterval),
- }
- }
- func (s StringInterval) String() string {
- return s.Length.String()
- }
- func (s StringInterval) IsKnown() bool {
- return s.Length.IsKnown()
- }
- type StringSliceConstraint struct {
- aConstraint
- X ssa.Value
- Lower ssa.Value
- Upper ssa.Value
- }
- type StringIntersectionConstraint struct {
- aConstraint
- ranges Ranges
- A ssa.Value
- B ssa.Value
- Op token.Token
- I IntInterval
- resolved bool
- }
- type StringConcatConstraint struct {
- aConstraint
- A ssa.Value
- B ssa.Value
- }
- type StringLengthConstraint struct {
- aConstraint
- X ssa.Value
- }
- type StringIntervalConstraint struct {
- aConstraint
- I IntInterval
- }
- func NewStringSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
- return &StringSliceConstraint{NewConstraint(y), x, lower, upper}
- }
- func NewStringIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
- return &StringIntersectionConstraint{
- aConstraint: NewConstraint(y),
- ranges: ranges,
- A: a,
- B: b,
- Op: op,
- }
- }
- func NewStringConcatConstraint(a, b, y ssa.Value) Constraint {
- return &StringConcatConstraint{NewConstraint(y), a, b}
- }
- func NewStringLengthConstraint(x ssa.Value, y ssa.Value) Constraint {
- return &StringLengthConstraint{NewConstraint(y), x}
- }
- func NewStringIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
- return &StringIntervalConstraint{NewConstraint(y), i}
- }
- func (c *StringSliceConstraint) Operands() []ssa.Value {
- vs := []ssa.Value{c.X}
- if c.Lower != nil {
- vs = append(vs, c.Lower)
- }
- if c.Upper != nil {
- vs = append(vs, c.Upper)
- }
- return vs
- }
- func (c *StringIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
- func (c StringConcatConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
- func (c *StringLengthConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
- func (s *StringIntervalConstraint) Operands() []ssa.Value { return nil }
- func (c *StringSliceConstraint) String() string {
- var lname, uname string
- if c.Lower != nil {
- lname = c.Lower.Name()
- }
- if c.Upper != nil {
- uname = c.Upper.Name()
- }
- return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
- }
- func (c *StringIntersectionConstraint) String() string {
- return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
- }
- func (c StringConcatConstraint) String() string {
- return fmt.Sprintf("%s = %s + %s", c.Y().Name(), c.A.Name(), c.B.Name())
- }
- func (c *StringLengthConstraint) String() string {
- return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
- }
- func (c *StringIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
- func (c *StringSliceConstraint) Eval(g *Graph) Range {
- lr := NewIntInterval(NewZ(0), NewZ(0))
- if c.Lower != nil {
- lr = g.Range(c.Lower).(IntInterval)
- }
- ur := g.Range(c.X).(StringInterval).Length
- if c.Upper != nil {
- ur = g.Range(c.Upper).(IntInterval)
- }
- if !lr.IsKnown() || !ur.IsKnown() {
- return StringInterval{}
- }
- ls := []Z{
- ur.Lower.Sub(lr.Lower),
- ur.Upper.Sub(lr.Lower),
- ur.Lower.Sub(lr.Upper),
- ur.Upper.Sub(lr.Upper),
- }
- // TODO(dh): if we don't truncate lengths to 0 we might be able to
- // easily detect slices with high < low. we'd need to treat -∞
- // specially, though.
- for i, l := range ls {
- if l.Sign() == -1 {
- ls[i] = NewZ(0)
- }
- }
- return StringInterval{
- Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
- }
- }
- func (c *StringIntersectionConstraint) Eval(g *Graph) Range {
- var l IntInterval
- switch r := g.Range(c.A).(type) {
- case StringInterval:
- l = r.Length
- case IntInterval:
- l = r
- }
- if !l.IsKnown() {
- return StringInterval{c.I}
- }
- return StringInterval{
- Length: l.Intersection(c.I),
- }
- }
- func (c StringConcatConstraint) Eval(g *Graph) Range {
- i1, i2 := g.Range(c.A).(StringInterval), g.Range(c.B).(StringInterval)
- if !i1.Length.IsKnown() || !i2.Length.IsKnown() {
- return StringInterval{}
- }
- return StringInterval{
- Length: i1.Length.Add(i2.Length),
- }
- }
- func (c *StringLengthConstraint) Eval(g *Graph) Range {
- i := g.Range(c.X).(StringInterval).Length
- if !i.IsKnown() {
- return NewIntInterval(NewZ(0), PInfinity)
- }
- return i
- }
- func (c *StringIntervalConstraint) Eval(*Graph) Range { return StringInterval{c.I} }
- func (c *StringIntersectionConstraint) Futures() []ssa.Value {
- return []ssa.Value{c.B}
- }
- func (c *StringIntersectionConstraint) Resolve() {
- if (c.A.Type().Underlying().(*types.Basic).Info() & types.IsString) != 0 {
- // comparing two strings
- r, ok := c.ranges[c.B].(StringInterval)
- if !ok {
- c.I = NewIntInterval(NewZ(0), PInfinity)
- return
- }
- switch c.Op {
- case token.EQL:
- c.I = r.Length
- case token.GTR, token.GEQ:
- c.I = NewIntInterval(r.Length.Lower, PInfinity)
- case token.LSS, token.LEQ:
- c.I = NewIntInterval(NewZ(0), r.Length.Upper)
- case token.NEQ:
- default:
- panic("unsupported op " + c.Op.String())
- }
- } else {
- r, ok := c.ranges[c.B].(IntInterval)
- if !ok {
- c.I = NewIntInterval(NewZ(0), PInfinity)
- return
- }
- // comparing two lengths
- switch c.Op {
- case token.EQL:
- c.I = r
- case token.GTR:
- c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
- case token.GEQ:
- c.I = NewIntInterval(r.Lower, PInfinity)
- case token.LSS:
- c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
- case token.LEQ:
- c.I = NewIntInterval(NInfinity, r.Upper)
- case token.NEQ:
- default:
- panic("unsupported op " + c.Op.String())
- }
- }
- }
- func (c *StringIntersectionConstraint) IsKnown() bool {
- return c.I.IsKnown()
- }
- func (c *StringIntersectionConstraint) MarkUnresolved() {
- c.resolved = false
- }
- func (c *StringIntersectionConstraint) MarkResolved() {
- c.resolved = true
- }
- func (c *StringIntersectionConstraint) IsResolved() bool {
- return c.resolved
- }
|