private void OnGenerateCppScriptWrapperFunction(Builder.BuildData buildData, ClassInfo classInfo, FunctionInfo functionInfo, int scriptVTableSize, int scriptVTableIndex, StringBuilder contents)
        {
            // Generate C++ wrapper function to invoke Visual Script instead of overriden native function (with support for base method callback)

            BindingsGenerator.CppIncludeFiles.Add("Engine/Content/Assets/VisualScript.h");

            contents.AppendFormat("    {0} {1}_VisualScriptWrapper(", functionInfo.ReturnType, functionInfo.UniqueName);
            var separator = false;

            for (var i = 0; i < functionInfo.Parameters.Count; i++)
            {
                var parameterInfo = functionInfo.Parameters[i];
                if (separator)
                {
                    contents.Append(", ");
                }
                separator = true;

                contents.Append(parameterInfo.Type);
                contents.Append(' ');
                contents.Append(parameterInfo.Name);
            }

            contents.Append(')');
            contents.AppendLine();
            contents.AppendLine("    {");
            contents.AppendLine($"        auto object = ({classInfo.NativeName}*)this;");
            contents.AppendLine("        static THREADLOCAL bool IsDuringWrapperCall = false;");
            contents.AppendLine("        if (IsDuringWrapperCall)");
            contents.AppendLine("        {");
            contents.AppendLine("            // Prevent stack overflow by calling base method");
            contents.AppendLine("            const auto scriptVTableBase = object->GetType().Class.ScriptVTableBase;");
            contents.Append($"            return (object->**({functionInfo.UniqueName}_Signature*)&scriptVTableBase[{scriptVTableIndex} + 2])(");
            separator = false;
            for (var i = 0; i < functionInfo.Parameters.Count; i++)
            {
                var parameterInfo = functionInfo.Parameters[i];
                if (separator)
                {
                    contents.Append(", ");
                }
                separator = true;
                contents.Append(parameterInfo.Name);
            }
            contents.AppendLine(");");
            contents.AppendLine("        }");
            contents.AppendLine("        auto scriptVTable = (VisualScript::Method**)object->GetType().Class.ScriptVTable;");
            contents.AppendLine($"        ASSERT(scriptVTable && scriptVTable[{scriptVTableIndex}]);");

            if (functionInfo.Parameters.Count != 0)
            {
                contents.AppendLine($"        Variant parameters[{functionInfo.Parameters.Count}];");
                for (var i = 0; i < functionInfo.Parameters.Count; i++)
                {
                    var parameterInfo = functionInfo.Parameters[i];
                    contents.AppendLine($"        parameters[{i}] = {BindingsGenerator.GenerateCppWrapperNativeToVariant(buildData, parameterInfo.Type, classInfo, parameterInfo.Name)};");
                }
            }
            else
            {
                contents.AppendLine("        Variant* parameters = nullptr;");
            }

            contents.AppendLine("        IsDuringWrapperCall = true;");
            contents.AppendLine($"        auto __result = VisualScripting::Invoke(scriptVTable[{scriptVTableIndex}], object, Span<Variant>(parameters, {functionInfo.Parameters.Count}));");
            contents.AppendLine("        IsDuringWrapperCall = false;");

            if (!functionInfo.ReturnType.IsVoid)
            {
                contents.AppendLine($"        return {BindingsGenerator.GenerateCppWrapperVariantToNative(buildData, functionInfo.ReturnType, classInfo, "__result")};");
            }

            contents.AppendLine("    }");
            contents.AppendLine();
        }
Example #2
0
        private void OnGenerateCppScriptWrapperFunction(Builder.BuildData buildData, VirtualClassInfo classInfo, FunctionInfo functionInfo, int scriptVTableSize, int scriptVTableIndex, StringBuilder contents)
        {
            // Generate C++ wrapper function to invoke Visual Script instead of overridden native function (with support for base method callback)

            BindingsGenerator.CppIncludeFiles.Add("Engine/Content/Assets/VisualScript.h");

            contents.AppendFormat("    {0} {1}_VisualScriptWrapper(", functionInfo.ReturnType, functionInfo.UniqueName);
            var separator = false;

            for (var i = 0; i < functionInfo.Parameters.Count; i++)
            {
                var parameterInfo = functionInfo.Parameters[i];
                if (separator)
                {
                    contents.Append(", ");
                }
                separator = true;

                contents.Append(parameterInfo.Type);
                contents.Append(' ');
                contents.Append(parameterInfo.Name);
            }

            contents.Append(')');
            contents.AppendLine();
            contents.AppendLine("    {");
            string scriptVTableOffset;

            if (classInfo.IsInterface)
            {
                contents.AppendLine($"        auto object = ScriptingObject::FromInterface(this, {classInfo.NativeName}::TypeInitializer);");
                contents.AppendLine("        if (object == nullptr)");
                contents.AppendLine("        {");
                contents.AppendLine($"            LOG(Error, \"Failed to cast interface {{0}} to scripting object\", TEXT(\"{classInfo.Name}\"));");
                BindingsGenerator.GenerateCppReturn(buildData, contents, "            ", functionInfo.ReturnType);
                contents.AppendLine("        }");
                contents.AppendLine($"        const int32 scriptVTableOffset = {scriptVTableIndex} + object->GetType().GetInterface({classInfo.NativeName}::TypeInitializer)->ScriptVTableOffset;");
                scriptVTableOffset = "scriptVTableOffset";
            }
            else
            {
                contents.AppendLine($"        auto object = ({classInfo.NativeName}*)this;");
                scriptVTableOffset = scriptVTableIndex.ToString();
            }
            contents.AppendLine("        static THREADLOCAL void* WrapperCallInstance = nullptr;");
            contents.AppendLine("        if (WrapperCallInstance == object)");
            contents.AppendLine("        {");
            contents.AppendLine("            // Prevent stack overflow by calling base method");
            contents.AppendLine("            const auto scriptVTableBase = object->GetType().Script.ScriptVTableBase;");
            contents.Append($"            return (this->**({functionInfo.UniqueName}_Internal_Signature*)&scriptVTableBase[{scriptVTableOffset} + 2])(");
            separator = false;
            for (var i = 0; i < functionInfo.Parameters.Count; i++)
            {
                var parameterInfo = functionInfo.Parameters[i];
                if (separator)
                {
                    contents.Append(", ");
                }
                separator = true;
                contents.Append(parameterInfo.Name);
            }
            contents.AppendLine(");");
            contents.AppendLine("        }");
            contents.AppendLine("        auto scriptVTable = (VisualScript::Method**)object->GetType().Script.ScriptVTable;");
            contents.AppendLine($"        ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);");

            if (functionInfo.Parameters.Count != 0)
            {
                contents.AppendLine($"        Variant parameters[{functionInfo.Parameters.Count}];");
                for (var i = 0; i < functionInfo.Parameters.Count; i++)
                {
                    var parameterInfo = functionInfo.Parameters[i];
                    contents.AppendLine($"        parameters[{i}] = {BindingsGenerator.GenerateCppWrapperNativeToVariant(buildData, parameterInfo.Type, classInfo, parameterInfo.Name)};");
                }
            }
            else
            {
                contents.AppendLine("        Variant* parameters = nullptr;");
            }

            contents.AppendLine("        auto prevWrapperCallInstance = WrapperCallInstance;");
            contents.AppendLine("        WrapperCallInstance = object;");
            contents.AppendLine($"        auto __result = VisualScripting::Invoke(scriptVTable[{scriptVTableOffset}], object, Span<Variant>(parameters, {functionInfo.Parameters.Count}));");
            contents.AppendLine("        WrapperCallInstance = prevWrapperCallInstance;");

            if (!functionInfo.ReturnType.IsVoid)
            {
                contents.AppendLine($"        return {BindingsGenerator.GenerateCppWrapperVariantToNative(buildData, functionInfo.ReturnType, classInfo, "__result")};");
            }

            contents.AppendLine("    }");
            contents.AppendLine();
        }
Example #3
0
        public static bool MakeApiAssembly(ApiAssemblyType apiType, string config)
        {
            string apiName = apiType == ApiAssemblyType.Core ? ApiAssemblyNames.Core : ApiAssemblyNames.Editor;

            string editorPrebuiltApiDir = Path.Combine(GodotSharpDirs.DataEditorPrebuiltApiDir, config);
            string resAssembliesDir     = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, config);

            if (File.Exists(Path.Combine(editorPrebuiltApiDir, $"{apiName}.dll")))
            {
                using (var copyProgress = new EditorProgress("mono_copy_prebuilt_api_assembly", $"Copying prebuilt {apiName} assembly...", 1))
                {
                    copyProgress.Step($"Copying {apiName} assembly", 0);
                    return(CopyApiAssembly(editorPrebuiltApiDir, resAssembliesDir, apiName, apiType));
                }
            }

            const string apiSolutionName = ApiAssemblyNames.SolutionName;

            using (var pr = new EditorProgress($"mono_build_release_{apiSolutionName}", $"Building {apiSolutionName} solution...", 3))
            {
                pr.Step($"Generating {apiSolutionName} solution", 0);

                string apiSlnDir  = Path.Combine(GodotSharpDirs.MonoSolutionsDir, _ApiFolderName(ApiAssemblyType.Core));
                string apiSlnFile = Path.Combine(apiSlnDir, $"{apiSolutionName}.sln");

                if (!Directory.Exists(apiSlnDir) || !File.Exists(apiSlnFile))
                {
                    var bindingsGenerator = new BindingsGenerator();

                    if (!Godot.OS.IsStdoutVerbose())
                    {
                        bindingsGenerator.LogPrintEnabled = false;
                    }

                    Error err = bindingsGenerator.GenerateCsApi(apiSlnDir);
                    if (err != Error.Ok)
                    {
                        ShowBuildErrorDialog($"Failed to generate {apiSolutionName} solution. Error: {err}");
                        return(false);
                    }
                }

                pr.Step($"Building {apiSolutionName} solution", 1);

                if (!BuildApiSolution(apiSlnDir, config))
                {
                    return(false);
                }

                pr.Step($"Copying {apiName} assembly", 2);

                // Copy the built assembly to the assemblies directory
                string apiAssemblyDir = Path.Combine(apiSlnDir, apiName, "bin", config);
                if (!CopyApiAssembly(apiAssemblyDir, resAssembliesDir, apiName, apiType))
                {
                    return(false);
                }
            }

            return(true);
        }