Skip to content

Commit

Permalink
Merge branch 'master' into docs-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
cameronos committed Apr 15, 2024
2 parents 773fd8f + f929ce2 commit 50cee09
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
1 change: 1 addition & 0 deletions Tests/Kernels/Cosmos.Compiler.Tests.Bcl.System/Kernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected override void Run()
ConvertTests.Execute();
DateTimeTests.Execute();
TimeSpanTests.Execute();
ActivatorTests.Execute();

int count = Heap.Collect();
mDebugger.Send("Free");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using Cosmos.TestRunner;

namespace Cosmos.Compiler.Tests.Bcl.System
{
class ActivatorTests
{
public static unsafe void Execute()
{
// Till this moment (04/09/2024) the constructors, as any other method, are not included on the compilation result if never called,
// so we need to call them before.
Artesa dummy = new Artesa();
WrappedLogger dummy2 = new WrappedLogger();
ConsoleLogger dummy3 = new ConsoleLogger();


// Generic method test
ILogger logger = GetLogger<ConsoleLogger>();
// Null checks are just for clarification, a ctor would throw if the object/struct was null.
Assert.IsTrue(logger is not null, "Object incorrectly set.");
Assert.IsTrue(typeof(ConsoleLogger).Equals(logger.GetType()), "Type Incorrectly set");
logger.Log("Interface method Call works!");

// Generic method test (with different type)
logger = GetLogger<WrappedLogger>();
Assert.IsTrue(logger is not null, "Object incorrectly set.");
Assert.IsTrue(typeof(WrappedLogger).Equals(logger.GetType()), "Type Incorrectly set");
logger.Log("Interface method call really really works!");
((WrappedLogger)logger).Logger.Log("property get works!");
((WrappedLogger)logger).Success("Type's specific methods work too.");

// Struct Test
var artesa = Activator.CreateInstance(typeof(Artesa));
Assert.IsTrue(artesa is not null, "Struct incorrectly set.");
Assert.IsTrue(typeof(Artesa).Equals(artesa.GetType()), "Type Incorrectly set");

// Unboxing
Artesa art = (Artesa)artesa;
Assert.IsTrue(typeof(Artesa).Equals(art.GetType()), "Type Incorrectly set");

// Check property
Assert.IsTrue(art.Name is not null, "Property not set");

// Test method overrides
Assert.IsTrue(art.ToString() == $"{art.Name}-{art.LastName}", "Property not set");
}

public static T GetLogger<T>() where T : ILogger, new()
{
return new T();
}
}

struct Artesa
{
public Artesa()
{
Console.WriteLine("LOL");
Name = "Artesa";
LastName = "Apple";
}
public string Name { get; }
public string LastName { get; }

public override string ToString()
{
return $"{Name}-{LastName}";
}
}

class WrappedLogger : ILogger
{
private ILogger logger;

public WrappedLogger() : this(ActivatorTests.GetLogger<ConsoleLogger>())
{

}

public WrappedLogger(ILogger logger)
{
this.logger = logger;
}

internal ILogger Logger => logger;

public void Log(string message) => logger.Log(message);
public void Success(string message) => Assert.Succeed(message);
}

class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine(message);
}

interface ILogger
{
void Log(string message);
}
}
81 changes: 81 additions & 0 deletions source/Cosmos.Core_Plugs/System/ActivatorImpl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Runtime.CompilerServices;
using Cosmos.Core;
using IL2CPU.API.Attribs;
using IL2CPU.API;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace Cosmos.Core_Plugs.System
{
[Plug(typeof(global::System.Activator))]
public static class ActivatorImpl
{
public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>()
{
return (T)Activator.CreateInstance(typeof(T))!;
}

// nonPublic is ignored since, at the moment, we cannot know if a ctor is public or not
// and we would get a Stack Corruption or Null reference exception if the ctor is not present either way.
public unsafe static object CreateInstance(CosmosRuntimeType ctr, bool nonPublic, bool wrapExceptions)
{
ArgumentNullException.ThrowIfNull(ctr, "Type");

// get the Type's VTable entry
var mType = VTablesImpl.mTypes[ctr.mTypeId];

// Calculate Object Size
uint dSize = 0;
if (ctr.IsValueType)
{
// For value types this property holds the correct size, so we can avoid the iteration
dSize = mType.Size;
}
else
{
// Calculate Object Actual Size.
var gcType = VTablesImpl.gcTypes[ctr.mTypeId];
for (int i = 0; i < gcType.GCFieldTypes.Length; i++)
{
dSize += VTablesImpl.GetSize(gcType.GCFieldTypes[i]);
}
}

// Object Allocation
uint ptr = GCImplementation.AllocNewObject(ObjectUtils.FieldDataOffset + dSize);

// Set Fields
var vptr = (uint*)ptr;
vptr[0] = ctr.mTypeId; // Type
vptr[1] = ptr; // Address/Handler?
vptr[2] = dSize; // Data Area Size

object obj = Unsafe.Read<object>(vptr)!;
// We make the wild assumption that the ctor is the first method address, this may change on the future if the VTable is reworked.
var ctoraddress = mType.MethodAddresses[0];

try
{
if (ctr.IsValueType)
{
// Struct Ctor Call
var cctor = (delegate*<void*, void>)ctoraddress;
cctor(vptr + 3); // Struct pointer
}
else
{
// Object Ctor Call
var cctor = (delegate*<object, void>)ctoraddress;
cctor(obj);
}

return obj;
}
catch (Exception inner) when (wrapExceptions)
{
throw new TargetInvocationException(inner);
}
}
}
}

0 comments on commit 50cee09

Please sign in to comment.