Implement reflective method invocation.
Change-Id: Ib3af9d7e00bf226398610b5ac6efbfe3eb2d15e8
diff --git a/src/reflection.cc b/src/reflection.cc
new file mode 100644
index 0000000..3edb9f7
--- /dev/null
+++ b/src/reflection.cc
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2011 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 "reflection.h"
+
+#include "class_linker.h"
+#include "jni_internal.h"
+#include "object.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+Method* gBoolean_valueOf;
+Method* gByte_valueOf;
+Method* gCharacter_valueOf;
+Method* gDouble_valueOf;
+Method* gFloat_valueOf;
+Method* gInteger_valueOf;
+Method* gLong_valueOf;
+Method* gShort_valueOf;
+
+void InitBoxingMethod(JNIEnv* env, Method*& m, jclass c, const char* method_signature) {
+ m = DecodeMethod(env->GetStaticMethodID(c, "valueOf", method_signature));
+}
+
+void InitBoxingMethods(JNIEnv* env) {
+ InitBoxingMethod(env, gBoolean_valueOf, JniConstants::booleanClass, "(Z)Ljava/lang/Boolean;");
+ InitBoxingMethod(env, gByte_valueOf, JniConstants::byteClass, "(B)Ljava/lang/Byte;");
+ InitBoxingMethod(env, gCharacter_valueOf, JniConstants::characterClass, "(C)Ljava/lang/Character;");
+ InitBoxingMethod(env, gDouble_valueOf, JniConstants::doubleClass, "(D)Ljava/lang/Double;");
+ InitBoxingMethod(env, gFloat_valueOf, JniConstants::floatClass, "(F)Ljava/lang/Float;");
+ InitBoxingMethod(env, gInteger_valueOf, JniConstants::integerClass, "(I)Ljava/lang/Integer;");
+ InitBoxingMethod(env, gLong_valueOf, JniConstants::longClass, "(J)Ljava/lang/Long;");
+ InitBoxingMethod(env, gShort_valueOf, JniConstants::shortClass, "(S)Ljava/lang/Short;");
+}
+
+bool VerifyObjectInClass(JNIEnv* env, Object* o, Class* c) {
+ if (o == NULL) {
+ jniThrowNullPointerException(env, "receiver for non-static field access was null");
+ return false;
+ }
+ if (!o->InstanceOf(c)) {
+ std::string expectedClassName(PrettyDescriptor(c->GetDescriptor()));
+ std::string actualClassName(PrettyTypeOf(o));
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "expected receiver of type %s, but got %s",
+ expectedClassName.c_str(), actualClassName.c_str());
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Convert primitive, boxed data from "srcPtr" to "dstPtr".
+ *
+ * Section v2 2.6 lists the various conversions and promotions. We
+ * allow the "widening" and "identity" conversions, but don't allow the
+ * "narrowing" conversions.
+ *
+ * Allowed:
+ * byte to short, int, long, float, double
+ * short to int, long, float double
+ * char to int, long, float, double
+ * int to long, float, double
+ * long to float, double
+ * float to double
+ * Values of types byte, char, and short are "internally" widened to int.
+ *
+ * Returns the width in 32-bit words of the destination primitive, or
+ * -1 if the conversion is not allowed.
+ */
+bool ConvertPrimitiveValue(Class* src_class, Class* dst_class, const JValue& src, JValue& dst) {
+ Class::PrimitiveType srcType = src_class->GetPrimitiveType();
+ Class::PrimitiveType dstType = dst_class->GetPrimitiveType();
+ switch (dstType) {
+ case Class::kPrimBoolean:
+ case Class::kPrimChar:
+ case Class::kPrimByte:
+ if (srcType == dstType) {
+ dst.i = src.i;
+ return true;
+ }
+ break;
+ case Class::kPrimShort:
+ if (srcType == Class::kPrimByte || srcType == Class::kPrimShort) {
+ dst.i = src.i;
+ return true;
+ }
+ break;
+ case Class::kPrimInt:
+ if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+ srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+ dst.i = src.i;
+ return true;
+ }
+ break;
+ case Class::kPrimLong:
+ if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+ srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+ dst.j = src.i;
+ return true;
+ } else if (srcType == Class::kPrimLong) {
+ dst.j = src.j;
+ return true;
+ }
+ break;
+ case Class::kPrimFloat:
+ if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+ srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+ dst.f = src.i;
+ return true;
+ } else if (srcType == Class::kPrimLong) {
+ dst.f = src.j;
+ return true;
+ } else if (srcType == Class::kPrimFloat) {
+ dst.i = src.i;
+ return true;
+ }
+ break;
+ case Class::kPrimDouble:
+ if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
+ srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
+ dst.d = src.i;
+ return true;
+ } else if (srcType == Class::kPrimLong) {
+ dst.d = src.j;
+ return true;
+ } else if (srcType == Class::kPrimFloat) {
+ dst.d = src.f;
+ return true;
+ } else if (srcType == Class::kPrimDouble) {
+ dst.j = src.j;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+ "invalid primitive conversion from %s to %s",
+ PrettyDescriptor(src_class->GetDescriptor()).c_str(),
+ PrettyDescriptor(dst_class->GetDescriptor()).c_str());
+ return false;
+}
+
+void BoxPrimitive(JNIEnv* env, Class* src_class, JValue& value) {
+ if (!src_class->IsPrimitive()) {
+ return;
+ }
+
+ Method* m = NULL;
+ UniquePtr<byte[]> args(new byte[8]);
+ memset(&args[0], 0, 8);
+ switch (src_class->GetPrimitiveType()) {
+ case Class::kPrimBoolean:
+ m = gBoolean_valueOf;
+ *reinterpret_cast<uint32_t*>(&args[0]) = value.z;
+ break;
+ case Class::kPrimByte:
+ m = gByte_valueOf;
+ *reinterpret_cast<uint32_t*>(&args[0]) = value.b;
+ break;
+ case Class::kPrimChar:
+ m = gCharacter_valueOf;
+ *reinterpret_cast<uint32_t*>(&args[0]) = value.c;
+ break;
+ case Class::kPrimDouble:
+ m = gDouble_valueOf;
+ *reinterpret_cast<double*>(&args[0]) = value.d;
+ break;
+ case Class::kPrimFloat:
+ m = gFloat_valueOf;
+ *reinterpret_cast<float*>(&args[0]) = value.f;
+ break;
+ case Class::kPrimInt:
+ m = gInteger_valueOf;
+ *reinterpret_cast<uint32_t*>(&args[0]) = value.i;
+ break;
+ case Class::kPrimLong:
+ m = gLong_valueOf;
+ *reinterpret_cast<uint64_t*>(&args[0]) = value.j;
+ break;
+ case Class::kPrimShort:
+ m = gShort_valueOf;
+ *reinterpret_cast<uint32_t*>(&args[0]) = value.s;
+ break;
+ case Class::kPrimVoid:
+ // There's no such thing as a void field, and void methods invoked via reflection return null.
+ value.l = NULL;
+ return;
+ default:
+ LOG(FATAL) << PrettyClass(src_class);
+ }
+
+ Thread* self = Thread::Current();
+ ScopedThreadStateChange tsc(self, Thread::kRunnable);
+ m->Invoke(self, NULL, args.get(), &value);
+}
+
+bool UnboxPrimitive(JNIEnv* env, Object* o, Class* dst_class, JValue& unboxed_value) {
+ if (dst_class->GetPrimitiveType() == Class::kPrimNot) {
+ if (o != NULL && !o->InstanceOf(dst_class)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "expected object of type %s, but got %s",
+ PrettyDescriptor(dst_class->GetDescriptor()).c_str(),
+ PrettyTypeOf(o).c_str());
+ return false;
+ }
+ unboxed_value.l = o;
+ return true;
+ } else if (dst_class->GetPrimitiveType() == Class::kPrimVoid) {
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+ "can't unbox to void");
+ return false;
+ }
+
+ if (o == NULL) {
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+ "null passed for boxed primitive type");
+ return false;
+ }
+
+ JValue boxed_value = { 0 };
+ const String* src_descriptor = o->GetClass()->GetDescriptor();
+ Class* src_class = NULL;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Field* primitive_field = o->GetClass()->GetIFields()->Get(0);
+ if (src_descriptor->Equals("Ljava/lang/Boolean;")) {
+ src_class = class_linker->FindPrimitiveClass('Z');
+ boxed_value.z = primitive_field->GetBoolean(o);
+ } else if (src_descriptor->Equals("Ljava/lang/Byte;")) {
+ src_class = class_linker->FindPrimitiveClass('B');
+ boxed_value.b = primitive_field->GetByte(o);
+ } else if (src_descriptor->Equals("Ljava/lang/Character;")) {
+ src_class = class_linker->FindPrimitiveClass('C');
+ boxed_value.c = primitive_field->GetChar(o);
+ } else if (src_descriptor->Equals("Ljava/lang/Float;")) {
+ src_class = class_linker->FindPrimitiveClass('F');
+ boxed_value.f = primitive_field->GetFloat(o);
+ } else if (src_descriptor->Equals("Ljava/lang/Double;")) {
+ src_class = class_linker->FindPrimitiveClass('D');
+ boxed_value.d = primitive_field->GetDouble(o);
+ } else if (src_descriptor->Equals("Ljava/lang/Integer;")) {
+ src_class = class_linker->FindPrimitiveClass('I');
+ boxed_value.i = primitive_field->GetInt(o);
+ } else if (src_descriptor->Equals("Ljava/lang/Long;")) {
+ src_class = class_linker->FindPrimitiveClass('J');
+ boxed_value.j = primitive_field->GetLong(o);
+ } else if (src_descriptor->Equals("Ljava/lang/Short;")) {
+ src_class = class_linker->FindPrimitiveClass('S');
+ boxed_value.s = primitive_field->GetShort(o);
+ } else {
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+ "%s is not a boxed primitive type", PrettyDescriptor(src_descriptor).c_str());
+ return false;
+ }
+
+ return ConvertPrimitiveValue(src_class, dst_class, boxed_value, unboxed_value);
+}
+
+} // namespace art