blob: 491d13926f19ef70ec5e22da3741f86013c2a90a [file] [log] [blame]
Orion Hodsonba28f9f2016-10-26 10:56:25 +01001/*
2 * Copyright (C) 2016 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 "method_handles.h"
18
19#include "method_handles-inl.h"
20#include "jvalue.h"
21#include "jvalue-inl.h"
22#include "reflection.h"
23#include "reflection-inl.h"
24
25namespace art {
26
27namespace {
28
29static const char* kBoxedBooleanClass = "Ljava/lang/Boolean;";
30static const char* kBoxedByteClass = "Ljava/lang/Byte;";
31static const char* kBoxedCharacterClass = "Ljava/lang/Character;";
32static const char* kBoxedDoubleClass = "Ljava/lang/Double;";
33static const char* kBoxedFloatClass = "Ljava/lang/Float;";
34static const char* kBoxedIntegerClass = "Ljava/lang/Integer;";
35static const char* kBoxedLongClass = "Ljava/lang/Long;";
36static const char* kBoxedShortClass = "Ljava/lang/Short;";
37
38// Assigns |type| to the primitive type associated with |klass|. Returns
39// true iff. |klass| was a boxed type (Integer, Long etc.), false otherwise.
40bool GetUnboxedPrimitiveType(ObjPtr<mirror::Class> klass, Primitive::Type* type)
41 REQUIRES_SHARED(Locks::mutator_lock_) {
42 ScopedAssertNoThreadSuspension ants(__FUNCTION__);
43 if (klass->DescriptorEquals(kBoxedBooleanClass)) {
44 (*type) = Primitive::kPrimBoolean;
45 return true;
46 } else if (klass->DescriptorEquals(kBoxedByteClass)) {
47 (*type) = Primitive::kPrimByte;
48 return true;
49 } else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
50 (*type) = Primitive::kPrimChar;
51 return true;
52 } else if (klass->DescriptorEquals(kBoxedFloatClass)) {
53 (*type) = Primitive::kPrimFloat;
54 return true;
55 } else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
56 (*type) = Primitive::kPrimDouble;
57 return true;
58 } else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
59 (*type) = Primitive::kPrimInt;
60 return true;
61 } else if (klass->DescriptorEquals(kBoxedLongClass)) {
62 (*type) = Primitive::kPrimLong;
63 return true;
64 } else if (klass->DescriptorEquals(kBoxedShortClass)) {
65 (*type) = Primitive::kPrimShort;
66 return true;
67 } else {
68 return false;
69 }
70}
71
72// Returns the class corresponding to the boxed type for the primitive |type|.
73ObjPtr<mirror::Class> GetBoxedPrimitiveClass(Primitive::Type type)
74 REQUIRES_SHARED(Locks::mutator_lock_) {
75 ScopedAssertNoThreadSuspension ants(__FUNCTION__);
76 ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
77 switch (type) {
78 case Primitive::kPrimBoolean:
79 return class_linker->FindSystemClass(Thread::Current(), kBoxedBooleanClass);
80 case Primitive::kPrimByte:
81 return class_linker->FindSystemClass(Thread::Current(), kBoxedByteClass);
82 case Primitive::kPrimChar:
83 return class_linker->FindSystemClass(Thread::Current(), kBoxedCharacterClass);
84 case Primitive::kPrimShort:
85 return class_linker->FindSystemClass(Thread::Current(), kBoxedShortClass);
86 case Primitive::kPrimInt:
87 return class_linker->FindSystemClass(Thread::Current(), kBoxedIntegerClass);
88 case Primitive::kPrimLong:
89 return class_linker->FindSystemClass(Thread::Current(), kBoxedLongClass);
90 case Primitive::kPrimFloat:
91 return class_linker->FindSystemClass(Thread::Current(), kBoxedFloatClass);
92 case Primitive::kPrimDouble:
93 return class_linker->FindSystemClass(Thread::Current(), kBoxedDoubleClass);
94 case Primitive::kPrimNot:
95 case Primitive::kPrimVoid:
96 LOG(FATAL) << "Unreachable";
97 return nullptr;
98 }
99}
100
101// Returns true if |klass| is a boxed primitive type or a sub-class of a boxed primitive type.
102bool IsSubClassOfBoxedPrimitive(const Handle<mirror::Class>& klass)
103 REQUIRES_SHARED(Locks::mutator_lock_) {
104 StackHandleScope<1> hs(Thread::Current());
105 MutableHandle<mirror::Class> h_klass(hs.NewHandle(klass.Get()));
106 do {
107 Primitive::Type type;
108 if (GetUnboxedPrimitiveType(h_klass.Get(), &type)) {
109 return true;
110 }
111 h_klass.Assign(h_klass->GetSuperClass());
112 } while (h_klass.Get() != nullptr);
113 return false;
114}
115
116// Unboxed the value |o| to |unboxed_value| of type |dst_class|.
117// |unboxed_value| must be zero on entry to avoid dangling pointers.
118// Returns true on success, false if an exception is raised.
119bool UnboxPrimitiveForMethodHandles(ObjPtr<mirror::Object> o,
120 ObjPtr<mirror::Class> dst_class,
121 JValue* unboxed_value)
122 REQUIRES_SHARED(Locks::mutator_lock_) {
123 // Check unboxed_value does not contain a dangling pointer.
124 DCHECK_EQ(unboxed_value->GetJ(), 0);
125 DCHECK(dst_class->IsPrimitive());
126
127 // This is derived from UnboxPrimitive() in reflection.cc, but with
128 // exceptions appropriate to method handles.
129 if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
130 ThrowClassCastException(o->GetClass(), dst_class);
131 return false;
132 }
133 if (UNLIKELY(o == nullptr)) {
134 ThrowNullPointerException(
135 StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
136 dst_class->PrettyDescriptor().c_str()).c_str());
137 return false;
138 }
139
140 JValue boxed_value;
141 ObjPtr<mirror::Class> klass = o->GetClass();
142 ObjPtr<mirror::Class> src_class = nullptr;
143 ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
144 ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
145 if (klass->DescriptorEquals(kBoxedBooleanClass)) {
146 src_class = class_linker->FindPrimitiveClass('Z');
147 boxed_value.SetZ(primitive_field->GetBoolean(o));
148 } else if (klass->DescriptorEquals(kBoxedByteClass)) {
149 src_class = class_linker->FindPrimitiveClass('B');
150 boxed_value.SetB(primitive_field->GetByte(o));
151 } else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
152 src_class = class_linker->FindPrimitiveClass('C');
153 boxed_value.SetC(primitive_field->GetChar(o));
154 } else if (klass->DescriptorEquals(kBoxedFloatClass)) {
155 src_class = class_linker->FindPrimitiveClass('F');
156 boxed_value.SetF(primitive_field->GetFloat(o));
157 } else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
158 src_class = class_linker->FindPrimitiveClass('D');
159 boxed_value.SetD(primitive_field->GetDouble(o));
160 } else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
161 src_class = class_linker->FindPrimitiveClass('I');
162 boxed_value.SetI(primitive_field->GetInt(o));
163 } else if (klass->DescriptorEquals(kBoxedLongClass)) {
164 src_class = class_linker->FindPrimitiveClass('J');
165 boxed_value.SetJ(primitive_field->GetLong(o));
166 } else if (klass->DescriptorEquals(kBoxedShortClass)) {
167 src_class = class_linker->FindPrimitiveClass('S');
168 boxed_value.SetS(primitive_field->GetShort(o));
169 } else {
170 std::string temp;
171 ThrowIllegalArgumentException(
172 StringPrintf("result has type %s, got %s",
173 dst_class->PrettyDescriptor().c_str(),
174 PrettyDescriptor(o->GetClass()->GetDescriptor(&temp)).c_str()).c_str());
175 return false;
176 }
177
178 if (!ConvertPrimitiveValueNoThrow(src_class->GetPrimitiveType(),
179 dst_class->GetPrimitiveType(),
180 boxed_value,
181 unboxed_value)) {
182 ThrowClassCastException(src_class, dst_class);
183 return false;
184 }
185 return true;
186}
187
188inline bool IsReferenceType(Primitive::Type type) {
189 return type == Primitive::kPrimNot;
190}
191
192inline bool IsPrimitiveType(Primitive::Type type) {
193 return !IsReferenceType(type);
194}
195
196} // namespace
197
198bool ConvertJValueCommon(
199 Handle<mirror::MethodType> callsite_type,
200 Handle<mirror::MethodType> callee_type,
201 ObjPtr<mirror::Class> from,
202 ObjPtr<mirror::Class> to,
203 JValue* value) {
204 // The reader maybe concerned about the safety of the heap object
205 // that may be in |value|. There is only one case where allocation
206 // is obviously needed and that's for boxing. However, in the case
207 // of boxing |value| contains a non-reference type.
208
209 const Primitive::Type from_type = from->GetPrimitiveType();
210 const Primitive::Type to_type = to->GetPrimitiveType();
211
212 // This method must be called only when the types don't match.
213 DCHECK(from != to);
214
215 if (IsPrimitiveType(from_type) && IsPrimitiveType(to_type)) {
216 // The source and target types are both primitives.
217 if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, to_type, *value, value))) {
218 ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
219 value->SetJ(0);
220 return false;
221 }
222 return true;
223 } else if (IsReferenceType(from_type) && IsReferenceType(to_type)) {
224 // They're both reference types. If "from" is null, we can pass it
225 // through unchanged. If not, we must generate a cast exception if
226 // |to| is not assignable from the dynamic type of |ref|.
227 //
228 // Playing it safe with StackHandleScope here, not expecting any allocation
229 // in mirror::Class::IsAssignable().
230 StackHandleScope<2> hs(Thread::Current());
231 Handle<mirror::Class> h_to(hs.NewHandle(to));
232 Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
233
234 // |value| will now be the result value, invalidate its existing value
235 // as |h_obj| now owns it.
236 value->SetJ(0);
237
238 if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
239 ThrowClassCastException(h_to.Get(), h_obj->GetClass());
240 return false;
241 }
242 value->SetL(h_obj.Get());
243 return true;
244 } else if (IsReferenceType(to_type)) {
245 DCHECK(IsPrimitiveType(from_type));
246 // Playing it safe with StackHandleScope here with regards to
247 // GetUnboxedPrimitiveType() and GetBoxedPrimitiveClass().
248 StackHandleScope<1> hs(Thread::Current());
249 Handle<mirror::Class> h_to(hs.NewHandle(to));
250 // The source type is a primitive and the target type is a reference, so we must box.
251 // The target type maybe a super class of the boxed source type, for example,
252 // if the source type is int, it's boxed type is java.lang.Integer, and the target
253 // type could be java.lang.Number.
254 Primitive::Type type;
255 if (!GetUnboxedPrimitiveType(to, &type)) {
256 ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type);
257 if (boxed_from_class->IsSubClass(h_to.Get())) {
258 type = from_type;
259 } else {
260 value->SetJ(0);
261 ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
262 return false;
263 }
264 }
265
266 if (UNLIKELY(from_type != type)) {
267 value->SetJ(0);
268 ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
269 return false;
270 }
271
272 if (!ConvertPrimitiveValueNoThrow(from_type, type, *value, value)) {
273 value->SetJ(0);
274 ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
275 return false;
276 }
277
278 // Then perform the actual boxing, and then set the reference.
279 ObjPtr<mirror::Object> boxed = BoxPrimitive(type, *value);
280 value->SetL(boxed.Ptr());
281 return true;
282 } else {
283 // The source type is a reference and the target type is a primitive, so we must unbox.
284 DCHECK(IsReferenceType(from_type));
285 DCHECK(IsPrimitiveType(to_type));
286
287 // Use StackHandleScope to protect |from|, |to|, and the reference
288 // in |value| from heap re-arrangements that could be triggered
289 // ahead of unboxing step.
290 StackHandleScope<3> hs(Thread::Current());
291 Handle<mirror::Class> h_to(hs.NewHandle(to));
292 Handle<mirror::Class> h_from(hs.NewHandle(from));
293 Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
294
295 // |value| will now be the result value, invalidate its existing value
296 // as |h_obj| now owns it.
297 value->SetJ(0);
298
299 // Check source type is a boxed primitive or has a boxed primitive super-class.
300 ObjPtr<mirror::Class> boxed_to_class = GetBoxedPrimitiveClass(to_type);
301 if (!IsSubClassOfBoxedPrimitive(h_from) && !boxed_to_class->IsSubClass(h_from.Get())) {
302 ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
303 return false;
304 }
305
306 if (h_obj.Get() == nullptr) {
307 ThrowNullPointerException(
308 StringPrintf("Expected to unbox a '%s' but instance was null",
309 h_from->PrettyDescriptor().c_str()).c_str());
310 return false;
311 }
312
313 return UnboxPrimitiveForMethodHandles(h_obj.Get(), h_to.Get(), value);
314 }
315}
316
317} // namespace art