Browse Source

Add VTable

sunnycase 6 years ago
parent
commit
17820d0af1
4 changed files with 239 additions and 103 deletions
  1. 26 6
      src/Native/natsu.fcall.cpp
  2. 3 2
      src/Native/natsu.gc.cpp
  3. 49 20
      src/Native/natsu.runtime.h
  4. 161 75
      src/Natsu.Compiler/Program.cs

+ 26 - 6
src/Native/natsu.fcall.cpp

@@ -19,6 +19,27 @@ namespace System_Private_CorLib
     return _this->header_.length_;
 }
 
+::System_Private_CorLib::System::Int32 System::Array::get_Rank(::natsu::gc_obj_ref<::System_Private_CorLib::System::Array> _this)
+{
+    return 1;
+}
+
+::System_Private_CorLib::System::Int32 System::Array::get_Length(::natsu::gc_obj_ref<::System_Private_CorLib::System::Array> _this)
+{
+    return _this->header_.length_;
+}
+
+::System_Private_CorLib::System::Int64 System::Array::get_LongLength(::natsu::gc_obj_ref<::System_Private_CorLib::System::Array> _this)
+{
+    return _this->header_.length_;
+}
+
+::System_Private_CorLib::System::Char System::String::get_Chars(::natsu::gc_obj_ref<::System_Private_CorLib::System::String> _this, ::System_Private_CorLib::System::Int32 index)
+{
+    assert((uint32_t)index < (uint32_t)_this->_stringLength);
+    return (&_this->_firstChar)[index];
+}
+
 void System::Diagnostics::Debug::_s_WriteCore(::natsu::gc_obj_ref<::System_Private_CorLib::System::String> message)
 {
 #ifdef WIN32
@@ -46,11 +67,10 @@ void System::Buffer::_s_Memmove(::natsu::gc_ptr<::System_Private_CorLib::System:
 
 ::natsu::gc_obj_ref<::System_Private_CorLib::System::String> System::String::_s_FastAllocateString(::System_Private_CorLib::System::Int32 length)
 {
-    auto size = sizeof(::System_Private_CorLib::System::String) + length * sizeof(::System_Private_CorLib::System::Char);
-    auto obj = natsu::gc_alloc(size);
-    auto ptr = reinterpret_cast<::System_Private_CorLib::System::String *>(obj);
-    ptr->_stringLength = length;
-    (&ptr->_firstChar)[length] = 0;
-    return ::natsu::gc_obj_ref<::System_Private_CorLib::System::String>(ptr);
+    auto size = sizeof(System::String) + length * sizeof(System::Char);
+    auto obj = natsu::gc_new<System::String>(size);
+    obj->_stringLength = length;
+    (&obj->_firstChar)[length] = 0;
+    return obj;
 }
 }

+ 3 - 2
src/Native/natsu.gc.cpp

@@ -383,10 +383,11 @@ static void prvInsertBlockIntoFreeList(BlockLink_t *pBlockToInsert)
 
 namespace natsu
 {
-uint8_t *gc_alloc(size_t size)
+gc_obj_ref<object> gc_alloc(const vtable_t &vtable, size_t size)
 {
-    auto ptr = (uint8_t *)HeapAlloc(size);
+    gc_obj_ref<object> ptr(reinterpret_cast<object *>(HeapAlloc(size)));
     assert(ptr);
+    ptr->header_.vtable_ = &vtable;
     return ptr;
 }
 } // namespace  natsu

+ 49 - 20
src/Native/natsu.runtime.h

@@ -69,6 +69,11 @@ struct gc_ref
     {
     }
 
+    constexpr operator bool() const noexcept
+    {
+        return true;
+    }
+
     explicit operator uintptr_t() const noexcept
     {
         return reinterpret_cast<uintptr_t>(ptr_);
@@ -123,6 +128,11 @@ struct gc_ptr
         return reinterpret_cast<uintptr_t>(ptr_);
     }
 
+    operator bool() const noexcept
+    {
+        return ptr_;
+    }
+
     operator T *() const noexcept
     {
         return ptr_;
@@ -270,14 +280,27 @@ struct gc_obj_ref
     }
 };
 
-struct vtable
+typedef struct _vtable
 {
-};
+    virtual ~_vtable() = default;
+} vtable_t;
 
 struct object_header
 {
-    vtable *vtable__;
+    const vtable_t *vtable_;
     uintptr_t length_;
+
+    template <class TVTable>
+    const TVTable *vtable() const noexcept
+    {
+        return static_cast<const TVTable *>(vtable_);
+    }
+
+    template <class TVTable>
+    const TVTable *vtable_as() const noexcept
+    {
+        return dynamic_cast<const TVTable *>(vtable_);
+    }
 };
 
 struct object
@@ -296,23 +319,38 @@ struct natsu_exception
     gc_obj_ref<::System_Private_CorLib::System::Exception> exception_;
 };
 
-uint8_t *gc_alloc(size_t length);
+template <class T>
+struct static_holder
+{
+    static T value;
+};
+
+template <class T>
+T static_holder<T>::value;
+
+gc_obj_ref<object> gc_alloc(const vtable_t &vtable, size_t size);
+
+template <class T>
+gc_obj_ref<T> gc_new(size_t size)
+{
+    auto ptr = gc_alloc(static_holder<typename T::VTable>::value, size);
+    return gc_obj_ref<T>(ptr);
+}
 
 template <class T>
 gc_obj_ref<T> gc_new()
 {
-    auto ptr = gc_alloc(sizeof(T));
-    return gc_obj_ref<T>(reinterpret_cast<T *>(ptr));
+    return gc_new<T>(sizeof(T));
 }
 
 template <class T>
 gc_obj_ref<::System_Private_CorLib::System::SZArray_1<T>> gc_new_array(int length)
 {
-    auto size = sizeof(::System_Private_CorLib::System::SZArray_1<T>) + (size_t)length * sizeof(T);
-    auto obj = gc_alloc(size);
-    auto ptr = reinterpret_cast<::System_Private_CorLib::System::SZArray_1<T> *>(obj);
-    ptr->header_.length_ = length;
-    return gc_obj_ref<::System_Private_CorLib::System::SZArray_1<T>>(ptr);
+    using obj_t = ::System_Private_CorLib::System::SZArray_1<T>;
+    auto size = sizeof(obj_t) + (size_t)length * sizeof(T);
+    auto obj = gc_new<obj_t>(size);
+    obj->header_.length_ = length;
+    return obj;
 }
 
 template <class T, bool IsValueType, class... TArgs>
@@ -374,15 +412,6 @@ constexpr bool operator==(null_gc_obj_ref, const gc_obj_ref<T> &rhs) noexcept
     return !rhs.ptr_;
 }
 
-template <class T>
-struct static_holder
-{
-    static T value;
-};
-
-template <class T>
-T static_holder<T>::value;
-
 constexpr null_gc_obj_ref null;
 
 gc_obj_ref<::System_Private_CorLib::System::String> load_string(std::u16string_view string);

+ 161 - 75
src/Natsu.Compiler/Program.cs

@@ -146,13 +146,6 @@ namespace Natsu.Compiler
                 foreach (var field in type.TypeDef.Fields)
                     AddTypeRef(type, field.FieldType, false);
 
-                foreach (var method in type.TypeDef.Methods)
-                {
-                    AddTypeRef(type, method.ReturnType, false);
-                    foreach (var param in method.Parameters)
-                        AddTypeRef(type, param.Type, false);
-                }
-
                 var baseType = GetTypeDef(GetBaseType(type.TypeDef));
                 if (baseType != null)
                     AddTypeRef(type, baseType, true);
@@ -221,10 +214,7 @@ namespace Natsu.Compiler
 
         private ITypeDefOrRef GetBaseType(TypeDef type)
         {
-            if (type.FullName == "System.ValueType")
-                return null;
-            var baseType = type.BaseType;
-            return baseType;
+            return type.BaseType;
         }
 
         private void AddTypeRef(TypeDesc declareDesc, TypeSig fieldType, bool force)
@@ -240,6 +230,8 @@ namespace Natsu.Compiler
                     case ElementType.Ptr:
                     case ElementType.CModReqd:
                     case ElementType.GenericInst:
+                    case ElementType.Object:
+                    case ElementType.Class:
                         break;
                     case ElementType.Boolean:
                     case ElementType.Char:
@@ -262,8 +254,6 @@ namespace Natsu.Compiler
                         }
                         break;
                     case ElementType.ValueType:
-                    case ElementType.Class:
-                    case ElementType.Object:
                         AddTypeRef(declareDesc, cntSig.TryGetTypeDef(), force);
                         break;
                     case ElementType.SZArray:
@@ -327,7 +317,7 @@ namespace Natsu.Compiler
             }
             else
             {
-                writer.Ident(ident).Write($"using {EscapeTypeName(type.Name)} = {fowardName};");
+                writer.Ident(ident).Write($"using {EscapeTypeName(type.Name)} = {fowardName}; ");
             }
 
             foreach (var ns in nss)
@@ -337,7 +327,7 @@ namespace Natsu.Compiler
         #region Forward Declares
         private void WriteTypeForwardDeclares(StreamWriter writer)
         {
-            var types = _typeDescs.Values.Where(x => !x.TypeDef.IsValueType).ToList();
+            var types = _typeDescs.Values.ToList();
             var index = 0;
             foreach (var type in types)
             {
@@ -402,11 +392,24 @@ namespace Natsu.Compiler
 
             writer.Ident(ident).Write($"struct {type.Name}");
             if (!type.TypeDef.IsValueType)
-                writer.WriteLine(" : public ::natsu::object");
+            {
+                var baseType = GetBaseType(type.TypeDef);
+                if (baseType != null)
+                    writer.WriteLine(" : public " + EscapeTypeName(baseType, true, true));
+                else
+                    writer.WriteLine(" : public ::natsu::object");
+            }
             else
+            {
                 writer.WriteLine();
+            }
+
             writer.Ident(ident).WriteLine("{");
 
+            // VTable
+            WriteVTableDeclare(writer, ident + 1, type);
+            writer.WriteLine();
+
             foreach (var field in type.TypeDef.Fields)
             {
                 if (field.HasConstant && field.ElementType != ElementType.String)
@@ -481,69 +484,53 @@ namespace Natsu.Compiler
                 writer.Ident(ident).WriteLine("};");
             }
 
-            // VTable
-            writer.WriteLine();
-            WriteVTable(writer, ident, type);
-
             foreach (var ns in nss)
                 writer.Write("} ");
             writer.WriteLine();
         }
 
-        private void WriteVTable(StreamWriter writer, int ident, TypeDesc type)
+        private void WriteVTableDeclare(StreamWriter writer, int ident, TypeDesc type)
         {
-            if (type.TypeDef.HasGenericParameters)
-            {
-                var typeNames = type.TypeDef.GenericParameters.Select(x => "class " + x.Name.String).ToList();
-                writer.Ident(ident).WriteLine($"template <{string.Join(", ", typeNames)}> ");
-            }
-
-            writer.Ident(ident).Write($"struct {type.Name}_VTable : public ");
+            writer.Ident(ident).Write($"struct VTable : public ");
             var baseType = GetBaseType(type.TypeDef);
             if (baseType == null)
-                writer.Write("virtual natsu::vtable");
+                writer.Write("virtual natsu::vtable_t");
             else
-                writer.Write($"{EscapeTypeName(baseType, true, true)}_VTable");
+                writer.Write($"{EscapeTypeName(baseType, true, true)}::VTable");
 
-            writer.WriteLine("{");
-            writer.WriteLine("};");
-        }
+            foreach (var iface in type.TypeDef.Interfaces)
+            {
+                var ifaceType = iface.Interface;
+                writer.Write($", public virtual {EscapeTypeName(ifaceType, true, true)}::VTable");
+            }
 
-        private string EscapeTypeName(ITypeDefOrRef type, TypeDefOrRefSig sig, bool isBaseType, bool hasModuleName)
-        {
-            var sb = new StringBuilder();
-            if (!isBaseType && !sig.IsValueType)
-                sb.Append("::natsu::gc_obj_ref<");
+            writer.WriteLine();
 
-            if (hasModuleName)
-                sb.Append("::" + EscapeModuleName(type.DefinitionAssembly) + "::");
-            var nss = type.Namespace.Split('.', StringSplitOptions.RemoveEmptyEntries)
-                .Select(EscapeNamespaceName).ToList();
-            foreach (var ns in nss)
-                sb.Append($"{ns}::");
-            sb.Append(EscapeTypeName(type.Name));
-            if (type is TypeDef typeDef && typeDef.HasGenericParameters && !type.ContainsGenericParameter)
+            writer.Ident(ident).WriteLine("{");
+
+            foreach (var method in type.TypeDef.Methods)
             {
-                sb.Append("<");
-                sb.Append(string.Join(", ", typeDef.GenericParameters.Select(x => x.Name)));
-                sb.Append(">");
+                if (!method.IsInstanceConstructor && !method.IsStatic)
+                    WriteVTableMethodDeclare(writer, ident + 1, method);
             }
 
-            if (!isBaseType && !sig.IsValueType)
-                sb.Append(">");
+            writer.Ident(ident).WriteLine("};");
+        }
 
-            return sb.ToString();
+        private string EscapeTypeName(ITypeDefOrRef type, TypeDefOrRefSig sig, bool isBaseType, bool hasModuleName, bool isVTable)
+        {
+            return EscapeTypeNameImpl(type, sig.IsValueType, isBaseType, hasModuleName, isVTable, true);
         }
 
-        private string EscapeTypeName(ITypeDefOrRef type, bool isBaseType, bool hasModuleName)
+        private string EscapeTypeNameImpl(ITypeDefOrRef type, bool isValueType, bool isBaseType, bool hasModuleName, bool isVTable, bool hasGen)
         {
             if (type is TypeSpec typeSpec)
             {
-                return EscapeTypeName(typeSpec.TypeSig, null, isBaseType);
+                return EscapeTypeName(typeSpec.TypeSig, null, isBaseType, isVTable);
             }
 
             var sb = new StringBuilder();
-            if (!isBaseType && !type.IsValueType)
+            if (!isBaseType && !isValueType)
                 sb.Append("::natsu::gc_obj_ref<");
 
             if (hasModuleName)
@@ -553,25 +540,26 @@ namespace Natsu.Compiler
             foreach (var ns in nss)
                 sb.Append($"{ns}::");
             sb.Append(EscapeTypeName(type.Name));
-            if (false && type.NumberOfGenericParameters != 0)
+            if (isVTable)
+                sb.Append("_VTable");
+            if (hasGen && type is TypeDef typeDef && typeDef.HasGenericParameters && !type.ContainsGenericParameter)
             {
                 sb.Append("<");
-                if (type is TypeDef typeDef)
-                {
-                    sb.Append(string.Join(", ", typeDef.GenericParameters.Select(x => x.Name)));
-                }
-                else
-                    throw new NotImplementedException();
-
+                sb.Append(string.Join(", ", typeDef.GenericParameters.Select(x => x.Name)));
                 sb.Append(">");
             }
 
-            if (!isBaseType && !type.IsValueType)
+            if (!isBaseType && !isValueType)
                 sb.Append(">");
 
             return sb.ToString();
         }
 
+        private string EscapeTypeName(ITypeDefOrRef type, bool isBaseType, bool hasModuleName, bool isVTable = false)
+        {
+            return EscapeTypeNameImpl(type, type.IsValueType, isBaseType, hasModuleName, isVTable, false);
+        }
+
         private static string EscapeTypeName(string name)
         {
             return name.Replace('<', '_').Replace('>', '_').Replace('`', '_');
@@ -618,14 +606,14 @@ namespace Natsu.Compiler
             writer.WriteLine($");");
         }
 
-        private string EscapeTypeName(TypeSig fieldType, TypeDef declaringType = null, bool isBaseType = false)
+        private string EscapeTypeName(TypeSig fieldType, TypeDef declaringType = null, bool isBaseType = false, bool isVTable = false)
         {
             var sb = new StringBuilder();
-            EscapeTypeName(sb, fieldType, declaringType, isBaseType);
+            EscapeTypeName(sb, fieldType, declaringType, isBaseType, isVTable);
             return sb.ToString();
         }
 
-        private void EscapeTypeName(StringBuilder sb, TypeSig cntSig, TypeDef declaringType = null, bool isBaseType = false)
+        private void EscapeTypeName(StringBuilder sb, TypeSig cntSig, TypeDef declaringType = null, bool isBaseType = false, bool isVTable = false)
         {
             switch (cntSig.ElementType)
             {
@@ -654,7 +642,7 @@ namespace Natsu.Compiler
                                 if (sig.TypeDef == declaringType)
                                     sb.Append(GetConstantTypeName(cntSig.ElementType));
                                 else
-                                    sb.Append(EscapeTypeName(sig.TypeDefOrRef, sig, isBaseType: isBaseType, hasModuleName: true));
+                                    sb.Append(EscapeTypeName(sig.TypeDefOrRef, sig, isBaseType: isBaseType, hasModuleName: true, isVTable: isVTable));
                                 break;
                             default:
                                 throw new NotSupportedException();
@@ -671,7 +659,7 @@ namespace Natsu.Compiler
                                 if (sig.IsValueType && declaringType != null && sig.TypeDef == declaringType)
                                     sb.Append(GetConstantTypeName(cntSig.ElementType));
                                 else
-                                    sb.Append(EscapeTypeName(sig.TypeDefOrRef, sig, isBaseType: isBaseType, hasModuleName: true));
+                                    sb.Append(EscapeTypeName(sig.TypeDefOrRef, sig, isBaseType: isBaseType, hasModuleName: true, isVTable: isVTable));
                                 break;
                             default:
                                 throw new NotSupportedException();
@@ -689,7 +677,7 @@ namespace Natsu.Compiler
                 case ElementType.GenericInst:
                     {
                         var sig = cntSig.ToGenericInstSig();
-                        sb.Append(EscapeTypeName(sig.GenericType.TypeDefOrRef, isBaseType: isBaseType, hasModuleName: true));
+                        sb.Append(EscapeTypeName(sig.GenericType.TypeDefOrRef, isBaseType: isBaseType, hasModuleName: true, isVTable: isVTable));
                         sb.Append("<");
                         for (int i = 0; i < sig.GenericArguments.Count; i++)
                         {
@@ -765,12 +753,21 @@ namespace Natsu.Compiler
         {
             foreach (var method in type.TypeDef.Methods)
             {
-                if (!method.IsInternalCall && !method.IsAbstract)
+                if (inHeader == (type.TypeDef.HasGenericParameters || method.HasGenericParameters))
                 {
-                    if (inHeader == (type.TypeDef.HasGenericParameters || method.HasGenericParameters))
+                    if (!method.IsAbstract)
                     {
-                        WriteMethodBody(writer, ident, method);
-                        writer.WriteLine();
+                        if (!method.IsInstanceConstructor && !method.IsStatic)
+                        {
+                            WriteVTableMethodBody(writer, ident, method);
+                            writer.WriteLine();
+                        }
+
+                        if (!method.IsInternalCall)
+                        {
+                            WriteMethodBody(writer, ident, method);
+                            writer.WriteLine();
+                        }
                     }
                 }
             }
@@ -817,6 +814,95 @@ namespace Natsu.Compiler
             writer.Flush();
         }
 
+        private void WriteVTableMethodDeclare(StreamWriter writer, int ident, MethodDef method)
+        {
+            writer.Ident(ident);
+
+            if (method.HasGenericParameters)
+                throw new NotSupportedException("Virtual generic methods is not supported");
+
+            if (method.IsVirtual)
+                writer.Write("virtual ");
+            writer.Write(EscapeTypeName(method.ReturnType) + " ");
+            writer.Write(EscapeMethodName(method) + "(");
+
+            var index = 0;
+            var parameters = method.Parameters;
+            foreach (var param in parameters)
+            {
+                writer.Write(EscapeTypeName(param.Type));
+                var paramName = param.IsHiddenThisParameter ? "_this" : param.Name;
+                writer.Write(" " + EscapeIdentifier(paramName));
+                if (index++ != parameters.Count - 1)
+                    writer.Write(", ");
+            }
+
+            writer.Write(") const");
+            if (method.IsAbstract)
+            {
+                writer.WriteLine(" = 0;");
+            }
+            else
+            {
+                writer.WriteLine(";");
+            }
+
+            writer.Flush();
+        }
+
+        private void WriteVTableMethodBody(StreamWriter writer, int ident, MethodDef method)
+        {
+            writer.Ident(ident);
+            var typeGens = new List<string>();
+            var methodGens = new List<string>();
+
+            if (method.DeclaringType.HasGenericParameters)
+                typeGens.AddRange(method.DeclaringType.GenericParameters.Select(x => x.Name.String));
+            if (method.HasGenericParameters)
+                throw new NotSupportedException("Virtual generic methods is not supported");
+
+            if (typeGens.Any() || methodGens.Any())
+                writer.WriteLine($"template <{string.Join(", ", typeGens.Concat(methodGens).Select(x => "class " + x))}>");
+
+            writer.Write(EscapeTypeName(method.ReturnType) + " ");
+            writer.Write(EscapeTypeName(method.DeclaringType, isBaseType: true, hasModuleName: false));
+            if (typeGens.Any())
+                writer.Write($"<{string.Join(", ", typeGens)}>");
+            writer.Write("::VTable::" + EscapeMethodName(method) + "(");
+
+            var index = 0;
+            var parameters = method.Parameters;
+            foreach (var param in parameters)
+            {
+                writer.Write(EscapeTypeName(param.Type));
+                var paramName = param.IsHiddenThisParameter ? "_this" : param.Name;
+                writer.Write(" " + EscapeIdentifier(paramName));
+                if (index++ != parameters.Count - 1)
+                    writer.Write(", ");
+            }
+
+            writer.WriteLine(") const");
+            writer.Ident(ident).WriteLine("{");
+            writer.Ident(ident + 1).WriteLine("assert(_this);");
+            writer.Ident(ident + 1).Write("return ");
+            writer.Write(EscapeTypeName(method.DeclaringType, true, true));
+            if (typeGens.Any())
+                writer.Write($"<{string.Join(", ", typeGens)}>");
+            writer.Write("::" + EscapeMethodName(method) + "(");
+            index = 0;
+            foreach (var param in parameters)
+            {
+                var paramName = param.IsHiddenThisParameter ? "_this" : param.Name;
+                writer.Write(EscapeIdentifier(paramName));
+                if (index++ != parameters.Count - 1)
+                    writer.Write(", ");
+            }
+            writer.WriteLine(");");
+            writer.Ident(ident).WriteLine("}");
+
+            writer.Flush();
+        }
+
         private void WriteILBody(StreamWriter writer, int ident, MethodDef method)
         {
             var body = method.Body;
@@ -1207,7 +1293,7 @@ namespace Natsu.Compiler
                     para.Add((member.DeclaringType.ToTypeSig(), stack.Pop()));
 
                 para.Reverse();
-                stack.Push(method.RetType, $"VTable{EscapeTypeName(member.DeclaringType, isBaseType: true, hasModuleName: true)}::{EscapeMethodName(member)}({string.Join(", ", para.Select(x => CastExpression(x.destType, x.src)))})");
+                stack.Push(method.RetType, $"{para[0].src.expression}->header_.vtable_as<{EscapeTypeName(member.DeclaringType, isBaseType: true, hasModuleName: true)}::VTable>()->{EscapeMethodName(member)}({string.Join(", ", para.Select(x => CastExpression(x.destType, x.src)))})");
             }
 
             void ConvertNewobj(IMethodDefOrRef member)