Add support for basic classes to Scripting Type (without scripting object as a base)

This commit is contained in:
Wojtek Figat
2020-12-22 12:55:57 +01:00
parent f25064fae8
commit 4665e8fbdb
8 changed files with 150 additions and 110 deletions

View File

@@ -60,16 +60,16 @@ ScriptingType::ScriptingType()
, Module(nullptr)
, InitRuntime(nullptr)
, Fullname(nullptr, 0)
, Type(ScriptingTypes::Class)
, Type(ScriptingTypes::Script)
, BaseTypePtr(nullptr)
{
Class.Spawn = nullptr;
Class.VTable = nullptr;
Class.ScriptVTable = nullptr;
Class.ScriptVTableBase = nullptr;
Class.SetupScriptVTable = nullptr;
Class.SetupScriptObjectVTable = nullptr;
Class.DefaultInstance = nullptr;
Script.Spawn = nullptr;
Script.VTable = nullptr;
Script.ScriptVTable = nullptr;
Script.ScriptVTableBase = nullptr;
Script.SetupScriptVTable = nullptr;
Script.SetupScriptObjectVTable = nullptr;
Script.DefaultInstance = nullptr;
}
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, const ScriptingTypeHandle& baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable)
@@ -77,21 +77,39 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
, Module(module)
, InitRuntime(initRuntime)
, Fullname(fullname)
, Type(ScriptingTypes::Class)
, Type(ScriptingTypes::Script)
, BaseTypeHandle(baseType)
, BaseTypePtr(nullptr)
, Size(size)
{
Class.Spawn = spawn;
Class.VTable = nullptr;
Class.ScriptVTable = nullptr;
Class.ScriptVTableBase = nullptr;
Class.SetupScriptVTable = setupScriptVTable;
Class.SetupScriptObjectVTable = setupScriptObjectVTable;
Class.DefaultInstance = nullptr;
Script.Spawn = spawn;
Script.VTable = nullptr;
Script.ScriptVTable = nullptr;
Script.ScriptVTableBase = nullptr;
Script.SetupScriptVTable = setupScriptVTable;
Script.SetupScriptObjectVTable = setupScriptObjectVTable;
Script.DefaultInstance = nullptr;
}
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, ScriptingTypeInitializer* baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable)
: ManagedClass(nullptr)
, Module(module)
, InitRuntime(initRuntime)
, Fullname(fullname)
, Type(ScriptingTypes::Script)
, BaseTypePtr(baseType)
, Size(size)
{
Script.Spawn = spawn;
Script.VTable = nullptr;
Script.ScriptVTable = nullptr;
Script.ScriptVTableBase = nullptr;
Script.SetupScriptVTable = setupScriptVTable;
Script.SetupScriptObjectVTable = setupScriptObjectVTable;
Script.DefaultInstance = nullptr;
}
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType)
: ManagedClass(nullptr)
, Module(module)
, InitRuntime(initRuntime)
@@ -100,13 +118,8 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul
, BaseTypePtr(baseType)
, Size(size)
{
Class.Spawn = spawn;
Class.VTable = nullptr;
Class.ScriptVTable = nullptr;
Class.ScriptVTableBase = nullptr;
Class.SetupScriptVTable = setupScriptVTable;
Class.SetupScriptObjectVTable = setupScriptObjectVTable;
Class.DefaultInstance = nullptr;
Class.Ctor = ctor;
Class.Dtor = dtor;
}
ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType)
@@ -139,14 +152,14 @@ ScriptingType::ScriptingType(const ScriptingType& other)
{
switch (other.Type)
{
case ScriptingTypes::Class:
Class.Spawn = other.Class.Spawn;
Class.VTable = nullptr;
Class.ScriptVTable = nullptr;
Class.ScriptVTableBase = nullptr;
Class.SetupScriptVTable = other.Class.SetupScriptVTable;
Class.SetupScriptObjectVTable = other.Class.SetupScriptObjectVTable;
Class.DefaultInstance = nullptr;
case ScriptingTypes::Script:
Script.Spawn = other.Script.Spawn;
Script.VTable = nullptr;
Script.ScriptVTable = nullptr;
Script.ScriptVTableBase = nullptr;
Script.SetupScriptVTable = other.Script.SetupScriptVTable;
Script.SetupScriptObjectVTable = other.Script.SetupScriptObjectVTable;
Script.DefaultInstance = nullptr;
break;
case ScriptingTypes::Structure:
Struct.Ctor = other.Struct.Ctor;
@@ -175,18 +188,18 @@ ScriptingType::ScriptingType(ScriptingType&& other)
{
switch (other.Type)
{
case ScriptingTypes::Class:
Class.Spawn = other.Class.Spawn;
Class.VTable = other.Class.VTable;
other.Class.VTable = nullptr;
Class.ScriptVTable = other.Class.ScriptVTable;
other.Class.ScriptVTable = nullptr;
Class.ScriptVTableBase = other.Class.ScriptVTableBase;
other.Class.ScriptVTableBase = nullptr;
Class.SetupScriptVTable = other.Class.SetupScriptVTable;
Class.SetupScriptObjectVTable = other.Class.SetupScriptObjectVTable;
Class.DefaultInstance = other.Class.DefaultInstance;
other.Class.DefaultInstance = nullptr;
case ScriptingTypes::Script:
Script.Spawn = other.Script.Spawn;
Script.VTable = other.Script.VTable;
other.Script.VTable = nullptr;
Script.ScriptVTable = other.Script.ScriptVTable;
other.Script.ScriptVTable = nullptr;
Script.ScriptVTableBase = other.Script.ScriptVTableBase;
other.Script.ScriptVTableBase = nullptr;
Script.SetupScriptVTable = other.Script.SetupScriptVTable;
Script.SetupScriptObjectVTable = other.Script.SetupScriptObjectVTable;
Script.DefaultInstance = other.Script.DefaultInstance;
other.Script.DefaultInstance = nullptr;
break;
case ScriptingTypes::Structure:
Struct.Ctor = other.Struct.Ctor;
@@ -207,13 +220,13 @@ ScriptingType::~ScriptingType()
{
switch (Type)
{
case ScriptingTypes::Class:
if (Class.DefaultInstance)
Delete(Class.DefaultInstance);
if (Class.VTable)
Platform::Free((byte*)Class.VTable - GetVTablePrefix());
Platform::Free(Class.ScriptVTable);
Platform::Free(Class.ScriptVTableBase);
case ScriptingTypes::Script:
if (Script.DefaultInstance)
Delete(Script.DefaultInstance);
if (Script.VTable)
Platform::Free((byte*)Script.VTable - GetVTablePrefix());
Platform::Free(Script.ScriptVTable);
Platform::Free(Script.ScriptVTableBase);
break;
case ScriptingTypes::Structure:
break;
@@ -235,17 +248,17 @@ ScriptingTypeHandle ScriptingType::GetHandle() const
ScriptingObject* ScriptingType::GetDefaultInstance() const
{
ASSERT(Type == ScriptingTypes::Class);
if (!Class.DefaultInstance)
ASSERT(Type == ScriptingTypes::Script);
if (!Script.DefaultInstance)
{
const ScriptingObjectSpawnParams params(Guid::New(), GetHandle());
Class.DefaultInstance = Class.Spawn(params);
if (!Class.DefaultInstance)
Script.DefaultInstance = Script.Spawn(params);
if (!Script.DefaultInstance)
{
LOG(Error, "Failed to create default instance of type {0}", ToString());
}
}
return Class.DefaultInstance;
return Script.DefaultInstance;
}
String ScriptingType::ToString() const
@@ -268,6 +281,21 @@ ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const S
module->TypeNameToTypeIndex[typeName] = TypeIndex;
}
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType)
: ScriptingTypeHandle(module, module->Types.Count())
{
module->Types.AddUninitialized();
new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, baseType);
const MString typeName(fullname.Get(), fullname.Length());
#if BUILD_DEBUG
if (module->TypeNameToTypeIndex.ContainsKey(typeName))
{
LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName()));
}
#endif
module->TypeNameToTypeIndex[typeName] = TypeIndex;
}
ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType)
: ScriptingTypeHandle(module, module->Types.Count())
{
@@ -368,7 +396,7 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp
// Create native object
ScriptingTypeHandle managedTypeHandle = params.Type;
const ScriptingType* managedTypePtr = &managedTypeHandle.GetType();
while (managedTypePtr->Class.Spawn != &ManagedObjectSpawn)
while (managedTypePtr->Script.Spawn != &ManagedObjectSpawn)
{
managedTypeHandle = managedTypePtr->GetBaseType();
managedTypePtr = &managedTypeHandle.GetType();
@@ -376,12 +404,12 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp
ScriptingType& managedType = (ScriptingType&)*managedTypePtr;
ScriptingTypeHandle nativeTypeHandle = managedType.GetBaseType();
const ScriptingType* nativeTypePtr = &nativeTypeHandle.GetType();
while (nativeTypePtr->Class.Spawn == &ManagedObjectSpawn)
while (nativeTypePtr->Script.Spawn == &ManagedObjectSpawn)
{
nativeTypeHandle = nativeTypePtr->GetBaseType();
nativeTypePtr = &nativeTypeHandle.GetType();
}
ScriptingObject* object = nativeTypePtr->Class.Spawn(params);
ScriptingObject* object = nativeTypePtr->Script.Spawn(params);
if (!object)
{
return nullptr;
@@ -392,9 +420,9 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp
// We create a custom vtable for the C# objects that use a native class object with virtual functions overrides.
// To make it easy to use in C++ we inject custom wrapper methods into C++ object vtable to call C# code from them.
// Because virtual member functions calls are C++ ABI and impl-defined this is quite hard. But works.
if (managedType.Class.ScriptVTable)
if (managedType.Script.ScriptVTable)
{
if (!managedType.Class.VTable)
if (!managedType.Script.VTable)
{
// Duplicate vtable
void** vtable = *(void***)object;
@@ -403,23 +431,23 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp
while (vtable[entriesCount] && entriesCount < 200)
entriesCount++;
const int32 size = entriesCount * sizeof(void*);
managedType.Class.VTable = (void**)((byte*)Platform::Allocate(prefixSize + size, 16) + prefixSize);
Platform::MemoryCopy((byte*)managedType.Class.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size);
managedType.Script.VTable = (void**)((byte*)Platform::Allocate(prefixSize + size, 16) + prefixSize);
Platform::MemoryCopy((byte*)managedType.Script.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size);
// Override vtable entries by the class
for (ScriptingTypeHandle e = nativeTypeHandle; e;)
{
const ScriptingType& eType = e.GetType();
if (eType.Class.SetupScriptObjectVTable)
if (eType.Script.SetupScriptObjectVTable)
{
eType.Class.SetupScriptObjectVTable(managedType.Class.ScriptVTable, managedType.Class.ScriptVTableBase, managedType.Class.VTable, entriesCount, 0);
eType.Script.SetupScriptObjectVTable(managedType.Script.ScriptVTable, managedType.Script.ScriptVTableBase, managedType.Script.VTable, entriesCount, 0);
}
e = eType.GetBaseType();
}
}
// Override object vtable with hacked one that has C# functions calls
*(void**)object = managedType.Class.VTable;
*(void**)object = managedType.Script.VTable;
}
// Mark as managed type
@@ -557,7 +585,7 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly)
if (baseClassModule->TypeNameToTypeIndex.TryGet(baseClass->GetFullName(), typeIndex))
{
nativeType = ScriptingTypeHandle(baseClassModule, typeIndex);
if (nativeType.GetType().Class.Spawn != &ManagedObjectSpawn)
if (nativeType.GetType().Script.Spawn != &ManagedObjectSpawn)
break;
}
baseClass = baseClass->GetBaseClass();
@@ -592,14 +620,14 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly)
for (ScriptingTypeHandle e = nativeType; e;)
{
const ScriptingType& eType = e.GetType();
if (eType.Class.SetupScriptVTable)
if (eType.Script.SetupScriptVTable)
{
ASSERT(eType.ManagedClass);
eType.Class.SetupScriptVTable(eType.ManagedClass, type.Class.ScriptVTable, type.Class.ScriptVTableBase);
eType.Script.SetupScriptVTable(eType.ManagedClass, type.Script.ScriptVTable, type.Script.ScriptVTableBase);
}
e = eType.GetBaseType();
}
MMethod** scriptVTable = (MMethod**)type.Class.ScriptVTable;
MMethod** scriptVTable = (MMethod**)type.Script.ScriptVTable;
while (scriptVTable && *scriptVTable)
{
const MMethod* referenceMethod = *scriptVTable;
@@ -657,10 +685,10 @@ void ManagedBinaryModule::OnUnloading(MAssembly* assembly)
for (ScriptingType& type : Types)
{
type.ManagedClass = nullptr;
if (type.Type == ScriptingTypes::Class && type.Class.ScriptVTable)
if (type.Type == ScriptingTypes::Script && type.Script.ScriptVTable)
{
Platform::Free(type.Class.ScriptVTable);
type.Class.ScriptVTable = nullptr;
Platform::Free(type.Script.ScriptVTable);
type.Script.ScriptVTable = nullptr;
}
}
ClassToTypeIndex.Clear();