Ejemplo n.º 1
0
        public string InsertEmscriptenStaticAssertsForStruct(TypeDefinition type, bool generateStaticAssertsForOffsets, int bits)
        {
            string code =
                @"
#ifdef {{{bits}}}
{{{beginNamespace}}}
static_assert(sizeof({{{structName}}}) == {{{sizeofStruct}}}, ""{{{structName}}} size mismatch"");
{{{static_assert(offsetof(...));}}}
{{{endNamespace}}}
#endif";

            // In C++, all types, even a "struct {}", must have a nonzero size. However when such a struct is
            // derived from, in the derived class its size is generally 0 (Empty Base Optimization). Because
            // of this duality, we don't store in TypeUtils.ValueTypeAlignment the non-zero size since that
            // is also used for inherited size computations, but instead here when generating final code,
            // generate sizes of empty structs as 1.
            int sizeOfStruct = Math.Max(TypeUtils.AlignAndSizeOfType(type, bits).size, 1);

            code = WrapInNamespace(code, type.Namespace);
            code = ExpandStringTemplate(code,
                                        "bits", bits == 32 ? "UT_32BIT" : "UT_64BIT",
                                        "structName", type.CppName(),
                                        "sizeofStruct", sizeOfStruct.ToString());

            if (generateStaticAssertsForOffsets)
            {
                List <string> fieldsChecks = new List <string>();
                foreach (var field in type.Fields)
                {
                    if (field.IsStatic)
                    {
                        continue;
                    }
                    if (field.FieldType.IsDynamicArray())
                    {
                        continue;
                    }
                    fieldsChecks.Add(
                        $"static_assert(offsetof({type.Name}, {field.Name}) == {TypeUtils.AlignAndSizeOfField(field, bits).offset}, \"{type.Name}.{field.Name} offset mismatch\");");
                }

                // Anything that's internal has the C++ side handled by hand
                code = ExpandStringTemplate(code, "static_assert(offsetof(...));", fieldsChecks);
            }
            else
            {
                code = ExpandStringTemplate(code, "static_assert(offsetof(...));", "");
            }
            return(code);
        }
