diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h
index d2cd50afd..3790db4eb 100644
--- a/Source/Engine/Core/Collections/Array.h
+++ b/Source/Engine/Core/Collections/Array.h
@@ -144,11 +144,11 @@ public:
/// The reference to this.
Array& operator=(std::initializer_list initList) noexcept
{
- Memory::DestructItems(_allocation.Get(), _count);
- _count = _capacity = (int32)initList.size();
- if (_capacity > 0)
+ Clear();
+ if (initList.size() > 0)
{
- _allocation.Allocate(_capacity);
+ EnsureCapacity((int32)initList.size());
+ _count = (int32)initList.size();
Memory::ConstructItems(_allocation.Get(), initList.begin(), _count);
}
return *this;
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
index e4f8eb2aa..a05043485 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
@@ -239,6 +239,16 @@ namespace Flax.Build.Bindings
// Find across in-build types
if (InBuildTypes.TryGetValue(typeInfo, out result))
return result;
+ if (typeInfo.IsRef)
+ {
+ typeInfo.IsRef = false;
+ if (InBuildTypes.TryGetValue(typeInfo, out result))
+ {
+ typeInfo.IsRef = true;
+ return result;
+ }
+ typeInfo.IsRef = true;
+ }
// Find across all loaded modules for this build
foreach (var e in buildData.ModulesInfo)
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
index 2599aae28..ddf3a0f18 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
@@ -99,7 +99,7 @@ namespace Flax.Build.Bindings
return sb.ToString();
}
- private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller, bool isOut, out bool useLocalVar)
+ private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller, bool isRef, out bool useLocalVar)
{
useLocalVar = false;
var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null);
@@ -107,7 +107,7 @@ namespace Flax.Build.Bindings
if (!string.IsNullOrEmpty(nativeToManaged))
{
result = string.Format(nativeToManaged, paramName);
- if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' && !isOut)
+ if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' && !isRef)
{
// Pass pointer value
}
@@ -124,7 +124,7 @@ namespace Flax.Build.Bindings
else
{
result = paramName;
- if (paramType.IsRef && !paramType.IsConst && !isOut)
+ if (paramType.IsRef && !paramType.IsConst && !isRef)
{
// Pass reference as a pointer
result = '&' + result;
@@ -814,10 +814,10 @@ namespace Flax.Build.Bindings
}
}
- private static string GenerateCppWrapperNativeToBox(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, string value)
+ private static string GenerateCppWrapperNativeToBox(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, out ApiTypeInfo apiType, string value)
{
// Optimize passing scripting objects
- var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
+ apiType = FindApiTypeInfo(buildData, typeInfo, caller);
if (apiType != null && apiType.IsScriptingObject)
return $"ScriptingObject::ToManaged((ScriptingObject*){value})";
@@ -1208,6 +1208,8 @@ namespace Flax.Build.Bindings
contents.Append(')');
contents.AppendLine();
contents.AppendLine(" {");
+
+ // Get object
string scriptVTableOffset;
if (classInfo.IsInterface)
{
@@ -1227,6 +1229,7 @@ namespace Flax.Build.Bindings
}
contents.AppendLine(" static THREADLOCAL void* WrapperCallInstance = nullptr;");
+ // Base method call
contents.AppendLine(" ScriptingTypeHandle managedTypeHandle = object->GetTypeHandle();");
contents.AppendLine(" const ScriptingType* managedTypePtr = &managedTypeHandle.GetType();");
contents.AppendLine(" while (managedTypePtr->Script.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)");
@@ -1234,53 +1237,80 @@ namespace Flax.Build.Bindings
contents.AppendLine(" managedTypeHandle = managedTypePtr->GetBaseType();");
contents.AppendLine(" managedTypePtr = &managedTypeHandle.GetType();");
contents.AppendLine(" }");
-
contents.AppendLine(" if (WrapperCallInstance == object)");
contents.AppendLine(" {");
GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "managedTypePtr->Script.ScriptVTableBase", scriptVTableOffset);
contents.AppendLine(" }");
+
contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;");
contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);");
contents.AppendLine($" auto method = scriptVTable[{scriptVTableOffset}];");
- contents.AppendLine($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::{functionInfo.Name}\");");
- contents.AppendLine(" MonoObject* exception = nullptr;");
+ contents.AppendLine($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::{functionInfo.Name}\");");
+
+ contents.AppendLine(" MonoObject* exception = nullptr;");
contents.AppendLine(" auto prevWrapperCallInstance = WrapperCallInstance;");
contents.AppendLine(" WrapperCallInstance = object;");
- contents.AppendLine("#if USE_MONO_AOT");
if (functionInfo.Parameters.Count == 0)
contents.AppendLine(" void** params = nullptr;");
else
contents.AppendLine($" void* params[{functionInfo.Parameters.Count}];");
- for (var i = 0; i < functionInfo.Parameters.Count; i++)
+ contents.AppendLine("#if USE_MONO_AOT");
+
+ // Convert parameters into managed format as pointers to value
+ if (functionInfo.Parameters.Count != 0)
{
- var parameterInfo = functionInfo.Parameters[i];
- var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, parameterInfo.IsOut, out _);
- contents.Append($" params[{i}] = {paramValue};").AppendLine();
+ for (var i = 0; i < functionInfo.Parameters.Count; i++)
+ {
+ var parameterInfo = functionInfo.Parameters[i];
+ var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
+ var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, paramIsRef, out CppParamsThatNeedLocalVariable[i]);
+ contents.Append($" params[{i}] = {paramValue};").AppendLine();
+ }
}
+ // Invoke method
contents.AppendLine(" auto __result = mono_runtime_invoke(method->GetNative(), object->GetOrCreateManagedInstance(), params, &exception);");
+
contents.AppendLine("#else");
+ // Convert parameters into managed format as boxed values
var thunkParams = string.Empty;
var thunkCall = string.Empty;
- separator = functionInfo.Parameters.Count != 0;
- for (var i = 0; i < functionInfo.Parameters.Count; i++)
+ if (functionInfo.Parameters.Count != 0)
{
- var parameterInfo = functionInfo.Parameters[i];
- if (separator)
- thunkParams += ", ";
- if (separator)
- thunkCall += ", ";
- separator = true;
- thunkParams += "void*";
+ separator = functionInfo.Parameters.Count != 0;
+ for (var i = 0; i < functionInfo.Parameters.Count; i++)
+ {
+ var parameterInfo = functionInfo.Parameters[i];
+ var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
+ var paramValue = GenerateCppWrapperNativeToBox(buildData, parameterInfo.Type, classInfo, out var apiType, parameterInfo.Name);
+ var useLocalVar = false;
+ if (paramIsRef)
+ {
+ // Pass as pointer to value when using ref/out parameter
+ contents.Append($" auto __param_{parameterInfo.Name} = {paramValue};").AppendLine();
+ var useLocalVarPointer = !apiType.IsValueType;
+ paramValue = $"{(useLocalVarPointer ? "&" : "")}__param_{parameterInfo.Name}";
+ CppParamsThatNeedConversion[i] = useLocalVarPointer;
+ useLocalVar = true;
+ }
+ CppParamsThatNeedLocalVariable[i] = useLocalVar;
- // Mono thunk call uses boxed values as objects
- thunkCall += GenerateCppWrapperNativeToBox(buildData, parameterInfo.Type, classInfo, parameterInfo.Name);
+ if (separator)
+ thunkParams += ", ";
+ if (separator)
+ thunkCall += ", ";
+ separator = true;
+ thunkParams += "void*";
+ contents.Append($" params[{i}] = {paramValue};").AppendLine();
+ thunkCall += $"params[{i}]";
+ }
}
+ // Invoke method thunk
var returnType = functionInfo.ReturnType;
if (returnType.IsVoid)
{
@@ -1300,11 +1330,66 @@ namespace Flax.Build.Bindings
contents.AppendLine(" if (exception)");
contents.AppendLine(" DebugLog::LogException(exception);");
+ // Convert parameter values back from managed to native (could be modified there)
+ bool anyRefOut = false;
+ for (var i = 0; i < functionInfo.Parameters.Count; i++)
+ {
+ var parameterInfo = functionInfo.Parameters[i];
+ var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
+ if (paramIsRef && !parameterInfo.Type.IsConst)
+ {
+ if (!anyRefOut)
+ {
+ anyRefOut = true;
+ contents.AppendLine("#if USE_MONO_AOT");
+ }
+
+ // Direct value convert
+ var managedToNative = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, classInfo, out var managedType, out var apiType, null, out _);
+ var passAsParamPtr = managedType.EndsWith("*");
+ var useLocalVarPointer = CppParamsThatNeedConversion[i] && !apiType.IsValueType;
+ var paramValue = useLocalVarPointer ? $"*({managedType}{(passAsParamPtr ? "" : "*")}*)params[{i}]" : $"({managedType}{(passAsParamPtr ? "" : "*")})params[{i}]";
+ if (!string.IsNullOrEmpty(managedToNative))
+ {
+ if (!passAsParamPtr)
+ paramValue = '*' + paramValue;
+ paramValue = string.Format(managedToNative, paramValue);
+ }
+ else if (!passAsParamPtr)
+ paramValue = '*' + paramValue;
+ contents.Append($" {parameterInfo.Name} = {paramValue};").AppendLine();
+ }
+ }
+ if (anyRefOut)
+ {
+ contents.AppendLine("#else");
+ for (var i = 0; i < functionInfo.Parameters.Count; i++)
+ {
+ var parameterInfo = functionInfo.Parameters[i];
+ var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
+ if (paramIsRef && !parameterInfo.Type.IsConst)
+ {
+ // Unbox from MonoObject*
+ parameterInfo.Type.IsRef = false;
+ var useLocalVarPointer = CppParamsThatNeedConversion[i];
+ var boxedValueCast = useLocalVarPointer ? "*(MonoObject**)" : "(MonoObject*)";
+ contents.Append($" {parameterInfo.Name} = MUtils::Unbox<{parameterInfo.Type}>({boxedValueCast}params[{i}]);").AppendLine();
+ parameterInfo.Type.IsRef = true;
+ }
+ }
+ contents.AppendLine("#endif");
+ }
+
+ // Unbox returned value
if (!returnType.IsVoid)
{
if (returnType.IsRef)
throw new NotSupportedException($"Passing return value by reference is not supported for virtual API methods. Used on method '{functionInfo}'.");
+ // mono_runtime_invoke always returns boxed value as MonoObject*, but thunk might return value within pointer (eg. as int or boolean)
+ contents.AppendLine("#if USE_MONO_AOT");
+ contents.AppendLine($" return MUtils::Unbox<{returnType}>(__result);");
+ contents.AppendLine("#else");
switch (returnType.Type)
{
case "bool":
@@ -1326,6 +1411,7 @@ namespace Flax.Build.Bindings
contents.AppendLine($" return MUtils::Unbox<{returnType}>(__result);");
break;
}
+ contents.AppendLine("#endif");
}
contents.AppendLine(" }");
@@ -1649,8 +1735,8 @@ namespace Flax.Build.Bindings
{
var paramType = eventInfo.Type.GenericArgs[i];
var paramName = "arg" + i;
- var paramIsOut = paramType.IsRef && !paramType.IsConst;
- var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, paramIsOut, out CppParamsThatNeedConversion[i]);
+ var paramIsRef = paramType.IsRef && !paramType.IsConst;
+ var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, paramIsRef, out CppParamsThatNeedConversion[i]);
contents.Append($" params[{i}] = {paramValue};").AppendLine();
}
if (eventInfo.IsStatic)
@@ -1663,8 +1749,8 @@ namespace Flax.Build.Bindings
for (var i = 0; i < paramsCount; i++)
{
var paramType = eventInfo.Type.GenericArgs[i];
- var paramIsOut = paramType.IsRef && !paramType.IsConst;
- if (paramIsOut)
+ var paramIsRef = paramType.IsRef && !paramType.IsConst;
+ if (paramIsRef)
{
// Convert value back from managed to native (could be modified there)
paramType.IsRef = false;