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(); }
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(); }
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); }