preliminary implementation of inheritable method binding definitions and properly choosing the appropriate implementing method

This commit is contained in:
Michael Becker 2025-10-26 00:03:43 -04:00
parent 2b4b8b8408
commit 79d86a933c
4 changed files with 123 additions and 3 deletions

View File

@ -50,6 +50,8 @@ namespace Mocha.Core
}
public static class Boolean
{
// {989b5a95-d5bb-470c-b067-487316d22da2}
public static Guid Abstract { get; } = new Guid("{868d682b-f77b-4ed4-85a9-337f338635c1}");
public static Guid EvaluateWorkSet { get; } = new Guid("{62c28f9e-5ce8-4ce5-8a56-1e80f1af7f6a}");
public static Guid MethodIsOfTypeSpecified { get; } = new Guid("{6e9df667-0f95-4320-a4be-5cdb00f1d4ee}");
public static Guid DisplayVersionInBadge { get; } = new Guid("{BE5966A4-C4CA-49A6-B504-B6E8759F392D}");

View File

@ -114,6 +114,8 @@ namespace Mocha.Core
public static Guid Method_Binding__executes__Method { get; } = new Guid("{B782A592-8AF5-4228-8296-E3D0B24C70A8}");
public static Guid Method__has_return_type__Class { get; } = new Guid("{1241c599-e55d-4dcf-9200-d0e48c217ef8}");
public static Guid Method__implements__Method { get; } = new Guid("{83c992d7-03ec-483f-b6f1-225083f201e3}");
public static Guid Method__implemented_by__Method { get; } = new Guid("{f19e7779-a6b6-4914-a5cc-3c48fa1c9491}");
public static Guid Method_Binding__has__Parameter_Assignment { get; } = new Guid("{24938109-94f1-463a-9314-c49e667cf45b}");
public static Guid Parameter_Assignment__for__Method_Binding { get; } = new Guid("{19c4a5db-fd26-44b8-b431-e081e6ffff8a}");
@ -481,5 +483,6 @@ namespace Mocha.Core
public static Guid Invoke_Web_Service_Method__uses__Executable_returning_Element { get; } = new Guid("{020e26ba-565d-47a4-bdcc-f0050ff6b848}");
public static Guid Invoke_Web_Service_Method__correlated_instances_from__Executable_returning_Instance_Set { get; } = new Guid("{8f1406b4-f48f-45fb-a6ef-2ad51998735f}");
public static Guid Invoke_Web_Service_Method__has_error__Executable_returning_Element { get; } = new Guid("{86aa5338-d913-4044-af8d-7aa4e1a9ddbc}");
}
}
}

View File

