Skip to content

[Java.Interop.Tools.JavaCallableWrappers] Error on bad IJavaObject #179

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

Merged

Conversation

jonpryor
Copy link
Contributor

Context: https://bugzilla.xamarin.com/show_bug.cgi?id=56819

Nothing prevents "anybody" from custom implementing IJavaObject:

class MyBadClass : Android.Runtime.IJavaObject {
	public IntPtr Handle {
		get {return IntPtr.Zero;}
	}

	public void Dispose () {}
}

The problem is that the above doesn't actually work: the
IJavaObject.Handle value contains the JNI Object Reference to pass
into JNI. The above code will thus result in always passing null
into Java code, which could result in a NullPointerException, but
will always result in not doing what was intended.

While it is theoretically possible to manually implement
IJavaObject, it's not something I would want to contemplate, nor
is it for the faint of heart, so long ago we decided to emit a warning
if we encounter a type which:

  1. Implements Android.Runtime.IJavaObject, and
  2. Does not also inherit Java.Lang.Object or
    Java.Lang.Throwable.

As such, the above MyBadClass would elicit the warning:

Type 'MyBadClass' implements Android.Runtime.IJavaObject but does not inherit from Java.Lang.Object. It is not supported.

This is all well and good, but (effectively) nobody reads warnings,
especially since our toolchain emits so many warnings that enabling
/warnaserror is for the truly foolhardy, so lots of warnings is,
unfortunately, normal.

Which brings us to Bug #56819: Could we make this an error?

Answer: Of course we can. That said, I have no idea how much existing
code this will break if we turned it into an error. That might be
good and useful, but if there's no way to disable the error,
existing apps which (appear to) work will no longer build.

That's not desirable.

Turn JavaTypeScanner from a static class into a normal class, and
add a new JavaTypeScanner.ErrorOnCustomJavaObject property which,
when true, will cause JavaTypeScanner to emit an error instead of
a warning when it encounters types such as MyBadClass. (I'm turning
it into a normal class instead of adding a new
JavaTypeScanner.GetJavaTypes() overload in case we need to add
additional such "control" parameters in the future. Method overloads
will otherwise get unwieldy.)

JavaTypeScanner.ErrorOnCustomJavaObject is all well and good, but
how do we actually emit a warning vs. an error? The previous
Action<string, object[]> log delegate doesn't allow distinguishing
between warnings and errors (and possible diagnostic messages), so how
would we eventually hook this up into xamarin-android's
<GenerateJavaStubs/> task?

Attempt to better handle that question by replacing
Action<string, object[]> delegates with Action<TraceLevel, string>
delegates. This allows messages to explicitly specify
TraceLevel.Error, TraceLevel.Warning, or TraceLevel.Info (or others!)
so that messages can be sanely processed by downstream users.
Additionally, add a new Diagnostic.CreateConsoleLogger() method
which creates an Action<TraceLevel, string> which forwards messages
to Console.Error or Console.Out, as appropriate.

Context: https://bugzilla.xamarin.com/show_bug.cgi?id=56819

Nothing *prevents* "anybody" from custom implementing `IJavaObject`:

	class MyBadClass : Android.Runtime.IJavaObject {
		public IntPtr Handle {
			get {return IntPtr.Zero;}
		}

		public void Dispose () {}
	}

The problem is that the above doesn't actually work: the
`IJavaObject.Handle` value contains the JNI Object Reference to pass
into JNI. The above code will thus result in always passing `null`
into Java code, which could result in a `NullPointerException`, but
will always result in *not* doing what was intended.

While it is *theoretically* possible to manually implement
`IJavaObject`, it's not something *I* would want to contemplate, nor
is it for the faint of heart, so long ago we decided to emit a warning
if we encounter a type which:

 1. Implements `Android.Runtime.IJavaObject`, and
 2. Does *not* also inherit `Java.Lang.Object` or
    `Java.Lang.Throwable`.

As such, the above `MyBadClass` would elicit the warning:

	Type 'MyBadClass' implements Android.Runtime.IJavaObject but does not inherit from Java.Lang.Object. It is not supported.

This is all well and good, but (effectively) *nobody* reads warnings,
*especially* since our toolchain emits so many warnings that enabling
`/warnaserror` is for the truly foolhardy, so *lots* of warnings is,
unfortunately, normal.

Which brings us to Bug #56819: Could we make this an error?

Answer: Of course we can. That said, I have no idea how much existing
code this will *break* if we turned it into an error. That might be
good and useful, but if there's no way to *disable* the error,
existing apps which (appear to) work will no longer build.

That's not desirable.

Turn `JavaTypeScanner` from a `static` class into a normal class, and
add a new `JavaTypeScanner.ErrorOnCustomJavaObject` property which,
when true, will cause `JavaTypeScanner` to emit an error instead of
a warning when it encounters types such as `MyBadClass`. (I'm turning
it into a normal class instead of adding a new
`JavaTypeScanner.GetJavaTypes()` overload in case we need to add
additional such "control" parameters in the future. Method overloads
will otherwise get unwieldy.)

`JavaTypeScanner.ErrorOnCustomJavaObject` is all well and good, but
how do we *actually* emit a warning vs. an error? The previous
`Action<string, object[]> log` delegate doesn't allow distinguishing
between warnings and errors (and possible diagnostic messages), so how
would we eventually hook this up into xamarin-android's
`<GenerateJavaStubs/>` task?

Attempt to better handle that question by replacing
`Action<string, object[]>` delegates with `Action<TraceLevel, string>`
delegates. This allows messages to explicitly specify
TraceLevel.Error, TraceLevel.Warning, or TraceLevel.Info (or others!)
so that messages can be sanely processed by downstream users.
Additionally, add a new `Diagnostic.CreateConsoleLogger()` method
which creates an `Action<TraceLevel, string>` which forwards messages
to `Console.Error` or `Console.Out`, as appropriate.
@jonpryor jonpryor force-pushed the jonp-error-without-inheriting-JLO branch from 355eea4 to 6994d39 Compare August 29, 2017 18:37
@atsushieno
Copy link
Contributor

Most of the lines of the changes were rather about logging. I'd say it's better to have a split commit so that in case of regressions we don't have to revert/cherry-pick/undo-redo-cherry-pick irrelevant changes, but the changes are looking good to me.

@dellis1972 dellis1972 self-requested a review August 30, 2017 09:00
Copy link
Contributor

@dellis1972 dellis1972 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me.

@atsushieno atsushieno merged commit ab3c2b2 into dotnet:master Aug 30, 2017
@github-actions github-actions bot locked and limited conversation to collaborators Apr 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants