示例#1
0
        public void HandleEnumType(TypeDefinition type)
        {
            if (type.HasAttribute("CppCustomImpl"))
            {
                return;
            }

            BindGem.WarningIf(type.HasAttribute("CppName"),
                              $"Type {type} has CppName but is not CppCustomImpl -- CppName will be ignored for fully generated types");

            var    enumSize    = type.GetEnumSize();
            string baseTypeStr = null;

            if (enumSize == 8)
            {
                baseTypeStr = "int8_t";
            }
            else if (enumSize == 16)
            {
                baseTypeStr = "int16_t";
            }
            else if (enumSize == 32)
            {
                baseTypeStr = "int32_t";
            }
            else if (enumSize == 64)
            {
                baseTypeStr = "int64_t";
            }

            string enumTemplate =
                @"enum class {{{typeName}}} : {{{baseEnumType}}} {
{{{name=value}}}
};";

            var enumList   = type.GetEnumKeyValues();
            var enumValues = enumList.Select(e => $"  {e.key} = {e.value}");
            var code       = ExpandStringTemplate(enumTemplate,
                                                  "typeName", type.Name,
                                                  "baseEnumType", baseTypeStr,
                                                  "name=value", string.Join(",\n", enumValues));

            if (!BindGem.DOTS)
            {
                // Register Enum
                var name           = type.Name;
                var namespacedName = type.JSName();
                var identifier     = type.FullyQualifiedCppName("_").ToLower();
                var members        = enumList.Select(e => $"  ut::meta::enumv(\"{e.key}\", {name}::{e.key})");
                var body           = $"{(members.Count() > 0 ? ",\n" : "")}{string.Join(",\n", members)}";
                code += ExpandStringTemplate(
                    "\nREFLECT_ENUM({{{name}}}, \"{{{namespacedName}}}\", {{{identifier}}}{{{body}}})",
                    "name", name,
                    "namespacedName", namespacedName,
                    "identifier", identifier,
                    "body", body);
            }

            hpp(WrapInNamespace(code, type.CppNamespace()));
        }
示例#2
0
        internal static JSSpecialType JavaScriptSpecialType(this TypeReference typeRef)
        {
            TypeDefinition type = typeRef.Resolve();

            if (!type.HasAttribute("JSCustomImpl"))
            {
                return(JSSpecialType.None);
            }

            switch (type.Name)
            {
            case "Vector2": return(JSSpecialType.Vector2);

            case "Vector3": return(JSSpecialType.Vector3);

            case "Vector4": return(JSSpecialType.Vector4);

            case "Quaternion": return(JSSpecialType.Quaternion);

            case "Matrix3x3": return(JSSpecialType.Matrix3);

            case "Matrix4x4": return(JSSpecialType.Matrix4);

            case null: return(JSSpecialType.None);
            }

            BindGem.FatalError($"Unknown JSCustomImpl '{type.Name}'");//, type.Locations[0]);
            return(JSSpecialType.None);
        }
