diff --git a/mocha-common b/mocha-common index a18167f..a40a8b3 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit a18167f13f14d45bff57f8d88cea1e9060d3fa60 +Subproject commit a40a8b3685a377ab183eb0c906acf8c806b4e215 diff --git a/mocha-dotnet-new b/mocha-dotnet-new new file mode 100755 index 0000000..a5501c3 --- /dev/null +++ b/mocha-dotnet-new @@ -0,0 +1,9 @@ +#!/bin/bash + +# mocha-dotnet-new +# creates a new DotNet project and adds all the references needed to use +# Mocha from within .NET + +dotnet new console +dotnet add reference /usr/lib/mocha/dotnet/Mocha.Core.dll +dotnet add reference /usr/lib/mocha/dotnet/MBS.Core.dll diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index 76dd40f..7eba1ca 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -64,20 +64,8 @@ public abstract class Oms { InitializeInternal(); - AccessModifier.Private = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.Private)); - AccessModifier.Protected = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.Protected)); - AccessModifier.Public = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.Public)); - AccessModifier.RootA2 = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.RootA2)); + UpdateSyntacticSugar(); - PropertyInfo[] pis = typeof(RelationalOperator).GetProperties(BindingFlags.Static | BindingFlags.Public); - foreach (PropertyInfo pi in pis) - { - PropertyInfo pi2 = typeof(KnownInstanceGuids.RelationalOperators).GetProperty(pi.Name, BindingFlags.Public | BindingFlags.Static); - Guid guid = (Guid)pi2.GetValue(null); - - pi.SetValue(null, new RelationalOperator(GetInstance(guid))); - } - RegisterSystemRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, new SystemAttributeRoutine(this, KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, delegate (Oms oms, OmsContext context) { return RuntimeVersion.ToString(); @@ -141,12 +129,45 @@ public abstract class Oms public TenantHandle CurrentTenant { get; private set; } - protected abstract bool SelectTenantInternal(TenantHandle tenant); + private void UpdateSyntacticSugar() + { + if (CurrentTenant != TenantHandle.Empty) + { + // now that tenant is selected, update the syntactic sugar properties + // !!! THIS IS PROBABLY NOT THREAD-SAFE. BE VERY VERY CAREFUL !!! + UpdateSyntacticSugar(typeof(KnownInstanceGuids.AccessModifiers)); + UpdateSyntacticSugar(typeof(KnownInstanceGuids.RelationalOperators)); + } + } + + private void UpdateSyntacticSugar(Type sourceType) where T : ConcreteInstanceWrapper + { + Type destinationType = typeof(T); + PropertyInfo[] pis = destinationType.GetProperties(BindingFlags.Static | BindingFlags.Public); + foreach (PropertyInfo pi in pis) + { + PropertyInfo pi2 = sourceType.GetProperty(pi.Name, BindingFlags.Public | BindingFlags.Static); + Guid guid = (Guid)pi2.GetValue(null); + + if (TryGetInstance(guid, out InstanceHandle ih)) + { + object objT = destinationType.Assembly.CreateInstance(destinationType.FullName, false, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { ih }, null, null); + pi.SetValue(null, objT); + } + else + { + } + } + } + + protected abstract bool SelectTenantInternal(TenantHandle tenant); public void SelectTenant(TenantHandle tenant) { if (SelectTenantInternal(tenant)) { CurrentTenant = tenant; + + UpdateSyntacticSugar(); } } @@ -1116,6 +1137,11 @@ public abstract class Oms { AddLibraryReferenceInternal(library); } + protected abstract void RemoveLibraryReferenceInternal(LibraryHandle library); + public void RemoveLibraryReference(LibraryHandle library) + { + RemoveLibraryReferenceInternal(library); + } private Dictionary _libraries = new Dictionary(); /// @@ -1249,6 +1275,20 @@ public abstract class Oms return lh; } + private Dictionary _libraryFileNames = new Dictionary(); + + public LibraryHandle LoadLibrary(Type type, string manifestResourceStreamName) + { + Stream? st = type.Assembly.GetManifestResourceStream(manifestResourceStreamName); + if (st != null) + { + LibraryHandle lh = LoadLibrary(st); + _libraryFileNames[lh] = manifestResourceStreamName; + return lh; + } + throw new KeyNotFoundException(String.Format("manifest resource stream named '{0}' not found", manifestResourceStreamName)); + } + protected abstract void InitializeLibraryInternal(LibraryHandle lh, Library lib); public string? GetTenantName(TenantHandle tenant) diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs index f8fb3ca..81547ca 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs @@ -34,6 +34,9 @@ public class MemoryOms : Oms private Dictionary>> _Attributes = new Dictionary>>(); private Dictionary>> _Relationships = new Dictionary>>(); + // private Dictionary _libraryInst_gs = new Dictionary(); + // private Dictionary _libraryInst_is = new Dictionary(); + public Guid GetGlobalIdentifier(InstanceHandle instance) { if (_guidsByInstance.ContainsKey(instance)) @@ -240,6 +243,16 @@ public class MemoryOms : Oms _libraryReferences[_CurrentTenant].Add(library); } } + protected override void RemoveLibraryReferenceInternal(LibraryHandle library) + { + if (_libraryReferences.ContainsKey(_CurrentTenant)) + { + if (_libraryReferences[_CurrentTenant].Contains(library)) + { + _libraryReferences[_CurrentTenant].Remove(library); + } + } + } protected override Guid GetGlobalIdentifierInternal(InstanceHandle instance) { @@ -249,10 +262,19 @@ public class MemoryOms : Oms Guid gid = _CurrentTenantData.GetGlobalIdentifier(instance); if (gid == Guid.Empty) { - if (_libraryInst_is.ContainsKey(instance)) + if (_libraryReferences.ContainsKey(_CurrentTenant)) { - gid = _libraryInst_is[instance].GlobalIdentifier; + foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant]) + { + gid = _libraries[lh].GetGlobalIdentifier(instance); + if (gid != Guid.Empty) + return gid; + } } + // if (_libraryInst_is.ContainsKey(instance)) + // { + // gid = _libraryInst_is[instance].GlobalIdentifier; + // } } return gid; } @@ -298,9 +320,9 @@ public class MemoryOms : Oms { InstanceHandle ih = lib.CreateInstance(inst.InstanceGuid); - LibraryInst_g lig = new LibraryInst_g() { GlobalIdentifier = inst.InstanceGuid, InstanceHandle = ih, SourceLibrary = lh }; - _libraryInst_is[ih] = lig; - _libraryInst_gs[inst.InstanceGuid] = lig; + // LibraryInst_g lig = new LibraryInst_g() { GlobalIdentifier = inst.InstanceGuid, InstanceHandle = ih, SourceLibrary = lh }; + // _libraryInst_is[ih] = lig; + // _libraryInst_gs[inst.InstanceGuid] = lig; } foreach (LibraryAttribute att in data.Attributes) { @@ -328,8 +350,6 @@ public class MemoryOms : Oms public InstanceHandle InstanceHandle; public Guid GlobalIdentifier; } - private Dictionary _libraryInst_gs = new Dictionary(); - private Dictionary _libraryInst_is = new Dictionary(); protected override bool TryGetInstanceInternal(Guid globalIdentifier, out InstanceHandle ih) { @@ -339,27 +359,30 @@ public class MemoryOms : Oms bool v = _CurrentTenantData.TryGetInstance(globalIdentifier, out ih); if (!v) { - if (_libraryInst_gs.ContainsKey(globalIdentifier)) - { - ih = _libraryInst_gs[globalIdentifier].InstanceHandle; - return true; - } - else - { - foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant]) + // if (_libraryInst_gs.ContainsKey(globalIdentifier)) + // { + // ih = _libraryInst_gs[globalIdentifier].InstanceHandle; + // return true; + // } + // else + // { + if (_libraryReferences.ContainsKey(_CurrentTenant)) { - if (_libraries.ContainsKey(lh)) + foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant]) { - if (_libraries[lh].TryGetInstance(globalIdentifier, out ih)) + if (_libraries.ContainsKey(lh)) { - LibraryInst_g g = new LibraryInst_g() { InstanceHandle = ih, SourceLibrary = lh, GlobalIdentifier = globalIdentifier }; - _libraryInst_gs[globalIdentifier] = g; - _libraryInst_is[ih] = g; - return true; + if (_libraries[lh].TryGetInstance(globalIdentifier, out ih)) + { + // LibraryInst_g g = new LibraryInst_g() { InstanceHandle = ih, SourceLibrary = lh, GlobalIdentifier = globalIdentifier }; + // _libraries[lh]._libraryInst_gs[globalIdentifier] = g; + // _libraries[lh]._libraryInst_is[ih] = g; + return true; + } } } } - } + // } } return v; } diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs index 7013aab..ee13564 100644 --- a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs @@ -142,9 +142,11 @@ public class McxMiniLibraryPlugin : LibraryPlugin Guid sourceInstanceGuid = guids[sourceInstanceIndex]; Guid relationshipInstanceGuid = guids[relationshipInstanceIndex]; - Guid targetInstanceGuid = guids[targetInstanceIndex]; - - library.Relationships.Add(new LibraryRelationship(sourceInstanceGuid, relationshipInstanceGuid, targetInstanceGuid)); + if (targetInstanceIndex >= 0 && targetInstanceIndex < guids.Count) + { + Guid targetInstanceGuid = guids[targetInstanceIndex]; + library.Relationships.Add(new LibraryRelationship(sourceInstanceGuid, relationshipInstanceGuid, targetInstanceGuid)); + } } r.BaseStream.Seek(stringTableSection.Offset, SeekOrigin.Begin); diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs index 6e56d1a..c1220bb 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs @@ -131,4 +131,23 @@ public class BasicTests : OmsTestsBase Oms.SetAttributeValue(irTestClassInstance, irTestTextAttribute, irTestClass); }, Throws.ArgumentException, "Assigning something other than a System.String value to a Text Attribute MUST throw an ArgumentException."); } + + [Test] + public void TextAttribute_GetInstance_returns_same_InstanceHandle_before_and_after_LoadLibrary() + { + // remove the Web reference just for this test + // Oms.RemoveLibraryReference(lhWeb); + + InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute); + + // load the other library, but do not reference it yet + LibraryHandle lhWeb = Oms.LoadLibrary(typeof(LibraryLoaderTests), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.Web.mcl"); + + // add back the reference to the other library + // Oms.AddLibraryReference(lhWeb); + + InstanceHandle ihTextAttribute2 = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute); + Assert.That(ihTextAttribute2, Is.EqualTo(ihTextAttribute)); + } + } \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/EmbeddedMiniOms.cs b/mocha-dotnet/tests/Mocha.Core.Tests/EmbeddedMiniOms.cs index f5b0688..be70bf5 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/EmbeddedMiniOms.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/EmbeddedMiniOms.cs @@ -8,19 +8,10 @@ public class EmbeddedMiniOms : MemoryOms { base.InitializeInternal(); + LibraryHandle lh = LoadLibrary(typeof(EmbeddedMiniOms), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.System.mcl"); + TenantHandle th = CreateTenant("super"); SelectTenant(th); - - System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); - Stream? stream = asm.GetManifestResourceStream("Mocha.Core.Tests.Resources.net.alcetech.Mocha.System.mcl"); - if (stream != null) - { - LibraryHandle lh = LoadLibrary(stream); - this.AddLibraryReference(lh); - } - else - { - throw new InvalidOperationException("Manifest resource stream not found!"); - } + AddLibraryReference(lh); } } \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/InheritanceTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/InheritanceTests.cs new file mode 100644 index 0000000..0378d39 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Core.Tests/InheritanceTests.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of Mocha.NET. +// +// Mocha.NET is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Mocha.NET is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Mocha.NET. If not, see . + +using System.Runtime.CompilerServices; + +using Mocha.Core.OmsImplementations.Mini; + +namespace Mocha.Core.Tests; + +[TestFixture] +public class InheritanceTests : OmsTestsBase +{ + [Test] + public void TextAttribute_ParentClass_is_Class() + { + 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 TextAttribute_ParentClass_is_Class_when_multiple_libraries_are_referenced() + { + // add a reference to the other library + LibraryHandle lhWeb = Oms.LoadLibrary(typeof(LibraryLoaderTests), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.Web.mcl"); + Oms.AddLibraryReference(lhWeb); + + // ??? this is weird. the moment we add that library reference, this suddenly breaks + // 2024-10-09 08:24 beckermj - this is why we have this unit test + // the reason is that the inst for guid {c2f36542...} is NOT the same instance handle as the originally registered one + // we need to fix GetInstance so that it returns the same InstanceHandle for the same GUID + // ... or maybe get rid of InstanceHandles entirely + + 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)); + } +} \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/LibraryLoaderTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/LibraryLoaderTests.cs new file mode 100644 index 0000000..3a601e8 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Core.Tests/LibraryLoaderTests.cs @@ -0,0 +1,84 @@ +// Copyright (C) 2024 Michael Becker +// +// This file is part of Mocha.NET. +// +// Mocha.NET is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Mocha.NET is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Mocha.NET. If not, see . + +using System.Runtime.CompilerServices; + +using Mocha.Core.OmsImplementations.Mini; + +namespace Mocha.Core.Tests; + +[TestFixture] +public class LibraryLoaderTests : OmsTestsBase +{ + private LibraryHandle lhWeb; + + protected override void AfterSetup() + { + base.AfterSetup(); + + // add a reference to the other library + lhWeb = Oms.LoadLibrary(typeof(LibraryLoaderTests), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.Web.mcl"); + Oms.AddLibraryReference(lhWeb); + } + + [Test] + public void TextAttribute_GetInstance_returns_same_InstanceHandle_before_and_after_AddLibraryReference() + { + // remove the Web reference just for this test + Oms.RemoveLibraryReference(lhWeb); + + InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute); + + // add back the reference to the other library + Oms.AddLibraryReference(lhWeb); + + InstanceHandle ihTextAttribute2 = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute); + Assert.That(ihTextAttribute, Is.EqualTo(ihTextAttribute2)); + } + + [Test] + public void Class_referenced_from_One_library_Accessible_from_Another() + { + // First we make sure that Class (defined in Mocha.System, which we are known to have) loads + InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class); + Assert.That(ihClass, Is.Not.EqualTo(InstanceHandle.Empty)); + + // Now we try loading Mocha.Web::`Route`, if this fails, we fail our test + bool succeeded = Oms.TryGetInstance(KnownInstanceGuids.Classes.Route, out InstanceHandle ihRoute); + Assert.That(succeeded, Is.True); + + Assert.That(ihRoute, Is.Not.EqualTo(InstanceHandle.Empty)); + } + + [Test] + public void Parent_Class_referenced_from_One_library_Accessible_from_Another() + { + // First we make sure that Class (defined in Mocha.System, which we are known to have) loads + InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class); + Assert.That(ihClass, Is.Not.EqualTo(InstanceHandle.Empty)); + + // Now we try loading Mocha.Web::`Route`, if this fails, we fail our test + bool succeeded = Oms.TryGetInstance(KnownInstanceGuids.Classes.Route, out InstanceHandle ihRoute); + Assert.That(succeeded, Is.True); + + Assert.That(ihRoute, Is.Not.EqualTo(InstanceHandle.Empty)); + + // `Route` parent class should be `Class` + InstanceHandle ihPClass = Oms.GetParentClass(ihRoute); + Assert.That(ihPClass, Is.EqualTo(ihClass)); + } +} \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/Mocha.Core.Tests.csproj b/mocha-dotnet/tests/Mocha.Core.Tests/Mocha.Core.Tests.csproj index ca177d3..c4cedfd 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/Mocha.Core.Tests.csproj +++ b/mocha-dotnet/tests/Mocha.Core.Tests/Mocha.Core.Tests.csproj @@ -44,6 +44,7 @@ + diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/Resources/net.alcetech.Mocha.Web.mcl b/mocha-dotnet/tests/Mocha.Core.Tests/Resources/net.alcetech.Mocha.Web.mcl new file mode 120000 index 0000000..ac4f683 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Core.Tests/Resources/net.alcetech.Mocha.Web.mcl @@ -0,0 +1 @@ +../../../../mocha-common/mocha-common/output/net.alcetech.Mocha.Web.mcl \ No newline at end of file