Skip to content

Commit a27a5c2

Browse files
committed
[Java.Interop.Dynamic] Add DynamicJavaInstance.
DynamicJavaInstance is an IDynamicMetaObjectProvider for invoking Java instance methods on Java instances through JNI. Refactor the shit out of everything. The previous world order was that a DynamicJavaClass instance would contain the applicable members for the corresponding Java type. The problem with this is that we want to support invoking instance members, and invoking instance members shouldn't happen on DynamicJavaClass (unless you really want to pass `this` as an explicit parameter, which is WRONG), which means DynamicJavaInstance also needs access to the applicable fields and methods of the class, which raises a conundrum: Both DynamicJavaClass and DynamicJavaInstance need access to the members available on the Java type. How do we support this? 1. Hava DynamicJavaInstance refer to a DynamicJavaClass; or 2. Refactor out the Java member stuff from DynamicJavaClass. The problem with (1) is that a DynamicJavaClass might not be available (e.g. using DynamicJavaInstance with an instance obtained via `new`). Furthermore, the fact that DynamicJavaClass exposes a Dispose() method makes things possibly ugly/interesting/buggy, e.g. if you Dispose() the DynamicJavaClass while still using the DynamicJavaInstance, things break badly. So this patch follows (2): split out the Java member information/etc. from DynamicJavaClass into a new (internal) JavaClassInfo type. JavaClassInfo instances are "cached", so that if a JavaClassInfo instance exists for a Java type, it'll be reused for all future DynamicJavaClass and DynamicJavaInstance uses until all "clients" are Dispose()d. This allows the class information to be cached, and cleaned up when the information is no longer necessary. (Behold a (bad?) use of reference counting and weak references!) Finally (lol), add support for invoking constructors to DynamicJavaClass. A question here is one of syntax: given a DynamicJavaClass instance: dynamic Integer = new DynamicJavaClass ("java/lang/Integer"); How do we create instances of that class? Unfortunately, the "obvious" and desirable approach doesn't work: dynamic value = new Integer (42); It fails because you can't use `new $variable` in C#, so the compiler complains loudly. Another choice would be to use `new` as a method: dynamic value = Integer.@new(42); This is workable, but is fugly because we need to escape the `new` C# keyword. Using `New` would avoid the need for escaping, but means that if a Java type had a method named `New()`, it couldn't be invoked. (Is this probable? Not really. But it's *possible*.) The implemented solution is to mimic Swift: "invoking" the class creates an instance: dynamic value = Integer (42); The value returned is a DynamicJavaInstance which is wrapping the "real" underlying IJavaObject implementation, which in turn allows invoking instance methods on the value. We also support conversion support, which allows for fairly intuitive use of instance methods, as shown in the Demo() test: dynamic Integer = new DynamicJavaClass ("java/lang/Integer"); dynamic value = Integer (42); sbyte byteV = value.byteValue (); Assert.AreEqual ((sbyte) 42, byteV); dynamic str = value.toString (); var s = (string) str; Assert.AreEqual ("42", s); ~~Implementation Notes~~ JniPeerStaticFields.GetValue(), JniPeerStaticFields.SetValue(), and JniPeerStaticMethods.CallMethod() were removed because they were originally added to support Java.Interop.Dynamic, but we now want to wrap returned object references in a DynamicJavaInstance, and Java.Interop.dll can't refer to Java.Interop.Dynamic.dll. Moving the logic of those methods into Java.Interop.Dynamic.dll made more sense. Remember commit 8bb341f, which turned Java.Interop.Dynamic.dll into a PCL and removed the need for JniPeerMembers to take a managedPeerType value? This was a mistake, because the whole constructor invocation infrastructure via JniPeerInstanceMethods.StartCreateInstance() and JniPeerInstanceMethods.FinishCreateInstance() require that managedPeerType be set; if it isn't, we hit a fallback code path, and start trying to find constructors on the wrong JNI type, and everything breaks. (Oops.) Unfortunately, there's still no way to create new Type instances in PCLs, so we "skirt" this problem by using a new internal JavaInstanceProxy type, which exists solely to be used as the managedPeerType value for JniPeerMembers. This in turn allows constructor lookup to work, and makes everyone happy.
1 parent 46b7aee commit a27a5c2

18 files changed

+1295
-421
lines changed

src/Java.Interop.Dynamic/Java.Interop.Dynamic.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@
3434
<ItemGroup>
3535
<Compile Include="Properties\AssemblyInfo.cs" />
3636
<Compile Include="Java.Interop.Dynamic\DynamicJavaClass.cs" />
37+
<Compile Include="Java.Interop.Dynamic\DynamicJavaInstance.cs" />
38+
<Compile Include="Java.Interop.Dynamic\JavaClassInfo.cs" />
39+
<Compile Include="Java.Interop.Dynamic\JavaConstructorInfo.cs" />
40+
<Compile Include="Java.Interop.Dynamic\JavaFieldInfo.cs" />
41+
<Compile Include="Java.Interop.Dynamic\JavaInstanceProxy.cs" />
42+
<Compile Include="Java.Interop.Dynamic\JavaMemberInfo.cs" />
43+
<Compile Include="Java.Interop.Dynamic\JavaMethodBase.cs" />
44+
<Compile Include="Java.Interop.Dynamic\JavaMethodInfo.cs" />
45+
<Compile Include="Java.Interop.Dynamic\JniMetaObject.cs" />
3746
</ItemGroup>
3847
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
3948
<ItemGroup>

0 commit comments

Comments
 (0)