示例#3
0
        public void HandleStructType(TypeDefinition type)
        {
            string cppCode = "{{{sizeCheckNoteForInternalStructTypes}}}"
                             + InsertEmscriptenStaticAssertsForStruct(type, !type.HasAttribute("CppCustomImpl"), 32)
                             + InsertEmscriptenStaticAssertsForStruct(type, !type.HasAttribute("CppCustomImpl"), 64);

            if (type.HasAttribute("CppCustomImpl"))
            {
                // insert static asserts for offsets
                cppCode = ExpandStringTemplate(cppCode,
                                               "sizeCheckNoteForInternalStructTypes", $"/* Generating only size check for internal struct {type.FullName} */\n");
            }
            else
            {
                // Anything that's internal has the C++ side handled by hand
                cppCode = ExpandStringTemplate(cppCode,
                                               "sizeCheckNoteForInternalStructTypes", "");
                BindGem.WarningIf(type.HasAttribute("CppName"),
                                  $"Type {type} has CppName but is not CppCustomImpl -- CppName will be ignored for fully generated types");
            }
            cpp(cppCode);

            if (!type.HasAttribute("CppCustomImpl"))
            {
                BindGem.FatalErrorIf(type.HasAttribute("SharedPtrAttribute") /*, type*/,
                                     $"Can't have a SharedPtr type {type} that's not also [Internal]");
                var allFields = type.Fields.Where(f => !f.IsStatic);
                var fields    = allFields.Select(field => (field.FieldType.IsDynamicArray() ? $"/* DYNAMIC ARRAY -- {field.Name}; */" : $"{field.FieldType.CppFieldType()} {field.Name};"));

                string hppTemplate =
                    @"struct {{{structName}}} {
  {{{type field;}}}
};";
                var hppCode = ExpandStringTemplate(hppTemplate,
                                                   "structName", type.Name,
                                                   "type field;", fields);

                if (!BindGem.DOTS)
                {
                    // Register Struct
                    var name           = type.Name;
                    var namespacedName = type.JSName();
                    var identifier     = type.FullyQualifiedCppName("_").ToLower();
                    var members        = type.Fields.Select(f => $"  ut::meta::member(\"{f.Name}\", &{name}::{f.Name})");
                    var body           = $"{(members.Count() > 0 ? ",\n" : "")}{string.Join(",\n", members)}";
                    hppCode += ExpandStringTemplate(
                        "\nREFLECT_STRUCT({{{name}}}, \"{{{namespacedName}}}\", {{{identifier}}}{{{body}}})",
                        "name", name,
                        "namespacedName", namespacedName,
                        "identifier", identifier,
                        "body", body);
                }

                hpp(WrapInNamespace(hppCode, type.Namespace));
            }
        }
示例#4
0
        public void HandleCallbackType(TypeDefinition type)
        {
            var cppFnTypeName = type.FullyQualifiedCppName("_");

            if (BindGem.PureJS)
            {
                BindGem.FatalError(
                    "Callback (delegates) cannot be used with pure JS generation (what are you trying to bind anyway?)");
            }

            var fn        = type.DelegateInvokeMethod();
            var fnParams  = fn.Parameters.Select(p => p.ParameterType.Resolve()).ToList();
            var numParams = fn.Parameters.Count;

            BindGem.FatalErrorIf(fn.ReturnType.Resolve().MetadataType != MetadataType.Void, /*type,*/ $"Delegate (callback) {type.Name} returning non-void not supported (yet, fixable!)");

            // for delegates, we have to convert each parameter to its JS representation, similar to what we would do for return types
            // We only handle very simple types here

            exportSymbol($"{type.CppFnPrefix()}_call");
            cpp($"using {type.CppFnPrefix()}_callType = std::function<void({string.Join(",", fnParams.Select(p=>p.EmCppArgType()).ToArray())})>;");
            cpp($"UT_JSFN void {type.CppFnPrefix()}_call(");
            cpp($"  uint32_t self");
            for (int p = 0; p < numParams; ++p)
            {
                cpp($" ,{fnParams[p].EmCppArgType()} arg{p + 1}");
            }
            cpp($") {{");
            cpp($"#ifdef __EMSCRIPTEN__");
            cpp($"  EM_ASM({{");

            var passParams = new StringBuilder();
            var argParams  = new StringBuilder();

            for (int p = 0; p < numParams; ++p)
            {
                string passParam;
                cpp($"    {fnParams[p].JSCallbackArgToEmscripten(p + 1, out passParam)}");
                passParams.Append($", {passParam}");
                argParams.Append($"${p + 1}");
                if (p != numParams - 1)
                {
                    argParams.Append(", ");
                }
            }
            cpp($"    {type.JSName()}._cb.map[$0]({argParams});");
            cpp($"  }}, self{passParams});");
            cpp($"#else");
            cpp($" // TODO: Invoke callback for C#");
            cpp($"#endif");
            cpp($"}}");
        }
