Ian Rogers | 57b86d4 | 2012-03-27 16:05:41 -0700 | [diff] [blame] | 1 | /* |
Elliott Hughes | 0f3c553 | 2012-03-30 14:51:51 -0700 | [diff] [blame] | 2 | * Copyright (C) 2012 The Android Open Source Project |
Ian Rogers | 57b86d4 | 2012-03-27 16:05:41 -0700 | [diff] [blame] | 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 "callee_save_frame.h" |
| 18 | #include "dex_verifier.h" |
| 19 | #include "object.h" |
| 20 | #include "object_utils.h" |
| 21 | #include "runtime_support.h" |
| 22 | #include "thread.h" |
| 23 | |
| 24 | namespace art { |
| 25 | |
| 26 | // Deliver an exception that's pending on thread helping set up a callee save frame on the way. |
| 27 | extern "C" void artDeliverPendingExceptionFromCode(Thread* thread, Method** sp) { |
| 28 | FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); |
| 29 | thread->DeliverException(); |
| 30 | } |
| 31 | |
| 32 | // Called by generated call to throw an exception. |
| 33 | extern "C" void artDeliverExceptionFromCode(Throwable* exception, Thread* thread, Method** sp) { |
| 34 | /* |
| 35 | * exception may be NULL, in which case this routine should |
| 36 | * throw NPE. NOTE: this is a convenience for generated code, |
| 37 | * which previously did the null check inline and constructed |
| 38 | * and threw a NPE if NULL. This routine responsible for setting |
| 39 | * exception_ in thread and delivering the exception. |
| 40 | */ |
| 41 | FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); |
| 42 | if (exception == NULL) { |
| 43 | thread->ThrowNewException("Ljava/lang/NullPointerException;", "throw with null exception"); |
| 44 | } else { |
| 45 | thread->SetException(exception); |
| 46 | } |
| 47 | thread->DeliverException(); |
| 48 | } |
| 49 | |
| 50 | // Called by generated call to throw a NPE exception. |
| 51 | extern "C" void artThrowNullPointerExceptionFromCode(Thread* self, Method** sp) { |
| 52 | FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); |
Elliott Hughes | 6a14433 | 2012-04-03 13:07:11 -0700 | [diff] [blame^] | 53 | Frame frame = self->GetTopOfStack(); |
| 54 | uintptr_t throw_native_pc = frame.GetReturnPC(); |
| 55 | frame.Next(); |
| 56 | Method* throw_method = frame.GetMethod(); |
Ian Rogers | 57b86d4 | 2012-03-27 16:05:41 -0700 | [diff] [blame] | 57 | uint32_t dex_pc = throw_method->ToDexPC(throw_native_pc - 2); |
| 58 | const DexFile::CodeItem* code = MethodHelper(throw_method).GetCodeItem(); |
| 59 | CHECK_LT(dex_pc, code->insns_size_in_code_units_); |
| 60 | const Instruction* instr = Instruction::At(&code->insns_[dex_pc]); |
| 61 | DecodedInstruction dec_insn(instr); |
| 62 | switch (instr->Opcode()) { |
| 63 | case Instruction::INVOKE_DIRECT: |
| 64 | case Instruction::INVOKE_DIRECT_RANGE: |
| 65 | ThrowNullPointerExceptionForMethodAccess(self, throw_method, dec_insn.vB, kDirect); |
| 66 | break; |
| 67 | case Instruction::INVOKE_VIRTUAL: |
| 68 | case Instruction::INVOKE_VIRTUAL_RANGE: |
| 69 | ThrowNullPointerExceptionForMethodAccess(self, throw_method, dec_insn.vB, kVirtual); |
| 70 | break; |
| 71 | case Instruction::IGET: |
| 72 | case Instruction::IGET_WIDE: |
| 73 | case Instruction::IGET_OBJECT: |
| 74 | case Instruction::IGET_BOOLEAN: |
| 75 | case Instruction::IGET_BYTE: |
| 76 | case Instruction::IGET_CHAR: |
| 77 | case Instruction::IGET_SHORT: { |
| 78 | Field* field = |
| 79 | Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false); |
| 80 | ThrowNullPointerExceptionForFieldAccess(self, field, true /* read */); |
| 81 | break; |
| 82 | } |
| 83 | case Instruction::IPUT: |
| 84 | case Instruction::IPUT_WIDE: |
| 85 | case Instruction::IPUT_OBJECT: |
| 86 | case Instruction::IPUT_BOOLEAN: |
| 87 | case Instruction::IPUT_BYTE: |
| 88 | case Instruction::IPUT_CHAR: |
| 89 | case Instruction::IPUT_SHORT: { |
| 90 | Field* field = |
| 91 | Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false); |
| 92 | ThrowNullPointerExceptionForFieldAccess(self, field, false /* write */); |
| 93 | break; |
| 94 | } |
| 95 | case Instruction::AGET: |
| 96 | case Instruction::AGET_WIDE: |
| 97 | case Instruction::AGET_OBJECT: |
| 98 | case Instruction::AGET_BOOLEAN: |
| 99 | case Instruction::AGET_BYTE: |
| 100 | case Instruction::AGET_CHAR: |
| 101 | case Instruction::AGET_SHORT: |
| 102 | self->ThrowNewException("Ljava/lang/NullPointerException;", |
| 103 | "Attempt to read from null array"); |
| 104 | break; |
| 105 | case Instruction::APUT: |
| 106 | case Instruction::APUT_WIDE: |
| 107 | case Instruction::APUT_OBJECT: |
| 108 | case Instruction::APUT_BOOLEAN: |
| 109 | case Instruction::APUT_BYTE: |
| 110 | case Instruction::APUT_CHAR: |
| 111 | case Instruction::APUT_SHORT: |
| 112 | self->ThrowNewException("Ljava/lang/NullPointerException;", |
| 113 | "Attempt to write to null array"); |
| 114 | break; |
| 115 | default: { |
| 116 | const DexFile& dex_file = Runtime::Current()->GetClassLinker() |
| 117 | ->FindDexFile(throw_method->GetDeclaringClass()->GetDexCache()); |
| 118 | std::string message("Null pointer exception during instruction '"); |
| 119 | message += instr->DumpString(&dex_file); |
| 120 | message += "'"; |
| 121 | self->ThrowNewException("Ljava/lang/NullPointerException;", message.c_str()); |
| 122 | break; |
| 123 | } |
| 124 | } |
| 125 | self->DeliverException(); |
| 126 | } |
| 127 | |
| 128 | // Called by generated call to throw an arithmetic divide by zero exception. |
| 129 | extern "C" void artThrowDivZeroFromCode(Thread* thread, Method** sp) { |
| 130 | FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); |
| 131 | thread->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero"); |
| 132 | thread->DeliverException(); |
| 133 | } |
| 134 | |
| 135 | // Called by generated call to throw an array index out of bounds exception. |
| 136 | extern "C" void artThrowArrayBoundsFromCode(int index, int limit, Thread* thread, Method** sp) { |
| 137 | FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); |
| 138 | thread->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", |
| 139 | "length=%d; index=%d", limit, index); |
| 140 | thread->DeliverException(); |
| 141 | } |
| 142 | |
| 143 | extern "C" void artThrowStackOverflowFromCode(Thread* thread, Method** sp) { |
| 144 | FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); |
| 145 | // Remove extra entry pushed onto second stack during method tracing |
| 146 | if (Runtime::Current()->IsMethodTracingActive()) { |
| 147 | TraceMethodUnwindFromCode(thread); |
| 148 | } |
| 149 | thread->SetStackEndForStackOverflow(); // Allow space on the stack for constructor to execute |
| 150 | thread->ThrowNewExceptionF("Ljava/lang/StackOverflowError;", |
| 151 | "stack size %zdkb; default stack size: %zdkb", |
| 152 | thread->GetStackSize() / KB, Runtime::Current()->GetDefaultStackSize() / KB); |
| 153 | thread->ResetDefaultStackEnd(); // Return to default stack size |
| 154 | thread->DeliverException(); |
| 155 | } |
| 156 | |
| 157 | extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self, Method** sp) { |
| 158 | FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); |
| 159 | Frame frame = self->GetTopOfStack(); // We need the calling method as context for the method_idx |
| 160 | frame.Next(); |
| 161 | Method* method = frame.GetMethod(); |
| 162 | self->ThrowNewException("Ljava/lang/NoSuchMethodError;", |
| 163 | MethodNameFromIndex(method, method_idx, verifier::VERIFY_ERROR_REF_METHOD, false).c_str()); |
| 164 | self->DeliverException(); |
| 165 | } |
| 166 | |
| 167 | static std::string ClassNameFromIndex(Method* method, uint32_t ref, |
| 168 | verifier::VerifyErrorRefType ref_type, bool access) { |
| 169 | ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); |
| 170 | const DexFile& dex_file = class_linker->FindDexFile(method->GetDeclaringClass()->GetDexCache()); |
| 171 | |
| 172 | uint16_t type_idx = 0; |
| 173 | if (ref_type == verifier::VERIFY_ERROR_REF_FIELD) { |
| 174 | const DexFile::FieldId& id = dex_file.GetFieldId(ref); |
| 175 | type_idx = id.class_idx_; |
| 176 | } else if (ref_type == verifier::VERIFY_ERROR_REF_METHOD) { |
| 177 | const DexFile::MethodId& id = dex_file.GetMethodId(ref); |
| 178 | type_idx = id.class_idx_; |
| 179 | } else if (ref_type == verifier::VERIFY_ERROR_REF_CLASS) { |
| 180 | type_idx = ref; |
| 181 | } else { |
| 182 | CHECK(false) << static_cast<int>(ref_type); |
| 183 | } |
| 184 | |
| 185 | std::string class_name(PrettyDescriptor(dex_file.StringByTypeIdx(type_idx))); |
| 186 | if (!access) { |
| 187 | return class_name; |
| 188 | } |
| 189 | |
| 190 | std::string result; |
| 191 | result += "tried to access class "; |
| 192 | result += class_name; |
| 193 | result += " from class "; |
| 194 | result += PrettyDescriptor(method->GetDeclaringClass()); |
| 195 | return result; |
| 196 | } |
| 197 | |
| 198 | extern "C" void artThrowVerificationErrorFromCode(int32_t kind, int32_t ref, Thread* self, Method** sp) { |
| 199 | FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); |
| 200 | Frame frame = self->GetTopOfStack(); // We need the calling method as context to interpret 'ref' |
| 201 | frame.Next(); |
| 202 | Method* method = frame.GetMethod(); |
| 203 | |
| 204 | verifier::VerifyErrorRefType ref_type = |
| 205 | static_cast<verifier::VerifyErrorRefType>(kind >> verifier::kVerifyErrorRefTypeShift); |
| 206 | |
| 207 | const char* exception_class = "Ljava/lang/VerifyError;"; |
| 208 | std::string msg; |
| 209 | |
| 210 | switch (static_cast<verifier::VerifyError>(kind & ~(0xff << verifier::kVerifyErrorRefTypeShift))) { |
| 211 | case verifier::VERIFY_ERROR_NO_CLASS: |
| 212 | exception_class = "Ljava/lang/NoClassDefFoundError;"; |
| 213 | msg = ClassNameFromIndex(method, ref, ref_type, false); |
| 214 | break; |
| 215 | case verifier::VERIFY_ERROR_NO_FIELD: |
| 216 | exception_class = "Ljava/lang/NoSuchFieldError;"; |
| 217 | msg = FieldNameFromIndex(method, ref, ref_type, false); |
| 218 | break; |
| 219 | case verifier::VERIFY_ERROR_NO_METHOD: |
| 220 | exception_class = "Ljava/lang/NoSuchMethodError;"; |
| 221 | msg = MethodNameFromIndex(method, ref, ref_type, false); |
| 222 | break; |
| 223 | case verifier::VERIFY_ERROR_ACCESS_CLASS: |
| 224 | exception_class = "Ljava/lang/IllegalAccessError;"; |
| 225 | msg = ClassNameFromIndex(method, ref, ref_type, true); |
| 226 | break; |
| 227 | case verifier::VERIFY_ERROR_ACCESS_FIELD: |
| 228 | exception_class = "Ljava/lang/IllegalAccessError;"; |
| 229 | msg = FieldNameFromIndex(method, ref, ref_type, true); |
| 230 | break; |
| 231 | case verifier::VERIFY_ERROR_ACCESS_METHOD: |
| 232 | exception_class = "Ljava/lang/IllegalAccessError;"; |
| 233 | msg = MethodNameFromIndex(method, ref, ref_type, true); |
| 234 | break; |
| 235 | case verifier::VERIFY_ERROR_CLASS_CHANGE: |
| 236 | exception_class = "Ljava/lang/IncompatibleClassChangeError;"; |
| 237 | msg = ClassNameFromIndex(method, ref, ref_type, false); |
| 238 | break; |
| 239 | case verifier::VERIFY_ERROR_INSTANTIATION: |
| 240 | exception_class = "Ljava/lang/InstantiationError;"; |
| 241 | msg = ClassNameFromIndex(method, ref, ref_type, false); |
| 242 | break; |
| 243 | case verifier::VERIFY_ERROR_BAD_CLASS_SOFT: |
| 244 | case verifier::VERIFY_ERROR_BAD_CLASS_HARD: |
| 245 | // Generic VerifyError; use default exception, no message. |
| 246 | break; |
| 247 | case verifier::VERIFY_ERROR_NONE: |
| 248 | CHECK(false); |
| 249 | break; |
| 250 | } |
| 251 | self->ThrowNewException(exception_class, msg.c_str()); |
| 252 | self->DeliverException(); |
| 253 | } |
| 254 | |
| 255 | } // namespace art |