summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland Levillain <rpl@google.com>2018-02-12 20:00:18 +0000
committerRoland Levillain <rpl@google.com>2018-02-15 18:51:24 +0000
commitad0777d89df7eb21d7d2001f9743882d10de3f5c (patch)
tree15bf8fbfd1a3534972195d4ada9d92f1d0c98903
parentdb8d9091bbab41060584ab80882b60df20337da7 (diff)
downloadandroid_art-ad0777d89df7eb21d7d2001f9743882d10de3f5c.tar.gz
android_art-ad0777d89df7eb21d7d2001f9743882d10de3f5c.tar.bz2
android_art-ad0777d89df7eb21d7d2001f9743882d10de3f5c.zip
Visit proxy methods reference arguments when visiting Quick frames roots.
The arguments of a proxy method, stored in the proxy method's stack frame, need to be visited as GC roots. This is especially important in the case of a moving GC, where these reference arguments may be moved like any object. Previously, we would only visit the target (`this` argument) of proxy methods when visiting Quick frames roots. Test: art/test/testrunner/testrunner.py --gcstress -t 999-proxy-method-arguments Test: m test-art-host Test: m test-art-target Bug: 73149739 Bug: 70216372 Bug: 67679263 Change-Id: Ieacc966ab1038935600f2193c14e6ca01e88602e
-rw-r--r--runtime/entrypoints/quick/quick_entrypoints.h1
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc107
-rw-r--r--runtime/thread.cc29
-rw-r--r--test/1945-proxy-method-arguments/expected.txt26
-rw-r--r--test/1945-proxy-method-arguments/get_args.cc113
-rw-r--r--test/1945-proxy-method-arguments/info.txt7
-rw-r--r--test/1945-proxy-method-arguments/src/Main.java149
-rw-r--r--test/Android.bp7
8 files changed, 418 insertions, 21 deletions
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 6cd9dc1d71..795faa8ecc 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -38,6 +38,7 @@ class Object;
class ArtMethod;
template<class MirrorType> class GcRoot;
+template<class MirrorType> class StackReference;
class Thread;
// Pointers to functions that are called by quick compiler generated code via thread-local storage.
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index a8c328fbc7..344e5bedaa 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -649,10 +649,6 @@ extern "C" mirror::Object* artQuickGetProxyThisObject(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
return QuickArgumentVisitor::GetProxyThisObjectReference(sp)->AsMirrorPtr();
}
-extern "C" StackReference<mirror::Object>* artQuickGetProxyThisObjectReference(ArtMethod** sp)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return QuickArgumentVisitor::GetProxyThisObjectReference(sp);
-}
// Visits arguments on the stack placing them into the shadow frame.
class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor {
@@ -953,7 +949,8 @@ extern "C" uint64_t artQuickProxyInvokeHandler(
std::vector<jvalue> args;
uint32_t shorty_len = 0;
const char* shorty = non_proxy_method->GetShorty(&shorty_len);
- BuildQuickArgumentVisitor local_ref_visitor(sp, false, shorty, shorty_len, &soa, &args);
+ BuildQuickArgumentVisitor local_ref_visitor(
+ sp, /* is_static */ false, shorty, shorty_len, &soa, &args);
local_ref_visitor.VisitArguments();
DCHECK_GT(args.size(), 0U) << proxy_method->PrettyMethod();
@@ -982,6 +979,106 @@ extern "C" uint64_t artQuickProxyInvokeHandler(
return result.GetJ();
}
+// Visitor returning a reference argument at a given position in a Quick stack frame.
+// NOTE: Only used for testing purposes.
+class GetQuickReferenceArgumentAtVisitor FINAL : public QuickArgumentVisitor {
+ public:
+ GetQuickReferenceArgumentAtVisitor(ArtMethod** sp,
+ const char* shorty,
+ uint32_t shorty_len,
+ size_t arg_pos)
+ : QuickArgumentVisitor(sp, /* is_static */ false, shorty, shorty_len),
+ cur_pos_(0u),
+ arg_pos_(arg_pos),
+ ref_arg_(nullptr) {
+ CHECK_LT(arg_pos, shorty_len) << "Argument position greater than the number arguments";
+ }
+
+ void Visit() REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE {
+ if (cur_pos_ == arg_pos_) {
+ Primitive::Type type = GetParamPrimitiveType();
+ CHECK_EQ(type, Primitive::kPrimNot) << "Argument at searched position is not a reference";
+ ref_arg_ = reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
+ }
+ ++cur_pos_;
+ }
+
+ StackReference<mirror::Object>* GetReferenceArgument() {
+ return ref_arg_;
+ }
+
+ private:
+ // The position of the currently visited argument.
+ size_t cur_pos_;
+ // The position of the searched argument.
+ const size_t arg_pos_;
+ // The reference argument, if found.
+ StackReference<mirror::Object>* ref_arg_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetQuickReferenceArgumentAtVisitor);
+};
+
+// Returning reference argument at position `arg_pos` in Quick stack frame at address `sp`.
+// NOTE: Only used for testing purposes.
+extern "C" StackReference<mirror::Object>* artQuickGetProxyReferenceArgumentAt(size_t arg_pos,
+ ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* proxy_method = *sp;
+ ArtMethod* non_proxy_method = proxy_method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ CHECK(!non_proxy_method->IsStatic())
+ << proxy_method->PrettyMethod() << " " << non_proxy_method->PrettyMethod();
+ uint32_t shorty_len = 0;
+ const char* shorty = non_proxy_method->GetShorty(&shorty_len);
+ GetQuickReferenceArgumentAtVisitor ref_arg_visitor(sp, shorty, shorty_len, arg_pos);
+ ref_arg_visitor.VisitArguments();
+ StackReference<mirror::Object>* ref_arg = ref_arg_visitor.GetReferenceArgument();
+ return ref_arg;
+}
+
+// Visitor returning all the reference arguments in a Quick stack frame.
+class GetQuickReferenceArgumentsVisitor FINAL : public QuickArgumentVisitor {
+ public:
+ GetQuickReferenceArgumentsVisitor(ArtMethod** sp,
+ bool is_static,
+ const char* shorty,
+ uint32_t shorty_len)
+ : QuickArgumentVisitor(sp, is_static, shorty, shorty_len) {}
+
+ void Visit() REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE {
+ Primitive::Type type = GetParamPrimitiveType();
+ if (type == Primitive::kPrimNot) {
+ StackReference<mirror::Object>* ref_arg =
+ reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
+ ref_args_.push_back(ref_arg);
+ }
+ }
+
+ std::vector<StackReference<mirror::Object>*> GetReferenceArguments() {
+ return ref_args_;
+ }
+
+ private:
+ // The reference arguments.
+ std::vector<StackReference<mirror::Object>*> ref_args_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetQuickReferenceArgumentsVisitor);
+};
+
+// Returning all reference arguments in Quick stack frame at address `sp`.
+std::vector<StackReference<mirror::Object>*> GetProxyReferenceArguments(ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* proxy_method = *sp;
+ ArtMethod* non_proxy_method = proxy_method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ CHECK(!non_proxy_method->IsStatic())
+ << proxy_method->PrettyMethod() << " " << non_proxy_method->PrettyMethod();
+ uint32_t shorty_len = 0;
+ const char* shorty = non_proxy_method->GetShorty(&shorty_len);
+ GetQuickReferenceArgumentsVisitor ref_args_visitor(sp, /* is_static */ false, shorty, shorty_len);
+ ref_args_visitor.VisitArguments();
+ std::vector<StackReference<mirror::Object>*> ref_args = ref_args_visitor.GetReferenceArguments();
+ return ref_args;
+}
+
// Read object references held in arguments from quick frames and place in a JNI local references,
// so they don't get garbage collected.
class RememberForGcArgumentVisitor FINAL : public QuickArgumentVisitor {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 2ee7f9deda..1de7b20737 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3436,7 +3436,7 @@ bool Thread::HoldsLock(ObjPtr<mirror::Object> object) const {
return object != nullptr && object->GetLockOwnerThreadId() == GetThreadId();
}
-extern "C" StackReference<mirror::Object>* artQuickGetProxyThisObjectReference(ArtMethod** sp)
+extern std::vector<StackReference<mirror::Object>*> GetProxyReferenceArguments(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_);
// RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor).
@@ -3482,7 +3482,7 @@ class ReferenceMapVisitor : public StackVisitor {
}
}
// Mark lock count map required for structured locking checks.
- shadow_frame->GetLockCountData().VisitMonitors(visitor_, -1, this);
+ shadow_frame->GetLockCountData().VisitMonitors(visitor_, /* vreg */ -1, this);
}
private:
@@ -3520,7 +3520,7 @@ class ReferenceMapVisitor : public StackVisitor {
}
}
mirror::Object* new_ref = klass.Ptr();
- visitor_(&new_ref, -1, this);
+ visitor_(&new_ref, /* vreg */ -1, this);
if (new_ref != klass) {
method->CASDeclaringClass(klass.Ptr(), new_ref->AsClass());
}
@@ -3583,17 +3583,20 @@ class ReferenceMapVisitor : public StackVisitor {
}
}
}
- } else if (!m->IsStatic() && !m->IsRuntimeMethod() && m->IsProxyMethod()) {
- // If this is a non-static proxy method, visit its target (`this` object).
+ } else if (!m->IsRuntimeMethod() && m->IsProxyMethod()) {
+ // If this is a proxy method, visit its reference arguments.
+ DCHECK(!m->IsStatic());
DCHECK(!m->IsNative());
- StackReference<mirror::Object>* ref_addr =
- artQuickGetProxyThisObjectReference(cur_quick_frame);
- mirror::Object* ref = ref_addr->AsMirrorPtr();
- if (ref != nullptr) {
- mirror::Object* new_ref = ref;
- visitor_(&new_ref, -1, this);
- if (ref != new_ref) {
- ref_addr->Assign(new_ref);
+ std::vector<StackReference<mirror::Object>*> ref_addrs =
+ GetProxyReferenceArguments(cur_quick_frame);
+ for (StackReference<mirror::Object>* ref_addr : ref_addrs) {
+ mirror::Object* ref = ref_addr->AsMirrorPtr();
+ if (ref != nullptr) {
+ mirror::Object* new_ref = ref;
+ visitor_(&new_ref, /* vreg */ -1, this);
+ if (ref != new_ref) {
+ ref_addr->Assign(new_ref);
+ }
}
}
}
diff --git a/test/1945-proxy-method-arguments/expected.txt b/test/1945-proxy-method-arguments/expected.txt
new file mode 100644
index 0000000000..1824953202
--- /dev/null
+++ b/test/1945-proxy-method-arguments/expected.txt
@@ -0,0 +1,26 @@
+JNI_OnLoad called
+proxy: $Proxy0
+Proxy for interface TestInterface.method0
+ arg0: $Proxy0
+Proxy for interface TestInterface.method1
+ arg0: $Proxy0
+ arg1: java.lang.String "a"
+Proxy for interface TestInterface.method10
+ arg0: $Proxy0
+ arg1: java.lang.String "one"
+ arg2: java.lang.String "two"
+ arg3: java.lang.String "three"
+ arg4: java.lang.String "four"
+ arg5: java.lang.String "five"
+ arg6: java.lang.String "six"
+ arg7: java.lang.String "seven"
+ arg8: java.lang.String "eight"
+ arg9: java.lang.String "nine"
+ arg10: java.lang.String "ten"
+Proxy for interface TestInterface.method10Even
+ arg0: $Proxy0
+ arg2: java.lang.String "two"
+ arg4: java.lang.String "four"
+ arg6: java.lang.String "six"
+ arg8: java.lang.String "eight"
+ arg10: java.lang.String "ten"
diff --git a/test/1945-proxy-method-arguments/get_args.cc b/test/1945-proxy-method-arguments/get_args.cc
new file mode 100644
index 0000000000..211ae10ab0
--- /dev/null
+++ b/test/1945-proxy-method-arguments/get_args.cc
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arch/context.h"
+#include "art_method-inl.h"
+#include "jni.h"
+#include "oat_quick_method_header.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+// Visit a proxy method Quick frame at a given depth.
+class GetProxyQuickFrameVisitor FINAL : public StackVisitor {
+ public:
+ GetProxyQuickFrameVisitor(art::Thread* target, art::Context* ctx, size_t frame_depth)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ cur_depth_(0u),
+ frame_depth_(frame_depth),
+ quick_frame_(nullptr) {}
+
+ bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (GetMethod()->IsRuntimeMethod()) {
+ return true;
+ }
+ if (cur_depth_ == frame_depth_) {
+ // Found frame.
+ ShadowFrame* shadow_frame = GetCurrentShadowFrame();
+ if (shadow_frame != nullptr) {
+ // Nothing to do.
+ } else {
+ VisitQuickFrameAtSearchedDepth();
+ }
+ return false;
+ } else {
+ ++cur_depth_;
+ return true;
+ }
+ }
+
+ void VisitQuickFrameAtSearchedDepth() REQUIRES_SHARED(Locks::mutator_lock_) {
+ quick_frame_ = GetCurrentQuickFrame();
+ CHECK(quick_frame_ != nullptr);
+ ArtMethod* method = *quick_frame_;
+ CHECK(method != nullptr);
+ CHECK(method->IsProxyMethod()) << method->PrettyMethod();
+ }
+
+ // Return the found Quick frame.
+ ArtMethod** GetQuickFrame() {
+ return quick_frame_;
+ }
+
+ private:
+ // The depth of the currently visited frame.
+ size_t cur_depth_;
+ // The depth of the currently searched frame.
+ size_t frame_depth_;
+ // The quick frame, if found.
+ ArtMethod** quick_frame_;
+ // Method name
+
+ DISALLOW_COPY_AND_ASSIGN(GetProxyQuickFrameVisitor);
+};
+
+extern "C" StackReference<mirror::Object>* artQuickGetProxyReferenceArgumentAt(size_t arg_pos,
+ ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+jobject GetProxyReferenceArgument(size_t arg_pos, size_t proxy_method_frame_depth) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ std::unique_ptr<Context> context(Context::Create());
+
+ GetProxyQuickFrameVisitor visitor(self, context.get(), proxy_method_frame_depth);
+ visitor.WalkStack();
+ ArtMethod** quick_frame = visitor.GetQuickFrame();
+ CHECK(quick_frame != nullptr);
+
+ // Find reference argument in frame.
+ StackReference<mirror::Object>* ref_arg =
+ artQuickGetProxyReferenceArgumentAt(arg_pos, quick_frame);
+ CHECK(ref_arg != nullptr);
+ art::ObjPtr<mirror::Object> obj = ref_arg->AsMirrorPtr();
+
+ return obj.IsNull() ? nullptr : soa.AddLocalReference<jobject>(obj);
+}
+
+extern "C" JNIEXPORT jobject JNICALL Java_TestInvocationHandler_getArgument(
+ JNIEnv* env ATTRIBUTE_UNUSED, jobject thiz ATTRIBUTE_UNUSED, int arg_pos, int frame_depth) {
+ return GetProxyReferenceArgument(arg_pos, frame_depth);
+}
+
+} // namespace
+
+} // namespace art
diff --git a/test/1945-proxy-method-arguments/info.txt b/test/1945-proxy-method-arguments/info.txt
new file mode 100644
index 0000000000..15ccc44ac9
--- /dev/null
+++ b/test/1945-proxy-method-arguments/info.txt
@@ -0,0 +1,7 @@
+Test checking that reference arguments of proxy methods are visited as
+thread stack roots when visiting Quick frames roots (b/73149739).
+Previously, if the proxy method (direcly or indirectly) executed code
+accessing one of these reference arguments in the proxy method stack
+frame, it could end up with a stale reference, as the corresponding
+object may have been moved by the garbage collector, but the stack
+reference would not have been updated.
diff --git a/test/1945-proxy-method-arguments/src/Main.java b/test/1945-proxy-method-arguments/src/Main.java
new file mode 100644
index 0000000000..b04a661a4b
--- /dev/null
+++ b/test/1945-proxy-method-arguments/src/Main.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+interface TestInterface {
+ void method0();
+ void method1(String arg);
+ void method10(String arg1, String arg2, String arg3, String arg4, String arg5,
+ String arg6, String arg7, String arg8, String arg9, String arg10);
+ void method10Even(byte arg1, String arg2, short arg3, String arg4, int arg5,
+ String arg6, long arg7, String arg8, double arg9, String arg10);
+}
+
+class TestInvocationHandler implements InvocationHandler {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ // Force garbage collection to try to make `proxy` move in memory
+ // (in the case of a moving garbage collector).
+ System.gc();
+
+ System.out.println("Proxy for " + TestInterface.class + "." + method.getName());
+ if (method.getName().equals("method0")) {
+ testMethod0(proxy, args);
+ } else if (method.getName().equals("method1")) {
+ testMethod1(proxy, args);
+ } else if (method.getName().equals("method10")) {
+ testMethod10(proxy, args);
+ } else if (method.getName().equals("method10Even")) {
+ testMethod10Even(proxy, args);
+ }
+ return null;
+ }
+
+ private void testMethod0(Object proxy, Object[] args) {
+ // Get argument 0 (method target) from the proxy method frame ($Proxy0.method0 activation).
+ Object arg0 = getProxyMethodArgument(0);
+ System.out.println(" arg0: " + arg0.getClass().getName());
+ Main.assertEquals(proxy, arg0);
+ }
+
+ private void testMethod1(Object proxy, Object[] args) {
+ // Get argument 0 (method target) from the proxy method frame ($Proxy0.method0 activation).
+ Object arg0 = getProxyMethodArgument(0);
+ System.out.println(" arg0: " + arg0.getClass().getName());
+ Main.assertEquals(proxy, arg0);
+ // Get argument 1 from the proxy method frame ($Proxy0.method1 activation).
+ String arg1 = (String) getProxyMethodArgument(1);
+ System.out.println(" arg1: " + arg1.getClass().getName() + " \"" + arg1 + "\"");
+ Main.assertEquals(args[0], arg1);
+ }
+
+ private void testMethod10(Object proxy, Object[] args) {
+ // Get argument 0 (method target) from the proxy method frame ($Proxy0.method10 activation).
+ Object arg0 = getProxyMethodArgument(0);
+ System.out.println(" arg0: " + arg0.getClass().getName());
+ Main.assertEquals(proxy, arg0);
+ // Get argument `i` from the proxy method frame ($Proxy0.method10 activation).
+ for (int i = 0; i < 10; ++i) {
+ int arg_pos = i + 1;
+ String arg = (String) getProxyMethodArgument(arg_pos);
+ System.out.println(" arg" + arg_pos + ": " + arg.getClass().getName() + " \"" + arg + "\"");
+ Main.assertEquals(args[i], arg);
+ }
+ }
+
+ private void testMethod10Even(Object proxy, Object[] args) {
+ // Get argument 0 (method target) from the proxy method frame ($Proxy0.method10Even
+ // activation).
+ Object arg0 = getProxyMethodArgument(0);
+ System.out.println(" arg0: " + arg0.getClass().getName());
+ Main.assertEquals(proxy, arg0);
+ // Get argument `i` from the proxy method frame ($Proxy0.method10Even activation).
+ for (int i = 1; i < 10; i += 2) {
+ int arg_pos = i + 1;
+ String arg = (String) getProxyMethodArgument(arg_pos);
+ System.out.println(" arg" + arg_pos + ": " + arg.getClass().getName() + " \"" + arg + "\"");
+ Main.assertEquals(args[i], arg);
+ }
+ }
+
+ // Get reference argument at position `arg_pos` in proxy frame.
+ // This method should only be called from one of the
+ // `TestInvocationHandler.testMethod*` methods via `TestInvocationHandler.invoke`.
+ private Object getProxyMethodArgument(int arg_pos) {
+ // Find proxy frame in stack (from a testMethod* method).
+ //
+ // depth method
+ // ----------------------------------------------------------------------
+ // 0 TestInvocationHandler.getArgument (outermost frame)
+ // 1 TestInvocationHandler.getProxyMethodArgument
+ // 2 TestInvocationHandler.testMethod*
+ // 3 TestInvocationHandler.invoke
+ // 4 java.lang.reflect.Proxy.invoke
+ // -> 5 TestInterface.method* (proxy method)
+ // 6 Main.main (innermost frame)
+ //
+ int proxy_method_frame_depth = 5;
+ return getArgument(arg_pos, proxy_method_frame_depth);
+ }
+
+ // Get reference argument at position `arg_pos` in frame at depth `frame_depth`.
+ private native Object getArgument(int arg_pos, int frame_depth);
+}
+
+public class Main {
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+
+ TestInvocationHandler invocationHandler = new TestInvocationHandler();
+ TestInterface proxy = (TestInterface) Proxy.newProxyInstance(
+ Main.class.getClassLoader(),
+ new Class<?>[] { TestInterface.class },
+ invocationHandler);
+ System.out.println("proxy: " + proxy.getClass().getName());
+
+ proxy.method0();
+ proxy.method1("a");
+ proxy.method10("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten");
+ proxy.method10Even((byte) 1, "two", (short) 3, "four", 5, "six", 7L, "eight", 9.0, "ten");
+ }
+
+ public static void assertEquals(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void assertEquals(String expected, String actual) {
+ if (expected != actual) {
+ throw new Error("Expected \"" + expected + "\", got \"" + actual + "\"");
+ }
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 5f39ffefa9..d2eeee481f 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -367,11 +367,9 @@ cc_defaults {
"art_defaults",
],
srcs: [
- "common/runtime_state.cc",
- "common/stack_inspect.cc",
"004-JniTest/jni_test.cc",
- "004-SignalTest/signaltest.cc",
"004-ReferenceMap/stack_walk_refmap_jni.cc",
+ "004-SignalTest/signaltest.cc",
"004-StackWalk/stack_walk_jni.cc",
"004-ThreadStress/thread_stress.cc",
"004-UnsafeTest/unsafe_test.cc",
@@ -388,6 +386,7 @@ cc_defaults {
"154-gc-loop/heap_interface.cc",
"167-visit-locks/visit_locks.cc",
"169-threadgroup-jni/jni_daemon_thread.cc",
+ "1945-proxy-method-arguments/get_args.cc",
"203-multi-checkpoint/multi_checkpoint.cc",
"305-other-fault-handler/fault_handler.cc",
"454-get-vreg/get_vreg_jni.cc",
@@ -411,6 +410,8 @@ cc_defaults {
"667-jit-jni-stub/jit_jni_stub_test.cc",
"674-hiddenapi/hiddenapi.cc",
"708-jit-cache-churn/jit.cc",
+ "common/runtime_state.cc",
+ "common/stack_inspect.cc",
],
shared_libs: [
"libdexfile",