d2ir: Fix infinite loop in triple globs

This commit is contained in:
Anmol Sethi 2023-08-16 00:02:09 -07:00
parent 4090e780f8
commit a24716d1f1
No known key found for this signature in database
GPG key ID: 8CEF1878FF10ADEB
6 changed files with 2258 additions and 1283 deletions

View file

@ -492,11 +492,15 @@ func (c *compiler) ensureGlobContext(refctx *RefContext) *globContext {
func (c *compiler) compileKey(refctx *RefContext) {
if refctx.Key.HasGlob() {
// These three printlns are for debugging infinite loops.
// println("og", refctx.Edge, refctx.Key, refctx.Scope, refctx.ScopeMap, refctx.ScopeAST)
for _, refctx2 := range c.globRefContextStack {
// println("st", refctx2.Edge, refctx2.Key, refctx2.Scope, refctx2.ScopeMap, refctx2.ScopeAST)
if refctx.Equal(refctx2) {
// Break the infinite loop.
return
}
// println("keys", d2format.Format(refctx2.Key), d2format.Format(refctx.Key))
}
c.globRefContextStack = append(c.globRefContextStack, refctx)
defer func() {

View file

@ -575,7 +575,8 @@ func (rc *RefContext) EdgeIndex() int {
func (rc *RefContext) Equal(rc2 *RefContext) bool {
// We intentionally ignore edges here because the same glob can produce multiple RefContexts that should be treated the same with only the edge as the difference.
return rc.Key.Equals(rc2.Key) && rc.Scope == rc2.Scope && rc.ScopeMap == rc2.ScopeMap && rc.ScopeAST == rc2.ScopeAST
// Same with ScopeMap.
return rc.Key.Equals(rc2.Key) && rc.Scope == rc2.Scope && rc.ScopeAST == rc2.ScopeAST
}
func (m *Map) FieldCountRecursive() int {
@ -708,19 +709,30 @@ func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext, create bool, c
}
func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create bool, gctx *globContext, c *compiler, fa *[]*Field) error {
faAppend := func(fa2 ...*Field) {
for _, f := range fa2 {
if gctx != nil {
// Always match all commons, sources and destinations for edge globs.
if len(refctx.Key.Edges) == 0 || kp == refctx.Key.EdgeKey {
ks := d2format.Format(d2ast.MakeKeyPath(BoardIDA(f)))
if _, ok := gctx.appliedFields[ks]; ok {
continue
}
filter := func(f *Field, passthrough bool) bool {
if gctx != nil {
// For globs with edges, we only ignore duplicate fields if the glob is not at the terminal of the keypath, the glob is on the common key or the glob is on the edge key.
if !kp.HasGlob() {
return true
}
lastEl := kp.Path[len(kp.Path)-1]
if len(refctx.Key.Edges) == 0 || lastEl.UnquotedString == nil || len(lastEl.UnquotedString.Pattern) == 0 || kp == refctx.Key.Key || kp == refctx.Key.EdgeKey {
ks := d2format.Format(d2ast.MakeKeyPath(BoardIDA(f)))
if _, ok := gctx.appliedFields[ks]; ok {
return false
}
if !passthrough {
gctx.appliedFields[ks] = struct{}{}
}
}
*fa = append(*fa, f)
}
return true
}
faAppend := func(fa2 ...*Field) {
for _, f := range fa2 {
if filter(f, false) {
*fa = append(*fa, f)
}
}
}
@ -732,6 +744,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
faAppend(fa2...)
} else {
for _, f := range fa2 {
if !filter(f, true) {
return nil
}
if f.Map() == nil {
f.Composite = &Map{
parent: f,
@ -750,6 +765,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
if i == len(kp.Path)-1 {
faAppend(f)
} else {
if !filter(f, true) {
return nil
}
if f.Map() == nil {
f.Composite = &Map{
parent: f,
@ -804,6 +822,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
faAppend(f)
return nil
}
if !filter(f, true) {
return nil
}
if _, ok := f.Composite.(*Array); ok {
return d2parser.Errorf(kp.Path[i].Unbox(), "cannot index into array")
}
@ -832,7 +853,10 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b
}
m.Fields = append(m.Fields, f)
if i+1 == len(kp.Path) {
*fa = append(*fa, f)
faAppend(f)
return nil
}
if !filter(f, true) {
return nil
}
if f.Composite == nil {

View file

@ -432,6 +432,7 @@ layers: {
}
`)
assert.Success(t, err)
t.Log(m)
assertQuery(t, m, 14, 0, nil, "")
},
},
@ -450,6 +451,24 @@ c
assertQuery(t, m, 4, 3, nil, "")
},
},
{
name: "alixander-review/3",
run: func(t testing.TB) {
m, err := compile(t, `
a
***.b -> c
layers: {
z: {
d
}
}
`)
assert.Success(t, err)
assertQuery(t, m, 8, 2, nil, "")
},
},
}
runa(t, tca)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1880,284 +1880,6 @@
}
}
}
},
{
"string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "x"
}
]
},
"key_path": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "scenarios"
}
]
}
},
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "x"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
"value": [
{
"string": "**",
"raw_string": "**"
}
],
"pattern": [
"*",
"",
"*"
]
}
}
]
},
"context": {
"edge": null,
"key": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-2:1:20",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "scenarios"
}
]
}
},
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "x"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
"value": [
{
"string": "**",
"raw_string": "**"
}
],
"pattern": [
"*",
"",
"*"
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:4:4-2:1:20",
"nodes": [
{
"map_key": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:12:18",
"key": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
"value": [
{
"string": "shape",
"raw_string": "shape"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:8:14-1:12:18",
"value": [
{
"string": "page",
"raw_string": "page"
}
]
}
}
}
}
]
}
}
}
}
},
{
"string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "x"
}
]
},
"key_path": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "scenarios"
}
]
}
},
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "x"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
"value": [
{
"string": "**",
"raw_string": "**"
}
],
"pattern": [
"*",
"",
"*"
]
}
}
]
},
"context": {
"edge": null,
"key": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-2:1:20",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "scenarios"
}
]
}
},
{
"unquoted_string": {
"range": ",0:0:0-0:0:0",
"value": [
{
"string": "x"
}
]
}
},
{
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:0:0-0:2:2",
"value": [
{
"string": "**",
"raw_string": "**"
}
],
"pattern": [
"*",
"",
"*"
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "TestCompile/patterns/double-glob/defaults.d2,0:4:4-2:1:20",
"nodes": [
{
"map_key": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:12:18",
"key": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
"path": [
{
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:1:7-1:6:12",
"value": [
{
"string": "shape",
"raw_string": "shape"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "TestCompile/patterns/double-glob/defaults.d2,1:8:14-1:12:18",
"value": [
{
"string": "page",
"raw_string": "page"
}
]
}
}
}
}
]
}
}
}
}
}
]
},