Skip to content

Optimize System.String overloads #994

Open
@jonpryor

Description

@jonpryor

generator emits System.String overloads for Java members which accept or return java.lang.ICharSequence. For example, consider TextView.setText(), which is combined with getText() into a TextFormatted property:

namespace Android.Widget {
	public partial class TextView {
		public unsafe Java.Lang.ICharSequence? TextFormatted {
			get =>,
			set {
				const string __id = "setText.(Ljava/lang/CharSequence;)V";
				IntPtr native_value = CharSequence.ToLocalJniHandle (value);
				try {
					JniArgumentValue* __args = stackalloc JniArgumentValue [1];
					__args [0] = new JniArgumentValue (native_value);
					_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, this, __args);
				} finally {
					JNIEnv.DeleteLocalRef (native_value);
					global::System.GC.KeepAlive (value);
				}
			}
		}
	}
}

To simplify use, generator "overloads" this property to accept System.String:

namespace Android.Widget {
	public partial class TextView {
		public string? Text {
			get =>  TextFormatted == null ? null : TextFormatted.ToString (),
			set {
				var jls = value == null ? null : new global::Java.Lang.String (value);
				TextFormatted = jls;
				if (jls != null) jls.Dispose ();
			}
		}
	}
}

This works, but has some implicit overheads: creating Java.Lang.String instances requires registering them with the JniRuntime.JniValueManager, which is "silly" when we will immediately Dispose() of the value. This can show up when profiling apps which call TextView.set_Text() frequently.

We could optimize these "overloads" by skipping Java.Lang.String instances, and instead using JniObjectReference and JniEnvironment.Strings.NewString(string?).

Unfortunately, just because we can doesn't mean that it will be straightforward. There are two "reasonable" ways to implement this:

Copy most of the code from e.g. set_TextFormatted() into set_Text():

namespace Android.Widget {
	public partial class TextView {
		public unsafe string? Text {
			set {
				const string __id = "setText.(Ljava/lang/CharSequence;)V";
				JniObjectReference native_value = JniEnvironment.Strings.NewString (value);
				try {
					JniArgumentValue* __args = stackalloc JniArgumentValue [1];
					__args [0] = new JniArgumentValue (native_value);
					_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, this, __args);
				} finally {
					JniObjectReference.Dispose (ref native_value);
				}
			}
		}
	}
}

Alternatively, we can look into Issue #795, and move the _members.InstanceMethods.InvokeNonvirtualVoidMethod() invocation into an "ABI method." Instead of fully adopting #795, we could put the "ABI method" into the same scope, and use that from both TextFormatted and Text:

namespace Android.Widget {
	public partial class TextView {
		public ICharSequence? TextFormatted {
			set {
				IntPtr native_value = CharSequence.ToLocalJniHandle (value);
				try {
					abi_setText (this, native_value);
				} finally {
					JNIEnv.DeleteLocalRef (native_value);
					global::System.GC.KeepAlive (value);
				}
			}
		}

		public string? Text {
			set {
				JniObjectReference native_value = JniEnvironment.Strings.NewString (value);
				try {
					abi_setText (this, native_value);
				} finally {
					JniObjectReference.Dispose (ref native_value);
				}
			}
		}

		private static void abi_setText (IJavaPeerable self, JniObjectReference text)
		{
			const string __id = "setText.(Ljava/lang/CharSequence;)V";
			try {
				JniArgumentValue* __args = stackalloc JniArgumentValue [1];
				__args [0] = new JniArgumentValue (text);
				_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, this, __args);
			} finally {
				global::System.GC.KeepAlive (self);
			}
		}
	}
}

This has the benefit of reducing code duplication.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementProposed change to current functionalitygeneratorIssues binding a Java library (generator, class-parse, etc.)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions