Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Java Bytecode Generator #535

Open
jonpryor opened this issue Dec 9, 2019 · 0 comments
Open

Implement Java Bytecode Generator #535

jonpryor opened this issue Dec 9, 2019 · 0 comments
Labels
callable-wrappers Issues with Java Callable Wrappers enhancement Proposed change to current functionality

Comments

@jonpryor
Copy link
Member

jonpryor commented Dec 9, 2019

Context: Issue #524
Context: Issue #534

Allowing JVM-hosted code to invoke managed code requires the use of Java Callable Wrappers for all Java.Lang.Object subclasses. The Java Callable Wrapper contains Java native method declarations for all methods implemented in managed code, e.g. by overriding a virtual method, implementing an interface method, or placing [Java.Interop.ExportAttribute] on a method.

Java Callable Wrappers are currently implemented by using Java source code as an intermediary: JavaCallableWrapperGenerator generates Java source code, which is later compiled by javac to produce Java byte code for later inclusion into an Android application.

We have historically pondered updating Xamarin.Android.Tools.Bytecode to be a read/write API instead of a read-only API, largely for performance reasons: we could avoid the overhead of writing .java files and invoking javac if we just directly emitted a .jar file containing relevant .class files.

We now have another reason to ponder this idea: Full Kotlin support. Kotlin "name mangles" method names in various contexts, emitting Java byte code method names which are not valid Java identifiers:

// Kotlin
package example;

public open abstract class AbstractUnsigned {
	public abstract fun m(value : UInt)
}
// javap example.AbstractUnsigned
public abstract class example.AbstractUnsigned {
  public abstract void m-WZ4Q5Ns(int);
  public example.AbstractUnsigned();
}

Note the example.AbstractUnsigned.m-WZ4Q5Ns() method: it cannot be overridden from Java:

// error: JavaSubclassAbstractUnsigned is not abstract and does not override abstract method m-WZ4Q5Ns(int) in AbstractUnsigned
class JavaSubclassAbstractUnsigned extends example.AbstractUnsigned {
}
class JavaSubclassAbstractUnsigned extends example.AbstractUnsigned {
	public void  m-WZ4Q5Ns(int value) {		
// error: '(' expected
//	public void  m-WZ4Q5Ns(int value) {		
//	              ^
// error: invalid method declaration; return type required
//	public void  m-WZ4Q5Ns(int value) {		
//	               ^
	}
}

So long as we use Java source code as an intermediary, we cannot override or implement Kotlin name-mangled methods.

We could use Kotlin as an intermediate language ("Kotlin Callable Wrappers," anyone?)...

...but why add Kotlin as an intermediate language when we could instead just write Java byte code directly. This would allow the Java Callable Wrapper process to be faster -- no javac invocation would be required -- and means that we avoid any "pesky" issues caused by Kotlin name mangling.

@jonpryor jonpryor added the enhancement Proposed change to current functionality label Dec 9, 2019
jonpryor pushed a commit that referenced this issue Dec 12, 2019
…537)

Context: #534
Context: #535
Context: 0065de4

There are cases where Kotlin generates Java methods that are not
valid Java identifiers:

	// `javap` output for a Kotlin type using UInt parameters
	public abstract class Bar {
	    abstract void foo-WZ4Q5Ns(int);
	}

	public interface IBar {
	    void foo-WZ4Q5Ns(int);
	}

In this case we cannot allow the user to inherit from the class or
implement the interface as the mangled method cannot be present
within Java Callable Wrapper source code.

We still need the class to be bound because there might be a Kotlin-
created subclass that needs the base class to exist.

There's no foolproof way to mark these as "not implementable".  The
best we can do for now is to detect if the user implements them in
the `<GenerateJavaStubs/>` task / `JavaCallableWrapperGenerator` and
give an informative error at that point.

Update `JavaCallableWrapperGenerator` so that when a Kotlin-generated
mangled method name is encountered, an XA4213 error is emitted:

	error XA4213: Cannot override Kotlin-generated method 'foo-WZ4Q5Ns'
	because it is not a valid Java method name.
	This method can only be overridden from Kotlin.
jonpryor pushed a commit that referenced this issue Dec 12, 2019
…537)

Context: #534
Context: #535
Context: 0065de4

There are cases where Kotlin generates Java methods that are not
valid Java identifiers:

	// `javap` output for a Kotlin type using UInt parameters
	public abstract class Bar {
	    abstract void foo-WZ4Q5Ns(int);
	}

	public interface IBar {
	    void foo-WZ4Q5Ns(int);
	}

In this case we cannot allow the user to inherit from the class or
implement the interface as the mangled method cannot be present
within Java Callable Wrapper source code.

We still need the class to be bound because there might be a Kotlin-
created subclass that needs the base class to exist.

There's no foolproof way to mark these as "not implementable".  The
best we can do for now is to detect if the user implements them in
the `<GenerateJavaStubs/>` task / `JavaCallableWrapperGenerator` and
give an informative error at that point.

Update `JavaCallableWrapperGenerator` so that when a Kotlin-generated
mangled method name is encountered, an XA4213 error is emitted:

	error XA4213: Cannot override Kotlin-generated method 'foo-WZ4Q5Ns'
	because it is not a valid Java method name.
	This method can only be overridden from Kotlin.
@jpobst jpobst added the callable-wrappers Issues with Java Callable Wrappers label Apr 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
callable-wrappers Issues with Java Callable Wrappers enhancement Proposed change to current functionality
Projects
None yet
Development

No branches or pull requests

2 participants