示例#5
0
        // Outputs the given block of code wrapped inside nested
        // "namespace A { namespace B { /* code */ } }" format.
        // Use C# period "." to address nested namespaces, instead of C++ "::",
        // i.e. "A.B" instead of "A::B".
        // If the code contains template blocks {{{beginNamespace}}} or {{{endNamespace}}},
        // the namespace is expanded in their place. Otherwise the whole code block
        // is wrapped inside the namespace.
        internal static string WrapInNamespace(string code, string nameSpace)
        {
            if (nameSpace == null || nameSpace.Length == 0)
            {
                code = code.Replace("{{{beginNamespace}}}", "");
                code = code.Replace("{{{endNamespace}}}", "");
                return(code);
            }
            var namespaces = nameSpace.Split('.');

            namespaces[0] = BindGem.TranslateCppTopLevelNamespace(namespaces[0]);
            string pre = "", post = "";

            foreach (var name in namespaces)
            {
                pre  += $"namespace {name} {{ ";
                post += "}";
            }
            if (code.Contains("{{{beginNamespace}}}"))
            {
                code = code.Replace("{{{beginNamespace}}}", pre);
            }
            else
            {
                code = pre + '\n' + code;
            }

            if (code.Contains("{{{endNamespace}}}"))
            {
                code = code.Replace("{{{endNamespace}}}", post);
            }
            else
            {
                code = code + '\n' + post;
            }

            return(code);
        }
示例#6
0
        public void HandleComponentType(TypeDefinition type)
        {
            if (type.HasAttribute("CppCustomImpl"))
            {
                cpp($"/* Skipped generating code for internal component {type} */");
                return;
            }

            BindGem.WarningIf(type.HasAttribute("CppName"),
                              $"Type {type} has CppName but is not CppCustomImpl -- CppName will be ignored for fully generated types");

            // Validate the field names
            if (!BindGem.DOTS)
            {
                foreach (var f in type.Fields)
                {
                    BindGem.FatalErrorIf(!Char.IsLower(f.Name[0]), /*f, TODO*/ $"Field {f.Name} of {type.Name} must start with a lowercase letter!");
                }
            }

            // g++ has a bug where it doesn't actually emit this function unless it's explicitly marked as used.
            // But we must not use this for emscripten, because (used) means export it to JS as well.
            string entityFixupTemplate =
                @"  struct ComponentReflectionData {
    using ReflectionDataType_ = {{{name}}};
    static const std::initializer_list<int>&
    #if defined(__GNUC__) && !defined(__EMSCRIPTEN__) && !defined(__clang__)
    __attribute__((noinline, used))
    #endif
    EntityOffsets() {
      static const std::initializer_list<int> l = {
        {{{entityfixups}}}
      };
      return l;
    }
  };";

            string hppTemplate =
                @"struct {{{name}}} {{{maybeExtends}}}{
  {{{fields}}}
  {{{constructors}}}
  {{{memberFunctions}}}
  {{{entityOffsetDecl}}}
};";

            // Data fields
            var fields = type.Fields.Where(f => !f.IsStatic).Select(field => $"{field.FieldType.CppFieldType()} {field.Name};").ToList();

            string        entityOffsetDecl = null;
            List <string> ctors            = new List <string>();
            List <string> memberFunctions  = new List <string>();

            if (!BindGem.DOTS)
            {
                // Internal entity field offsets, if any
                // Note we are assuming 32-bit platform here for offset calculations
                var entityOffsets = TypeUtils.GetEntityFieldOffsets(type, 32);
                if (entityOffsets.Count != 0)
                {
                    entityOffsetDecl = ExpandStringTemplate(entityFixupTemplate,
                                                            "name", type.Name,
                                                            "entityfixups", string.Join(",", entityOffsets));
                }

                // Constructors
                bool hasZeroArgConstructor = false;
                foreach (var ctor in type.Constructors())
                {
                    //var args = ctor.Parameters.Select(arg => $"{arg.ParameterType.Resolve().EmCppArgType()} {arg.Name}");
                    var args = UnnamedParameterList(ctor.Parameters);
                    if (args.Count() == 0)
                    {
                        hasZeroArgConstructor = true;
                    }
                    ctors.Add($"{type.Name}({string.Join(",", args)});");
                }

                if (ctors.Count > 0 && !hasZeroArgConstructor)
                {
                    ctors.Insert(0, $"{type.Name}() = default;");
                }

                // Member functions
                foreach (var method in type.MemberFunctions())
                {
                    // TODO: I think this is better to keep the argument names when generating, rather than an unnamed list "arg0,arg1", but keep the output
                    // identical for easy verification for now.
                    //                var args = method.Parameters.Select(arg => $"{arg.ParameterType.Resolve().EmCppArgType()} {arg.Name}");
                    var args = UnnamedParameterList(method.Parameters);
                    memberFunctions.Add(
                        $"{type.EmCppReturnType(method)} {method.CppName()}({string.Join(",", args)});");
                }
            }

            var extends = "";
            var ns      = "ut";

            if (BindGem.DOTS)
            {
                ns = "Unity::Entities";
            }

            if (type.IsComplex())
            {
                extends = $": {ns}::IComplexComponentData ";
            }
            else if (type.IsSharedComponentType())
            {
                extends = $": {ns}::ISharedComponentData ";
            }
            else if (type.IsSystemStateComponentType())
            {
                extends = $": {ns}::ISystemStateComponentData ";
            }
            else if (type.IsBufferElementComponentType())
            {
                extends = $": {ns}::IBufferElementData ";
            }

            string hppCode = ExpandStringTemplate(hppTemplate,
                                                  "name", type.Name,
                                                  "maybeExtends", extends,
                                                  "fields", fields,
                                                  "constructors", ctors,
                                                  "memberFunctions", memberFunctions,
                                                  "entityOffsetDecl", entityOffsetDecl);

            if (!BindGem.DOTS)
            {
                // Register Component
                var name           = type.Name;
                var namespacedName = type.JSName();
                var identifier     = type.FullyQualifiedCppName("_").ToLower();
                var members        = type.Fields.Select(f => $"  ut::meta::member(\"{f.Name}\", &{name}::{f.Name})");
                var body           = $"{(members.Count() > 0 ? ",\n" : "")}{string.Join(",\n", members)}";
                hppCode += ExpandStringTemplate(
                    "\nREFLECT_COMPONENT({{{name}}}, \"{{{namespacedName}}}\", {{{identifier}}}{{{body}}})",
                    "name", name,
                    "namespacedName", namespacedName,
                    "identifier", identifier,
                    "body", body);
            }

            hpp(WrapInNamespace(hppCode, type.Namespace));

            // insert static asserts for field offsets in the C++
            cpp(InsertEmscriptenStaticAssertsForStruct(type, true, 32));
            cpp(InsertEmscriptenStaticAssertsForStruct(type, true, 64));

            // Make the component info templated functions extern for compile time speedups
            // Explicitly don't do this for ComponentId<T>, since we want that to get inlined
            // as much as possible
            // extern template ComponentTypeId ComponentId<::{nsn}>();
            // template ComponentTypeId ComponentId<::{nsn}>();

            var nsn         = type.FullyQualifiedCppName();
            var thisprivate = $"priv_{BindGem.BaseOutputName}_{componentIndex}";

            componentIndex++;

            if (!BindGem.DOTS)
            {
                hpp($@"
namespace ut {{
#if !defined({BindGem.DefineGuard})
extern template ComponentInfo& InitComponentInfoFor<::{nsn}>();
#endif
}}
");
            }
            else
            {
                hpp($@"
#if !defined({BindGem.DefineGuard})
    extern DLLIMPORT ComponentTypeId {thisprivate}_cid;
#else
    extern DLLEXPORT ComponentTypeId {thisprivate}_cid;
#endif

template<> inline ComponentTypeId ComponentId<::{nsn}>() {{
    return {thisprivate}_cid;
}}

template<> inline ComponentTypeId InitComponentId<::{nsn}>()
{{
    if ({thisprivate}_cid == -1) {{
        {thisprivate}_cid = Unity::Entities::TypeManager::TypeIndexForStableTypeHash({type.CalculateStableTypeHash()}ull);
    }}
    return {thisprivate}_cid;
}}
");
            }

            if (!BindGem.DOTS)
            {
                cpp($@"
namespace ut {{
template ComponentInfo& InitComponentInfoFor<::{nsn}>();
ComponentTypeId {thisprivate}_cid = -1;
template<> ComponentTypeId InitComponentId<::{nsn}>()
{{
    if ({thisprivate}_cid == -1) {{
        {thisprivate}_cid = InitComponentInfoFor<::{nsn}>().cid;
    }}
    return {thisprivate}_cid;
}}
}}
");
            }
            else
            {
                cpp($@"
DLLEXPORT ComponentTypeId {thisprivate}_cid = -1;
");
            }
        }
示例#7
0
        public void HandleInterfaceType(TypeDefinition type)
        {
            if (type.HasAttribute("PureJSService"))
            {
                return;
            }

            BindGem.FatalErrorIf(BindGem.PureJS,
                                 "Interfaces cannot be used with pure JS generation (what are you trying to bind anyway?)");

            var jsTypeName    = type.JSName();
            var cppTypeName   = type.FullyQualifiedCppName();
            var cppFnTypeName = type.CppFnPrefix();

            // C# interface with [SharedPtr]
            bool isSharedPtr    = type.IsSharedPtrType();
            bool isNonSharedPtr = type.IsNonSharedPtrType();
            // C# interface with [Service]
            bool isService = type.IsServiceRefType();
            // C# struct
            bool isStruct = type.IsStructValueType();

            //
            // Constructors and preamble
            //
            // At some point in the future we may support multiple constructors and constructors with arguments,
            // and some way to specify them.
            // For now, we have a Constructable attribute that enables a single, no-argument constructor.
            bool isConstructable = type.HasAttribute("Constructable");

            if (isConstructable)
            {
                dts($"function _{type.CppFnPrefix()}_{type.Name}(): number;");

                exportSymbol($"{type.CppFnPrefix()}_{type.Name}");
                cpp($"UT_JSFN {cppTypeName}* {type.CppFnPrefix()}_{type.Name}() {{");
                if (isSharedPtr)
                {
                    cpp($"  return ut::PtrTable::persist<{cppTypeName}>(std::make_shared<{cppTypeName}>());");
                }
                else
                {
                    cpp($"  return new {cppTypeName}();");
                }
                cpp($"}}");
            }

            if (isSharedPtr || isNonSharedPtr)
            {
                string releaseTemplate;
                exportSymbol($"{cppFnTypeName}_shRelease");
                if (isSharedPtr)
                {
                    releaseTemplate =
                        @"
UT_JSFN void {{{cppFnTypeName}}}_shRelease({{{cppTypeName}}}* ptr) {
  ut::PtrTable::release<{{{cppTypeName}}}>(ptr);
}
";
                }
                else
                {
                    releaseTemplate =
                        @"UT_JSFN void {{{cppFnTypeName}}}_shRelease({{{cppTypeName}}}* ptr) {
  delete ptr;
}";
                }

                dts($"function _{type.CppFnPrefix()}_shRelease(self: number): void;");

                var releaseFnString = CppBindingsGenerator.ExpandStringTemplate(releaseTemplate,
                                                                                "cppTypeName", cppTypeName,
                                                                                "cppFnTypeName", cppFnTypeName);
                cpp(releaseFnString);
            }

            HandleTypeMethods(type);
        }