| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2012 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 "common_throws.h" | 
|  | 18 |  | 
| Elliott Hughes | 07ed66b | 2012-12-12 18:34:25 -0800 | [diff] [blame] | 19 | #include "base/logging.h" | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 20 | #include "class_linker-inl.h" | 
| Ian Rogers | 4f6ad8a | 2013-03-18 15:27:28 -0700 | [diff] [blame^] | 21 | #include "dex_file-inl.h" | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 22 | #include "dex_instruction.h" | 
|  | 23 | #include "invoke_type.h" | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 24 | #include "mirror/abstract_method-inl.h" | 
| Ian Rogers | 4f6ad8a | 2013-03-18 15:27:28 -0700 | [diff] [blame^] | 25 | #include "mirror/class-inl.h" | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 26 | #include "mirror/object-inl.h" | 
|  | 27 | #include "mirror/object_array-inl.h" | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 28 | #include "object_utils.h" | 
|  | 29 | #include "thread.h" | 
|  | 30 |  | 
|  | 31 | #include <sstream> | 
|  | 32 |  | 
|  | 33 | namespace art { | 
|  | 34 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 35 | static void AddReferrerLocation(std::ostream& os, const mirror::AbstractMethod* referrer) | 
| Ian Rogers | b726dcb | 2012-09-05 08:57:23 -0700 | [diff] [blame] | 36 | SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 37 | if (referrer != NULL) { | 
|  | 38 | ClassHelper kh(referrer->GetDeclaringClass()); | 
|  | 39 | std::string location(kh.GetLocation()); | 
|  | 40 | if (!location.empty()) { | 
|  | 41 | os << " (accessed from " << location << ")"; | 
|  | 42 | } | 
|  | 43 | } | 
|  | 44 | } | 
|  | 45 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 46 | static void AddReferrerLocationFromClass(std::ostream& os, mirror::Class* referrer) | 
| Ian Rogers | b726dcb | 2012-09-05 08:57:23 -0700 | [diff] [blame] | 47 | SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 48 | if (referrer != NULL) { | 
|  | 49 | ClassHelper kh(referrer); | 
|  | 50 | std::string location(kh.GetLocation()); | 
|  | 51 | if (!location.empty()) { | 
|  | 52 | os << " (declaration of '" << PrettyDescriptor(referrer) | 
|  | 53 | << "' appears in " << location << ")"; | 
|  | 54 | } | 
|  | 55 | } | 
|  | 56 | } | 
|  | 57 |  | 
|  | 58 | // NullPointerException | 
|  | 59 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 60 | void ThrowNullPointerExceptionForFieldAccess(mirror::Field* field, bool is_read) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 61 | std::ostringstream msg; | 
|  | 62 | msg << "Attempt to " << (is_read ? "read from" : "write to") | 
|  | 63 | << " field '" << PrettyField(field, true) << "' on a null object reference"; | 
|  | 64 | Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str()); | 
|  | 65 | } | 
|  | 66 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 67 | void ThrowNullPointerExceptionForMethodAccess(mirror::AbstractMethod* caller, uint32_t method_idx, | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 68 | InvokeType type) { | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 69 | mirror::DexCache* dex_cache = caller->GetDeclaringClass()->GetDexCache(); | 
| Ian Rogers | 4445a7e | 2012-10-05 17:19:13 -0700 | [diff] [blame] | 70 | const DexFile& dex_file = *dex_cache->GetDexFile(); | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 71 | std::ostringstream msg; | 
| Ian Rogers | b726dcb | 2012-09-05 08:57:23 -0700 | [diff] [blame] | 72 | msg << "Attempt to invoke " << type << " method '" | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 73 | << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference"; | 
|  | 74 | Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", msg.str().c_str()); | 
|  | 75 | } | 
|  | 76 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 77 | void ThrowNullPointerExceptionFromDexPC(mirror::AbstractMethod* throw_method, uint32_t dex_pc) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 78 | const DexFile::CodeItem* code = MethodHelper(throw_method).GetCodeItem(); | 
|  | 79 | CHECK_LT(dex_pc, code->insns_size_in_code_units_); | 
|  | 80 | const Instruction* instr = Instruction::At(&code->insns_[dex_pc]); | 
|  | 81 | DecodedInstruction dec_insn(instr); | 
|  | 82 | switch (instr->Opcode()) { | 
|  | 83 | case Instruction::INVOKE_DIRECT: | 
|  | 84 | case Instruction::INVOKE_DIRECT_RANGE: | 
|  | 85 | ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kDirect); | 
|  | 86 | break; | 
|  | 87 | case Instruction::INVOKE_VIRTUAL: | 
|  | 88 | case Instruction::INVOKE_VIRTUAL_RANGE: | 
|  | 89 | ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kVirtual); | 
|  | 90 | break; | 
| Ian Rogers | 137e88f | 2012-10-08 17:46:47 -0700 | [diff] [blame] | 91 | case Instruction::INVOKE_INTERFACE: | 
|  | 92 | case Instruction::INVOKE_INTERFACE_RANGE: | 
|  | 93 | ThrowNullPointerExceptionForMethodAccess(throw_method, dec_insn.vB, kInterface); | 
|  | 94 | break; | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 95 | case Instruction::IGET: | 
|  | 96 | case Instruction::IGET_WIDE: | 
|  | 97 | case Instruction::IGET_OBJECT: | 
|  | 98 | case Instruction::IGET_BOOLEAN: | 
|  | 99 | case Instruction::IGET_BYTE: | 
|  | 100 | case Instruction::IGET_CHAR: | 
|  | 101 | case Instruction::IGET_SHORT: { | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 102 | mirror::Field* field = | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 103 | Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false); | 
|  | 104 | ThrowNullPointerExceptionForFieldAccess(field, true /* read */); | 
|  | 105 | break; | 
|  | 106 | } | 
|  | 107 | case Instruction::IPUT: | 
|  | 108 | case Instruction::IPUT_WIDE: | 
|  | 109 | case Instruction::IPUT_OBJECT: | 
|  | 110 | case Instruction::IPUT_BOOLEAN: | 
|  | 111 | case Instruction::IPUT_BYTE: | 
|  | 112 | case Instruction::IPUT_CHAR: | 
|  | 113 | case Instruction::IPUT_SHORT: { | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 114 | mirror::Field* field = | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 115 | Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false); | 
|  | 116 | ThrowNullPointerExceptionForFieldAccess(field, false /* write */); | 
|  | 117 | break; | 
|  | 118 | } | 
|  | 119 | case Instruction::AGET: | 
|  | 120 | case Instruction::AGET_WIDE: | 
|  | 121 | case Instruction::AGET_OBJECT: | 
|  | 122 | case Instruction::AGET_BOOLEAN: | 
|  | 123 | case Instruction::AGET_BYTE: | 
|  | 124 | case Instruction::AGET_CHAR: | 
|  | 125 | case Instruction::AGET_SHORT: | 
|  | 126 | Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", | 
|  | 127 | "Attempt to read from null array"); | 
|  | 128 | break; | 
|  | 129 | case Instruction::APUT: | 
|  | 130 | case Instruction::APUT_WIDE: | 
|  | 131 | case Instruction::APUT_OBJECT: | 
|  | 132 | case Instruction::APUT_BOOLEAN: | 
|  | 133 | case Instruction::APUT_BYTE: | 
|  | 134 | case Instruction::APUT_CHAR: | 
|  | 135 | case Instruction::APUT_SHORT: | 
|  | 136 | Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", | 
|  | 137 | "Attempt to write to null array"); | 
|  | 138 | break; | 
|  | 139 | case Instruction::ARRAY_LENGTH: | 
|  | 140 | Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", | 
|  | 141 | "Attempt to get length of null array"); | 
|  | 142 | break; | 
|  | 143 | default: { | 
| Ian Rogers | b726dcb | 2012-09-05 08:57:23 -0700 | [diff] [blame] | 144 | // TODO: We should have covered all the cases where we expect a NPE above, this | 
|  | 145 | //       message/logging is so we can improve any cases we've missed in the future. | 
| Ian Rogers | 4445a7e | 2012-10-05 17:19:13 -0700 | [diff] [blame] | 146 | const DexFile& dex_file = *throw_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 147 | std::string message("Null pointer exception during instruction '"); | 
|  | 148 | message += instr->DumpString(&dex_file); | 
|  | 149 | message += "'"; | 
|  | 150 | Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", message.c_str()); | 
|  | 151 | break; | 
|  | 152 | } | 
|  | 153 | } | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | // IllegalAccessError | 
|  | 157 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 158 | void ThrowIllegalAccessErrorClass(mirror::Class* referrer, mirror::Class* accessed) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 159 | std::ostringstream msg; | 
| Ian Rogers | b726dcb | 2012-09-05 08:57:23 -0700 | [diff] [blame] | 160 | msg << "Illegal class access: '" << PrettyDescriptor(referrer) << "' attempting to access '" | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 161 | << PrettyDescriptor(accessed) << "'"; | 
|  | 162 | AddReferrerLocationFromClass(msg, referrer); | 
|  | 163 | Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); | 
|  | 164 | } | 
|  | 165 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 166 | void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed, | 
|  | 167 | const mirror::AbstractMethod* caller, | 
|  | 168 | const mirror::AbstractMethod* called, | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 169 | InvokeType type) { | 
|  | 170 | std::ostringstream msg; | 
| Ian Rogers | b726dcb | 2012-09-05 08:57:23 -0700 | [diff] [blame] | 171 | msg << "Illegal class access ('" << PrettyDescriptor(referrer) << "' attempting to access '" | 
|  | 172 | << PrettyDescriptor(accessed) << "') in attempt to invoke " << type | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 173 | << " method " << PrettyMethod(called).c_str(); | 
|  | 174 | AddReferrerLocation(msg, caller); | 
|  | 175 | Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); | 
|  | 176 | } | 
|  | 177 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 178 | void ThrowIllegalAccessErrorMethod(mirror::Class* referrer, mirror::AbstractMethod* accessed) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 179 | std::ostringstream msg; | 
|  | 180 | msg << "Method '" << PrettyMethod(accessed) << "' is inaccessible to class '" | 
|  | 181 | << PrettyDescriptor(referrer) << "'"; | 
|  | 182 | AddReferrerLocationFromClass(msg, referrer); | 
|  | 183 | Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); | 
|  | 184 | } | 
|  | 185 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 186 | void ThrowIllegalAccessErrorField(mirror::Class* referrer, mirror::Field* accessed) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 187 | std::ostringstream msg; | 
|  | 188 | msg << "Field '" << PrettyField(accessed, false) << "' is inaccessible to class '" | 
|  | 189 | << PrettyDescriptor(referrer) << "'"; | 
|  | 190 | AddReferrerLocationFromClass(msg, referrer); | 
|  | 191 | Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); | 
|  | 192 | } | 
|  | 193 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 194 | void ThrowIllegalAccessErrorFinalField(const mirror::AbstractMethod* referrer, | 
|  | 195 | mirror::Field* accessed) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 196 | std::ostringstream msg; | 
|  | 197 | msg << "Final field '" << PrettyField(accessed, false) << "' cannot be written to by method '" | 
|  | 198 | << PrettyMethod(referrer) << "'"; | 
|  | 199 | AddReferrerLocation(msg, referrer); | 
|  | 200 | Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;", msg.str().c_str()); | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | // IncompatibleClassChangeError | 
|  | 204 |  | 
|  | 205 | void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type, | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 206 | mirror::AbstractMethod* method, | 
|  | 207 | const mirror::AbstractMethod* referrer) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 208 | std::ostringstream msg; | 
|  | 209 | msg << "The method '" << PrettyMethod(method) << "' was expected to be of type " | 
|  | 210 | << expected_type << " but instead was found to be of type " << found_type; | 
|  | 211 | AddReferrerLocation(msg, referrer); | 
|  | 212 | Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;", | 
|  | 213 | msg.str().c_str()); | 
|  | 214 | } | 
|  | 215 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 216 | void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const mirror::AbstractMethod* interface_method, | 
|  | 217 | mirror::Object* this_object, | 
|  | 218 | const mirror::AbstractMethod* referrer) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 219 | // Referrer is calling interface_method on this_object, however, the interface_method isn't | 
|  | 220 | // implemented by this_object. | 
|  | 221 | CHECK(this_object != NULL); | 
|  | 222 | std::ostringstream msg; | 
|  | 223 | msg << "Class '" << PrettyDescriptor(this_object->GetClass()) | 
|  | 224 | << "' does not implement interface '" | 
|  | 225 | << PrettyDescriptor(interface_method->GetDeclaringClass()) | 
|  | 226 | << "' in call to '" << PrettyMethod(interface_method) << "'"; | 
|  | 227 | AddReferrerLocation(msg, referrer); | 
|  | 228 | Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;", | 
|  | 229 | msg.str().c_str()); | 
|  | 230 | } | 
|  | 231 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 232 | void ThrowIncompatibleClassChangeErrorField(const mirror::Field* resolved_field, bool is_static, | 
|  | 233 | const mirror::AbstractMethod* referrer) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 234 | std::ostringstream msg; | 
|  | 235 | msg << "Expected '" << PrettyField(resolved_field) << "' to be a " | 
| Ian Rogers | b726dcb | 2012-09-05 08:57:23 -0700 | [diff] [blame] | 236 | << (is_static ? "static" : "instance") << " field" << " rather than a " | 
|  | 237 | << (is_static ? "instance" : "static") << " field"; | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 238 | AddReferrerLocation(msg, referrer); | 
|  | 239 | Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;", | 
|  | 240 | msg.str().c_str()); | 
|  | 241 | } | 
|  | 242 |  | 
|  | 243 | // NoSuchMethodError | 
|  | 244 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 245 | void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, | 
|  | 246 | const StringPiece& signature, const mirror::AbstractMethod* referrer) { | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 247 | std::ostringstream msg; | 
|  | 248 | ClassHelper kh(c); | 
|  | 249 | msg << "No " << type << " method " << name << signature | 
|  | 250 | << " in class " << kh.GetDescriptor() << " or its super classes"; | 
|  | 251 | AddReferrerLocation(msg, referrer); | 
|  | 252 | Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str()); | 
|  | 253 | } | 
|  | 254 |  | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 255 | void ThrowNoSuchMethodError(uint32_t method_idx, const mirror::AbstractMethod* referrer) { | 
|  | 256 | mirror::DexCache* dex_cache = referrer->GetDeclaringClass()->GetDexCache(); | 
| Ian Rogers | 4445a7e | 2012-10-05 17:19:13 -0700 | [diff] [blame] | 257 | const DexFile& dex_file = *dex_cache->GetDexFile(); | 
| Ian Rogers | 87e552d | 2012-08-31 15:54:48 -0700 | [diff] [blame] | 258 | std::ostringstream msg; | 
|  | 259 | msg << "No method '" << PrettyMethod(method_idx, dex_file, true) << "'"; | 
|  | 260 | AddReferrerLocation(msg, referrer); | 
|  | 261 | Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str()); | 
|  | 262 | } | 
|  | 263 |  | 
|  | 264 | }  // namespace art |