diff options
| author | 2016-11-04 11:09:53 +0000 | |
|---|---|---|
| committer | 2016-11-08 14:45:40 +0000 | |
| commit | 0d781e6e17a3f6285736c590001b59d50879f13b (patch) | |
| tree | 3a72db36bdd1b521f4f07005aa426515aa78f08a | |
| parent | 66e4d7102760ddc9aa8a3f8463252d69c88b9864 (diff) | |
MethodHandles: Constructor support
Test: m test-art-host-run-test-956-methodhandles
Bug: 30550796
Change-Id: Ied466d7e0f0f0b472e3a28909ca712dfdb99e422
| -rw-r--r-- | runtime/interpreter/interpreter_common.cc | 14 | ||||
| -rw-r--r-- | test/956-methodhandles/expected.txt | 1 | ||||
| -rw-r--r-- | test/956-methodhandles/src/Main.java | 210 |
3 files changed, 207 insertions, 18 deletions
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 4843c4dc59..79a2a4d5d7 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -934,16 +934,12 @@ inline bool DoInvokePolymorphic(Thread* self, // frame, which means that it is unknown at this point. We perform these // checks inside DoCallPolymorphic right before we do the actualy invoke. } else if (handle_kind == kInvokeDirect) { - if (called_method->IsConstructor()) { - // TODO(narayan) : We need to handle the case where the target method is a - // constructor here. - UNIMPLEMENTED(FATAL) << "Direct invokes for constructors are not implemented yet."; - return false; + // String constructors are a special case, they are replaced with StringFactory + // methods. + if (called_method->IsConstructor() && called_method->GetDeclaringClass()->IsStringClass()) { + DCHECK(handle_type->GetRType()->IsStringClass()); + called_method = WellKnownClasses::StringInitToStringFactory(called_method); } - - // Nothing special to do in the case where we're not dealing with a - // constructor. It's a private method, and we've already access checked at - // the point of creating the handle. } else if (handle_kind == kInvokeSuper) { ObjPtr<mirror::Class> declaring_class = called_method->GetDeclaringClass(); diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt index ad1c43c490..9ca448ce74 100644 --- a/test/956-methodhandles/expected.txt +++ b/test/956-methodhandles/expected.txt @@ -4,3 +4,4 @@ foo_A foo_B privateRyan_D Received exception: Expected (java.lang.String, java.lang.String)java.lang.String but was (java.lang.String, java.lang.Object)void +String constructors done. diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index 780513f9ed..d0c658f819 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -19,6 +19,8 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.invoke.WrongMethodTypeException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -65,6 +67,8 @@ public class Main { testfindVirtual(); testUnreflects(); testAsType(); + testConstructors(); + testStringConstructors(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -347,18 +351,17 @@ public class Main { privateConstructor.setAccessible(true); mh = MethodHandles.lookup().unreflectConstructor(privateConstructor); - // TODO(narayan): Method handle constructor invokes are not supported yet. - // - // UnreflectTester tester = (UnreflectTester) mh.invoke("foo"); - // UnreflectTester tester = (UnreflectTester) mh.invoke("fooExact"); - + instance = (UnreflectTester) mh.invokeExact("abc"); + assertEquals("abc", instance.publicField); + instance = (UnreflectTester) mh.invoke("def"); + assertEquals("def", instance.publicField); Constructor publicConstructor = UnreflectTester.class.getConstructor(String.class, boolean.class); mh = MethodHandles.lookup().unreflectConstructor(publicConstructor); - // TODO(narayan): Method handle constructor invokes are not supported yet. - // - // UnreflectTester tester = (UnreflectTester) mh.invoke("foo"); - // UnreflectTester tester = (UnreflectTester) mh.invoke("fooExact"); + instance = (UnreflectTester) mh.invokeExact("abc", false); + assertEquals("abc", instance.publicField); + instance = (UnreflectTester) mh.invoke("def", true); + assertEquals("def", instance.publicField); // TODO(narayan): Non exact invokes for field sets/gets are not implemented yet. // @@ -493,6 +496,195 @@ public class Main { System.out.println("fail"); Thread.dumpStack(); } + + public static void fail(String message) { + System.out.println("fail: " + message); + Thread.dumpStack(); + } + + public static void testConstructors() throws Throwable { + MethodHandle mh = + MethodHandles.lookup().findConstructor(Float.class, + MethodType.methodType(void.class, + float.class)); + Float value = (Float) mh.invokeExact(0.33f); + if (value.floatValue() != 0.33f) { + fail("Unexpected float value from invokeExact " + value.floatValue()); + } + + value = (Float) mh.invoke(3.34f); + if (value.floatValue() != 3.34f) { + fail("Unexpected float value from invoke " + value.floatValue()); + } + + mh = MethodHandles.lookup().findConstructor(Double.class, + MethodType.methodType(void.class, String.class)); + Double d = (Double) mh.invoke("8.45e3"); + if (d.doubleValue() != 8.45e3) { + fail("Unexpected double value from Double(String) " + value.doubleValue()); + } + + mh = MethodHandles.lookup().findConstructor(Double.class, + MethodType.methodType(void.class, double.class)); + d = (Double) mh.invoke(8.45e3); + if (d.doubleValue() != 8.45e3) { + fail("Unexpected double value from Double(double) " + value.doubleValue()); + } + + // Primitive type + try { + mh = MethodHandles.lookup().findConstructor(int.class, MethodType.methodType(void.class)); + fail("Unexpected lookup success for primitive constructor"); + } catch (NoSuchMethodException e) {} + + // Interface + try { + mh = MethodHandles.lookup().findConstructor(Readable.class, + MethodType.methodType(void.class)); + fail("Unexpected lookup success for interface constructor"); + } catch (NoSuchMethodException e) {} + + // Abstract + mh = MethodHandles.lookup().findConstructor(Process.class, MethodType.methodType(void.class)); + try { + mh.invoke(); + fail("Unexpected ability to instantiate an abstract class"); + } catch (InstantiationException e) {} + + // Non-existent + try { + MethodHandle bad = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(String.class, Float.class)); + fail("Unexpected success for non-existent constructor"); + } catch (NoSuchMethodException e) {} + + // Non-void constructor search. (I)I instead of (I)V. + try { + MethodHandle foo = MethodHandles.lookup().findConstructor( + Integer.class, MethodType.methodType(Integer.class, Integer.class)); + fail("Unexpected success for non-void type for findConstructor"); + } catch (NoSuchMethodException e) {} + } + + public static void testStringConstructors() throws Throwable { + final String testPattern = "The system as we know it is broken"; + + // String() + MethodHandle mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class)); + String s = (String) mh.invokeExact(); + if (!s.equals("")) { + fail("Unexpected empty string constructor result: '" + s + "'"); + } + + // String(String) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, String.class)); + s = (String) mh.invokeExact(testPattern); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(char[]) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, char[].class)); + s = (String) mh.invokeExact(testPattern.toCharArray()); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(char[], int, int) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, char[].class, int.class, int.class)); + s = (String) mh.invokeExact(new char [] { 'a', 'b', 'c', 'd', 'e'}, 2, 3); + if (!s.equals("cde")) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(int[] codePoints, int offset, int count) + StringBuffer sb = new StringBuffer(testPattern); + int[] codePoints = new int[sb.codePointCount(0, sb.length())]; + for (int i = 0; i < sb.length(); ++i) { + codePoints[i] = sb.codePointAt(i); + } + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, int[].class, int.class, int.class)); + s = (String) mh.invokeExact(codePoints, 0, codePoints.length); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(byte ascii[], int hibyte, int offset, int count) + byte [] ascii = testPattern.getBytes(StandardCharsets.US_ASCII); + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class, int.class, int.class)); + s = (String) mh.invokeExact(ascii, 0, ascii.length); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(byte bytes[], int offset, int length, String charsetName) + mh = MethodHandles.lookup().findConstructor( + String.class, + MethodType.methodType(void.class, byte[].class, int.class, int.class, String.class)); + s = (String) mh.invokeExact(ascii, 0, 5, StandardCharsets.US_ASCII.name()); + if (!s.equals(testPattern.substring(0, 5))) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(byte bytes[], int offset, int length, Charset charset) + mh = MethodHandles.lookup().findConstructor( + String.class, + MethodType.methodType(void.class, byte[].class, int.class, int.class, Charset.class)); + s = (String) mh.invokeExact(ascii, 0, 5, StandardCharsets.US_ASCII); + if (!s.equals(testPattern.substring(0, 5))) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(byte bytes[], String charsetName) + mh = MethodHandles.lookup().findConstructor( + String.class, + MethodType.methodType(void.class, byte[].class, String.class)); + s = (String) mh.invokeExact(ascii, StandardCharsets.US_ASCII.name()); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(byte bytes[], Charset charset) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class, Charset.class)); + s = (String) mh.invokeExact(ascii, StandardCharsets.US_ASCII); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(byte bytes[], int offset, int length) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class, int.class, int.class)); + s = (String) mh.invokeExact(ascii, 1, ascii.length - 2); + s = testPattern.charAt(0) + s + testPattern.charAt(testPattern.length() - 1); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(byte bytes[]) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, byte[].class)); + s = (String) mh.invokeExact(ascii); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + // String(StringBuffer buffer) + mh = MethodHandles.lookup().findConstructor( + String.class, MethodType.methodType(void.class, StringBuffer.class)); + s = (String) mh.invokeExact(sb); + if (!s.equals(testPattern)) { + fail("Unexpected string constructor result: '" + s + "'"); + } + + System.out.println("String constructors done."); + } } |