Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions samples/Hello/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Threading;

using Mono.Options;
Expand All @@ -9,10 +10,13 @@ namespace Hello
{
class App
{
const int N = 1000000;

public static void Main (string[] args)
{
string? jvmPath = global::Java.InteropTests.TestJVM.GetJvmLibraryPath ();
bool createMultipleVMs = false;
bool reportTiming = false;
bool showHelp = false;
var options = new OptionSet () {
"Using the JVM from C#!",
Expand All @@ -24,6 +28,9 @@ public static void Main (string[] args)
{ "m",
"Create multiple Java VMs. This will likely creash.",
v => createMultipleVMs = v != null },
{ "t",
$"Timing; invoke Object.hashCode() {N} times, print average.",
v => reportTiming = v != null },
{ "h|help",
"Show this message and exit.",
v => showHelp = v != null },
Expand All @@ -33,23 +40,25 @@ public static void Main (string[] args)
options.WriteOptionDescriptions (Console.Out);
return;
}
Console.WriteLine ("Hello World!");
var builder = new JreRuntimeOptions () {
JniAddNativeMethodRegistrationAttributePresent = true,
JvmLibraryPath = jvmPath,
};
builder.AddOption ("-Xcheck:jni");

var jvm = builder.CreateJreVM ();
Console.WriteLine ($"JniRuntime.CurrentRuntime == jvm? {ReferenceEquals (JniRuntime.CurrentRuntime, jvm)}");
foreach (var h in JniRuntime.GetAvailableInvocationPointers ()) {
Console.WriteLine ("PRE: GetCreatedJavaVMHandles: {0}", h);
}

CreateJLO ();
if (reportTiming) {
ReportTiming ();
return;
}

if (createMultipleVMs) {
CreateAnotherJVM ();
return;
}

CreateJLO ();
}

static void CreateJLO ()
Expand All @@ -58,6 +67,17 @@ static void CreateJLO ()
Console.WriteLine ($"binding? {jlo.ToString ()}");
}

static void ReportTiming ()
{
var jlo = new Java.Lang.Object ();
var t = Stopwatch.StartNew ();
for (int i = 0; i < N; ++i) {
jlo.GetHashCode ();
}
t.Stop ();
Console.WriteLine ($"Object.hashCode: {N} invocations. Total={t.Elapsed}; Average={t.Elapsed.TotalMilliseconds / (double) N}ms");
}

static unsafe void CreateAnotherJVM ()
{
Console.WriteLine ("Part 2!");
Expand Down
3 changes: 2 additions & 1 deletion src/Java.Interop/Java.Interop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
<OutputPath>$(ToolOutputFullPath)</OutputPath>
<DocumentationFile>$(ToolOutputFullPath)Java.Interop.xml</DocumentationFile>
<JNIEnvGenPath>$(BuildToolOutputFullPath)</JNIEnvGenPath>
<LangVersion>8.0</LangVersion>
<LangVersion Condition=" '$(JIBuildingForNetCoreApp)' == 'True' ">9.0</LangVersion>
<LangVersion Condition=" '$(LangVersion)' == '' ">8.0</LangVersion>
<Nullable>enable</Nullable>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<MSBuildWarningsAsMessages>NU1702</MSBuildWarningsAsMessages>
Expand Down
4 changes: 2 additions & 2 deletions src/Java.Interop/Java.Interop/JavaArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,9 @@ bool IList.IsFixedSize {

object? IList.this [int index] {
get {return this [index];}
#pragma warning disable 8601
#pragma warning disable 8600,8601
set {this [index] = (T) value;}
#pragma warning restore 8601
#pragma warning restore 8600,8601
}

void ICollection.CopyTo (Array array, int index)
Expand Down
28 changes: 27 additions & 1 deletion src/Java.Interop/Java.Interop/JniEnvironment.Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ static Types ()
}
}

public static unsafe JniObjectReference FindClass (string classname)
public static JniObjectReference FindClass (string classname)
{
return TryFindClass (classname, throwOnError: true);
}

static unsafe JniObjectReference TryFindClass (string classname, bool throwOnError)
{
if (classname == null)
throw new ArgumentNullException (nameof (classname));
Expand Down Expand Up @@ -85,6 +90,10 @@ public static unsafe JniObjectReference FindClass (string classname)
}
}

