Open AliveDevil opened 1 year ago
Thinking through the issue.
The problem is the Append method in your .NET class. That method accepts a first argument of type in ReadOnlySpan<char>
. So, it's a by-ref argument of a ref-like type. IKVM here is trying to generate a corresponding Java signature to represent that method. An existing 'hack' exists in IKVM for by-ref type arguments: generate a signature that accepts an array instead. During compilation of Java code the call site then is rewritten to unpack the first argument of that array and pass that. So, Java code can call this method by creating a new array, setting the first element, then calling it. The generated IL uses the ldelema
instruction to get a managed reference to that element, and pass that instead.
This hack works for most things. Except, in this case, ReadOnlySpan
So, we've found a bug that only breaks on Core, because of a new feature in Core.
Solutions:
a) Hide the method. This kind of sucks. The method is truly on the type, and should appear on the Class. b) Use a different signature type and call site rewriting for ref-like types.
Option (b) is preferable. It would allow Java code to actually call this method.
Option (b) comes in two parts: determining what the signature should be, and then the call site rewriting. We can solve your immediate problem by just doing the first half, setting us up to implement the second half later.
We need to decide what the signature should look like though. We can't use an array. We could use a new type: ikvm.lang.ByRef<?>. We would be implementing a new type for this. This would allow Java code to see signatures that accept a managed reference.
In terms of call site rewriting, we would rewrite usages of ikvm.lang.ByRef to equate to managed references. However, we can defer this to a later date, and have reflection working pretty easily.
JDK9 introduced java.lang.invoke.VarHandle, which sort of plays the role of a managed reference. With one missing feature, it doesn't support references to local stack variables.
If we were to coopt this, the Append method would appear to be void Append(VarHandle<Span<char>> range, ...)
. However, one could never call this straight from Java, as one would have no way to create a VarHandle referencing a local variable. But that might be okay. We're not trying to introduce new language features into Java.
Thing though is we have already sort of implemented a new language feature into Java: you can new ValueType()
. Java has no ValueTypes. But IKVM allows this. It does appear to box the value on return though. So it's never really a value type.
So how would a Java program use this:
public void foo() {
VarHandle<int> h = ClrMethodWithRefReturn();
Append(h, ...);
}
If the stack originates in Java, there'd be no way to get a real stack value. But that's normal. You can't. However, a user could use C#:
public void Foo()
{
var s = array.AsSpan();
JavaMethodThatTakesVarHandle(ref s);
}
The Java code now has a ref pointing to stack.
Relevant Java code: https://github.com/iterate-ch/cyberduck/blob/90fdf1bdde637e6e33f2725e3e70b8a7650bebe4/core/src/main/java/ch/cyberduck/core/LocalFactory.java#L42-L49
Inspected .NET type: https://github.com/iterate-ch/cyberduck/blob/90fdf1bdde637e6e33f2725e3e70b8a7650bebe4/core/src/main/csharp/ch/cyberduck/core/local/SystemLocal.cs
Runtime: .NET 6.0 IKVM: custom-built, before hotfix/8.5.1.