Skip to content
This repository was archived by the owner on Dec 12, 2024. It is now read-only.

[SoM] Code generation failure with Any param #88

Open
chamons opened this issue Dec 28, 2018 · 1 comment
Open

[SoM] Code generation failure with Any param #88

chamons opened this issue Dec 28, 2018 · 1 comment
Milestone

Comments

@chamons
Copy link
Contributor

chamons commented Dec 28, 2018

		[Test]
		public void AnyObject ()
		{
			var swiftCode = @"import Foundation;
public func Echo (name: String, object: Any) -> NSNumber { return 52.0; }";
			var callingCode = new CodeElementCollection<ICodeElement> ();

			TestRunning.TestAndExecute (swiftCode, callingCode, "".ToString (), platform: PlatformName.macOS);

		}
System.Exception : Failed to execute (exit code 1): /Library/Frameworks/Mono.framework/Versions/Current/bin/mcs -unsafe -define:_MAC_TS_TEST_ -nowarn:CS0169 -out:NameNotImportant.exe  /Users/donblas/Programming/maccore/tools/tom-swifty/tests/tom-swifty-test/bin/Debug/swift-o-matic-tests/3/NameNotImportant.cs /Users/donblas/Programming/maccore/tools/tom-swifty/tests/tom-swifty-test/bin/Debug/swift-o-matic-tests/3/TopLevelEntitiesNotificationTests.cs -lib:/Users/donblas/Programming/maccore/tools/tom-swifty/tests/tom-swifty-test/bin/Debug/../../../../SwiftRuntimeLibrary.Mac/bin/Debug -r:SwiftRuntimeLibrary.Mac -lib:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac -r:Xamarin.Mac 
/Users/donblas/Programming/maccore/tools/tom-swifty/tests/tom-swifty-test/bin/Debug/swift-o-matic-tests/3/TopLevelEntitiesNotificationTests.cs(26,25): error CS0103: The name `wiftAnyXamProxy' does not exist in the current context
/Users/donblas/Programming/maccore/tools/tom-swifty/tests/tom-swifty-test/bin/Debug/swift-o-matic-tests/3/TopLevelEntitiesNotificationTests.cs(25,56): error CS0452: The type `SwiftRuntimeLibrary.SwiftAny' must be a reference type in order to use it as type parameter `T' in the generic type or method `SwiftRuntimeLibrary.SwiftObjectRegistry.SwiftProtocolForInterface<T>(T, System.Func<SwiftRuntimeLibrary.SwiftProtocol>)'
/Users/donblas/Programming/maccore/tools/tom-swifty/tests/tom-swifty-test/bin/Debug/swift-o-matic-tests/3/TopLevelEntitiesNotificationTests.cs(34,60): error CS0452: The type `SwiftRuntimeLibrary.SwiftAny' must be a reference type in order to use it as type parameter `T' in the generic type or method `SwiftRuntimeLibrary.SwiftObjectRegistry.InterfaceForSwiftProtocol<T>(SwiftRuntimeLibrary.SwiftProtocol, System.Func<SwiftRuntimeLibrary.SwiftProtocol,T>)'
/Users/donblas/Programming/maccore/tools/tom-swifty/tests/tom-swifty-test/bin/Debug/swift-o-matic-tests/3/TopLevelEntitiesNotificationTests.cs(35,36): error CS0246: The type or namespace name `wiftAnyXamProxy' could not be found. Are you missing an assembly reference?
Compilation failed: 4 error(s), 0 warnings

Output: https://gist.github.com/chamons/8854214c5e01d119763e73148cf27780

@stephen-hawley
Copy link
Contributor

Here is some documentation about Swift.Any to understand what's going on here.

Swift has two opaque types, Any and AnyObject. Both are empty protocols with no methods, but AnyObject is special in that it is represented differently than typical protocols and is instead represented by just a pointer to the object.

All other protocols are represented by an existential container (swift term) which is the following:

+----------+
| payload  | 3 ptrs
+----------+
| metadata | 1 ptr
+----------+
| proto wit| n ptrs
+----------+

The payload is used to hold the data that subscribes to the protocol(s). The metadata is describes the type of the payload.

Right now we represent SwiftAny like this in C#:

	public interface ISwiftProtocolImpl {
		IntPtr Data0 { get; set; }
		IntPtr Data1 { get; set; }
		IntPtr Data2 { get; set; }
		SwiftMetatype ObjectMetadata { get; set; }
		IntPtr this [int index] { get; set; }
		int Count { get; }
	}

	[SwiftProtocolDescriptor(0)]
	public struct SwiftAny : ISwiftProtocolImpl {
		IntPtr d0, d1, d2;
		SwiftMetatype md;
		public IntPtr Data0 { get { return d0; } set { d0 = value; } }
		public IntPtr Data1 { get { return d1; } set { d1 = value; } }
		public IntPtr Data2 { get { return d2; } set { d2 = value; } }
		public SwiftMetatype ObjectMetadata { get { return md; } set { md = value; } }
		public IntPtr this [int index] { get { throw new NotSupportedException("SwiftAny has no witness table entries"); } set { throw new NotSupportedException ("SwiftAny has no witness table entries"); } }
		public int Count { get { return 0; } }
	}

Which creates a problem. What happens when I have a C# SwiftAny that contains a reference counted type and it goes out of scope? The answer is that we leak. That's bad.

What needs to happen is that SwiftAny needs to be a class, not a struct and it needs to implement IDisposable (best to make the interface implement it.

All data should be a byte array that is allocated appropriately based on the size: (4 + Count) * IntPtr.Size.

Other notes: Swift.Any does NOT have a static type metadata object. It has to be called through the metadata accessor _T0ypMa. We should have a public static property that is a cached value of the return value of this call.

I'm debating whether or not there should be a public constructor for SwiftAny.
Should the Data properties be get only?
An implementation of ToAny<T>(T value) and T FromAny<T>() should include the following swift:

public func toAny<T> (result: UnsafeMutablePointer<Any>, val: T) {
	result.initialize(to: val)
}

public func fromAny<T> (result: UnsafeMutablePointer<T>, any:Any) {
	result.initialize(to: any as! T)
}

Which can have pinvoke definitions like this:

public static extern ToAnyImpl(IntPtr result, IntPtr value, SwiftMetatype type);
public static extern FromAnyImpl(IntPtr result, IntPtr any, SwiftMetatype type);

and the implementations:

public unsafe static SwiftAny ToAny<T>(T value) {
    var any = new SwiftAny ();
    fixed (byte *src = stackalloc byte [StructMarshal.Marshaler.Strideof (typeof(T))]) {
        fixed (byte *dest = any.Data) {
             ToAnyImpl (new IntPtr (dest), new IntPtr (src), StructMarshal.Marshaler.Metatypeof(typeof(T)));
       }
    }
}

public unsafe static T FromAny<T>(SwiftAny value)
{
    var metadata = StructMarshal.Marshaler.Metatypeof(typeof(T));
    if (metadata != value.ObjectMetadata)
        throw new SwiftRuntimeException ($"type of SwiftAny does not match given type {typeof(T).Name}");
    fixed (byte *dest = stackalloc byte [StructMarshal.Marshaler.Sizeof(typeof(T))]) {
        fixed (byte *src = value.Data) {
            FromAnyImpl (new IntPtr (dest), new IntPtr (src), metadata);
            return StructMarshal.Marshaler.ToNet<T>(dest);
        }
    }
}

@stephen-hawley stephen-hawley transferred this issue from another repository Oct 29, 2019
@chamons chamons added this to the Future milestone Nov 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants