blob: 71567a343eb4570af769fef44a31383319119409 [file] [log] [blame]
Shih-wei Liaob0ee9d72012-03-07 16:39:26 -08001// Copyright 2012 Google Inc. All Rights Reserved.
2
3#include "runtime_support_common.h"
4
5
6namespace art {
7
8// Helper function to allocate array for FILLED_NEW_ARRAY.
9Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
10 Thread* self, bool access_check) {
11 if (UNLIKELY(component_count < 0)) {
12 self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count);
13 return NULL; // Failure
14 }
15 Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
16 if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
17 klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
18 if (klass == NULL) { // Error
19 DCHECK(Thread::Current()->IsExceptionPending());
20 return NULL; // Failure
21 }
22 }
23 if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
24 if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
25 Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
26 "Bad filled array request for type %s",
27 PrettyDescriptor(klass).c_str());
28 } else {
29 Thread::Current()->ThrowNewExceptionF("Ljava/lang/InternalError;",
30 "Found type %s; filled-new-array not implemented for anything but \'int\'",
31 PrettyDescriptor(klass).c_str());
32 }
33 return NULL; // Failure
34 } else {
35 if (access_check) {
36 Class* referrer = method->GetDeclaringClass();
37 if (UNLIKELY(!referrer->CanAccess(klass))) {
38 ThrowNewIllegalAccessErrorClass(self, referrer, klass);
39 return NULL; // Failure
40 }
41 }
42 DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
43 return Array::Alloc(klass, component_count);
44 }
45}
46
47// Slow path field resolution and declaring class initialization
48Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
49 bool is_static, bool is_primitive, bool is_set, size_t expected_size) {
50 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
51 Field* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
52 if (UNLIKELY(resolved_field == NULL)) {
53 DCHECK(self->IsExceptionPending()); // Throw exception and unwind
54 return NULL; // failure
55 } else {
56 Class* fields_class = resolved_field->GetDeclaringClass();
57 Class* referring_class = referrer->GetDeclaringClass();
58 if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
59 ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
60 return NULL; // failure
61 } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
62 resolved_field->GetAccessFlags()))) {
63 ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
64 return NULL; // failure
65 } else if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
66 ThrowNewIllegalAccessErrorFinalField(self, referrer, resolved_field);
67 return NULL; // failure
68 } else {
69 FieldHelper fh(resolved_field);
70 if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
71 fh.FieldSize() != expected_size)) {
72 self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
73 "Attempted read of %zd-bit %s on field '%s'",
74 expected_size * (32 / sizeof(int32_t)),
75 is_primitive ? "primitive" : "non-primitive",
76 PrettyField(resolved_field, true).c_str());
77 return NULL; // failure
78 } else if (!is_static) {
79 // instance fields must be being accessed on an initialized class
80 return resolved_field;
81 } else {
82 // If the class is already initializing, we must be inside <clinit>, or
83 // we'd still be waiting for the lock.
84 if (fields_class->IsInitializing()) {
85 return resolved_field;
86 } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
87 return resolved_field;
88 } else {
89 DCHECK(self->IsExceptionPending()); // Throw exception and unwind
90 return NULL; // failure
91 }
92 }
93 }
94 }
95}
96
97// Slow path method resolution
98Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
99 Thread* self, bool access_check, InvokeType type) {
100 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
101 bool is_direct = type == kStatic || type == kDirect;
102 Method* resolved_method = class_linker->ResolveMethod(method_idx, referrer, is_direct);
103 if (UNLIKELY(resolved_method == NULL)) {
104 DCHECK(self->IsExceptionPending()); // Throw exception and unwind
105 return NULL; // failure
106 } else {
107 if (!access_check) {
108 if (is_direct) {
109 return resolved_method;
110 } else if (type == kInterface) {
111 Method* interface_method =
112 this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
113 if (UNLIKELY(interface_method == NULL)) {
114 ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
115 resolved_method,
116 this_object);
117 return NULL; // failure
118 } else {
119 return interface_method;
120 }
121 } else {
122 ObjectArray<Method>* vtable;
123 uint16_t vtable_index = resolved_method->GetMethodIndex();
124 if (type == kSuper) {
125 vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
126 } else {
127 vtable = this_object->GetClass()->GetVTable();
128 }
129 // TODO: eliminate bounds check?
130 return vtable->Get(vtable_index);
131 }
132 } else {
133 Class* methods_class = resolved_method->GetDeclaringClass();
134 Class* referring_class = referrer->GetDeclaringClass();
135 if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
136 !referring_class->CanAccessMember(methods_class,
137 resolved_method->GetAccessFlags()))) {
138 // The referring class can't access the resolved method, this may occur as a result of a
139 // protected method being made public by implementing an interface that re-declares the
140 // method public. Resort to the dex file to determine the correct class for the access check
141 const DexFile& dex_file = class_linker->FindDexFile(referring_class->GetDexCache());
142 methods_class = class_linker->ResolveType(dex_file,
143 dex_file.GetMethodId(method_idx).class_idx_,
144 referring_class);
145 if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
146 ThrowNewIllegalAccessErrorClassForMethodDispatch(self, referring_class, methods_class,
147 referrer, resolved_method, type);
148 return NULL; // failure
149 } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
150 resolved_method->GetAccessFlags()))) {
151 ThrowNewIllegalAccessErrorMethod(self, referring_class, resolved_method);
152 return NULL; // failure
153 }
154 }
155 if (is_direct) {
156 return resolved_method;
157 } else if (type == kInterface) {
158 Method* interface_method =
159 this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
160 if (UNLIKELY(interface_method == NULL)) {
161 ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
162 resolved_method,
163 this_object);
164 return NULL; // failure
165 } else {
166 return interface_method;
167 }
168 } else {
169 ObjectArray<Method>* vtable;
170 uint16_t vtable_index = resolved_method->GetMethodIndex();
171 if (type == kSuper) {
172 Class* super_class = referring_class->GetSuperClass();
173 if (LIKELY(super_class != NULL)) {
174 vtable = referring_class->GetSuperClass()->GetVTable();
175 } else {
176 vtable = NULL;
177 }
178 } else {
179 vtable = this_object->GetClass()->GetVTable();
180 }
181 if (LIKELY(vtable != NULL &&
182 vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
183 return vtable->GetWithoutChecks(vtable_index);
184 } else {
185 // Behavior to agree with that of the verifier
186 self->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
187 "attempt to invoke %s method '%s' from '%s'"
188 " using incorrect form of method dispatch",
189 (type == kSuper ? "super class" : "virtual"),
190 PrettyMethod(resolved_method).c_str(),
191 PrettyMethod(referrer).c_str());
192 return NULL; // failure
193 }
194 }
195 }
196 }
197}
198
Shih-wei Liao399ed3f2012-03-08 01:27:04 -0800199Class* ResolveVerifyAndClinit(uint32_t type_idx, const Method* referrer, Thread* self,
200 bool can_run_clinit, bool verify_access) {
201 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
202 Class* klass = class_linker->ResolveType(type_idx, referrer);
203 if (UNLIKELY(klass == NULL)) {
204 CHECK(self->IsExceptionPending());
205 return NULL; // Failure - Indicate to caller to deliver exception
206 }
207 // Perform access check if necessary.
208 Class* referring_class = referrer->GetDeclaringClass();
209 if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) {
210 ThrowNewIllegalAccessErrorClass(self, referring_class, klass);
211 return NULL; // Failure - Indicate to caller to deliver exception
212 }
213 // If we're just implementing const-class, we shouldn't call <clinit>.
214 if (!can_run_clinit) {
215 return klass;
216 }
217 // If we are the <clinit> of this class, just return our storage.
218 //
219 // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
220 // running.
221 if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) {
222 return klass;
223 }
224 if (!class_linker->EnsureInitialized(klass, true)) {
225 CHECK(self->IsExceptionPending());
226 return NULL; // Failure - Indicate to caller to deliver exception
227 }
228 referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, klass);
229 return klass;
230}
231
Shih-wei Liaob0ee9d72012-03-07 16:39:26 -0800232} // namespace art