From 72f09636dac8d3c0370134480412b64f8b81e7dc Mon Sep 17 00:00:00 2001 From: Anderson Silva Date: Tue, 13 Aug 2024 18:42:04 -0500 Subject: [PATCH] UO.TryGetProperty should determine the result value (#2592) Issue https://github.com/microsoft/Power-Fx/issues/2591. --- .../Public/Values/UntypedObjectValue.cs | 25 ++++++++++++++++ .../EvalVisitor.cs | 7 ++++- .../PadUntypedObjectTests.cs | 30 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.PowerFx.Core/Public/Values/UntypedObjectValue.cs b/src/libraries/Microsoft.PowerFx.Core/Public/Values/UntypedObjectValue.cs index c922f5714..e62fdccfd 100644 --- a/src/libraries/Microsoft.PowerFx.Core/Public/Values/UntypedObjectValue.cs +++ b/src/libraries/Microsoft.PowerFx.Core/Public/Values/UntypedObjectValue.cs @@ -101,6 +101,31 @@ namespace Microsoft.PowerFx.Types public abstract bool TryGetProperty(string value, out IUntypedObject result); + /// + /// Get the property value of the untyped object. + /// Hosts should override this method in case a different return value is needed. For example, a host may want to return a ErrorValue in case of a missing property. + /// + /// + /// + /// + /// + public virtual FormulaValue GetProperty(string value, FormulaType returnType) + { + if (TryGetProperty(value, out var res)) + { + if (res.Type == FormulaType.Blank) + { + return new BlankValue(IRContext.NotInSource(returnType)); + } + + return new UntypedObjectValue(IRContext.NotInSource(returnType), res); + } + else + { + return new BlankValue(IRContext.NotInSource(returnType)); + } + } + public abstract bool TryGetPropertyNames(out IEnumerable propertyNames); /// diff --git a/src/libraries/Microsoft.PowerFx.Interpreter/EvalVisitor.cs b/src/libraries/Microsoft.PowerFx.Interpreter/EvalVisitor.cs index 53f3e7936..258bbbcca 100644 --- a/src/libraries/Microsoft.PowerFx.Interpreter/EvalVisitor.cs +++ b/src/libraries/Microsoft.PowerFx.Interpreter/EvalVisitor.cs @@ -558,7 +558,12 @@ namespace Microsoft.PowerFx if (arg1 is UntypedObjectValue cov && arg2 is StringValue sv) { if (cov.Impl.Type is ExternalType et && (et.Kind == ExternalTypeKind.Object || et.Kind == ExternalTypeKind.ArrayAndObject)) - { + { + if (cov.Impl is UntypedObjectBase untypedObjectBase) + { + return untypedObjectBase.GetProperty(sv.Value, node.IRContext.ResultType); + } + if (cov.Impl.TryGetProperty(sv.Value, out var res)) { if (res.Type == FormulaType.Blank) diff --git a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/PadUntypedObjectTests.cs b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/PadUntypedObjectTests.cs index edfe0905b..fbdcc52a6 100644 --- a/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/PadUntypedObjectTests.cs +++ b/src/tests/Microsoft.PowerFx.Interpreter.Tests.Shared/PadUntypedObjectTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; using Microsoft.PowerFx.Core.IR; +using Microsoft.PowerFx.Syntax; using Microsoft.PowerFx.Types; using Xunit; @@ -138,6 +139,30 @@ namespace Microsoft.PowerFx.Interpreter.Tests Assert.Equal(97m, result.ToObject()); } + [Fact] + public void PadUntypedObjectMissingProperty() + { + var uo1 = new PadUntypedObject(GetDataTable()); + var uo2 = new PadUntypedObject2(GetDataTable()); + + var uov1 = new UntypedObjectValue(IRContext.NotInSource(FormulaType.UntypedObject), uo1); + var uov2 = new UntypedObjectValue(IRContext.NotInSource(FormulaType.UntypedObject), uo2); + + RecalcEngine engine = new RecalcEngine(); + + engine.Config.SymbolTable.EnableMutationFunctions(); + engine.UpdateVariable("padTable1", uov1); + engine.UpdateVariable("padTable2", uov2); + + // PadUntypedObject does not override GetProperty. Returns blank if property is missing. + var result1 = engine.Eval(@"Index(padTable1, 1).Missing"); + Assert.IsType(result1); + + // PadUntypedObject2 overrides GetProperty. Returns error if property is missing. + var result2 = engine.Eval(@"Index(padTable2, 1).Missing"); + Assert.IsType(result2); + } + private DataTable GetDataTable() { var dt = new DataTable("someTable"); @@ -562,6 +587,11 @@ namespace Microsoft.PowerFx.Interpreter.Tests throw new CustomFunctionErrorException("Something went wrong.", ErrorKind.InvalidArgument); } + + public override FormulaValue GetProperty(string value, FormulaType returnType) + { + return new ErrorValue(IRContext.NotInSource(returnType), new ExpressionError() { Kind = ErrorKind.InvalidArgument }); + } } #endregion }