Skip to content

Commit

Permalink
Crash when accessing static hidden methods from zero touch nodes (Dyn…
Browse files Browse the repository at this point in the history
…amoDS#11238)

* handle hidden static methods from zero touch nodes
  • Loading branch information
pinzart90 authored Nov 12, 2020
1 parent 3f46686 commit 86485ee
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 43 deletions.
51 changes: 32 additions & 19 deletions src/Engine/ProtoCore/Lang/CallSite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,23 +1118,35 @@ private FunctionEndPoint SelectFEPFromMultiple(StackFrame stackFrame, RuntimeCor
Validity.Assert(svThisPtr.IsPointer,
"this pointer wasn't a pointer. {89635B06-AD53-4170-ADA5-065EB2AE5858}");

int typeID = svThisPtr.metaData.type;

//Test for exact match
List<FunctionEndPoint> exactFeps = new List<FunctionEndPoint>();

foreach (FunctionEndPoint fep in feps)
if (fep.ClassOwnerIndex == typeID)
exactFeps.Add(fep);

if (exactFeps.Count == 1)
{
return exactFeps[0];
}


// We have multiple possible scopes for the function call:
// 1. Static method call - no this pointer
// ex: ClassA.Method();
// Hidden static methods generate multiple feps.
// We do not need to check actually if the method has the "IsHideBySig" (https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.ishidebysig)
// because static methods can only be hidden.
// 2. Method call from an instance of a class - valid this pointer.
// ex: classAInstance.method();
// 3. Function from the global scope - no this pointer and no class scope.
// ex: SomeGlobalFunction();
//
// All 3 cases will run through the same matching steps.

// A static function call has an invalid this pointer and a valid class scope;
bool isValidStaticFuncCall = svThisPtr.Pointer == Constants.kInvalidIndex && stackFrame.ClassScope != Constants.kInvalidIndex;

int typeID = isValidStaticFuncCall ? stackFrame.ClassScope : svThisPtr.metaData.type;

// Try to match with feps belonging to the class scope (most derived class should have priority).
// In this case we simply select the function that belongs to the calling class.
// The assumption here is that all function end points in "feps" have already been checked that they have the same signature.
IEnumerable<FunctionEndPoint> exactFeps = feps.Where(x => x.ClassOwnerIndex == typeID);
if (exactFeps.Count() == 1)
{
return exactFeps.First();
}

//Walk the class tree structure to find the method

while (runtimeCore.DSExecutable.classTable.ClassNodes[typeID].Base != Constants.kInvalidIndex)
{
typeID = runtimeCore.DSExecutable.classTable.ClassNodes[typeID].Base;
Expand All @@ -1149,15 +1161,15 @@ private FunctionEndPoint SelectFEPFromMultiple(StackFrame stackFrame, RuntimeCor

foreach (FunctionEndPoint fep in feps)
{
int noArbitraries = 0;
int numArbitraryRanks = 0;

for (int i = 0; i < argumentsList.Count; i++)
{
if (fep.FormalParams[i].rank == Constants.kArbitraryRank)
noArbitraries++;

numberOfArbitraryRanks.Add(noArbitraries);
numArbitraryRanks++;
}

numberOfArbitraryRanks.Add(numArbitraryRanks);
}

int smallest = Int32.MaxValue;
Expand Down Expand Up @@ -1461,6 +1473,7 @@ private StackValue DispatchNew(
candidatesFeps.Add(fep);
}
}

funcGroup = new FunctionGroup(candidatesFeps);

#endregion
Expand Down
59 changes: 37 additions & 22 deletions src/Engine/ProtoCore/Reflection/Mirror.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,26 +335,27 @@ internal ClassMirror(StackValue svData, ProtoCore.Core core)
}

/// <summary>
/// Returns the constructors and static methods and properties
/// belonging to the type and its base types
/// Returns a list of constructors and static methods and properties
/// belonging to the type and its base types. Filters out repeating names.
/// </summary>
/// <returns></returns>
public IEnumerable<StaticMirror> GetMembers()
{
// TODO: Factor out reflection functionality for both LibraryServices and Mirrors
List<StaticMirror> members = new List<StaticMirror>();
members.AddRange(GetConstructors());
members.AddRange(GetFunctions().Where(m => m.IsStatic));
members.AddRange(GetProperties().Where(m => m.IsStatic));

IEnumerable<ClassMirror> baseClasses = this.GetClassHierarchy();
IEnumerable<ClassMirror> baseClasses = GetClassHierarchy();
foreach (var baseClass in baseClasses)
{
members.AddRange(baseClass.GetFunctions().Where(m => m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
members.AddRange(baseClass.GetProperties().Where(m => m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
members.AddRange(baseClass.GetFunctions().Where(m => m.IsStatic));
members.AddRange(baseClass.GetProperties().Where(m => m.IsStatic));
}

members.AddRange(this.GetConstructors().GroupBy(x => x.Name).Select(y => y.First()));
members.AddRange(this.GetFunctions().Where(m => m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
members.AddRange(this.GetProperties().Where(m => m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
return members;
// Return a list of members with unique names.
return members.GroupBy(x => x.Name).Select(x => x.First()).ToList();
}

/// <summary>
Expand Down Expand Up @@ -468,20 +469,28 @@ public IEnumerable<MethodMirror> GetOverloads(string methodName)
/// <summary>
/// Given a method name, return the matching list of
/// constructors or static methods on this type and its base types
/// Excludes hidden methods from base types.
/// </summary>
/// <param name="methodName"></param>
/// <returns></returns>
public IEnumerable<MethodMirror> GetOverloadsOnType(string methodName)
{
List<MethodMirror> members = new List<MethodMirror>();
IEnumerable<ClassMirror> baseClasses = this.GetClassHierarchy();
members.AddRange(GetConstructors().Where(x => x.MethodName == methodName));
members.AddRange(GetFunctions().Where(x => x.IsStatic && x.MethodName == methodName));

// Filter out hidden functions/properties:
// Create a set of unique function and constructor descriptions.
// In this case we use ToString() to get the unique description of the members (func signature, constructor names).
var derivedClassMembers = new HashSet<string>();
members.ForEach(x => derivedClassMembers.Add(x.ToString()));

IEnumerable<ClassMirror> baseClasses = GetClassHierarchy();
foreach (var baseClass in baseClasses)
{
members.AddRange(baseClass.GetFunctions().Where(x => x.IsStatic && x.MethodName == methodName));
members.AddRange(baseClass.GetFunctions().Where(x => x.IsStatic && x.MethodName == methodName && !derivedClassMembers.Contains(x.ToString())));
}

members.AddRange(this.GetConstructors().Where(x => x.MethodName == methodName));
members.AddRange(this.GetFunctions().Where(x => x.IsStatic && x.MethodName == methodName));

return members;
}

Expand All @@ -494,31 +503,37 @@ public IEnumerable<MethodMirror> GetOverloadsOnType(string methodName)
public IEnumerable<MethodMirror> GetOverloadsOnInstance(string methodName)
{
List<MethodMirror> members = new List<MethodMirror>();
IEnumerable<ClassMirror> baseClasses = this.GetClassHierarchy();
IEnumerable<ClassMirror> baseClasses = GetClassHierarchy();
foreach (var baseClass in baseClasses)
{
members.AddRange(baseClass.GetFunctions().Where(x => !x.IsStatic && x.MethodName == methodName));
}

members.AddRange(this.GetFunctions().Where(x => !x.IsStatic && x.MethodName == methodName));
members.AddRange(GetFunctions().Where(x => !x.IsStatic && x.MethodName == methodName));
return members;
}

/// <summary>
/// Returns the instance methods and properties
/// belonging to the type and its base types. Filters out repeating names.
/// </summary>
public IEnumerable<StaticMirror> GetInstanceMembers()
{
// TODO: Factor out reflection functionality for both LibraryServices and Mirrors

List<StaticMirror> members = new List<StaticMirror>();
members.AddRange(GetFunctions().Where(m => !m.IsStatic));
members.AddRange(GetProperties().Where(m => !m.IsStatic));

IEnumerable<ClassMirror> baseClasses = this.GetClassHierarchy();
IEnumerable<ClassMirror> baseClasses = GetClassHierarchy();
foreach (var baseClass in baseClasses)
{
members.AddRange(baseClass.GetFunctions().Where(m => !m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
members.AddRange(baseClass.GetProperties().Where(m => !m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
members.AddRange(baseClass.GetFunctions().Where(m => !m.IsStatic));
members.AddRange(baseClass.GetProperties().Where(m => !m.IsStatic));
}

members.AddRange(this.GetFunctions().Where(m => !m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
members.AddRange(this.GetProperties().Where(m => !m.IsStatic).GroupBy(x => x.Name).Select(y => y.First()));
return members;
// Return a list of members with unique names.
return members.GroupBy(x => x.Name).Select(y => y.First()).ToList();
}

public ClassAttributes GetClassAttributes()
Expand Down
17 changes: 15 additions & 2 deletions test/DynamoCoreTests/CodeBlockNodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2270,9 +2270,22 @@ public void TestCompletionOnDerivedTypeReturnsBaseType()
var codeCompletionServices = new CodeCompletionServices(libraryServicesCore);
var completions = codeCompletionServices.GetCompletionsOnType("", "DupTargetTest").ToList();
Assert.AreEqual(3, completions.Count);
Assert.AreEqual("Foo", completions[0].Text);
Assert.AreEqual("DupTargetTest", completions[1].Text);
Assert.AreEqual("DupTargetTest", completions[0].Text);
Assert.AreEqual("Bar", completions[1].Text);
Assert.AreEqual("Foo", completions[2].Text);
}

[Test]
[Category("UnitTests")]
public void TestCompletionOnDerivedTypeReturnsNonHiddenBaseMethods()
{
var codeCompletionServices = new CodeCompletionServices(libraryServicesCore);
var completions = codeCompletionServices.GetCompletionsOnType("", "FFITarget.HidesMethodFromClassA").ToList();
Assert.AreEqual(4, completions.Count);
Assert.AreEqual("HidesMethodFromClassA", completions[0].Text);
Assert.AreEqual("Baz", completions[1].Text);
Assert.AreEqual("Bar", completions[2].Text);
Assert.AreEqual("Foo", completions[3].Text);
}

[Test]
Expand Down
17 changes: 17 additions & 0 deletions test/Engine/ProtoTest/FFITests/CSFFITest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,23 @@ public void TestInheritanceCtorsVirtualMethods()
ExecuteAndVerify(code, data);
}

[Test]
public void TestStaticHiddenFunctionCallResolution()
{
string code = @"
d = HidesMethodFromClassA.Baz();
b = ClassA.Baz();
";

Type dummy = typeof(FFITarget.HidesMethodFromClassA);
code = string.Format("import(\"{0}\");\r\n{1}", dummy.AssemblyQualifiedName, code);
Console.WriteLine(code);
ValidationData[] data = { new ValidationData { ValueName = "d", ExpectedValue = 23, BlockIndex = 0 },
new ValidationData { ValueName = "b", ExpectedValue = 234, BlockIndex = 0 }
};
ExecuteAndVerify(code, data);
}

[Test]
public void TestInheritanceCtorsVirtualMethods2()
{
Expand Down

0 comments on commit 86485ee

Please sign in to comment.