blob: 3edb9f7fe602a9974c826ae99cff5d9f2f90a63c [file] [log] [blame]
Elliott Hughes418d20f2011-09-22 14:00:39 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "reflection.h"
18
19#include "class_linker.h"
20#include "jni_internal.h"
21#include "object.h"
22
23#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
24
25namespace art {
26
27Method* gBoolean_valueOf;
28Method* gByte_valueOf;
29Method* gCharacter_valueOf;
30Method* gDouble_valueOf;
31Method* gFloat_valueOf;
32Method* gInteger_valueOf;
33Method* gLong_valueOf;
34Method* gShort_valueOf;
35
36void InitBoxingMethod(JNIEnv* env, Method*& m, jclass c, const char* method_signature) {
37 m = DecodeMethod(env->GetStaticMethodID(c, "valueOf", method_signature));
38}
39
40void InitBoxingMethods(JNIEnv* env) {
41 InitBoxingMethod(env, gBoolean_valueOf, JniConstants::booleanClass, "(Z)Ljava/lang/Boolean;");
42 InitBoxingMethod(env, gByte_valueOf, JniConstants::byteClass, "(B)Ljava/lang/Byte;");
43 InitBoxingMethod(env, gCharacter_valueOf, JniConstants::characterClass, "(C)Ljava/lang/Character;");
44 InitBoxingMethod(env, gDouble_valueOf, JniConstants::doubleClass, "(D)Ljava/lang/Double;");
45 InitBoxingMethod(env, gFloat_valueOf, JniConstants::floatClass, "(F)Ljava/lang/Float;");
46 InitBoxingMethod(env, gInteger_valueOf, JniConstants::integerClass, "(I)Ljava/lang/Integer;");
47 InitBoxingMethod(env, gLong_valueOf, JniConstants::longClass, "(J)Ljava/lang/Long;");
48 InitBoxingMethod(env, gShort_valueOf, JniConstants::shortClass, "(S)Ljava/lang/Short;");
49}
50
51bool VerifyObjectInClass(JNIEnv* env, Object* o, Class* c) {
52 if (o == NULL) {
53 jniThrowNullPointerException(env, "receiver for non-static field access was null");
54 return false;
55 }
56 if (!o->InstanceOf(c)) {
57 std::string expectedClassName(PrettyDescriptor(c->GetDescriptor()));
58 std::string actualClassName(PrettyTypeOf(o));
59 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
60 "expected receiver of type %s, but got %s",
61 expectedClassName.c_str(), actualClassName.c_str());
62 return false;
63 }
64 return true;
65}
66
67/*
68 * Convert primitive, boxed data from "srcPtr" to "dstPtr".
69 *
70 * Section v2 2.6 lists the various conversions and promotions. We
71 * allow the "widening" and "identity" conversions, but don't allow the
72 * "narrowing" conversions.
73 *
74 * Allowed:
75 * byte to short, int, long, float, double
76 * short to int, long, float double
77 * char to int, long, float, double
78 * int to long, float, double
79 * long to float, double
80 * float to double
81 * Values of types byte, char, and short are "internally" widened to int.
82 *
83 * Returns the width in 32-bit words of the destination primitive, or
84 * -1 if the conversion is not allowed.
85 */
86bool ConvertPrimitiveValue(Class* src_class, Class* dst_class, const JValue& src, JValue& dst) {
87 Class::PrimitiveType srcType = src_class->GetPrimitiveType();
88 Class::PrimitiveType dstType = dst_class->GetPrimitiveType();
89 switch (dstType) {
90 case Class::kPrimBoolean:
91 case Class::kPrimChar:
92 case Class::kPrimByte:
93 if (srcType == dstType) {
94 dst.i = src.i;
95 return true;
96 }
97 break;
98 case Class::kPrimShort:
99 if (srcType == Class::kPrimByte || srcType == Class::kPrimShort) {
100 dst.i = src.i;
101 return true;
102 }
103 break;
104 case Class::kPrimInt:
105 if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
106 srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
107 dst.i = src.i;
108 return true;
109 }
110 break;
111 case Class::kPrimLong:
112 if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
113 srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
114 dst.j = src.i;
115 return true;
116 } else if (srcType == Class::kPrimLong) {
117 dst.j = src.j;
118 return true;
119 }
120 break;
121 case Class::kPrimFloat:
122 if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
123 srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
124 dst.f = src.i;
125 return true;
126 } else if (srcType == Class::kPrimLong) {
127 dst.f = src.j;
128 return true;
129 } else if (srcType == Class::kPrimFloat) {
130 dst.i = src.i;
131 return true;
132 }
133 break;
134 case Class::kPrimDouble:
135 if (srcType == Class::kPrimByte || srcType == Class::kPrimChar ||
136 srcType == Class::kPrimShort || srcType == Class::kPrimInt) {
137 dst.d = src.i;
138 return true;
139 } else if (srcType == Class::kPrimLong) {
140 dst.d = src.j;
141 return true;
142 } else if (srcType == Class::kPrimFloat) {
143 dst.d = src.f;
144 return true;
145 } else if (srcType == Class::kPrimDouble) {
146 dst.j = src.j;
147 return true;
148 }
149 break;
150 default:
151 break;
152 }
153 Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
154 "invalid primitive conversion from %s to %s",
155 PrettyDescriptor(src_class->GetDescriptor()).c_str(),
156 PrettyDescriptor(dst_class->GetDescriptor()).c_str());
157 return false;
158}
159
160void BoxPrimitive(JNIEnv* env, Class* src_class, JValue& value) {
161 if (!src_class->IsPrimitive()) {
162 return;
163 }
164
165 Method* m = NULL;
166 UniquePtr<byte[]> args(new byte[8]);
167 memset(&args[0], 0, 8);
168 switch (src_class->GetPrimitiveType()) {
169 case Class::kPrimBoolean:
170 m = gBoolean_valueOf;
171 *reinterpret_cast<uint32_t*>(&args[0]) = value.z;
172 break;
173 case Class::kPrimByte:
174 m = gByte_valueOf;
175 *reinterpret_cast<uint32_t*>(&args[0]) = value.b;
176 break;
177 case Class::kPrimChar:
178 m = gCharacter_valueOf;
179 *reinterpret_cast<uint32_t*>(&args[0]) = value.c;
180 break;
181 case Class::kPrimDouble:
182 m = gDouble_valueOf;
183 *reinterpret_cast<double*>(&args[0]) = value.d;
184 break;
185 case Class::kPrimFloat:
186 m = gFloat_valueOf;
187 *reinterpret_cast<float*>(&args[0]) = value.f;
188 break;
189 case Class::kPrimInt:
190 m = gInteger_valueOf;
191 *reinterpret_cast<uint32_t*>(&args[0]) = value.i;
192 break;
193 case Class::kPrimLong:
194 m = gLong_valueOf;
195 *reinterpret_cast<uint64_t*>(&args[0]) = value.j;
196 break;
197 case Class::kPrimShort:
198 m = gShort_valueOf;
199 *reinterpret_cast<uint32_t*>(&args[0]) = value.s;
200 break;
201 case Class::kPrimVoid:
202 // There's no such thing as a void field, and void methods invoked via reflection return null.
203 value.l = NULL;
204 return;
205 default:
206 LOG(FATAL) << PrettyClass(src_class);
207 }
208
209 Thread* self = Thread::Current();
210 ScopedThreadStateChange tsc(self, Thread::kRunnable);
211 m->Invoke(self, NULL, args.get(), &value);
212}
213
214bool UnboxPrimitive(JNIEnv* env, Object* o, Class* dst_class, JValue& unboxed_value) {
215 if (dst_class->GetPrimitiveType() == Class::kPrimNot) {
216 if (o != NULL && !o->InstanceOf(dst_class)) {
217 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
218 "expected object of type %s, but got %s",
219 PrettyDescriptor(dst_class->GetDescriptor()).c_str(),
220 PrettyTypeOf(o).c_str());
221 return false;
222 }
223 unboxed_value.l = o;
224 return true;
225 } else if (dst_class->GetPrimitiveType() == Class::kPrimVoid) {
226 Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
227 "can't unbox to void");
228 return false;
229 }
230
231 if (o == NULL) {
232 Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
233 "null passed for boxed primitive type");
234 return false;
235 }
236
237 JValue boxed_value = { 0 };
238 const String* src_descriptor = o->GetClass()->GetDescriptor();
239 Class* src_class = NULL;
240 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
241 Field* primitive_field = o->GetClass()->GetIFields()->Get(0);
242 if (src_descriptor->Equals("Ljava/lang/Boolean;")) {
243 src_class = class_linker->FindPrimitiveClass('Z');
244 boxed_value.z = primitive_field->GetBoolean(o);
245 } else if (src_descriptor->Equals("Ljava/lang/Byte;")) {
246 src_class = class_linker->FindPrimitiveClass('B');
247 boxed_value.b = primitive_field->GetByte(o);
248 } else if (src_descriptor->Equals("Ljava/lang/Character;")) {
249 src_class = class_linker->FindPrimitiveClass('C');
250 boxed_value.c = primitive_field->GetChar(o);
251 } else if (src_descriptor->Equals("Ljava/lang/Float;")) {
252 src_class = class_linker->FindPrimitiveClass('F');
253 boxed_value.f = primitive_field->GetFloat(o);
254 } else if (src_descriptor->Equals("Ljava/lang/Double;")) {
255 src_class = class_linker->FindPrimitiveClass('D');
256 boxed_value.d = primitive_field->GetDouble(o);
257 } else if (src_descriptor->Equals("Ljava/lang/Integer;")) {
258 src_class = class_linker->FindPrimitiveClass('I');
259 boxed_value.i = primitive_field->GetInt(o);
260 } else if (src_descriptor->Equals("Ljava/lang/Long;")) {
261 src_class = class_linker->FindPrimitiveClass('J');
262 boxed_value.j = primitive_field->GetLong(o);
263 } else if (src_descriptor->Equals("Ljava/lang/Short;")) {
264 src_class = class_linker->FindPrimitiveClass('S');
265 boxed_value.s = primitive_field->GetShort(o);
266 } else {
267 Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
268 "%s is not a boxed primitive type", PrettyDescriptor(src_descriptor).c_str());
269 return false;
270 }
271
272 return ConvertPrimitiveValue(src_class, dst_class, boxed_value, unboxed_value);
273}
274
275} // namespace art