blob: 5cb059ef60ca58a4702cbf24b2cd80e7775cff11 [file] [log] [blame]
Shih-wei Liaob0ee9d72012-03-07 16:39:26 -08001// Copyright 2012 Google Inc. All Rights Reserved.
2
3#ifndef ART_SRC_RUNTIME_SUPPORT_COMMON_H_
4#define ART_SRC_RUNTIME_SUPPORT_COMMON_H_
5
6#include "class_linker.h"
7#include "constants.h"
8#include "object.h"
9#include "object_utils.h"
10#include "thread.h"
11
12#include <stdint.h>
13
14namespace art {
15
16class Array;
17class Class;
18class Field;
19class Method;
20class Object;
21
22static void ThrowNewIllegalAccessErrorClass(Thread* self,
23 Class* referrer,
24 Class* accessed) {
25 self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
26 "illegal class access: '%s' -> '%s'",
27 PrettyDescriptor(referrer).c_str(),
28 PrettyDescriptor(accessed).c_str());
29}
30
31static void
32ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self,
33 Class* referrer,
34 Class* accessed,
35 const Method* caller,
36 const Method* called,
37 InvokeType type) {
38 std::ostringstream type_stream;
39 type_stream << type;
40 self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
41 "illegal class access ('%s' -> '%s')"
42 "in attempt to invoke %s method '%s' from '%s'",
43 PrettyDescriptor(referrer).c_str(),
44 PrettyDescriptor(accessed).c_str(),
45 type_stream.str().c_str(),
46 PrettyMethod(called).c_str(),
47 PrettyMethod(caller).c_str());
48}
49
50static void
51ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
52 const Method* referrer,
53 const Method* interface_method,
54 Object* this_object) {
55 self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
56 "class '%s' does not implement interface '%s' in call to '%s' from '%s'",
57 PrettyDescriptor(this_object->GetClass()).c_str(),
58 PrettyDescriptor(interface_method->GetDeclaringClass()).c_str(),
59 PrettyMethod(interface_method).c_str(), PrettyMethod(referrer).c_str());
60}
61
62static void ThrowNewIllegalAccessErrorField(Thread* self,
63 Class* referrer,
64 Field* accessed) {
65 self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
66 "Field '%s' is inaccessible to class '%s'",
67 PrettyField(accessed, false).c_str(),
68 PrettyDescriptor(referrer).c_str());
69}
70
71static void ThrowNewIllegalAccessErrorFinalField(Thread* self,
72 const Method* referrer,
73 Field* accessed) {
74 self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
75 "Final field '%s' cannot be written to by method '%s'",
76 PrettyField(accessed, false).c_str(),
77 PrettyMethod(referrer).c_str());
78}
79
80static void ThrowNewIllegalAccessErrorMethod(Thread* self,
81 Class* referrer,
82 Method* accessed) {
83 self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
84 "Method '%s' is inaccessible to class '%s'",
85 PrettyMethod(accessed).c_str(),
86 PrettyDescriptor(referrer).c_str());
87}
88
89static void ThrowNullPointerExceptionForFieldAccess(Thread* self,
90 Field* field,
91 bool is_read) {
92 self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
93 "Attempt to %s field '%s' on a null object reference",
94 is_read ? "read from" : "write to",
95 PrettyField(field, true).c_str());
96}
97
98static void ThrowNullPointerExceptionForMethodAccess(Thread* self,
99 Method* caller,
100 uint32_t method_idx,
101 InvokeType type) {
102 const DexFile& dex_file =
103 Runtime::Current()->GetClassLinker()->FindDexFile(caller->GetDeclaringClass()->GetDexCache());
104 std::ostringstream type_stream;
105 type_stream << type;
106 self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
107 "Attempt to invoke %s method '%s' from '%s' on a null object reference",
108 type_stream.str().c_str(),
109 PrettyMethod(method_idx, dex_file, true).c_str(),
110 PrettyMethod(caller).c_str());
111}
112
113// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
114// cannot be resolved, throw an error. If it can, use it to create an instance.
115// When verification/compiler hasn't been able to verify access, optionally perform an access
116// check.
117static Object* AllocObjectFromCode(uint32_t type_idx, Method* method, Thread* self,
118 bool access_check) {
119 Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
120 Runtime* runtime = Runtime::Current();
121 if (UNLIKELY(klass == NULL)) {
122 klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
123 if (klass == NULL) {
124 DCHECK(self->IsExceptionPending());
125 return NULL; // Failure
126 }
127 }
128 if (access_check) {
129 if (UNLIKELY(!klass->IsInstantiable())) {
130 self->ThrowNewException("Ljava/lang/InstantiationError;",
131 PrettyDescriptor(klass).c_str());
132 return NULL; // Failure
133 }
134 Class* referrer = method->GetDeclaringClass();
135 if (UNLIKELY(!referrer->CanAccess(klass))) {
136 ThrowNewIllegalAccessErrorClass(self, referrer, klass);
137 return NULL; // Failure
138 }
139 }
140 if (!runtime->GetClassLinker()->EnsureInitialized(klass, true)) {
141 DCHECK(self->IsExceptionPending());
142 return NULL; // Failure
143 }
144 return klass->AllocObject();
145}
146
147// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
148// it cannot be resolved, throw an error. If it can, use it to create an array.
149// When verification/compiler hasn't been able to verify access, optionally perform an access
150// check.
151static Array* AllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
152 Thread* self, bool access_check) {
153 if (UNLIKELY(component_count < 0)) {
154 Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
155 component_count);
156 return NULL; // Failure
157 }
158 Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
159 if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
160 klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
161 if (klass == NULL) { // Error
162 DCHECK(Thread::Current()->IsExceptionPending());
163 return NULL; // Failure
164 }
165 CHECK(klass->IsArrayClass()) << PrettyClass(klass);
166 }
167 if (access_check) {
168 Class* referrer = method->GetDeclaringClass();
169 if (UNLIKELY(!referrer->CanAccess(klass))) {
170 ThrowNewIllegalAccessErrorClass(self, referrer, klass);
171 return NULL; // Failure
172 }
173 }
174 return Array::Alloc(klass, component_count);
175}
176
177extern Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
178 Thread* self, bool access_check);
179
180extern Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
181 bool is_static, bool is_primitive, bool is_set,
182 size_t expected_size);
183
184// Fast path field resolution that can't throw exceptions
185static Field* FindFieldFast(uint32_t field_idx, const Method* referrer, bool is_primitive,
186 size_t expected_size, bool is_set) {
187 Field* resolved_field = referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx);
188 if (UNLIKELY(resolved_field == NULL)) {
189 return NULL;
190 }
191 Class* fields_class = resolved_field->GetDeclaringClass();
192 // Check class is initiliazed or initializing
193 if (UNLIKELY(!fields_class->IsInitializing())) {
194 return NULL;
195 }
196 Class* referring_class = referrer->GetDeclaringClass();
197 if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
198 !referring_class->CanAccessMember(fields_class,
199 resolved_field->GetAccessFlags()) ||
200 (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
201 // illegal access
202 return NULL;
203 }
204 FieldHelper fh(resolved_field);
205 if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
206 fh.FieldSize() != expected_size)) {
207 return NULL;
208 }
209 return resolved_field;
210}
211
212// Fast path method resolution that can't throw exceptions
213static Method* FindMethodFast(uint32_t method_idx, Object* this_object, const Method* referrer,
214 bool access_check, InvokeType type) {
215 bool is_direct = type == kStatic || type == kDirect;
216 if (UNLIKELY(this_object == NULL && !is_direct)) {
217 return NULL;
218 }
219 Method* resolved_method =
220 referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx);
221 if (UNLIKELY(resolved_method == NULL)) {
222 return NULL;
223 }
224 if (access_check) {
225 Class* methods_class = resolved_method->GetDeclaringClass();
226 Class* referring_class = referrer->GetDeclaringClass();
227 if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
228 !referring_class->CanAccessMember(methods_class,
229 resolved_method->GetAccessFlags()))) {
230 // potential illegal access
231 return NULL;
232 }
233 }
234 if (type == kInterface) { // Most common form of slow path dispatch.
235 return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
236 } else if (is_direct) {
237 return resolved_method;
238 } else if (type == kSuper) {
239 return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->
240 Get(resolved_method->GetMethodIndex());
241 } else {
242 DCHECK(type == kVirtual);
243 return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
244 }
245}
246
247extern Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
248 Thread* self, bool access_check, InvokeType type);
249
250} // namespace art
251
252#endif // ART_SRC_RUNTIME_SUPPORT_COMMON_H_