Ejemplo n.º 2
0
        private void GenerateComponentDeserializeFromNativeCppMethod(TypeDefinition type)
        {
            var    sz32       = TypeUtils.AlignAndSizeOfType(type, 32);
            var    sz64       = TypeUtils.AlignAndSizeOfType(type, 64);
            string sizeString = (sz32.size == sz64.size) ? sz32.size.ToString() : $"global::System.IntPtr.Size == 8 ? {sz64.size} : {sz32.size}";

            line($"        public int NativeCppComponentSize() {{ return {sizeString}; }}");

            for (int bitness = 32; bitness <= 64; bitness += 32)
            {
                var sz = TypeUtils.AlignAndSizeOfType(type, bitness);

                line($"        public unsafe void DeserializeFromNativeCpp{bitness}(global::System.IntPtr __srcData, int __size)");
                line("        {");

                if (type.IsPodType())
                {
                    line($"            throw new global::System.InvalidOperationException(\"This function should never get called (POD types match in ABI between C++ and C#)\"); // but so that all components adhere to same interface, even POD types need this function defined for them");
                    line("        }");
                    continue;
                }

                line($"            byte* __src = (byte*)__srcData;");

                int    memcpyStartOffset = -1;
                int    memcpyEndOffset   = -1;
                string memcpyStartField  = null;
                foreach (var f in type.Fields)
                {
                    var fieldSize = TypeUtils.AlignAndSizeOfField(f, bitness);
                    if (f.FieldType.IsComplex())
                    {
                        if (memcpyStartField != null)
                        {
                            line($"            UnsafeUtility.MemCpy((byte*)UnsafeUtility.AddressOf(ref {memcpyStartField}), __src + {memcpyStartOffset}, {memcpyEndOffset - memcpyStartOffset});");
                            memcpyStartField = null;
                        }
                        if (f.FieldType.MetadataType == MetadataType.String)
                        {
                            line($"            {CSharpIdentifierFor(f.Name)} = StringInterop.Utf8ToString(((NativeString*)(__src + {fieldSize.offset}))->mStr, ((NativeString*)(__src + {fieldSize.offset}))->mSize);");
                        }
                        else if (f.FieldType.IsDynamicArray())
                        {
                            if (f.FieldType.DynamicArrayElementType().MetadataType == MetadataType.String)
                            {
                                line($"          {CSharpIdentifierFor(f.Name)} = StringInterop.StringListFromExistingNativeBuffer((NativeBuffer*)(__src + {fieldSize.offset}));");
                            }
                            else
                            {
                                var arrayElementSize = TypeUtils.AlignAndSizeOfType(f.FieldType.DynamicArrayElementType(), bitness);
                                line($"            {CSharpIdentifierFor(f.Name)} = StringInterop.PodDataListFromExistingNativeBuffer<{f.FieldType.DynamicArrayElementType().AsCsName()}>((NativeBuffer*)(__src + {fieldSize.offset}), {arrayElementSize.size});");
                            }
                        }
                    }
                    else
                    {
                        if (memcpyStartField == null)
                        {
                            memcpyStartOffset = fieldSize.offset;
                            memcpyStartField  = CSharpIdentifierFor(f.Name);
                        }
                        memcpyEndOffset = fieldSize.offset + fieldSize.size;
                    }
                }
                if (memcpyStartField != null)
                {
                    line($"            UnsafeUtility.MemCpy((byte*)UnsafeUtility.AddressOf(ref {memcpyStartField}), __src + {memcpyStartOffset}, {memcpyEndOffset - memcpyStartOffset});");
                }
                line("        }");
            }
            line($"        public unsafe void DeserializeFromNativeCpp(global::System.IntPtr srcData, int size) {{ if (global::System.IntPtr.Size == 8) DeserializeFromNativeCpp64(srcData, size); else DeserializeFromNativeCpp32(srcData, size); }}");
        }
