entrypoint_utils.cc revision 57b86d47b66322693a070185fadfb43cb9c12eab
1/*
2 * Copyright 2012 Google Inc. All Rights Reserved.
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 "runtime_support.h"
18
19namespace art {
20
21void ThrowNewIllegalAccessErrorClass(Thread* self,
22                                     Class* referrer,
23                                     Class* accessed) {
24  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
25                           "illegal class access: '%s' -> '%s'",
26                           PrettyDescriptor(referrer).c_str(),
27                           PrettyDescriptor(accessed).c_str());
28}
29
30void ThrowNewIllegalAccessErrorClassForMethodDispatch(Thread* self,
31                                                      Class* referrer,
32                                                      Class* accessed,
33                                                      const Method* caller,
34                                                      const Method* called,
35                                                      InvokeType type) {
36  std::ostringstream type_stream;
37  type_stream << type;
38  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
39                           "illegal class access ('%s' -> '%s')"
40                           "in attempt to invoke %s method '%s' from '%s'",
41                           PrettyDescriptor(referrer).c_str(),
42                           PrettyDescriptor(accessed).c_str(),
43                           type_stream.str().c_str(),
44                           PrettyMethod(called).c_str(),
45                           PrettyMethod(caller).c_str());
46}
47
48void ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(Thread* self,
49                                                                   const Method* referrer,
50                                                                   const Method* interface_method,
51                                                                   Object* this_object) {
52  self->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
53                           "class '%s' does not implement interface '%s' in call to '%s' from '%s'",
54                           PrettyDescriptor(this_object->GetClass()).c_str(),
55                           PrettyDescriptor(interface_method->GetDeclaringClass()).c_str(),
56                           PrettyMethod(interface_method).c_str(), PrettyMethod(referrer).c_str());
57}
58
59void ThrowNewIllegalAccessErrorField(Thread* self,
60                                     Class* referrer,
61                                     Field* accessed) {
62  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
63                           "Field '%s' is inaccessible to class '%s'",
64                           PrettyField(accessed, false).c_str(),
65                           PrettyDescriptor(referrer).c_str());
66}
67
68void ThrowNewIllegalAccessErrorFinalField(Thread* self,
69                                          const Method* referrer,
70                                          Field* accessed) {
71  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
72                           "Final field '%s' cannot be written to by method '%s'",
73                           PrettyField(accessed, false).c_str(),
74                           PrettyMethod(referrer).c_str());
75}
76
77void ThrowNewIllegalAccessErrorMethod(Thread* self,
78                                      Class* referrer,
79                                      Method* accessed) {
80  self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
81                           "Method '%s' is inaccessible to class '%s'",
82                           PrettyMethod(accessed).c_str(),
83                           PrettyDescriptor(referrer).c_str());
84}
85
86void ThrowNullPointerExceptionForFieldAccess(Thread* self,
87                                                           Field* field,
88                                                           bool is_read) {
89  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
90                           "Attempt to %s field '%s' on a null object reference",
91                           is_read ? "read from" : "write to",
92                           PrettyField(field, true).c_str());
93}
94
95void ThrowNullPointerExceptionForMethodAccess(Thread* self,
96                                              Method* caller,
97                                              uint32_t method_idx,
98                                              InvokeType type) {
99  const DexFile& dex_file =
100      Runtime::Current()->GetClassLinker()->FindDexFile(caller->GetDeclaringClass()->GetDexCache());
101  std::ostringstream type_stream;
102  type_stream << type;
103  self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
104                           "Attempt to invoke %s method '%s' on a null object reference",
105                           type_stream.str().c_str(),
106                           PrettyMethod(method_idx, dex_file, true).c_str());
107}
108
109std::string FieldNameFromIndex(const Method* method, uint32_t ref,
110                               verifier::VerifyErrorRefType ref_type, bool access) {
111  CHECK_EQ(static_cast<int>(ref_type), static_cast<int>(verifier::VERIFY_ERROR_REF_FIELD));
112
113  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
114  const DexFile& dex_file = class_linker->FindDexFile(method->GetDeclaringClass()->GetDexCache());
115
116  const DexFile::FieldId& id = dex_file.GetFieldId(ref);
117  std::string class_name(PrettyDescriptor(dex_file.GetFieldDeclaringClassDescriptor(id)));
118  const char* field_name = dex_file.StringDataByIdx(id.name_idx_);
119  if (!access) {
120    return class_name + "." + field_name;
121  }
122
123  std::string result;
124  result += "tried to access field ";
125  result += class_name + "." + field_name;
126  result += " from class ";
127  result += PrettyDescriptor(method->GetDeclaringClass());
128  return result;
129}
130
131std::string MethodNameFromIndex(const Method* method, uint32_t ref,
132                                verifier::VerifyErrorRefType ref_type, bool access) {
133  CHECK_EQ(static_cast<int>(ref_type), static_cast<int>(verifier::VERIFY_ERROR_REF_METHOD));
134
135  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
136  const DexFile& dex_file = class_linker->FindDexFile(method->GetDeclaringClass()->GetDexCache());
137
138  const DexFile::MethodId& id = dex_file.GetMethodId(ref);
139  std::string class_name(PrettyDescriptor(dex_file.GetMethodDeclaringClassDescriptor(id)));
140  const char* method_name = dex_file.StringDataByIdx(id.name_idx_);
141  if (!access) {
142    return class_name + "." + method_name;
143  }
144
145  std::string result;
146  result += "tried to access method ";
147  result += class_name + "." + method_name + ":" +
148      dex_file.CreateMethodSignature(id.proto_idx_, NULL);
149  result += " from class ";
150  result += PrettyDescriptor(method->GetDeclaringClass());
151  return result;
152}
153
154// Helper function to allocate array for FILLED_NEW_ARRAY.
155Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
156                                  Thread* self, bool access_check) {
157  if (UNLIKELY(component_count < 0)) {
158    self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", component_count);
159    return NULL;  // Failure
160  }
161  Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
162  if (UNLIKELY(klass == NULL)) {  // Not in dex cache so try to resolve
163    klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
164    if (klass == NULL) {  // Error
165      DCHECK(Thread::Current()->IsExceptionPending());
166      return NULL;  // Failure
167    }
168  }
169  if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
170    if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
171      Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
172                                            "Bad filled array request for type %s",
173                                            PrettyDescriptor(klass).c_str());
174    } else {
175      Thread::Current()->ThrowNewExceptionF("Ljava/lang/InternalError;",
176                                            "Found type %s; filled-new-array not implemented for anything but \'int\'",
177                                            PrettyDescriptor(klass).c_str());
178    }
179    return NULL;  // Failure
180  } else {
181    if (access_check) {
182      Class* referrer = method->GetDeclaringClass();
183      if (UNLIKELY(!referrer->CanAccess(klass))) {
184        ThrowNewIllegalAccessErrorClass(self, referrer, klass);
185        return NULL;  // Failure
186      }
187    }
188    DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
189    return Array::Alloc(klass, component_count);
190  }
191}
192
193// Slow path field resolution and declaring class initialization
194Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
195                         bool is_static, bool is_primitive, bool is_set, size_t expected_size) {
196  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
197  Field* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
198  if (UNLIKELY(resolved_field == NULL)) {
199    DCHECK(self->IsExceptionPending());  // Throw exception and unwind
200    return NULL;  // failure
201  } else {
202    Class* fields_class = resolved_field->GetDeclaringClass();
203    Class* referring_class = referrer->GetDeclaringClass();
204    if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
205      ThrowNewIllegalAccessErrorClass(self, referring_class, fields_class);
206      return NULL;  // failure
207    } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
208                                                          resolved_field->GetAccessFlags()))) {
209      ThrowNewIllegalAccessErrorField(self, referring_class, resolved_field);
210      return NULL;  // failure
211    } else if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
212      ThrowNewIllegalAccessErrorFinalField(self, referrer, resolved_field);
213      return NULL;  // failure
214    } else {
215      FieldHelper fh(resolved_field);
216      if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
217                   fh.FieldSize() != expected_size)) {
218        self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
219                                 "Attempted read of %zd-bit %s on field '%s'",
220                                 expected_size * (32 / sizeof(int32_t)),
221                                 is_primitive ? "primitive" : "non-primitive",
222                                 PrettyField(resolved_field, true).c_str());
223        return NULL;  // failure
224      } else if (!is_static) {
225        // instance fields must be being accessed on an initialized class
226        return resolved_field;
227      } else {
228        // If the class is already initializing, we must be inside <clinit>, or
229        // we'd still be waiting for the lock.
230        if (fields_class->IsInitializing()) {
231          return resolved_field;
232        } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
233          return resolved_field;
234        } else {
235          DCHECK(self->IsExceptionPending());  // Throw exception and unwind
236          return NULL;  // failure
237        }
238      }
239    }
240  }
241}
242
243// Slow path method resolution
244Method* FindMethodFromCode(uint32_t method_idx, Object* this_object, const Method* referrer,
245                           Thread* self, bool access_check, InvokeType type) {
246  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
247  bool is_direct = type == kStatic || type == kDirect;
248  Method* resolved_method = class_linker->ResolveMethod(method_idx, referrer, is_direct);
249  if (UNLIKELY(resolved_method == NULL)) {
250    DCHECK(self->IsExceptionPending());  // Throw exception and unwind
251    return NULL;  // failure
252  } else {
253    if (!access_check) {
254      if (is_direct) {
255        return resolved_method;
256      } else if (type == kInterface) {
257        Method* interface_method =
258            this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
259        if (UNLIKELY(interface_method == NULL)) {
260          ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
261                                                                        resolved_method,
262                                                                        this_object);
263          return NULL;  // failure
264        } else {
265          return interface_method;
266        }
267      } else {
268        ObjectArray<Method>* vtable;
269        uint16_t vtable_index = resolved_method->GetMethodIndex();
270        if (type == kSuper) {
271          vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
272        } else {
273          vtable = this_object->GetClass()->GetVTable();
274        }
275        // TODO: eliminate bounds check?
276        return vtable->Get(vtable_index);
277      }
278    } else {
279      Class* methods_class = resolved_method->GetDeclaringClass();
280      Class* referring_class = referrer->GetDeclaringClass();
281      if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
282                   !referring_class->CanAccessMember(methods_class,
283                                                     resolved_method->GetAccessFlags()))) {
284        // The referring class can't access the resolved method, this may occur as a result of a
285        // protected method being made public by implementing an interface that re-declares the
286        // method public. Resort to the dex file to determine the correct class for the access check
287        const DexFile& dex_file = class_linker->FindDexFile(referring_class->GetDexCache());
288        methods_class = class_linker->ResolveType(dex_file,
289                                                  dex_file.GetMethodId(method_idx).class_idx_,
290                                                  referring_class);
291        if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
292          ThrowNewIllegalAccessErrorClassForMethodDispatch(self, referring_class, methods_class,
293                                                           referrer, resolved_method, type);
294          return NULL;  // failure
295        } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
296                                                              resolved_method->GetAccessFlags()))) {
297          ThrowNewIllegalAccessErrorMethod(self, referring_class, resolved_method);
298          return NULL;  // failure
299        }
300      }
301      if (is_direct) {
302        return resolved_method;
303      } else if (type == kInterface) {
304        Method* interface_method =
305            this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
306        if (UNLIKELY(interface_method == NULL)) {
307          ThrowNewIncompatibleClassChangeErrorClassForInterfaceDispatch(self, referrer,
308                                                                        resolved_method,
309                                                                        this_object);
310          return NULL;  // failure
311        } else {
312          return interface_method;
313        }
314      } else {
315        ObjectArray<Method>* vtable;
316        uint16_t vtable_index = resolved_method->GetMethodIndex();
317        if (type == kSuper) {
318          Class* super_class = referring_class->GetSuperClass();
319          if (LIKELY(super_class != NULL)) {
320            vtable = referring_class->GetSuperClass()->GetVTable();
321          } else {
322            vtable = NULL;
323          }
324        } else {
325          vtable = this_object->GetClass()->GetVTable();
326        }
327        if (LIKELY(vtable != NULL &&
328                   vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
329          return vtable->GetWithoutChecks(vtable_index);
330        } else {
331          // Behavior to agree with that of the verifier
332          self->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
333                                   "attempt to invoke %s method '%s' from '%s'"
334                                   " using incorrect form of method dispatch",
335                                   (type == kSuper ? "super class" : "virtual"),
336                                   PrettyMethod(resolved_method).c_str(),
337                                   PrettyMethod(referrer).c_str());
338          return NULL;  // failure
339        }
340      }
341    }
342  }
343}
344
345Class* ResolveVerifyAndClinit(uint32_t type_idx, const Method* referrer, Thread* self,
346                               bool can_run_clinit, bool verify_access) {
347  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
348  Class* klass = class_linker->ResolveType(type_idx, referrer);
349  if (UNLIKELY(klass == NULL)) {
350    CHECK(self->IsExceptionPending());
351    return NULL;  // Failure - Indicate to caller to deliver exception
352  }
353  // Perform access check if necessary.
354  Class* referring_class = referrer->GetDeclaringClass();
355  if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) {
356    ThrowNewIllegalAccessErrorClass(self, referring_class, klass);
357    return NULL;  // Failure - Indicate to caller to deliver exception
358  }
359  // If we're just implementing const-class, we shouldn't call <clinit>.
360  if (!can_run_clinit) {
361    return klass;
362  }
363  // If we are the <clinit> of this class, just return our storage.
364  //
365  // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
366  // running.
367  if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) {
368    return klass;
369  }
370  if (!class_linker->EnsureInitialized(klass, true)) {
371    CHECK(self->IsExceptionPending());
372    return NULL;  // Failure - Indicate to caller to deliver exception
373  }
374  referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, klass);
375  return klass;
376}
377
378}  // namespace art
379