@ -1347,9 +1347,9 @@ public abstract class Oms
{
if (ClassImplementations.TryGet<MethodImplementation>(parentClassId, out MethodImplementation? impl))
{
InstanceHandle forClass = GetRelatedInstance(methodOrMethodBinding, GetInstance(KnownRelationshipGuids.Method__for__Class));
if (targetInstance == null)
{
InstanceHandle forClass = GetRelatedInstance(methodOrMethodBinding, GetInstance(KnownRelationshipGuids.Method__for__Class));
if (forClass != InstanceHandle.Empty)
{
IInstanceReference? irTarget = context.GetWorkData<IInstanceReference?>(forClass);
@ -1366,6 +1366,29 @@ public abstract class Oms
//sthrow new NullReferenceException(String.Format("Attempt to call instance method `{0}` without an instance", methodOrMethodBinding));
}
bool is_abstract = GetAttributeValue<bool>(methodOrMethodBinding, GetInstance(KnownAttributeGuids.Boolean.Abstract));
if (is_abstract)
{
InstanceHandle forClassInstance = context.GetWorkData<InstanceHandle>(forClass);
InstanceHandle parentClass2 = GetParentClass(forClassInstance);
string parentClassName = GetAttributeValue<string>(parentClass2, GetInstance(KnownAttributeGuids.Text.Name));
IEnumerable<InstanceHandle> methodsImplementingMethod = GetRelatedInstances(methodOrMethodBinding, GetInstance(KnownRelationshipGuids.Method__implemented_by__Method));
foreach (InstanceHandle ih in methodsImplementingMethod)
{
InstanceHandle forClassInstance2 = GetRelatedInstance(ih, GetInstance(KnownRelationshipGuids.Method__for__Class));
if (parentClass2 == forClassInstance2)
{
// call implementing method instead
retval = Execute(context, ih, targetInstance);
if (retval != InstanceHandle.Empty)
{
return retval.GetValueOrDefault();
}
}
}
throw new InvalidOperationException(String.Format("No method implementation for abstract method {0} on class {1} ({2})", GetInstanceKey(methodOrMethodBinding), parentClassName.Replace(' ', '_') + "--IS", GetInstanceKey(parentClass2)));
}
if (targetInstance == null)
{
@ -2809,4 +2832,9 @@ public abstract class Oms
// find AttributeImplementation whose ClassGuid matches
return ClassImplementations.Get<AttributeImplementation>(classGuid);
}
public InstanceHandle GetInstance(object method__implements__Method)
{
throw new NotImplementedException();
}
}

View File

@ -15,6 +15,8 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using Mocha.Core.Oop;
using Mocha.Core.Oop.Methods;
using Mocha.Testing;
namespace Mocha.Core.Tests;
@ -47,8 +49,93 @@ public class InheritanceTests : OmsTestsBase
InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute);
InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
InstanceHandle ihParentClass = Oms.GetParentClass(ihTextAttribute);
Assert.That(ihParentClass, Is.EqualTo(ihClass));
}
[Test]
public void Unimplemented_Abstract_Method_Call_throws_Exception()
{
// this is what we want to achieve:
// Abstract Base Class (e.g. Layout) defines attribute `Widget Name` which should be overridden by subclasses
// Abstract Base Class also defines a single Get Attribute method which returns `Widget Name`
// Concrete Subclass (e.g. Stylized Header Layout) overrides `Widget Name` attribute by re-defining it on the subclass
// Calling `Abstract Base Class@get Widget Name` with the subclass as `this` parm SHOULD return the overridden attribute value
// The Method should only be defined ONCE, and changes based on the overridden attributes of the subclasses
InstanceHandle c_TestClass = Oms.GetInstance(TEST_CLASS_GUID);
// `Test Class@get Widget Name(BA)` which is Abstract
BuildAttributeMethod m_TestMethod = Oms.MethodBuilder.CreateBuildAttributeMethod(c_TestClass, "get", "Widget Name", Core.Oop.AccessModifier.Public, false, Oms.GetInstance(KnownAttributeGuids.Text.Name), "DO_NOT_USE");
Oms.SetAttributeValue(m_TestMethod, Oms.GetInstance(KnownAttributeGuids.Boolean.Abstract), true);
ReturnAttributeMethodBinding ramb = m_TestMethod.CreateMethodBinding(Oms);
InstanceHandle c_TestClass2 = Oms.GetInstance(TEST_CLASS2_GUID);
Oms.AddSuperClass(c_TestClass2, c_TestClass);
// i_TestClass1 is an instance of c_TestClass2, which by inheritance includes c_TestClass
InstanceHandle i_TestClass1 = Oms.CreateInstanceOf(c_TestClass2);
OmsContext context = Oms.CreateContext();
Assert.That(delegate ()
{
// set the c_TestClass to i_TestClass1
// this call SHOULD succeed given that c_TestClass is a superclass of c_TestClass2, but I don't think this is enforced (yet)
context.SetWorkData(c_TestClass, i_TestClass1);
// this should throw an exception since c_TestClass2 does not yet implement the m_TestMethod defined as abstract on c_TestClass
string nom = Oms.ExecuteReturningAttributeValue<string>(context, ramb);
}, Throws.InvalidOperationException);
}
[Test]
public void Overridden_Abstract_Method_Call_does_not_throw_Exception()
{
// this is what we want to achieve:
// Abstract Base Class (e.g. Layout) defines attribute `Widget Name` which should be overridden by subclasses
// Abstract Base Class also defines a single Get Attribute method which returns `Widget Name`
// Concrete Subclass (e.g. Stylized Header Layout) overrides `Widget Name` attribute by re-defining it on the subclass
// Calling `Abstract Base Class@get Widget Name` with the subclass as `this` parm SHOULD return the overridden attribute value
// The Method should only be defined ONCE, and changes based on the overridden attributes of the subclasses
InstanceHandle c_TestClass = Oms.GetInstance(TEST_CLASS_GUID);
// `Test Class@get Widget Name(BA)` which is Abstract
BuildAttributeMethod m_TestMethod = Oms.MethodBuilder.CreateBuildAttributeMethod(c_TestClass, "get", "Widget Name", Core.Oop.AccessModifier.Public, false, Oms.GetInstance(KnownAttributeGuids.Text.Name), "DO_NOT_USE");
Oms.SetAttributeValue(m_TestMethod, Oms.GetInstance(KnownAttributeGuids.Boolean.Abstract), true);
ReturnAttributeMethodBinding ramb = m_TestMethod.CreateMethodBinding(Oms);
InstanceHandle c_TestClass2 = Oms.GetInstance(TEST_CLASS2_GUID);
Oms.AddSuperClass(c_TestClass2, c_TestClass);
// `Test Class 2@get Widget Name(BA)` which is overriding the Abstract defined above
string NOM_TEST_VALUE = "Hello World Test Class";
BuildAttributeMethod m_TestMethod2 = Oms.MethodBuilder.CreateBuildAttributeMethod(c_TestClass2, "get", "Widget Name", Core.Oop.AccessModifier.Public, false, Oms.GetInstance(KnownAttributeGuids.Text.Name), NOM_TEST_VALUE);
ReturnAttributeMethodBinding ramb2 = m_TestMethod2.CreateMethodBinding(Oms);
Oms.AssignRelationship(m_TestMethod2, Oms.GetInstance(KnownRelationshipGuids.Method__implements__Method), m_TestMethod);
// i_TestClass1 is an instance of c_TestClass2, which by inheritance includes c_TestClass
InstanceHandle i_TestClass1 = Oms.CreateInstanceOf(c_TestClass2);
OmsContext context = Oms.CreateContext();
string nom = "";
Assert.That(delegate ()
{
// set the c_TestClass to i_TestClass1
// this call SHOULD succeed given that c_TestClass is a superclass of c_TestClass2, but I don't think this is enforced (yet)
context.SetWorkData(c_TestClass, i_TestClass1);
// this should throw an exception since c_TestClass2 does not yet implement the m_TestMethod defined as abstract on c_TestClass
nom = Oms.ExecuteReturningAttributeValue<string>(context, ramb);
}, Throws.Nothing);
Assert.That(nom, Is.EqualTo(NOM_TEST_VALUE));
}
}