if (!throwOnError) {
(pendingException as IJavaPeerable)?.Dispose ();
return default;
}
throw pendingException!;
#endif // !FEATURE_JNIENVIRONMENT_JI_PINVOKES
#if FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
Expand Down Expand Up @@ -120,10 +129,27 @@ public static unsafe JniObjectReference FindClass (string classname)
var loadClassThrown = new JniObjectReference (thrown, JniObjectReferenceType.Local);
LogCreateLocalRef (loadClassThrown);
pendingException = info.Runtime.GetExceptionForThrowable (ref loadClassThrown, JniObjectReferenceOptions.CopyAndDispose);
if (!throwOnError) {
(pendingException as IJavaPeerable)?.Dispose ();
return default;
}
throw pendingException!;
#endif // !FEATURE_JNIOBJECTREFERENCE_SAFEHANDLES
}

#if NET
public static bool TryFindClass (string classname, out JniObjectReference instance)
{
if (classname == null)
throw new ArgumentNullException (nameof (classname));
if (classname.Length == 0)
throw new ArgumentException ("'classname' cannot be a zero-length string.", nameof (classname));

instance = TryFindClass (classname, throwOnError: false);
return instance.IsValid;
}
#endif // NET

public static JniType? GetTypeFromInstance (JniObjectReference instance)
{
if (!instance.IsValid)
Expand Down
134 changes: 134 additions & 0 deletions src/Java.Interop/Java.Interop/JniMemberSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#nullable enable

#if NET

using System;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

namespace Java.Interop
{
public struct JniMemberSignature : IEquatable<JniMemberSignature>
{
public static readonly JniMemberSignature Empty;

string? memberName;
string? memberSignature;

public string MemberName => memberName ?? throw new InvalidOperationException ();
public string MemberSignature => memberSignature ?? throw new InvalidOperationException ();

public JniMemberSignature (string memberName, string memberSignature)
{
if (string.IsNullOrEmpty (memberName)) {
throw new ArgumentNullException (nameof (memberName));
}
if (string.IsNullOrEmpty (memberSignature)) {
throw new ArgumentNullException (nameof (memberSignature));
}
this.memberName = memberName;
this.memberSignature = memberSignature;
}

public static int GetParameterCountFromMethodSignature (string jniMethodSignature)
{
if (jniMethodSignature.Length < "()V".Length || jniMethodSignature [0] != '(' ) {
throw new ArgumentException (
$"Member signature `{jniMethodSignature}` is not a method signature. Method signatures must start with `(`.",
nameof (jniMethodSignature));
}
int count = 0;
int index = 1;
while (index < jniMethodSignature.Length &&
jniMethodSignature [index] != ')') {
ExtractType (jniMethodSignature, ref index);
count++;
}
return count;
}

internal static (int StartIndex, int Length) ExtractType (string signature, ref int index)
{
AssertSignatureIndex (signature, index);
var i = index++;
switch (signature [i]) {
case '[':
if ((i+1) >= signature.Length)
throw new InvalidOperationException ($"Missing array type after '[' at index {i} in: `{signature}`");
var rest = ExtractType (signature, ref index);
return (StartIndex: i, Length: index - i);
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'V':
case 'Z':
return (StartIndex: i, Length: 1);
case 'L':
int depth = 0;
int e = index;
while (e < signature.Length) {
var c = signature [e++];
if (depth == 0 && c == ';')
break;
}
if (e > signature.Length)
throw new InvalidOperationException ($"Missing reference type after `{signature [i]}` at index {i} in `{signature}`!");
index = e;
return (StartIndex: i, Length: (e - i));
default:
throw new InvalidOperationException ($"Unknown JNI Type `{signature [i]}` within: `{signature}`!");
}
}

internal static void AssertSignatureIndex (string signature, int index)
{
if (signature == null)
throw new ArgumentNullException (nameof (signature));
if (signature.Length == 0)
throw new ArgumentException ("Descriptor cannot be empty string", nameof (signature));
if (index >= signature.Length)
throw new ArgumentException ("index >= descriptor.Length", nameof (index));
}

public override int GetHashCode ()
{
return (memberName?.GetHashCode () ?? 0) ^
(memberSignature?.GetHashCode () ?? 0);
}

public override bool Equals (object? obj)
{
var v = obj as JniMemberSignature?;
if (v.HasValue)
return Equals (v.Value);
return false;
}

public bool Equals (JniMemberSignature other)
{
return memberName == other.memberName &&
memberSignature == other.memberSignature;
}

public override string ToString ()
{
return $"{nameof (JniMemberSignature)} {{ " +
$"{nameof (MemberName)} = {(memberName == null ? "null" : "\"" + memberName + "\"")}" +
$", {nameof (MemberSignature)} = {(memberSignature == null ? "null" : "\"" + memberSignature + "\"")}" +
$"}}";
}