Ejemplo n.º 3
0
        private void WriteType(TypeDefinition type)
        {
            if (type.HasAttribute("PureJSService"))
            {
                return;
            }

            var interfaces = type.Interfaces.Select(x => x.InterfaceType.AsCsName()).ToList();

            var isComponentData = type.Interfaces.Count(x => x.InterfaceType.Name == "IComponentData") != 0 ||
                                  type.Interfaces.Count(x => x.InterfaceType.Name == "ISharedComponentData") != 0 ||
                                  type.Interfaces.Count(x => x.InterfaceType.Name == "ISystemStateComponentData") != 0;

            if (isComponentData && !BindGem.PureJS)
            {
                interfaces.Add("UTiny.IComponentDataInternal");
                if (type.IsPodType())
                {
                    interfaces.Add("UTiny.IComponentIsPodData");
                }
            }

            var interfacesLine = isComponentData ? ": " + String.Join(", ", interfaces) : "";

            string kind;

            if (type.IsInterface)
            {
                kind = type.HasAttribute("Service") ? "static class" : "partial struct";
            }
            else
            {
                kind = type.HasAttribute("CsPartial") ? "partial struct" : "struct";
            }

            bool isService         = type.HasAttribute("Service");
            bool isNativeClassType = type.IsInterface && !isService;
            bool isConstructable   = type.HasAttribute("Constructable") && !isService;

            line("namespace " + type.Namespace);
            line("{");

            if (type.IsComponentType() || type.IsStructValueType())
            {
                line(
                    "    [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Sequential)]");
            }

            line($"    public {kind} {type.CsName()} {interfacesLine}");
            line("    {");

            var constructorName = $"ut_{type.Name}_{type.Name}";

            var cppFnName = type.FullyQualifiedCppName("_");

            if (isNativeClassType)
            {
                line($"        public global::System.IntPtr mThis;");

                line($"        [global::System.Runtime.InteropServices.DllImport(UTiny.Interop.NativeLibraryName)]");
                line($"        private static extern global::System.IntPtr {constructorName}();");
                line($"        public {type.CsName()}(global::System.IntPtr t) {{ mThis = t; }}");
                line("");

                line($"        [global::System.Runtime.InteropServices.DllImport(UTiny.Interop.NativeLibraryName)]");
                line($"        private static extern void ut_{type.Name}_shRelease(global::System.IntPtr ptr);");
                line($"        public void _NativeRelease() {{ ut_{type.Name}_shRelease(mThis); mThis = global::System.IntPtr.Zero; }}");
            }

            if (isConstructable)
            {
                line($"        public static {type.CsName()} New{type.CsName()}() {{ return new {type.CsName()}({constructorName}()); }}");
            }

            line("");

            if (isComponentData && !BindGem.PureJS)
            {
                line($@"        [global::System.Runtime.InteropServices.DllImport(UTiny.Interop.NativeLibraryName)]");
                line($@"        internal static extern int {cppFnName}_cid();");
                line($@"        public int NativeComponentId() {{ return {cppFnName}_cid(); }}");

                GenerateComponentSerializeToNativeCppMethod(type);
                GenerateComponentDeserializeFromNativeCppMethod(type);
            }

            line("");

            foreach (var f in type.Fields)
            {
                if (f.FieldType.MetadataType == MetadataType.String)
                {
                    line($"        public string {f.Name};");
                }
                else if (f.FieldType.IsDynamicArray())
                {
                    line($"        public {f.FieldType.DynamicArrayElementType().AsCsName()}[] {f.Name};");
                }
                else
                {
                    line($"        public {f.FieldType.AsCsName()} {CSharpIdentifierFor(f.Name)};");
                }
            }

            line("");

            line("");

            foreach (var m in type.MemberFunctions())
            {
                if (m.HasAttribute("CsCustomImpl"))
                {
                    continue;
                }

                // Declare the DllImported C call
                line($"        [global::System.Runtime.InteropServices.DllImport(UTiny.Interop.NativeLibraryName)]");
                if (m.ReturnType.Resolve().MetadataType == MetadataType.Boolean && !m.ReturnType.IsPointer)
                {
                    line($"        [return:global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.I1)]");
                }
                line($"        private static unsafe extern {m.ReturnType.AsCsReturnName()} {cppFnName}_{m.Name}({ParameterListForCCall(m, isService, isService).JoinWithComma()});");

                // Then declare the public C# API
                line($"        public {(isService || m.IsStatic ? "static " : "")}unsafe {m.ReturnType.AsCsName()} {m.Name}({ParameterList(m, true, false).JoinWithComma()})");
                line($"        {{");

                var callStr =
                    $"{cppFnName}_{m.Name}({ParameterNamesForCCall(m, isNativeClassType || isService, isService).JoinWithComma()})";

                if ((type.IsStructValueType() || type.IsComponentType()) && !m.IsStatic)
                {
                    line($"            //fixed ({type.AsCsName()}* self = &this)");
                    line($"            void* self = UnsafeUtility.AddressOf(ref this);");
                }

                // Marshal type conversions for C# -> C++ call
                for (int i = 0; i < m.Parameters.Count; ++i)
                {
                    if (m.Parameters[i].ParameterType.MetadataType == MetadataType.String)
                    {
                        line($"            NativeString arg{i}str = new NativeString();");
                        line($"            StringInterop.ut_nativestring_placement_create(&arg{i}str, (uint)StringInterop.StringLengthUtf8(arg{i}));");
                        line($"            StringInterop.StringToUtf8(arg{i}, arg{i}str.mStr, arg{i}str.mSize+1);");
                    }
                    else if (m.Parameters[i].ParameterType.IsDynamicArray())
                    {
                        if (m.Parameters[i].ParameterType.DynamicArrayElementType().MetadataType == MetadataType.String)
                        {
                            line($"            NativeBuffer arg{i}arr = StringInterop.StringListToNewNativeBuffer(arg{i});");
                        }
                        else
                        {
                            var    sz32       = TypeUtils.AlignAndSizeOfType(m.Parameters[i].ParameterType.DynamicArrayElementType(), 32);
                            var    sz64       = TypeUtils.AlignAndSizeOfType(m.Parameters[i].ParameterType.DynamicArrayElementType(), 64);
                            string sizeString = (sz32.size == sz64.size) ? sz32.size.ToString() : $"global::System.IntPtr.Size == 8 ? {sz64.size} : {sz32.size}";
                            line($"            NativeBuffer arg{i}arr = StringInterop.PodDataListToNewNativeBuffer(arg{i}, {sizeString});");
                        }
                    }
                }

                string retval = null;
                if (m.ReturnType.MetadataType == MetadataType.Void)
                {
                    line($"            {callStr};");
                }
                else if (m.ReturnType.MetadataType == MetadataType.String)
                {
                    line($"            NativeString retval;");
                    line($"            {callStr};");
                    line($"            string str = StringInterop.Utf8ToString(retval.mStr, retval.mSize);");
                    line($"            StringInterop.ut_nativestring_free_data(&retval);");
                    retval = "str";
                }
                else if (m.ReturnType.IsSharedPtrType() || m.ReturnType.IsNonSharedPtrType())
                {
                    line($"            var ptr = {callStr};");
                    retval = $"new {m.ReturnType.Resolve().AsCsName()}(ptr)";
                }
                else if (m.ReturnType.EmCppReturnToFirstArgPtr())
                {
                    line($"            {m.ReturnType.AsCsName()} retval;");
                    line($"            {callStr};");
                    retval = "retval";
                }
                else
                {
                    line($"            {m.ReturnType.AsCsName()} retval = {callStr};");
                    retval = "retval";
                }

                // Cleanup for marshalled type conversions for C# -> C++ calls
                for (int i = 0; i < m.Parameters.Count; ++i)
                {
                    if (m.Parameters[i].ParameterType.MetadataType == MetadataType.String)
                    {
                        line($"            StringInterop.ut_nativestring_placement_delete(&arg{i}str);");
                    }
                    else if (m.Parameters[i].ParameterType.IsDynamicArray())
                    {
                        if (m.Parameters[i].ParameterType.DynamicArrayElementType().MetadataType == MetadataType.String)
                        {
                            line($"            StringInterop.ut_nativebuffer_nativestring_placement_delete(&arg{i}arr);");
                        }
                        else
                        {
                            line($"            StringInterop.ut_nativebuffer_pod_placement_delete(&arg{i}arr);");
                        }
                    }
                }

                if (retval != null)
                {
                    line($"            return {retval};");
                }

                line($"        }}");
                line("");
            }

            line("");

            foreach (var e in type.NestedTypes.Where(x => x.IsEnum))
            {
                line($"        public enum {e.Name}");
                line("        {");
                foreach (var field in e.Fields.Where(f => f.IsStatic))
                {
                    line($"            {field.Name} = {field.Constant},");
                }
                line("        }");
            }

            line("    }");
            line("}");
        }
