Stackoverflow when calling DType.ContainsDataEntityType (#2521)

We are extending the range for
https://github.com/microsoft/Power-Fx/issues/2456.
This commit is contained in:
Anderson Silva 2024-07-05 08:36:02 -05:00 коммит произвёл GitHub
Родитель d37722db85
Коммит 233a31a8c4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 105 добавлений и 70 удалений

Просмотреть файл

@ -164,7 +164,7 @@ namespace Microsoft.PowerFx.Core.Texl.Builtins
}
// We only support accessing entities in collections if the collection has only 1 argument that contributes to it's type
if (argc != 2 && itemType.ContainsDataEntityType(DPath.Root))
if (argc != 2 && itemType.ContainsDataEntityType(DPath.Root, itemType.MaxDepth))
{
fValid &= DropAllOfKindNested(ref itemType, errors, args[i], DKind.DataEntity);
}
@ -237,7 +237,7 @@ namespace Microsoft.PowerFx.Core.Texl.Builtins
}
// We only support accessing entities in collections if the collection has only 1 argument that contributes to it's type
if (argc != 2 && itemType.ContainsDataEntityType(DPath.Root))
if (argc != 2 && itemType.ContainsDataEntityType(DPath.Root, itemType.MaxDepth))
{
fValid &= DropAllOfKindNested(ref itemType, errors, args[i], DKind.DataEntity);
}

Просмотреть файл

@ -343,7 +343,7 @@ namespace Microsoft.PowerFx.Intellisense
{
if (info.Function.IsLambdaParam(null, argPosition) && !info.Function.HasSuggestionsForParam(argPosition) && type.IsValid)
{
if (ContainsDataEntityType(type, type.MaxDepth))
if (type.ContainsDataEntityType(DPath.Root, type.MaxDepth))
{
var error = false;
type = type.DropAllOfTableRelationships(ref error, DPath.Root);
@ -898,18 +898,5 @@ namespace Microsoft.PowerFx.Intellisense
var colonLen = TexlLexer.PunctuatorColon.Length;
return script.Length >= cursorPos + colonLen && script.Substring(cursorPos, colonLen) == TexlLexer.PunctuatorColon;
}
internal static bool ContainsDataEntityType(DType type, int currentDepth)
{
Contracts.AssertValid(type);
if (currentDepth < 1)
{
return false;
}
return type.GetNames(DPath.Root).Any(n => n.Type.IsExpandEntity ||
(n.Type.IsAggregate && ContainsDataEntityType(n.Type, currentDepth - 1)));
}
}
}

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System.Linq;
using Microsoft.PowerFx.Core.Entities;
using Microsoft.PowerFx.Core.Entities.QueryOptions;
using Microsoft.PowerFx.Core.Utils;
@ -69,5 +70,16 @@ namespace Microsoft.PowerFx.Core.Types
metaFieldType = control;
return true;
}
public static bool ContainsDataEntityType(this DType self, DPath path, int currentDepth)
{
if (currentDepth < 1)
{
return false;
}
return self.GetNames(path).Any(n => n.Type.IsExpandEntity ||
(n.Type.IsAggregate && n.Type.ContainsDataEntityType(DPath.Root, currentDepth - 1)));
}
}
}

Просмотреть файл

@ -7,6 +7,7 @@ using System.Globalization;
using System.Linq;
using Microsoft.PowerFx.Core;
using Microsoft.PowerFx.Core.Functions;
using Microsoft.PowerFx.Core.Tests;
using Microsoft.PowerFx.Core.Texl;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.Types.Enums;
@ -555,59 +556,5 @@ namespace Microsoft.PowerFx.Tests.IntellisenseTests
// Just check that the execution didn't stack overflow.
Assert.True(suggestions.Any());
}
private class LazyRecursiveRecordType : RecordType
{
public override IEnumerable<string> FieldNames => GetFieldNames();
public bool EnumerableIterated = false;
public LazyRecursiveRecordType()
: base()
{
}
public override bool TryGetFieldType(string name, out FormulaType type)
{
switch (name)
{
case "SomeString":
type = FormulaType.String;
return true;
case "TableLoop":
type = ToTable();
return true;
case "Loop":
type = this;
return true;
case "Record":
type = RecordType.Empty().Add("Foo", FormulaType.Number);
return true;
default:
type = FormulaType.Blank;
return false;
}
}
private IEnumerable<string> GetFieldNames()
{
EnumerableIterated = true;
yield return "SomeString";
yield return "Loop";
yield return "Record";
yield return "TableLoop";
}
public override bool Equals(object other)
{
return other is LazyRecursiveRecordType; // All the same
}
public override int GetHashCode()
{
return 1;
}
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System.Collections.Generic;
using Microsoft.PowerFx.Types;
namespace Microsoft.PowerFx.Core.Tests
{
public class LazyRecursiveRecordType : RecordType
{
public override IEnumerable<string> FieldNames => GetFieldNames();
public bool EnumerableIterated = false;
public LazyRecursiveRecordType()
: base()
{
}
public override bool TryGetFieldType(string name, out FormulaType type)
{
switch (name)
{
case "SomeString":
type = FormulaType.String;
return true;
case "TableLoop":
type = ToTable();
return true;
case "Loop":
type = this;
return true;
case "Record":
type = RecordType.Empty().Add("Foo", FormulaType.Number);
return true;
default:
type = FormulaType.Blank;
return false;
}
}
private IEnumerable<string> GetFieldNames()
{
EnumerableIterated = true;
yield return "SomeString";
yield return "Loop";
yield return "Record";
yield return "TableLoop";
}
public override bool Equals(object other)
{
return other is LazyRecursiveRecordType; // All the same
}
public override int GetHashCode()
{
return 1;
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using Microsoft.PowerFx.Core.Texl.Builtins;
using Microsoft.PowerFx.Syntax;
using Xunit;
namespace Microsoft.PowerFx.Core.Tests
{
public class RecursiveTypeTest : PowerFxTest
{
[Theory]
[InlineData("Collect(TableLoop, First(TableLoop), First(TableLoop))")]
public void NoStackoverflowTest(string expression)
{
var symbols = ReadOnlySymbolTable.NewFromRecord(new LazyRecursiveRecordType());
var engine = new Engine();
engine.Config.SymbolTable.AddFunction(new CollectFunction());
var check = engine.Check(expression, symbolTable: symbols, options: new ParserOptions() { AllowsSideEffects = true });
// No stackoverflow has been thrown.
Assert.False(check.IsSuccess);
}
}
}