public static bool operator== (JniMemberSignature a, JniMemberSignature b) => a.Equals (b);
public static bool operator!= (JniMemberSignature a, JniMemberSignature b) => !a.Equals (b);
}
}

#endif // NET
5 changes: 5 additions & 0 deletions src/Java.Interop/Java.Interop/JniMethodInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ public sealed class JniMethodInfo

public bool IsStatic {get; private set;}

#if NET
internal JniType? StaticRedirect;
internal int? ParameterCount;
#endif //NET

internal bool IsValid {
get {return ID != IntPtr.Zero;}
}
Expand Down
44 changes: 38 additions & 6 deletions src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,46 @@ internal JniInstanceMethods GetConstructorsForType (Type declaringType)
public JniMethodInfo GetMethodInfo (string encodedMember)
{
lock (InstanceMethods) {
if (!InstanceMethods.TryGetValue (encodedMember, out var m)) {
string method, signature;
JniPeerMembers.GetNameAndSignature (encodedMember, out method, out signature);
m = JniPeerType.GetInstanceMethod (method, signature);
InstanceMethods.Add (encodedMember, m);
if (InstanceMethods.TryGetValue (encodedMember, out var m)) {
return m;
}
return m;
}
string method, signature;
JniPeerMembers.GetNameAndSignature (encodedMember, out method, out signature);
var info = GetMethodInfo (method, signature);
lock (InstanceMethods) {
if (InstanceMethods.TryGetValue (encodedMember, out var m)) {
return m;
}
InstanceMethods.Add (encodedMember, info);
}
return info;
}

JniMethodInfo GetMethodInfo (string method, string signature)
{
#if NET
var m = (JniMethodInfo?) null;
var newMethod = JniEnvironment.Runtime.TypeManager.GetReplacementMethodInfo (Members.JniPeerTypeName, method, signature);
if (newMethod.HasValue) {
var typeName = newMethod.Value.TargetJniType ?? Members.JniPeerTypeName;
var methodName = newMethod.Value.TargetJniMethodName ?? method;
var methodSig = newMethod.Value.TargetJniMethodSignature ?? signature;

using var t = new JniType (typeName);
if (newMethod.Value.TargetJniMethodInstanceToStatic &&
t.TryGetStaticMethod (methodName, methodSig, out m)) {
m.ParameterCount = newMethod.Value.TargetJniMethodParameterCount;
m.StaticRedirect = new JniType (typeName);
return m;
}
if (t.TryGetInstanceMethod (methodName, methodSig, out m)) {
return m;
}
Console.Error.WriteLine ($"warning: For declared method `{Members.JniPeerTypeName}.{method}.{signature}`, could not find requested method `{typeName}.{methodName}.{methodSig}`!");
}
#endif // NET
return JniPeerType.GetInstanceMethod (method, signature);
}

public unsafe JniObjectReference StartCreateInstance (string constructorSignature, Type declaringType, JniArgumentValue* parameters)
Expand Down
Loading