diff --git a/Dntc.Cli/Transpiler.cs b/Dntc.Cli/Transpiler.cs index ce21bc5..90b6c28 100644 --- a/Dntc.Cli/Transpiler.cs +++ b/Dntc.Cli/Transpiler.cs @@ -3,6 +3,7 @@ using Dntc.Common.Conversion; using Dntc.Common.Conversion.Mutators; using Dntc.Common.Definitions; +using Dntc.Common.Definitions.CustomDefinedTypes; using Dntc.Common.Definitions.Definers; using Dntc.Common.Definitions.Mutators; using Dntc.Common.Definitions.ReferenceTypeSupport; @@ -75,6 +76,7 @@ public async Task RunAsync() definitionCatalog.Add(NativeDefinedType.StandardTypes.Values); definitionCatalog.Add(NativeDefinedMethod.StandardMethods); definitionCatalog.Add(CustomDefinedMethod.StandardCustomMethods); + definitionCatalog.Add(CustomDefinedType.StandardCustomTypes); } foreach (var plugin in plugins) diff --git a/Dntc.Common/Conversion/PlannedFileConverter.cs b/Dntc.Common/Conversion/PlannedFileConverter.cs index 8a7855d..bb7e26d 100644 --- a/Dntc.Common/Conversion/PlannedFileConverter.cs +++ b/Dntc.Common/Conversion/PlannedFileConverter.cs @@ -111,7 +111,11 @@ private IReadOnlyList GetMethodStatements( } var statements = new List(); - + + if (dotNetDefinedMethod.Definition.HasBody == false) + { + return statements; + } Instruction methodInstruction = dotNetDefinedMethod.Definition.Body.Instructions.First(); OnBeforeGenerateInstruction(statements, dotNetDefinedMethod, methodInstruction); @@ -123,7 +127,11 @@ private IReadOnlyList GetMethodStatements( var local = dotNetDefinedMethod.Locals[x]; var localType = _conversionCatalog.Find(local.Type); var name = Utils.LocalName(dotNetDefinedMethod.Definition, x); - var variable = new Variable(localType, name, local.Type.IsPointer()); + var variable = new Variable(localType, name, + local.Type.IsPointer() || localType.OriginalTypeDefinition is DotNetDefinedType + { + Definition.IsValueType: false + }); if (locals.Add(name)) { diff --git a/Dntc.Common/Conversion/TypeConversionInfo.cs b/Dntc.Common/Conversion/TypeConversionInfo.cs index 8f4db32..a35669f 100644 --- a/Dntc.Common/Conversion/TypeConversionInfo.cs +++ b/Dntc.Common/Conversion/TypeConversionInfo.cs @@ -98,7 +98,12 @@ private void SetupDotNetType(DotNetDefinedType type) if (!type.Definition.IsValueType) { IsReferenceType = true; - ReferencedHeaders = [new HeaderName(""), new HeaderName("")]; + ReferencedHeaders = + [ + new HeaderName(""), + new HeaderName(""), + new HeaderName("") + ]; } } diff --git a/Dntc.Common/Definitions/CustomDefinedMethod.cs b/Dntc.Common/Definitions/CustomDefinedMethod.cs index f1debc0..1b8a846 100644 --- a/Dntc.Common/Definitions/CustomDefinedMethod.cs +++ b/Dntc.Common/Definitions/CustomDefinedMethod.cs @@ -44,6 +44,7 @@ protected CustomDefinedMethod( [ new FloatMinDefinedMethod(), new StaticConstructorInitializerDefinedMethod(), + new DynamicCastInterfaceDefinedMethod() ]; protected override IReadOnlyList GetReferencedTypesInternal() => []; diff --git a/Dntc.Common/Definitions/CustomDefinedMethods/DynamicCastInterfaceDefinedMethod.cs b/Dntc.Common/Definitions/CustomDefinedMethods/DynamicCastInterfaceDefinedMethod.cs new file mode 100644 index 0000000..2fae5dd --- /dev/null +++ b/Dntc.Common/Definitions/CustomDefinedMethods/DynamicCastInterfaceDefinedMethod.cs @@ -0,0 +1,52 @@ +using Dntc.Common.Conversion; +using Dntc.Common.Definitions.CustomDefinedTypes; +using Dntc.Common.Definitions.ReferenceTypeSupport; +using Dntc.Common.Syntax.Statements; + +namespace Dntc.Common.Definitions.CustomDefinedMethods; + +public class DynamicCastInterfaceDefinedMethod() : CustomDefinedMethod(MethodId, + ReferenceTypeConstants.ReferenceTypeBaseId, + new IlNamespace("DntC.Platform.Utils"), + new HeaderName("dntc.h"), + null, + new CFunctionName("dynamic_cast_interface"), + [ + new Parameter(ReferenceTypeConstants.ReferenceTypeBaseId, "instance", true), + new Parameter(NativeDefinedType.StandardTypes[typeof(uint)].IlName, "interface", false), + ], false) +{ + public static readonly IlMethodId MethodId = new("[Util: DynamicCastInterface]"); + + protected override IReadOnlyList GetReferencedTypesInternal() + { + return [TypeInfoDefinedType.TypeName]; + } + + public override CustomCodeStatementSet? GetCustomDeclaration() + { + var baseClass = ReferenceTypeConstants.ReferenceTypeBaseTypeName; + + + string content = @$" +static {baseClass}* dynamic_cast_interface({baseClass}* instance, uint32_t interface) {{ + if (instance && instance->type_info) {{ + TypeInfo* type_info = instance->type_info; + for (size_t i = 0; i < type_info->interface_count; ++i) {{ + if (type_info->implemented_interfaces[i] == interface) {{ + // Calculate the address of the interface implementation using the offset + return ({baseClass}*)((char*)instance + type_info->interface_offsets[i]); + }} + }} + }} + return NULL; // Interface is not implemented +}} +"; + return new CustomCodeStatementSet(content); + } + + public override CustomCodeStatementSet? GetCustomImplementation(ConversionCatalog catalog) + { + return null; + } +} \ No newline at end of file diff --git a/Dntc.Common/Definitions/CustomDefinedMethods/ReferenceTypeAllocationMethod.cs b/Dntc.Common/Definitions/CustomDefinedMethods/ReferenceTypeAllocationMethod.cs index 3b802bb..021703b 100644 --- a/Dntc.Common/Definitions/CustomDefinedMethods/ReferenceTypeAllocationMethod.cs +++ b/Dntc.Common/Definitions/CustomDefinedMethods/ReferenceTypeAllocationMethod.cs @@ -33,7 +33,7 @@ public ReferenceTypeAllocationMethod(TypeDefinition typeDefinition) foreach (var virtualMethod in _typeDefinition.Methods.Where(x => x.IsVirtual)) { - if (virtualMethod.IsReuseSlot) + if (virtualMethod.IsReuseSlot || virtualMethod.IsFinal) { InvokedMethods.Add(new InvokedMethod(new IlMethodId(virtualMethod.FullName))); } @@ -52,6 +52,7 @@ public ReferenceTypeAllocationMethod(TypeDefinition typeDefinition) public override CStatementSet? GetCustomImplementation(ConversionCatalog catalog) { var typeInfo = catalog.Find(new IlTypeName(_typeDefinition.FullName)); + var typeName = typeInfo.NameInC; var typeNameExpression = new LiteralValueExpression(typeInfo.NameInC.Value, typeInfo); var variable = new Variable(typeInfo, "result", true); var statements = new List @@ -61,7 +62,55 @@ public ReferenceTypeAllocationMethod(TypeDefinition typeDefinition) }; var sb = new StringBuilder(); - foreach (var virtualMethod in _typeDefinition.Methods.Where(x => x.IsVirtual)) + + sb.AppendLine($@" {typeName}* result = ({typeName}*) malloc(sizeof({typeName})); + memset(result, 0, sizeof({typeName}));"); + + var typeInfoName =typeName + "_TypeInfo"; + sb.AppendLine($"\t((ReferenceType_Base*)result)->type_info = &{typeInfoName};"); + + var interfaceMethods = new HashSet(); + + foreach (var iface in _typeDefinition.Interfaces) + { + var ifaceType = catalog.Find(new IlTypeName(iface.InterfaceType.FullName)); + sb.AppendLine($"\tresult->{ifaceType.NameInC}.implementer = result;"); + + foreach (var interfaceMethod in iface.InterfaceType.Resolve().Methods) + { + var implementingMethod = _typeDefinition.Methods.SingleOrDefault(x => interfaceMethod.SignatureCompatibleWith(x)); + + if (implementingMethod != null) + { + interfaceMethods.Add(new IlMethodId(implementingMethod.FullName)); + var interfaceMethodInfo = catalog.Find(new IlMethodId(interfaceMethod.FullName)); + var implementingMethodInfo = catalog.Find(new IlMethodId(implementingMethod.FullName)); + + sb.Append($"\tresult->{ifaceType.NameInC}.{interfaceMethodInfo.NameInC} = "); + + sb.Append($"({interfaceMethodInfo.ReturnTypeInfo.NativeNameWithPossiblePointer()} (*)("); + + for (var x = 0; x < interfaceMethodInfo.Parameters.Count; x++) + { + if (x > 0) sb.Append(", "); + var param = interfaceMethodInfo.Parameters[x]; + var paramType = param.ConversionInfo.NameInC.Value; + + if (x == 0) + { + paramType = "void"; + } + + var pointerSymbol = param.IsReference ? "*" : ""; + sb.Append($"{paramType}{pointerSymbol}"); + } + + sb.AppendLine($")){implementingMethodInfo.NameInC};"); + } + } + } + + foreach (var virtualMethod in _typeDefinition.Methods.Where(x => x.IsVirtual && !interfaceMethods.Contains(new IlMethodId(x.FullName)))) { if (virtualMethod.IsReuseSlot) { diff --git a/Dntc.Common/Definitions/CustomDefinedType.cs b/Dntc.Common/Definitions/CustomDefinedType.cs index de593b2..3ef7d6c 100644 --- a/Dntc.Common/Definitions/CustomDefinedType.cs +++ b/Dntc.Common/Definitions/CustomDefinedType.cs @@ -1,4 +1,5 @@ using Dntc.Common.Conversion; +using Dntc.Common.Definitions.CustomDefinedTypes; using Dntc.Common.Syntax.Statements; namespace Dntc.Common.Definitions; @@ -32,4 +33,9 @@ protected CustomDefinedType( } public abstract CustomCodeStatementSet? GetCustomTypeDeclaration(ConversionCatalog catalog); + + public static IReadOnlyList StandardCustomTypes { get; } = + [ + new TypeInfoDefinedType() + ]; } \ No newline at end of file diff --git a/Dntc.Common/Definitions/CustomDefinedTypes/ArrayDefinedType.cs b/Dntc.Common/Definitions/CustomDefinedTypes/ArrayDefinedType.cs index 3336286..7190145 100644 --- a/Dntc.Common/Definitions/CustomDefinedTypes/ArrayDefinedType.cs +++ b/Dntc.Common/Definitions/CustomDefinedTypes/ArrayDefinedType.cs @@ -1,7 +1,6 @@ using Dntc.Common.Conversion; using Dntc.Common.Syntax.Expressions; using Dntc.Common.Syntax.Statements; -using Mono.Cecil; namespace Dntc.Common.Definitions.CustomDefinedTypes; diff --git a/Dntc.Common/Definitions/CustomDefinedTypes/TypeInfoDefinedType.cs b/Dntc.Common/Definitions/CustomDefinedTypes/TypeInfoDefinedType.cs new file mode 100644 index 0000000..923b9c9 --- /dev/null +++ b/Dntc.Common/Definitions/CustomDefinedTypes/TypeInfoDefinedType.cs @@ -0,0 +1,28 @@ +using Dntc.Common.Conversion; +using Dntc.Common.Definitions.ReferenceTypeSupport.TypeInfo; +using Dntc.Common.Syntax.Statements; + +namespace Dntc.Common.Definitions.CustomDefinedTypes; + +public class TypeInfoDefinedType() : CustomDefinedType(TypeName, + new HeaderName("dntc.h"), + null, + new CTypeName("TypeInfo"), + [], + []) +{ + public static readonly IlTypeName TypeName = TypeInfoConstants.TypeInfoTypeId; + + public override CustomCodeStatementSet? GetCustomTypeDeclaration(ConversionCatalog catalog) + { + const string content = @" +typedef struct TypeInfo { + uint32_t* implemented_interfaces; // Array of implemented interfaces + size_t* interface_offsets; // Array of offsets for each interface + size_t interface_count; // Count of implemented interfaces +} TypeInfo; +"; + + return new CustomCodeStatementSet(content); + } +} \ No newline at end of file diff --git a/Dntc.Common/Definitions/DefinitionCatalog.cs b/Dntc.Common/Definitions/DefinitionCatalog.cs index cbb5660..3df9960 100644 --- a/Dntc.Common/Definitions/DefinitionCatalog.cs +++ b/Dntc.Common/Definitions/DefinitionCatalog.cs @@ -89,6 +89,28 @@ public IEnumerable GetMethodOverrides(DefinedMethod method) } } } + + public IEnumerable GetInterfaceMethods(DefinedMethod method) + { + if (method is DotNetDefinedMethod { Definition.IsVirtual: true } dntMethod) + { + var baseType = dntMethod.Definition.DeclaringType; + + foreach (var type in _types.Values.OfType().Where(x=>x.Definition.IsInterface)) + { + if (baseType.ImplementsInterface(type.Definition)) + { + foreach (var derivedMethod in type.Methods.Select(Get).OfType()) + { + if (derivedMethod.Definition.IsOverrideOf(dntMethod.Definition)) + { + yield return _methods[derivedMethod.Id]; + } + } + } + } + } + } private void Add(TypeDefinition type) { diff --git a/Dntc.Common/Definitions/DotNetDefinedMethod.cs b/Dntc.Common/Definitions/DotNetDefinedMethod.cs index b079d45..944a90d 100644 --- a/Dntc.Common/Definitions/DotNetDefinedMethod.cs +++ b/Dntc.Common/Definitions/DotNetDefinedMethod.cs @@ -191,39 +191,43 @@ private void Analyze() } var referencedHeaders = new HashSet(); - foreach (var instruction in Definition.Body.Instructions) + + if (Definition.HasBody) { - if (!KnownOpCodeHandlers.OpCodeHandlers.TryGetValue(instruction.OpCode.Code, out var handler)) + foreach (var instruction in Definition.Body.Instructions) { - var debugInfo = CecilUtils.LoggedSequencePointInfo( - CecilUtils.GetSequencePoint( - Definition, - instruction)); + if (!KnownOpCodeHandlers.OpCodeHandlers.TryGetValue(instruction.OpCode.Code, out var handler)) + { + var debugInfo = CecilUtils.LoggedSequencePointInfo( + CecilUtils.GetSequencePoint( + Definition, + instruction)); - var message = $"No handler for op code '{instruction.OpCode.Code}' {debugInfo}"; - throw new InvalidOperationException(message); - } + var message = $"No handler for op code '{instruction.OpCode.Code}' {debugInfo}"; + throw new InvalidOperationException(message); + } - var results = handler.Analyze(new AnalyzeContext(instruction, this)); + var results = handler.Analyze(new AnalyzeContext(instruction, this)); - foreach (var method in results.CalledMethods) - { - _invokedMethods.Add(method); - } + foreach (var method in results.CalledMethods) + { + _invokedMethods.Add(method); + } - foreach (var referencedType in results.ReferencedTypes) - { - _referencedTypes.Add(referencedType); - } + foreach (var referencedType in results.ReferencedTypes) + { + _referencedTypes.Add(referencedType); + } - foreach (var header in results.ReferencedHeaders) - { - referencedHeaders.Add(header); - } + foreach (var header in results.ReferencedHeaders) + { + referencedHeaders.Add(header); + } - if (results.ReferencedGlobal != null) - { - _referencedGlobals.Add(new IlFieldId(results.ReferencedGlobal.FullName)); + if (results.ReferencedGlobal != null) + { + _referencedGlobals.Add(new IlFieldId(results.ReferencedGlobal.FullName)); + } } } diff --git a/Dntc.Common/Definitions/DotNetDefinedType.cs b/Dntc.Common/Definitions/DotNetDefinedType.cs index ca5baef..91a578a 100644 --- a/Dntc.Common/Definitions/DotNetDefinedType.cs +++ b/Dntc.Common/Definitions/DotNetDefinedType.cs @@ -1,4 +1,5 @@ using Dntc.Common.Definitions.ReferenceTypeSupport; +using Dntc.Common.Definitions.CustomDefinedTypes; using Mono.Cecil; namespace Dntc.Common.Definitions; @@ -28,6 +29,8 @@ public DotNetDefinedType(TypeDefinition definition) .Where(x => !x.IsStatic) .Select(ConvertToField) .ToArray(); + + var referencedTypes = new List(); if (!definition.IsValueType) { @@ -38,8 +41,7 @@ public DotNetDefinedType(TypeDefinition definition) definition.BaseType.FullName != typeof(Enum).FullName) { var baseTypeName = new IlTypeName(definition.BaseType.FullName); - - OtherReferencedTypes = [baseTypeName]; + referencedTypes.Add(baseTypeName); } else { @@ -55,6 +57,10 @@ public DotNetDefinedType(TypeDefinition definition) } } + referencedTypes.AddRange(definition.Interfaces.Select(x => new IlTypeName(x.InterfaceType.FullName))); + + OtherReferencedTypes = referencedTypes.ToArray(); + Methods = definition.Methods .Select(x => new IlMethodId(x.FullName)) .ToArray(); diff --git a/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeBaseDefinedType.cs b/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeBaseDefinedType.cs index 59e76b8..511b11a 100644 --- a/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeBaseDefinedType.cs +++ b/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeBaseDefinedType.cs @@ -1,5 +1,6 @@ using System.Text; using Dntc.Common.Conversion; +using Dntc.Common.Definitions.CustomDefinedTypes; using Dntc.Common.Syntax.Statements; namespace Dntc.Common.Definitions.ReferenceTypeSupport; @@ -15,8 +16,8 @@ public ReferenceTypeBaseDefinedType() ReferenceTypeConstants.ReferenceTypeBaseId, ReferenceTypeConstants.HeaderFileName, ReferenceTypeConstants.SourceFileName, - new CTypeName("DntcReferenceTypeBase"), - [], + ReferenceTypeConstants.ReferenceTypeBaseTypeName, + [TypeInfoDefinedType.TypeName], []) { } @@ -37,8 +38,10 @@ public void AddField(DefinedField field) { var typeInfo = catalog.Find(field.Type); var fieldInfo = catalog.Find(field.Id); + + - code.AppendLine($"\t{typeInfo.NameInC} {fieldInfo.NameInC};"); + code.AppendLine($"\t{typeInfo.NativeNameWithPossiblePointer()} {fieldInfo.NameInC};"); } code.AppendLine($"}} {NativeName};"); diff --git a/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeConstants.cs b/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeConstants.cs index 7ab6b00..028d131 100644 --- a/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeConstants.cs +++ b/Dntc.Common/Definitions/ReferenceTypeSupport/ReferenceTypeConstants.cs @@ -7,5 +7,6 @@ public class ReferenceTypeConstants public static IlNamespace IlNamespace = new("Dntc.System"); public static IlTypeName ReferenceTypeBaseId = new($"{IlNamespace}.ReferenceTypeBase"); public static IlFieldId ReferenceTypeBaseFieldId = new($"{ReferenceTypeBaseId} {IlNamespace}::base"); + public static CTypeName ReferenceTypeBaseTypeName = new("DntcReferenceTypeBase"); public static CFieldName ReferenceTypeBaseFieldName = new("__reference_type_base"); } \ No newline at end of file diff --git a/Dntc.Common/Definitions/ReferenceTypeSupport/SimpleReferenceCounting/SimpleRefCountConfig.cs b/Dntc.Common/Definitions/ReferenceTypeSupport/SimpleReferenceCounting/SimpleRefCountConfig.cs index 7997ac4..6c919b3 100644 --- a/Dntc.Common/Definitions/ReferenceTypeSupport/SimpleReferenceCounting/SimpleRefCountConfig.cs +++ b/Dntc.Common/Definitions/ReferenceTypeSupport/SimpleReferenceCounting/SimpleRefCountConfig.cs @@ -7,11 +7,12 @@ public class SimpleRefCountConfig : IRefCountImplementationConfig { public void UpdateCatalog(DefinitionCatalog catalog) { - catalog.Add([new ActiveRefCountField()]); + catalog.Add([new TypeInfoField(), new ActiveRefCountField()]); } public void AddFieldsToReferenceTypeBase(ReferenceTypeBaseDefinedType referenceTypeBase) { + referenceTypeBase.AddField(new TypeInfoField()); referenceTypeBase.AddField(new ActiveRefCountField()); } } \ No newline at end of file diff --git a/Dntc.Common/Definitions/ReferenceTypeSupport/SimpleReferenceCounting/TypeInfoField.cs b/Dntc.Common/Definitions/ReferenceTypeSupport/SimpleReferenceCounting/TypeInfoField.cs new file mode 100644 index 0000000..83037e2 --- /dev/null +++ b/Dntc.Common/Definitions/ReferenceTypeSupport/SimpleReferenceCounting/TypeInfoField.cs @@ -0,0 +1,19 @@ +using Dntc.Common.Definitions.ReferenceTypeSupport.TypeInfo; +using Dntc.Common.Syntax.Statements; + +namespace Dntc.Common.Definitions.ReferenceTypeSupport.SimpleReferenceCounting; + +public class TypeInfoField() : CustomDefinedField(null, + null, + null, + TypeInfoConstants.TypeInfoFieldName, + TypeInfoConstants.TypeInfoFieldId, + TypeInfoConstants.TypeInfoTypeId.AsPointerType(), + false, + []) +{ + public override CustomCodeStatementSet? GetCustomDeclaration() + { + return null; + } +} \ No newline at end of file diff --git a/Dntc.Common/Definitions/ReferenceTypeSupport/TypeInfo/TypeInfoConstants.cs b/Dntc.Common/Definitions/ReferenceTypeSupport/TypeInfo/TypeInfoConstants.cs new file mode 100644 index 0000000..7305712 --- /dev/null +++ b/Dntc.Common/Definitions/ReferenceTypeSupport/TypeInfo/TypeInfoConstants.cs @@ -0,0 +1,11 @@ +namespace Dntc.Common.Definitions.ReferenceTypeSupport.TypeInfo; + +public static class TypeInfoConstants +{ + public static IlTypeName TypeInfoTypeId = new($"{ReferenceTypeConstants.IlNamespace}.TypeInfo"); + + public static IlFieldId TypeInfoFieldId + = new($"{TypeInfoTypeId} {TypeInfoTypeId}::TypeInfo"); + + public static CFieldName TypeInfoFieldName = new("type_info"); +} \ No newline at end of file diff --git a/Dntc.Common/Dependencies/DependencyGraph.cs b/Dntc.Common/Dependencies/DependencyGraph.cs index 9654518..ac14f99 100644 --- a/Dntc.Common/Dependencies/DependencyGraph.cs +++ b/Dntc.Common/Dependencies/DependencyGraph.cs @@ -124,13 +124,6 @@ public DependencyGraph(DefinitionCatalog definitionCatalog, IlFieldId global) if (method is DotNetDefinedMethod dotNetDefinedMethod) { - if (dotNetDefinedMethod.Definition.Body == null) - { - var message = $"Method call seen to '{methodId}', which is an abstract or interface method. Only " + - $"calls to concrete methods can be invoked"; - throw new InvalidOperationException(message); - } - foreach (var type in dotNetDefinedMethod.ReferencedTypes) { var typeNode = CreateNode(definitionCatalog, type, path); @@ -163,6 +156,18 @@ public DependencyGraph(DefinitionCatalog definitionCatalog, IlFieldId global) node.Children.Add(derivedNode); } } + + var interfaces = definitionCatalog.GetInterfaceMethods(method); + + foreach (var interfaceDefinedMethod in interfaces) + { + var derivedNode = CreateNode(definitionCatalog, interfaceDefinedMethod.Id, path, true); + + if (derivedNode != null) + { + node.Children.Add(derivedNode); + } + } } path.RemoveAt(path.Count - 1); diff --git a/Dntc.Common/OpCodeHandling/ExpressionStack.cs b/Dntc.Common/OpCodeHandling/ExpressionStack.cs index 892189e..6443e65 100644 --- a/Dntc.Common/OpCodeHandling/ExpressionStack.cs +++ b/Dntc.Common/OpCodeHandling/ExpressionStack.cs @@ -9,6 +9,8 @@ public class ExpressionStack private readonly List _expressions = []; public int Count => _expressions.Count; + + public CBaseExpression this[int index] => _expressions[index]; /// /// Pops the specified number of expressions from the stack. The expressions are returned diff --git a/Dntc.Common/OpCodeHandling/Handlers/CallHandlers.cs b/Dntc.Common/OpCodeHandling/Handlers/CallHandlers.cs index bb1c115..965b2b6 100644 --- a/Dntc.Common/OpCodeHandling/Handlers/CallHandlers.cs +++ b/Dntc.Common/OpCodeHandling/Handlers/CallHandlers.cs @@ -63,7 +63,8 @@ private static OpCodeHandlingResult CallMethodReference( HandleContext context, IlMethodId methodId, IlTypeName returnTypeName, - bool isVirtualCall = false) + bool isVirtualCall = false, + bool isInterface = false) { var conversionInfo = context.ConversionCatalog.Find(methodId); var returnType = conversionInfo.ReturnTypeInfo; @@ -76,7 +77,7 @@ private static OpCodeHandlingResult CallMethodReference( .ToArray(); var fnExpression = new LiteralValueExpression(conversionInfo.NameInC.Value, conversionInfo.ReturnTypeInfo); - var methodCallExpression = new MethodCallExpression(fnExpression, conversionInfo.Parameters, arguments, returnType, isVirtualCall); + var methodCallExpression = new MethodCallExpression(fnExpression, conversionInfo.Parameters, arguments, returnType, isVirtualCall, isInterface); if (ReturnsVoid(returnTypeName)) { @@ -256,10 +257,40 @@ public OpCodeHandlingResult Handle(HandleContext context) { var methodToCall = VirtualCallConverter.Convert(context.CurrentInstruction, context.CurrentDotNetMethod); var targetMethodDefinition = context.DefinitionCatalog.Get(methodToCall); + + bool virtualCall = false; + bool isInterface = false; - bool virtualCall = targetMethodDefinition is DotNetDefinedMethod dntDefinedMethod - && !dntDefinedMethod.Definition.DeclaringType.IsValueType - && dntDefinedMethod.Definition.IsVirtual; + if (targetMethodDefinition is DotNetDefinedMethod dntDefinedMethod) + { + var declaringType = dntDefinedMethod.Definition.DeclaringType; + isInterface = declaringType.IsInterface; + virtualCall = !declaringType.IsValueType && dntDefinedMethod.Definition.IsVirtual; + + if (virtualCall) + { + // If the this ptr declaring type matches the method declaring type, we can (need to) de-virtualize the call. + var thisType = (DotNetDefinedType)context.ExpressionStack[0].ResultingType.OriginalTypeDefinition; + if (declaringType.FullName == thisType.Definition.FullName && !thisType.Definition.IsInterface) + { + virtualCall = false; + } + + // TODO - some scenarios can be devirtualized... or should we just rely on msil to do it better? + /*if (virtualCall && isInterface && thisType.Definition.ImplementsInterface(declaringType) && context.ExpressionStack.Count == dntDefinedMethod.Parameters.Count + 1) + { + var devirtualizeCall = thisType.Definition.Methods.FirstOrDefault(x => + x.SignatureCompatibleWith(dntDefinedMethod.Definition)); + + if (devirtualizeCall != null) + { + virtualCall = false; + methodToCall = new IlMethodId(devirtualizeCall.FullName); + } + }*/ + } + } + if (targetMethodDefinition == null) { @@ -267,7 +298,7 @@ public OpCodeHandlingResult Handle(HandleContext context) throw new InvalidOperationException(message); } - return CallMethodReference(context, methodToCall, targetMethodDefinition.ReturnType, virtualCall); + return CallMethodReference(context, methodToCall, targetMethodDefinition.ReturnType, virtualCall, isInterface); } public OpCodeAnalysisResult Analyze(AnalyzeContext context) diff --git a/Dntc.Common/OpCodeHandling/Handlers/LoadHandlers.cs b/Dntc.Common/OpCodeHandling/Handlers/LoadHandlers.cs index b6ef710..b9004bb 100644 --- a/Dntc.Common/OpCodeHandling/Handlers/LoadHandlers.cs +++ b/Dntc.Common/OpCodeHandling/Handlers/LoadHandlers.cs @@ -64,6 +64,7 @@ public class LoadHandlers : IOpCodeHandlerCollection { Code.Ldobj, new LdObjHandler() }, { Code.Ldstr, new LdStrHandler() }, + { Code.Ldnull, new LdNullHandler() } }; private class LdFldHandler(bool getAddress) : IOpCodeHandler @@ -398,6 +399,22 @@ public OpCodeAnalysisResult Analyze(AnalyzeContext context) } } + private class LdNullHandler : IOpCodeHandler + { + public OpCodeHandlingResult Handle(HandleContext context) + { + var voidType = context.ConversionCatalog.Find(new IlTypeName(typeof(void).FullName!)); + context.ExpressionStack.Push(new LiteralValueExpression("NULL", voidType)); + + return new OpCodeHandlingResult(null); + } + + public OpCodeAnalysisResult Analyze(AnalyzeContext context) + { + return new OpCodeAnalysisResult(); + } + } + private class LdStrHandler : IOpCodeHandler { public OpCodeHandlingResult Handle(HandleContext context) diff --git a/Dntc.Common/OpCodeHandling/Handlers/MiscHandlers.cs b/Dntc.Common/OpCodeHandling/Handlers/MiscHandlers.cs index 69e0ec9..68ce8fc 100644 --- a/Dntc.Common/OpCodeHandling/Handlers/MiscHandlers.cs +++ b/Dntc.Common/OpCodeHandling/Handlers/MiscHandlers.cs @@ -111,6 +111,13 @@ public OpCodeHandlingResult Handle( ? context.ExpressionStack.Pop(1)[0] : null; + if (innerExpression != null && innerExpression.ResultingType.IlName != context.CurrentMethodConversion.ReturnTypeInfo.IlName) + { + // we need to cast the return value to the correct type + var castExpression = new CastExpression(innerExpression, context.CurrentMethodConversion.ReturnTypeInfo); + innerExpression = castExpression; + } + return new OpCodeHandlingResult(new ReturnStatementSet(innerExpression)); } diff --git a/Dntc.Common/OpCodeHandling/Handlers/StoreHandlers.cs b/Dntc.Common/OpCodeHandling/Handlers/StoreHandlers.cs index c9a7b31..d768d5f 100644 --- a/Dntc.Common/OpCodeHandling/Handlers/StoreHandlers.cs +++ b/Dntc.Common/OpCodeHandling/Handlers/StoreHandlers.cs @@ -1,5 +1,6 @@ using Dntc.Common.Conversion; using Dntc.Common.Definitions; +using Dntc.Common.Definitions.CustomDefinedMethods; using Dntc.Common.Syntax.Expressions; using Dntc.Common.Syntax.Statements; using Mono.Cecil; @@ -311,9 +312,48 @@ public OpCodeHandlingResult Handle(HandleContext context) } else if (!localVariable.ProducesAPointer && items[0].ProducesAPointer) { - // Set the local's value to the dereferenced value of the assigment's expression + if (localVariable.ResultingType.OriginalTypeDefinition is DotNetDefinedType { Definition.IsInterface: true } dotNetDefinedType) + { + left = localVariable; + if (localVariable.ResultingType.IlName != items[0].ResultingType.IlName) + { + right = new InterfaceDynamicCastExpression(localVariable, items[0], + dotNetDefinedType.Definition.MetadataToken.RID); + } + else + { + right = items[0]; + } + } + else if (items[0].ResultingType.OriginalTypeDefinition is DotNetDefinedType { Definition.IsValueType: false }) + { + left = localVariable; + right = items[0]; + } + else + { + // Set the local's value to the dereferenced value of the assigment's expression + left = localVariable; + right = new DereferencedValueExpression(items[0]); + } + + } + else if (!localVariable.ProducesAPointer && !items[0].ProducesAPointer && + localVariable.ResultingType.OriginalTypeDefinition is DotNetDefinedType + { + Definition.IsInterface: true + } dotNetDefinedType) + { left = localVariable; - right = new DereferencedValueExpression(items[0]); + if (localVariable.ResultingType.IlName != items[0].ResultingType.IlName) + { + right = new InterfaceDynamicCastExpression(localVariable, items[0], + dotNetDefinedType.Definition.MetadataToken.RID); + } + else + { + right = items[0]; + } } else { @@ -336,7 +376,13 @@ public OpCodeHandlingResult Handle(HandleContext context) public OpCodeAnalysisResult Analyze(AnalyzeContext context) { - return new OpCodeAnalysisResult(); + return new OpCodeAnalysisResult() + { + CalledMethods = + [ + new InvokedMethod(DynamicCastInterfaceDefinedMethod.MethodId) + ] + }; } } } \ No newline at end of file diff --git a/Dntc.Common/OpCodeHandling/Handlers/TwoValueOperatorHandlers.cs b/Dntc.Common/OpCodeHandling/Handlers/TwoValueOperatorHandlers.cs index 4e6d431..aa1c811 100644 --- a/Dntc.Common/OpCodeHandling/Handlers/TwoValueOperatorHandlers.cs +++ b/Dntc.Common/OpCodeHandling/Handlers/TwoValueOperatorHandlers.cs @@ -39,12 +39,20 @@ public OpCodeHandlingResult Handle( var items = context.ExpressionStack.Pop(2); var right = items[0]; var left = items[1]; + + var actualOperator = @operator; - var resultingType = BoolOperators.Contains(@operator) + if (actualOperator == ">" && right.ResultingType.IlName.Value == typeof(void).FullName) + { + // You cant do > NULL + actualOperator = "!="; + } + + var resultingType = BoolOperators.Contains(actualOperator) ? context.ConversionCatalog.Find(new IlTypeName(typeof(bool).FullName!)) : left.ResultingType; // Assume non-bool operators use the same type. This is probably incomplete. - var newExpression = new TwoExpressionEvalExpression(left, @operator, right, resultingType); + var newExpression = new TwoExpressionEvalExpression(left, actualOperator, right, resultingType); context.ExpressionStack.Push(newExpression); return new OpCodeHandlingResult(null); diff --git a/Dntc.Common/Syntax/Expressions/InterfaceDynamicCastExpression.cs b/Dntc.Common/Syntax/Expressions/InterfaceDynamicCastExpression.cs new file mode 100644 index 0000000..9e5c86f --- /dev/null +++ b/Dntc.Common/Syntax/Expressions/InterfaceDynamicCastExpression.cs @@ -0,0 +1,24 @@ +using Dntc.Common.Conversion; +using Dntc.Common.Definitions.ReferenceTypeSupport; + +namespace Dntc.Common.Syntax.Expressions; + +public record InterfaceDynamicCastExpression(VariableValueExpression Variable, CBaseExpression Local, uint TypeCode) : CBaseExpression(Variable.ResultingType.IsPointer) +{ + public override TypeConversionInfo ResultingType => Variable.ResultingType; + + public override async ValueTask WriteCodeStringAsync(StreamWriter writer) + { + var referenceTypeBase = ReferenceTypeConstants.ReferenceTypeBaseTypeName; + await writer.WriteAsync($"({ResultingType.NameInC}*)dynamic_cast_interface(({referenceTypeBase}*)"); + await Local.WriteCodeStringAsync(writer); + await writer.WriteAsync($", {TypeCode})"); + } + + public override CBaseExpression? ReplaceExpression(CBaseExpression search, CBaseExpression replacement) + { + throw new NotImplementedException(); + //var inner = ReplaceExpression(Variable, search, replacement); + //return inner != null ? this with { Expression = inner } : null; + } +} \ No newline at end of file diff --git a/Dntc.Common/Syntax/Expressions/MethodCallExpression.cs b/Dntc.Common/Syntax/Expressions/MethodCallExpression.cs index d613834..4fb24c2 100644 --- a/Dntc.Common/Syntax/Expressions/MethodCallExpression.cs +++ b/Dntc.Common/Syntax/Expressions/MethodCallExpression.cs @@ -8,7 +8,7 @@ public record MethodCallExpression( IReadOnlyList Parameters, IReadOnlyList Arguments, TypeConversionInfo ReturnType, - bool IsVirtualCall = false) + bool IsVirtualCall = false, bool IsInterface = false) : CBaseExpression(ReturnType.IsPointer) { public override TypeConversionInfo ResultingType => ReturnType; @@ -17,16 +17,48 @@ public override async ValueTask WriteCodeStringAsync(StreamWriter writer) { if (IsVirtualCall) { - var thisExpression = Arguments[0]; - var targetExpression = Parameters[0]; - await writer.WriteAsync($"(({targetExpression.ConversionInfo.NativeNameWithPointer()})"); - await thisExpression.WriteCodeStringAsync(writer); - await writer.WriteAsync(")->"); - await FnExpression.WriteCodeStringAsync(writer); + if (IsInterface) + { + var thisExpression = Arguments[0]; - await writer.WriteAsync("("); - await WriteParametersAsync(writer); - await writer.WriteAsync(")"); + var isInterfacePtrCall = + thisExpression.ResultingType.OriginalTypeDefinition is DotNetDefinedType dotNetDefinedType && + dotNetDefinedType.Definition.IsInterface; + + await thisExpression.WriteCodeStringAsync(writer); + var targetInterface = Parameters[0].ConversionInfo.NameInC.Value; + await writer.WriteAsync("->"); + + if (!isInterfacePtrCall) + { + await writer.WriteAsync(targetInterface); + await writer.WriteAsync("."); + } + + await FnExpression.WriteCodeStringAsync(writer); + await writer.WriteAsync($"("); + if (isInterfacePtrCall) + { + await thisExpression.WriteCodeStringAsync(writer); + await writer.WriteAsync("->implementer"); + } + + await WriteParametersAsync(writer, isInterfacePtrCall ? 1 : 0); + await writer.WriteAsync(")"); + } + else + { + var thisExpression = Arguments[0]; + var targetExpression = Parameters[0]; + await writer.WriteAsync($"(({targetExpression.ConversionInfo.NativeNameWithPointer()})"); + await thisExpression.WriteCodeStringAsync(writer); + await writer.WriteAsync(")->"); + await FnExpression.WriteCodeStringAsync(writer); + + await writer.WriteAsync("("); + await WriteParametersAsync(writer); + await writer.WriteAsync(")"); + } } else { diff --git a/Dntc.Common/Syntax/TypeDeclaration.cs b/Dntc.Common/Syntax/TypeDeclaration.cs index 8a3857d..c63e8bb 100644 --- a/Dntc.Common/Syntax/TypeDeclaration.cs +++ b/Dntc.Common/Syntax/TypeDeclaration.cs @@ -45,7 +45,18 @@ private async Task WriteDotNetDefinedTypeAsync(StreamWriter writer, DotNetDefine { if (Catalog.TryFind(new IlTypeName(dotNetDefinedType.Definition.BaseType.FullName), out var baseType)) { - await writer.WriteLineAsync($"\t{baseType.NativeNameWithPossiblePointer()} base;"); + await writer.WriteLineAsync($"\t{baseType.NameInC} base;"); + } + } + } + + if (dotNetDefinedType.Definition.HasInterfaces) + { + foreach (var iface in dotNetDefinedType.Definition.Interfaces) + { + if (Catalog.TryFind(new IlTypeName(iface.InterfaceType.FullName), out var ifaceType)) + { + await writer.WriteLineAsync($"\t{ifaceType.NativeNameWithPossiblePointer()} {ifaceType.NameInC};"); } } } @@ -65,7 +76,13 @@ private async Task WriteDotNetDefinedTypeAsync(StreamWriter writer, DotNetDefine var virtualMethodCount = 0; if (!dotNetDefinedType.Definition.IsValueType) { - foreach (var virtualMethod in dotNetDefinedType.Definition.Methods.Where(x => x.IsVirtual && x.IsNewSlot)) + if (dotNetDefinedType.Definition.IsInterface) + { + await writer.WriteLineAsync($"\tvoid* implementer;"); + } + + // IsFinal will be set for interfaces. + foreach (var virtualMethod in dotNetDefinedType.Definition.Methods.Where(x => x is { IsVirtual: true, IsNewSlot: true, IsFinal: false })) { virtualMethodCount++; var methodInfo = Catalog.Find(new IlMethodId(virtualMethod.FullName)); @@ -84,10 +101,18 @@ await writer.WriteAsync( structKeyword = "struct "; } - var pointerSymbol = param.IsReference ? "*" : ""; - await writer.WriteAsync($"{structKeyword}{paramType.NameInC}{pointerSymbol} {param.Name}"); + if (dotNetDefinedType.Definition.IsInterface && x == 0) + { + // this argument for interface... change to void* + await writer.WriteAsync($"void* {param.Name}"); + } + else + { + var pointerSymbol = param.IsReference ? "*" : ""; + await writer.WriteAsync($"{structKeyword}{paramType.NameInC}{pointerSymbol} {param.Name}"); + } + } - await writer.WriteLineAsync(");"); } } @@ -99,6 +124,48 @@ await writer.WriteAsync( } await writer.WriteLineAsync($"}} {nativeName};"); + + // Write TypeInformation. + if (dotNetDefinedType.Definition is { IsValueType: false, IsInterface: false }) + { + await writer.WriteLineAsync(); + await writer.WriteLineAsync("TypeInfo " + TypeConversion.NameInC + "_TypeInfo = {"); + + if (dotNetDefinedType.Definition.HasInterfaces) + { + await writer.WriteAsync("\t(uint32_t[]){ "); + foreach (var iface in dotNetDefinedType.Definition.Interfaces) + { + await writer.WriteAsync($"{iface.InterfaceType.MetadataToken.RID}, "); + } + + await writer.WriteLineAsync(" },"); + } + else + { + await writer.WriteLineAsync("\tNULL,"); + } + + if (dotNetDefinedType.Definition.HasInterfaces) + { + await writer.WriteAsync("\t(size_t[]){ "); + foreach (var iface in dotNetDefinedType.Definition.Interfaces) + { + var ifaceType = Catalog.Find(new IlTypeName(iface.InterfaceType.FullName)); + + await writer.WriteAsync($"offsetof({TypeConversion.NameInC}, {ifaceType.NameInC}), "); + } + + await writer.WriteLineAsync("},"); + } + else + { + await writer.WriteLineAsync("\tNULL,"); + } + + await writer.WriteLineAsync($"\t{dotNetDefinedType.Definition.Interfaces.Count}"); + await writer.WriteLineAsync("};"); + } } diff --git a/Dntc.Common/Utils.cs b/Dntc.Common/Utils.cs index ad1ad32..73c0801 100644 --- a/Dntc.Common/Utils.cs +++ b/Dntc.Common/Utils.cs @@ -170,7 +170,7 @@ public static bool IsConsideredReferenceType(this ParameterDefinition definition public static bool IsOverrideOf(this MethodDefinition method, MethodDefinition baseMethod) { // Method must be virtual and reuse slot (override keyword) - if (!method.IsVirtual || !method.IsReuseSlot) + if (!method.IsVirtual || method is { IsReuseSlot: false, IsDefinition: false }) return false; return method.SignatureCompatibleWith(baseMethod); @@ -200,6 +200,18 @@ public static bool SignatureCompatibleWith(this MethodDefinition method, MethodD return true; } + + public static bool ImplementsInterface(this TypeDefinition type, TypeDefinition interfaceType) + { + // Check direct interfaces + foreach (var @interface in type.Interfaces) + { + if (@interface.InterfaceType.FullName == interfaceType.FullName) + return true; + } + + return false; + } public static bool IsSubclassOf(this TypeDefinition type, TypeDefinition baseType) { @@ -207,7 +219,7 @@ public static bool IsSubclassOf(this TypeDefinition type, TypeDefinition baseTyp var current = type; while (current.BaseType != null) { - if (current.BaseType.FullName == baseType.FullName) + if (current.BaseType.FullName == baseType.FullName || current.Interfaces.Any(x=>x.InterfaceType.FullName == baseType.FullName)) return true; // Continue up the inheritance chain diff --git a/Samples/HelloWorld/Program.cs b/Samples/HelloWorld/Program.cs index 9620069..8eb940d 100644 --- a/Samples/HelloWorld/Program.cs +++ b/Samples/HelloWorld/Program.cs @@ -6,6 +6,18 @@ namespace HelloWorld { + public interface IMyInterface + { + void Foo(); + + void Bar(); + } + + public interface IMyInterface2 + { + int Sum(int a, int b); + } + public class ConsoleBase { private int _arg; @@ -27,7 +39,7 @@ public virtual void VirtualMethod2() } } - public class Console1 : Console + public class Console1 : Console, IMyInterface, IMyInterface2 { public Console1(int arg) : base(arg) { @@ -44,6 +56,21 @@ public override void VirtualMethod() Program.Printf("Console 1 vm + \n"); base.VirtualMethod(); } + + public void Foo() + { + Program.Printf("Console1::Foo \n"); + } + + public void Bar() + { + Program.Printf("Console1::Bar \n"); + } + + public int Sum(int a, int b) + { + return a + b; + } } public class Console : ConsoleBase @@ -76,6 +103,8 @@ public override void VirtualMethod2() static class Program { + public static int Value = 0; + [NativeFunctionCall("printf", "")] public static void Printf(string value) { @@ -89,10 +118,30 @@ public static void Printf(string template, T input1) public static void Test() { - var console = new Console1(1); - + Printf("%u", Value); + Console1 console = new Console1(1); + IMyInterface myInterface = new Console1(2); console.VirtualMethod(); console.VirtualMethod2(); + + + + myInterface.Foo(); + myInterface.Bar(); + + + if (console is IMyInterface iface) + { + iface.Foo(); + iface.Bar(); + } + + if (console is IMyInterface2 iface2) + { + var sum = iface2.Sum(1, 2); + + Printf("sum: %u\n", sum); + } } [CustomFunctionName("main")] diff --git a/Samples/HelloWorld/generated/dntc.h b/Samples/HelloWorld/generated/dntc.h new file mode 100644 index 0000000..dbcbeeb --- /dev/null +++ b/Samples/HelloWorld/generated/dntc.h @@ -0,0 +1,32 @@ +#ifndef DNTC_H_ +#define DNTC_H_ + +typedef struct TypeInfo { + uint32_t* implemented_interfaces; // Array of implemented interfaces + size_t* interface_offsets; // Array of offsets for each interface + size_t interface_count; // Count of implemented interfaces +} TypeInfo; + +// all types will inherit this. +typedef struct ReferenceType_Base { + TypeInfo* type_info; +} ReferenceType_Base; + +// Function to check if an instance implements a specific interface +// Function to check if an instance implements a specific interface and return a pointer to it +void* dynamic_cast_interface(ReferenceType_Base* instance, uint32_t interface) { + if (instance && instance->type_info) { + TypeInfo* type_info = instance->type_info; + for (size_t i = 0; i < type_info->interface_count; ++i) { + if (type_info->implemented_interfaces[i] == interface) { + // Calculate the address of the interface implementation using the offset + return (void*)((char*)instance + type_info->interface_offsets[i]); + } + } + } + return NULL; // Interface is not implemented +} + + + +#endif \ No newline at end of file diff --git a/Samples/HelloWorld/manifest.json b/Samples/HelloWorld/manifest.json index e5bd760..0ffcbf2 100644 --- a/Samples/HelloWorld/manifest.json +++ b/Samples/HelloWorld/manifest.json @@ -10,5 +10,6 @@ ], "OutputDirectory": "./generated", "SingleGeneratedSourceFileName":"HelloWorld.c", - "DebugInfoMode": "None" + "DebugInfoMode": "None", + "AllowExperimentalReferenceTypeSupport": true }