Ejemplo n.º 4
0
        // Complex component types need to be marshalled down to C++ format when transitioning them between the C# <-> C++ language boundary. This is to
        // avoid memory leaks from strings and arrays from occurring when the component type is manipulated in C# side.
        private void GenerateComponentSerializeToNativeCppMethod(TypeDefinition type)
        {
            for (int bitness = 32; bitness <= 64; bitness += 32)
            {
                var sz = TypeUtils.AlignAndSizeOfType(type, bitness);
                line($"        public unsafe NativeBuffer SerializeToNativeCpp{bitness}()");
                line("        {");

                if (type.IsPodType())
                {
                    line($"            throw new global::System.InvalidOperationException(\"This function should never get called (POD types match in ABI between C++ and C#)\"); // but so that all components adhere to same interface, even POD types need this function defined for them");
                    line("        }");
                    continue;
                }

                line($"            NativeBuffer __buffer = new NativeBuffer();");
                line($"            StringInterop.ut_nativebuffer_pod_placement_create_uninitialized((void*)&__buffer, {sz.size}, 1);");
                line($"            byte* __data = (byte*)StringInterop.ut_nativebuffer_data(&__buffer);");
                int    memcpyStartOffset = -1;
                int    memcpyEndOffset   = -1;
                string memcpyStartField  = null;
                foreach (var f in type.Fields)
                {
                    var fieldSize = TypeUtils.AlignAndSizeOfField(f, bitness);
                    var ident     = CSharpIdentifierFor(f.Name);
                    if (f.FieldType.IsComplex())
                    {
                        if (memcpyStartField != null)
                        {
                            line($"            UnsafeUtility.MemCpy(__data + {memcpyStartOffset}, (byte*)UnsafeUtility.AddressOf(ref {memcpyStartField}), {memcpyEndOffset - memcpyStartOffset});");
                            memcpyStartField = null;
                        }
                        if (f.FieldType.MetadataType == MetadataType.String)
                        {
                            line($"          {{");
                            line($"            uint __len = ({ident} == null || {ident}.Length == 0) ? 0 : (uint)StringInterop.StringLengthUtf8({ident});");
                            line($"            StringInterop.ut_nativestring_placement_create((void*)(__data + {fieldSize.offset}), __len);");
                            line($"            if (__len > 0) {{");
                            line($"                StringInterop.StringToUtf8({ident}, ((NativeString*)(__data + {fieldSize.offset}))->mStr, ((NativeString*)(__data + {fieldSize.offset}))->mSize+1);");
                            line($"            }}");
                            line($"          }}");
                        }
                        else if (f.FieldType.IsDynamicArray())
                        {
                            if (f.FieldType.DynamicArrayElementType().MetadataType == MetadataType.String)
                            {
                                line($"          {{");
                                line($"            uint __len = ({ident} == null || {ident}.Length == 0) ? 0 : (uint){ident}.Length;");
                                line($"            StringInterop.ut_nativebuffer_nativestring_placement_create((void*)(__data + {fieldSize.offset}), __len);");
                                line($"            if (__len > 0) {{");
                                line($"                StringInterop.StringListToExistingNativeBuffer((NativeBuffer*)(__data + {fieldSize.offset}), {ident});");
                                line($"            }}");
                                line($"          }}");
                            }
                            else
                            {
                                var arrayElementSize = TypeUtils.AlignAndSizeOfType(f.FieldType.DynamicArrayElementType(), bitness);
                                line($"          {{");
                                line($"            uint __len = ({ident} == null || {ident}.Length == 0) ? 0 : (uint){ident}.Length;");
                                line($"            StringInterop.ut_nativebuffer_pod_placement_create_uninitialized((void*)(__data + {fieldSize.offset}), __len, {arrayElementSize.size});");
                                line($"            if (__len > 0) {{");
                                line($"                StringInterop.PodDataListToExistingNativeBuffer((NativeBuffer*)(__data + {fieldSize.offset}), {ident}, {arrayElementSize.size});");
                                line($"            }}");
                                line($"          }}");
                            }
                        }
                    }
                    else
                    {
                        if (memcpyStartField == null)
                        {
                            memcpyStartOffset = fieldSize.offset;
                            memcpyStartField  = CSharpIdentifierFor(f.Name);
                        }
                        memcpyEndOffset = fieldSize.offset + fieldSize.size;
                    }
                }
                if (memcpyStartField != null)
                {
                    line($"            UnsafeUtility.MemCpy(__data + {memcpyStartOffset}, (byte*)UnsafeUtility.AddressOf(ref {memcpyStartField}), {memcpyEndOffset - memcpyStartOffset});");
                }
                line("            return __buffer;");
                line("        }");
            }
            line($"        public unsafe NativeBuffer SerializeToNativeCpp() {{ if (global::System.IntPtr.Size == 8) return SerializeToNativeCpp64(); else return SerializeToNativeCpp32(); }}");
        }