|  | // Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "vm/exceptions.h" | 
|  |  | 
|  | #include "platform/address_sanitizer.h" | 
|  |  | 
|  | #include "lib/stacktrace.h" | 
|  |  | 
|  | #include "vm/dart_api_impl.h" | 
|  | #include "vm/dart_entry.h" | 
|  | #include "vm/datastream.h" | 
|  | #include "vm/debugger.h" | 
|  | #include "vm/deopt_instructions.h" | 
|  | #include "vm/flags.h" | 
|  | #include "vm/log.h" | 
|  | #include "vm/longjump.h" | 
|  | #include "vm/object.h" | 
|  | #include "vm/object_store.h" | 
|  | #include "vm/stack_frame.h" | 
|  | #include "vm/stub_code.h" | 
|  | #include "vm/symbols.h" | 
|  | #include "vm/tags.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | DECLARE_FLAG(bool, enable_interpreter); | 
|  | DECLARE_FLAG(bool, trace_deoptimization); | 
|  | DEFINE_FLAG(bool, | 
|  | print_stacktrace_at_throw, | 
|  | false, | 
|  | "Prints a stack trace everytime a throw occurs."); | 
|  |  | 
|  | class StackTraceBuilder : public ValueObject { | 
|  | public: | 
|  | StackTraceBuilder() {} | 
|  | virtual ~StackTraceBuilder() {} | 
|  |  | 
|  | virtual void AddFrame(const Object& code, const Smi& offset) = 0; | 
|  | }; | 
|  |  | 
|  | class RegularStackTraceBuilder : public StackTraceBuilder { | 
|  | public: | 
|  | explicit RegularStackTraceBuilder(Zone* zone) | 
|  | : code_list_( | 
|  | GrowableObjectArray::Handle(zone, GrowableObjectArray::New())), | 
|  | pc_offset_list_( | 
|  | GrowableObjectArray::Handle(zone, GrowableObjectArray::New())) {} | 
|  | ~RegularStackTraceBuilder() {} | 
|  |  | 
|  | const GrowableObjectArray& code_list() const { return code_list_; } | 
|  | const GrowableObjectArray& pc_offset_list() const { return pc_offset_list_; } | 
|  |  | 
|  | virtual void AddFrame(const Object& code, const Smi& offset) { | 
|  | code_list_.Add(code); | 
|  | pc_offset_list_.Add(offset); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const GrowableObjectArray& code_list_; | 
|  | const GrowableObjectArray& pc_offset_list_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(RegularStackTraceBuilder); | 
|  | }; | 
|  |  | 
|  | class PreallocatedStackTraceBuilder : public StackTraceBuilder { | 
|  | public: | 
|  | explicit PreallocatedStackTraceBuilder(const Instance& stacktrace) | 
|  | : stacktrace_(StackTrace::Cast(stacktrace)), | 
|  | cur_index_(0), | 
|  | dropped_frames_(0) { | 
|  | ASSERT(stacktrace_.raw() == | 
|  | Isolate::Current()->object_store()->preallocated_stack_trace()); | 
|  | } | 
|  | ~PreallocatedStackTraceBuilder() {} | 
|  |  | 
|  | virtual void AddFrame(const Object& code, const Smi& offset); | 
|  |  | 
|  | private: | 
|  | static const int kNumTopframes = StackTrace::kPreallocatedStackdepth / 2; | 
|  |  | 
|  | const StackTrace& stacktrace_; | 
|  | intptr_t cur_index_; | 
|  | intptr_t dropped_frames_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(PreallocatedStackTraceBuilder); | 
|  | }; | 
|  |  | 
|  | void PreallocatedStackTraceBuilder::AddFrame(const Object& code, | 
|  | const Smi& offset) { | 
|  | if (cur_index_ >= StackTrace::kPreallocatedStackdepth) { | 
|  | // The number of frames is overflowing the preallocated stack trace object. | 
|  | Object& frame_code = Object::Handle(); | 
|  | Smi& frame_offset = Smi::Handle(); | 
|  | intptr_t start = StackTrace::kPreallocatedStackdepth - (kNumTopframes - 1); | 
|  | intptr_t null_slot = start - 2; | 
|  | // We are going to drop one frame. | 
|  | dropped_frames_++; | 
|  | // Add an empty slot to indicate the overflow so that the toString | 
|  | // method can account for the overflow. | 
|  | if (stacktrace_.CodeAtFrame(null_slot) != Code::null()) { | 
|  | stacktrace_.SetCodeAtFrame(null_slot, frame_code); | 
|  | // We drop an extra frame here too. | 
|  | dropped_frames_++; | 
|  | } | 
|  | // Encode the number of dropped frames into the pc offset. | 
|  | frame_offset ^= Smi::New(dropped_frames_); | 
|  | stacktrace_.SetPcOffsetAtFrame(null_slot, frame_offset); | 
|  | // Move frames one slot down so that we can accommodate the new frame. | 
|  | for (intptr_t i = start; i < StackTrace::kPreallocatedStackdepth; i++) { | 
|  | intptr_t prev = (i - 1); | 
|  | frame_code = stacktrace_.CodeAtFrame(i); | 
|  | frame_offset = stacktrace_.PcOffsetAtFrame(i); | 
|  | stacktrace_.SetCodeAtFrame(prev, frame_code); | 
|  | stacktrace_.SetPcOffsetAtFrame(prev, frame_offset); | 
|  | } | 
|  | cur_index_ = (StackTrace::kPreallocatedStackdepth - 1); | 
|  | } | 
|  | stacktrace_.SetCodeAtFrame(cur_index_, code); | 
|  | stacktrace_.SetPcOffsetAtFrame(cur_index_, offset); | 
|  | cur_index_ += 1; | 
|  | } | 
|  |  | 
|  | static void BuildStackTrace(StackTraceBuilder* builder) { | 
|  | StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, | 
|  | Thread::Current(), | 
|  | StackFrameIterator::kNoCrossThreadIteration); | 
|  | StackFrame* frame = frames.NextFrame(); | 
|  | ASSERT(frame != NULL);  // We expect to find a dart invocation frame. | 
|  | Code& code = Code::Handle(); | 
|  | Bytecode& bytecode = Bytecode::Handle(); | 
|  | Smi& offset = Smi::Handle(); | 
|  | while (frame != NULL) { | 
|  | if (frame->IsDartFrame()) { | 
|  | if (frame->is_interpreted()) { | 
|  | bytecode = frame->LookupDartBytecode(); | 
|  | ASSERT(bytecode.ContainsInstructionAt(frame->pc())); | 
|  | offset = Smi::New(frame->pc() - bytecode.PayloadStart()); | 
|  | builder->AddFrame(bytecode, offset); | 
|  | } else { | 
|  | code = frame->LookupDartCode(); | 
|  | ASSERT(code.ContainsInstructionAt(frame->pc())); | 
|  | offset = Smi::New(frame->pc() - code.PayloadStart()); | 
|  | builder->AddFrame(code, offset); | 
|  | } | 
|  | } | 
|  | frame = frames.NextFrame(); | 
|  | } | 
|  | } | 
|  |  | 
|  | class ExceptionHandlerFinder : public StackResource { | 
|  | public: | 
|  | explicit ExceptionHandlerFinder(Thread* thread) | 
|  | : StackResource(thread), thread_(thread) {} | 
|  |  | 
|  | // Iterate through the stack frames and try to find a frame with an | 
|  | // exception handler. Once found, set the pc, sp and fp so that execution | 
|  | // can continue in that frame. Sets 'needs_stacktrace' if there is no | 
|  | // cath-all handler or if a stack-trace is specified in the catch. | 
|  | bool Find() { | 
|  | StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, | 
|  | Thread::Current(), | 
|  | StackFrameIterator::kNoCrossThreadIteration); | 
|  | StackFrame* frame = frames.NextFrame(); | 
|  | if (frame == NULL) return false;  // No Dart frame. | 
|  | handler_pc_set_ = false; | 
|  | needs_stacktrace = false; | 
|  | bool is_catch_all = false; | 
|  | uword temp_handler_pc = kUwordMax; | 
|  | bool is_optimized = false; | 
|  | code_ = NULL; | 
|  | catch_entry_moves_cache_ = thread_->isolate()->catch_entry_moves_cache(); | 
|  |  | 
|  | while (!frame->IsEntryFrame()) { | 
|  | if (frame->IsDartFrame()) { | 
|  | if (frame->FindExceptionHandler(thread_, &temp_handler_pc, | 
|  | &needs_stacktrace, &is_catch_all, | 
|  | &is_optimized)) { | 
|  | if (!handler_pc_set_) { | 
|  | handler_pc_set_ = true; | 
|  | handler_pc = temp_handler_pc; | 
|  | handler_sp = frame->sp(); | 
|  | handler_fp = frame->fp(); | 
|  | if (is_optimized) { | 
|  | pc_ = frame->pc(); | 
|  | code_ = &Code::Handle(frame->LookupDartCode()); | 
|  | CatchEntryMovesRefPtr* cached_catch_entry_moves = | 
|  | catch_entry_moves_cache_->Lookup(pc_); | 
|  | if (cached_catch_entry_moves != NULL) { | 
|  | cached_catch_entry_moves_ = *cached_catch_entry_moves; | 
|  | } | 
|  | #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER) | 
|  | intptr_t num_vars = Smi::Value(code_->variables()); | 
|  | if (cached_catch_entry_moves_.IsEmpty()) { | 
|  | GetCatchEntryMovesFromDeopt(num_vars, frame); | 
|  | } | 
|  | #else | 
|  | if (cached_catch_entry_moves_.IsEmpty()) { | 
|  | ReadCompressedCatchEntryMoves(); | 
|  | } | 
|  | #endif  // !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER) | 
|  | } | 
|  | } | 
|  | if (needs_stacktrace || is_catch_all) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | }  // if frame->IsDartFrame | 
|  | frame = frames.NextFrame(); | 
|  | ASSERT(frame != NULL); | 
|  | }  // while !frame->IsEntryFrame | 
|  | ASSERT(frame->IsEntryFrame()); | 
|  | if (!handler_pc_set_) { | 
|  | handler_pc = frame->pc(); | 
|  | handler_sp = frame->sp(); | 
|  | handler_fp = frame->fp(); | 
|  | } | 
|  | // No catch-all encountered, needs stacktrace. | 
|  | needs_stacktrace = true; | 
|  | return handler_pc_set_; | 
|  | } | 
|  |  | 
|  | // When entering catch block in the optimized code we need to execute | 
|  | // catch entry moves that would morph the state of the frame into | 
|  | // what catch entry expects. | 
|  | void PrepareFrameForCatchEntry() { | 
|  | if (code_ == nullptr || !code_->is_optimized()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (cached_catch_entry_moves_.IsEmpty()) { | 
|  | catch_entry_moves_cache_->Insert( | 
|  | pc_, CatchEntryMovesRefPtr(catch_entry_moves_)); | 
|  | } else { | 
|  | catch_entry_moves_ = &cached_catch_entry_moves_.moves(); | 
|  | } | 
|  |  | 
|  | ExecuteCatchEntryMoves(*catch_entry_moves_); | 
|  | } | 
|  |  | 
|  | void ExecuteCatchEntryMoves(const CatchEntryMoves& moves) { | 
|  | uword fp = handler_fp; | 
|  | ObjectPool* pool = nullptr; | 
|  | for (int j = 0; j < moves.count(); j++) { | 
|  | const CatchEntryMove& move = moves.At(j); | 
|  |  | 
|  | RawObject* value; | 
|  | switch (move.source_kind()) { | 
|  | case CatchEntryMove::SourceKind::kConstant: | 
|  | if (pool == nullptr) { | 
|  | pool = &ObjectPool::Handle(code_->object_pool()); | 
|  | } | 
|  | value = pool->ObjectAt(move.src_slot()); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kTaggedSlot: | 
|  | value = *TaggedSlotAt(fp, move.src_slot()); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kDoubleSlot: | 
|  | value = Double::New(*SlotAt<double>(fp, move.src_slot())); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kFloat32x4Slot: | 
|  | value = Float32x4::New(*SlotAt<simd128_value_t>(fp, move.src_slot())); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kFloat64x2Slot: | 
|  | value = Float64x2::New(*SlotAt<simd128_value_t>(fp, move.src_slot())); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kInt32x4Slot: | 
|  | value = Int32x4::New(*SlotAt<simd128_value_t>(fp, move.src_slot())); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kInt64PairSlot: | 
|  | value = Integer::New( | 
|  | Utils::LowHighTo64Bits(*SlotAt<uint32_t>(fp, move.src_lo_slot()), | 
|  | *SlotAt<int32_t>(fp, move.src_hi_slot()))); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kInt64Slot: | 
|  | value = Integer::New(*SlotAt<int64_t>(fp, move.src_slot())); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kInt32Slot: | 
|  | value = Integer::New(*SlotAt<int32_t>(fp, move.src_slot())); | 
|  | break; | 
|  |  | 
|  | case CatchEntryMove::SourceKind::kUint32Slot: | 
|  | value = Integer::New(*SlotAt<uint32_t>(fp, move.src_slot())); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | *TaggedSlotAt(fp, move.dest_slot()) = value; | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER) | 
|  | void ReadCompressedCatchEntryMoves() { | 
|  | intptr_t pc_offset = pc_ - code_->PayloadStart(); | 
|  | const TypedData& td = TypedData::Handle(code_->catch_entry_moves_maps()); | 
|  | NoSafepointScope no_safepoint; | 
|  | ReadStream stream(static_cast<uint8_t*>(td.DataAddr(0)), td.Length()); | 
|  |  | 
|  | intptr_t prefix_length = 0, suffix_length = 0, suffix_offset = 0; | 
|  | while (stream.PendingBytes() > 0) { | 
|  | intptr_t target_pc_offset = Reader::Read(&stream); | 
|  | prefix_length = Reader::Read(&stream); | 
|  | suffix_length = Reader::Read(&stream); | 
|  | suffix_offset = Reader::Read(&stream); | 
|  | if (pc_offset == target_pc_offset) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Skip the moves. | 
|  | for (intptr_t j = 0; j < prefix_length; j++) { | 
|  | CatchEntryMove::ReadFrom(&stream); | 
|  | } | 
|  | } | 
|  | ASSERT((stream.PendingBytes() > 0) || (prefix_length == 0)); | 
|  |  | 
|  | CatchEntryMoves* moves = | 
|  | CatchEntryMoves::Allocate(prefix_length + suffix_length); | 
|  | for (int j = 0; j < prefix_length; j++) { | 
|  | moves->At(j) = CatchEntryMove::ReadFrom(&stream); | 
|  | } | 
|  | ReadCompressedCatchEntryMovesSuffix(&stream, suffix_offset, suffix_length, | 
|  | moves, prefix_length); | 
|  | catch_entry_moves_ = moves; | 
|  | } | 
|  |  | 
|  | void ReadCompressedCatchEntryMovesSuffix(ReadStream* stream, | 
|  | intptr_t offset, | 
|  | intptr_t length, | 
|  | CatchEntryMoves* moves, | 
|  | intptr_t moves_offset) { | 
|  | stream->SetPosition(offset); | 
|  | Reader::Read(stream);  // skip pc_offset | 
|  | Reader::Read(stream);  // skip variables | 
|  | intptr_t suffix_length = Reader::Read(stream); | 
|  | intptr_t suffix_offset = Reader::Read(stream); | 
|  | intptr_t to_read = length - suffix_length; | 
|  | for (int j = 0; j < to_read; j++) { | 
|  | moves->At(moves_offset + j) = CatchEntryMove::ReadFrom(stream); | 
|  | } | 
|  | if (suffix_length > 0) { | 
|  | ReadCompressedCatchEntryMovesSuffix(stream, suffix_offset, suffix_length, | 
|  | moves, moves_offset + to_read); | 
|  | } | 
|  | } | 
|  |  | 
|  | #else | 
|  | void GetCatchEntryMovesFromDeopt(intptr_t num_vars, StackFrame* frame) { | 
|  | Isolate* isolate = thread_->isolate(); | 
|  | DeoptContext* deopt_context = | 
|  | new DeoptContext(frame, *code_, DeoptContext::kDestIsAllocated, NULL, | 
|  | NULL, true, false /* deoptimizing_code */); | 
|  | isolate->set_deopt_context(deopt_context); | 
|  |  | 
|  | catch_entry_moves_ = deopt_context->ToCatchEntryMoves(num_vars); | 
|  |  | 
|  | isolate->set_deopt_context(NULL); | 
|  | delete deopt_context; | 
|  | } | 
|  | #endif  // defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER) | 
|  |  | 
|  | bool needs_stacktrace; | 
|  | uword handler_pc; | 
|  | uword handler_sp; | 
|  | uword handler_fp; | 
|  |  | 
|  | private: | 
|  | template <typename T> | 
|  | static T* SlotAt(uword fp, int stack_slot) { | 
|  | #if defined(TARGET_ARCH_DBC) | 
|  | return reinterpret_cast<T*>(fp + stack_slot * kWordSize); | 
|  | #else | 
|  | const intptr_t frame_slot = | 
|  | runtime_frame_layout.FrameSlotForVariableIndex(-stack_slot); | 
|  | return reinterpret_cast<T*>(fp + frame_slot * kWordSize); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static RawObject** TaggedSlotAt(uword fp, int stack_slot) { | 
|  | return SlotAt<RawObject*>(fp, stack_slot); | 
|  | } | 
|  |  | 
|  | typedef ReadStream::Raw<sizeof(intptr_t), intptr_t> Reader; | 
|  | Thread* thread_; | 
|  | Code* code_; | 
|  | bool handler_pc_set_; | 
|  | intptr_t pc_;             // Current pc in the handler frame. | 
|  |  | 
|  | const CatchEntryMoves* catch_entry_moves_ = nullptr; | 
|  | CatchEntryMovesCache* catch_entry_moves_cache_ = nullptr; | 
|  | CatchEntryMovesRefPtr cached_catch_entry_moves_; | 
|  | }; | 
|  |  | 
|  | CatchEntryMove CatchEntryMove::ReadFrom(ReadStream* stream) { | 
|  | using Reader = ReadStream::Raw<sizeof(intptr_t), intptr_t>; | 
|  | const intptr_t src = Reader::Read(stream); | 
|  | const intptr_t dest_and_kind = Reader::Read(stream); | 
|  | return CatchEntryMove(src, dest_and_kind); | 
|  | } | 
|  |  | 
|  | #if !defined(DART_PRECOMPILED_RUNTIME) | 
|  | void CatchEntryMove::WriteTo(WriteStream* stream) { | 
|  | using Writer = WriteStream::Raw<sizeof(intptr_t), intptr_t>; | 
|  | Writer::Write(stream, src_); | 
|  | Writer::Write(stream, dest_and_kind_); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void FindErrorHandler(uword* handler_pc, | 
|  | uword* handler_sp, | 
|  | uword* handler_fp) { | 
|  | StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, | 
|  | Thread::Current(), | 
|  | StackFrameIterator::kNoCrossThreadIteration); | 
|  | StackFrame* frame = frames.NextFrame(); | 
|  | ASSERT(frame != NULL); | 
|  | while (!frame->IsEntryFrame()) { | 
|  | frame = frames.NextFrame(); | 
|  | ASSERT(frame != NULL); | 
|  | } | 
|  | ASSERT(frame->IsEntryFrame()); | 
|  | *handler_pc = frame->pc(); | 
|  | *handler_sp = frame->sp(); | 
|  | *handler_fp = frame->fp(); | 
|  | } | 
|  |  | 
|  | static uword RemapExceptionPCForDeopt(Thread* thread, | 
|  | uword program_counter, | 
|  | uword frame_pointer) { | 
|  | #if !defined(TARGET_ARCH_DBC) | 
|  | MallocGrowableArray<PendingLazyDeopt>* pending_deopts = | 
|  | thread->isolate()->pending_deopts(); | 
|  | if (pending_deopts->length() > 0) { | 
|  | // Check if the target frame is scheduled for lazy deopt. | 
|  | for (intptr_t i = 0; i < pending_deopts->length(); i++) { | 
|  | if ((*pending_deopts)[i].fp() == frame_pointer) { | 
|  | // Deopt should now resume in the catch handler instead of after the | 
|  | // call. | 
|  | (*pending_deopts)[i].set_pc(program_counter); | 
|  |  | 
|  | // Jump to the deopt stub instead of the catch handler. | 
|  | program_counter = StubCode::DeoptimizeLazyFromThrow().EntryPoint(); | 
|  | if (FLAG_trace_deoptimization) { | 
|  | THR_Print("Throwing to frame scheduled for lazy deopt fp=%" Pp "\n", | 
|  | frame_pointer); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif  // !DBC | 
|  | return program_counter; | 
|  | } | 
|  |  | 
|  | static void ClearLazyDeopts(Thread* thread, uword frame_pointer) { | 
|  | #if !defined(TARGET_ARCH_DBC) | 
|  | MallocGrowableArray<PendingLazyDeopt>* pending_deopts = | 
|  | thread->isolate()->pending_deopts(); | 
|  | if (pending_deopts->length() > 0) { | 
|  | // We may be jumping over frames scheduled for lazy deopt. Remove these | 
|  | // frames from the pending deopt table, but only after unmarking them so | 
|  | // any stack walk that happens before the stack is unwound will still work. | 
|  | { | 
|  | DartFrameIterator frames(thread, | 
|  | StackFrameIterator::kNoCrossThreadIteration); | 
|  | StackFrame* frame = frames.NextFrame(); | 
|  | while ((frame != NULL) && (frame->fp() < frame_pointer)) { | 
|  | if (frame->IsMarkedForLazyDeopt()) { | 
|  | frame->UnmarkForLazyDeopt(); | 
|  | } | 
|  | frame = frames.NextFrame(); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(DEBUG) | 
|  | ValidateFrames(); | 
|  | #endif | 
|  |  | 
|  | for (intptr_t i = 0; i < pending_deopts->length(); i++) { | 
|  | if ((*pending_deopts)[i].fp() < frame_pointer) { | 
|  | if (FLAG_trace_deoptimization) { | 
|  | THR_Print( | 
|  | "Lazy deopt skipped due to throw for " | 
|  | "fp=%" Pp ", pc=%" Pp "\n", | 
|  | (*pending_deopts)[i].fp(), (*pending_deopts)[i].pc()); | 
|  | } | 
|  | pending_deopts->RemoveAt(i); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(DEBUG) | 
|  | ValidateFrames(); | 
|  | #endif | 
|  | } | 
|  | #endif  // !DBC | 
|  | } | 
|  |  | 
|  | static void JumpToExceptionHandler(Thread* thread, | 
|  | uword program_counter, | 
|  | uword stack_pointer, | 
|  | uword frame_pointer, | 
|  | const Object& exception_object, | 
|  | const Object& stacktrace_object) { | 
|  | uword remapped_pc = | 
|  | RemapExceptionPCForDeopt(thread, program_counter, frame_pointer); | 
|  | thread->set_active_exception(exception_object); | 
|  | thread->set_active_stacktrace(stacktrace_object); | 
|  | thread->set_resume_pc(remapped_pc); | 
|  | uword run_exception_pc = StubCode::RunExceptionHandler().EntryPoint(); | 
|  | Exceptions::JumpToFrame(thread, run_exception_pc, stack_pointer, | 
|  | frame_pointer, false /* do not clear deopt */); | 
|  | } | 
|  |  | 
|  | NO_SANITIZE_SAFE_STACK  // This function manipulates the safestack pointer. | 
|  | void Exceptions::JumpToFrame(Thread* thread, | 
|  | uword program_counter, | 
|  | uword stack_pointer, | 
|  | uword frame_pointer, | 
|  | bool clear_deopt_at_target) { | 
|  | uword fp_for_clearing = | 
|  | (clear_deopt_at_target ? frame_pointer + 1 : frame_pointer); | 
|  | ClearLazyDeopts(thread, fp_for_clearing); | 
|  | #if defined(USING_SIMULATOR) | 
|  | // Unwinding of the C++ frames and destroying of their stack resources is done | 
|  | // by the simulator, because the target stack_pointer is a simulated stack | 
|  | // pointer and not the C++ stack pointer. | 
|  |  | 
|  | // Continue simulating at the given pc in the given frame after setting up the | 
|  | // exception object in the kExceptionObjectReg register and the stacktrace | 
|  | // object (may be raw null) in the kStackTraceObjectReg register. | 
|  |  | 
|  | Simulator::Current()->JumpToFrame(program_counter, stack_pointer, | 
|  | frame_pointer, thread); | 
|  | #else | 
|  |  | 
|  | #if !defined(DART_PRECOMPILED_RUNTIME) | 
|  | // TODO(regis): We still possibly need to unwind interpreter frames if they | 
|  | // are callee frames of the C++ frame handling the exception. | 
|  | if (FLAG_enable_interpreter) { | 
|  | Interpreter* interpreter = thread->interpreter(); | 
|  | if ((interpreter != NULL) && interpreter->HasFrame(frame_pointer)) { | 
|  | interpreter->JumpToFrame(program_counter, stack_pointer, frame_pointer, | 
|  | thread); | 
|  | } | 
|  | } | 
|  | #endif  // !defined(DART_PRECOMPILED_RUNTIME) | 
|  |  | 
|  | // Prepare for unwinding frames by destroying all the stack resources | 
|  | // in the previous frames. | 
|  | StackResource::Unwind(thread); | 
|  |  | 
|  | // Call a stub to set up the exception object in kExceptionObjectReg, | 
|  | // to set up the stacktrace object in kStackTraceObjectReg, and to | 
|  | // continue execution at the given pc in the given frame. | 
|  | typedef void (*ExcpHandler)(uword, uword, uword, Thread*); | 
|  | ExcpHandler func = | 
|  | reinterpret_cast<ExcpHandler>(StubCode::JumpToFrame().EntryPoint()); | 
|  |  | 
|  | // Unpoison the stack before we tear it down in the generated stub code. | 
|  | uword current_sp = OSThread::GetCurrentStackPointer() - 1024; | 
|  | ASAN_UNPOISON(reinterpret_cast<void*>(current_sp), | 
|  | stack_pointer - current_sp); | 
|  |  | 
|  | // We are jumping over C++ frames, so we have to set the safestack pointer | 
|  | // back to what it was when we entered the runtime from Dart code. | 
|  | #if defined(USING_SAFE_STACK) | 
|  | const uword saved_ssp = thread->saved_safestack_limit(); | 
|  | OSThread::SetCurrentSafestackPointer(saved_ssp); | 
|  | #endif | 
|  |  | 
|  | func(program_counter, stack_pointer, frame_pointer, thread); | 
|  | #endif | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | static RawField* LookupStackTraceField(const Instance& instance) { | 
|  | if (instance.GetClassId() < kNumPredefinedCids) { | 
|  | // 'class Error' is not a predefined class. | 
|  | return Field::null(); | 
|  | } | 
|  | Thread* thread = Thread::Current(); | 
|  | Zone* zone = thread->zone(); | 
|  | Isolate* isolate = thread->isolate(); | 
|  | Class& error_class = | 
|  | Class::Handle(zone, isolate->object_store()->error_class()); | 
|  | if (error_class.IsNull()) { | 
|  | const Library& core_lib = Library::Handle(zone, Library::CoreLibrary()); | 
|  | error_class = core_lib.LookupClass(Symbols::Error()); | 
|  | ASSERT(!error_class.IsNull()); | 
|  | isolate->object_store()->set_error_class(error_class); | 
|  | } | 
|  | // If instance class extends 'class Error' return '_stackTrace' field. | 
|  | Class& test_class = Class::Handle(zone, instance.clazz()); | 
|  | AbstractType& type = AbstractType::Handle(zone, AbstractType::null()); | 
|  | while (true) { | 
|  | if (test_class.raw() == error_class.raw()) { | 
|  | return error_class.LookupInstanceFieldAllowPrivate( | 
|  | Symbols::_stackTrace()); | 
|  | } | 
|  | type = test_class.super_type(); | 
|  | if (type.IsNull()) return Field::null(); | 
|  | test_class = type.type_class(); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | return Field::null(); | 
|  | } | 
|  |  | 
|  | RawStackTrace* Exceptions::CurrentStackTrace() { | 
|  | return GetStackTraceForException(); | 
|  | } | 
|  |  | 
|  | static void ThrowExceptionHelper(Thread* thread, | 
|  | const Instance& incoming_exception, | 
|  | const Instance& existing_stacktrace, | 
|  | const bool is_rethrow) { | 
|  | Zone* zone = thread->zone(); | 
|  | Isolate* isolate = thread->isolate(); | 
|  | bool use_preallocated_stacktrace = false; | 
|  | Instance& exception = Instance::Handle(zone, incoming_exception.raw()); | 
|  | if (exception.IsNull()) { | 
|  | exception ^= | 
|  | Exceptions::Create(Exceptions::kNullThrown, Object::empty_array()); | 
|  | } else if (exception.raw() == isolate->object_store()->out_of_memory() || | 
|  | exception.raw() == isolate->object_store()->stack_overflow()) { | 
|  | use_preallocated_stacktrace = true; | 
|  | } | 
|  | // Find the exception handler and determine if the handler needs a | 
|  | // stacktrace. | 
|  | ExceptionHandlerFinder finder(thread); | 
|  | bool handler_exists = finder.Find(); | 
|  | uword handler_pc = finder.handler_pc; | 
|  | uword handler_sp = finder.handler_sp; | 
|  | uword handler_fp = finder.handler_fp; | 
|  | bool handler_needs_stacktrace = finder.needs_stacktrace; | 
|  | Instance& stacktrace = Instance::Handle(zone); | 
|  | if (use_preallocated_stacktrace) { | 
|  | if (handler_pc == 0) { | 
|  | // No Dart frame. | 
|  | ASSERT(incoming_exception.raw() == | 
|  | isolate->object_store()->out_of_memory()); | 
|  | const UnhandledException& error = UnhandledException::Handle( | 
|  | zone, isolate->object_store()->preallocated_unhandled_exception()); | 
|  | thread->long_jump_base()->Jump(1, error); | 
|  | UNREACHABLE(); | 
|  | } | 
|  | stacktrace ^= isolate->object_store()->preallocated_stack_trace(); | 
|  | PreallocatedStackTraceBuilder frame_builder(stacktrace); | 
|  | ASSERT(existing_stacktrace.IsNull() || | 
|  | (existing_stacktrace.raw() == stacktrace.raw())); | 
|  | ASSERT(existing_stacktrace.IsNull() || is_rethrow); | 
|  | if (handler_needs_stacktrace && existing_stacktrace.IsNull()) { | 
|  | BuildStackTrace(&frame_builder); | 
|  | } | 
|  | } else { | 
|  | if (!existing_stacktrace.IsNull()) { | 
|  | // If we have an existing stack trace then this better be a rethrow. The | 
|  | // reverse is not necessarily true (e.g. Dart_PropagateError can cause | 
|  | // a rethrow being called without an existing stacktrace.) | 
|  | ASSERT(is_rethrow); | 
|  | stacktrace = existing_stacktrace.raw(); | 
|  | } else { | 
|  | // Get stacktrace field of class Error to determine whether we have a | 
|  | // subclass of Error which carries around its stack trace. | 
|  | const Field& stacktrace_field = | 
|  | Field::Handle(zone, LookupStackTraceField(exception)); | 
|  | if (!stacktrace_field.IsNull() || handler_needs_stacktrace) { | 
|  | // Collect the stacktrace if needed. | 
|  | ASSERT(existing_stacktrace.IsNull()); | 
|  | stacktrace = Exceptions::CurrentStackTrace(); | 
|  | // If we have an Error object, then set its stackTrace field only if it | 
|  | // not yet initialized. | 
|  | if (!stacktrace_field.IsNull() && | 
|  | (exception.GetField(stacktrace_field) == Object::null())) { | 
|  | exception.SetField(stacktrace_field, stacktrace); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | // We expect to find a handler_pc, if the exception is unhandled | 
|  | // then we expect to at least have the dart entry frame on the | 
|  | // stack as Exceptions::Throw should happen only after a dart | 
|  | // invocation has been done. | 
|  | ASSERT(handler_pc != 0); | 
|  |  | 
|  | if (FLAG_print_stacktrace_at_throw) { | 
|  | THR_Print("Exception '%s' thrown:\n", exception.ToCString()); | 
|  | THR_Print("%s\n", stacktrace.ToCString()); | 
|  | } | 
|  | if (handler_exists) { | 
|  | finder.PrepareFrameForCatchEntry(); | 
|  | // Found a dart handler for the exception, jump to it. | 
|  | JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp, | 
|  | exception, stacktrace); | 
|  | } else { | 
|  | // No dart exception handler found in this invocation sequence, | 
|  | // so we create an unhandled exception object and return to the | 
|  | // invocation stub so that it returns this unhandled exception | 
|  | // object. The C++ code which invoked this dart sequence can check | 
|  | // and do the appropriate thing (rethrow the exception to the | 
|  | // dart invocation sequence above it, print diagnostics and terminate | 
|  | // the isolate etc.). This can happen in the compiler, which is not | 
|  | // allowed to allocate in new space, so we pass the kOld argument. | 
|  | const UnhandledException& unhandled_exception = UnhandledException::Handle( | 
|  | zone, UnhandledException::New(exception, stacktrace, Heap::kOld)); | 
|  | stacktrace = StackTrace::null(); | 
|  | JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp, | 
|  | unhandled_exception, stacktrace); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | // Static helpers for allocating, initializing, and throwing an error instance. | 
|  |  | 
|  | // Return the script of the Dart function that called the native entry or the | 
|  | // runtime entry. The frame iterator points to the callee. | 
|  | RawScript* Exceptions::GetCallerScript(DartFrameIterator* iterator) { | 
|  | StackFrame* caller_frame = iterator->NextFrame(); | 
|  | ASSERT(caller_frame != NULL && caller_frame->IsDartFrame()); | 
|  | const Function& caller = Function::Handle(caller_frame->LookupDartFunction()); | 
|  | ASSERT(!caller.IsNull()); | 
|  | return caller.script(); | 
|  | } | 
|  |  | 
|  | // Allocate a new instance of the given class name. | 
|  | // TODO(hausner): Rename this NewCoreInstance to call out the fact that | 
|  | // the class name is resolved in the core library implicitly? | 
|  | RawInstance* Exceptions::NewInstance(const char* class_name) { | 
|  | Thread* thread = Thread::Current(); | 
|  | Zone* zone = thread->zone(); | 
|  | const String& cls_name = | 
|  | String::Handle(zone, Symbols::New(thread, class_name)); | 
|  | const Library& core_lib = Library::Handle(Library::CoreLibrary()); | 
|  | // No ambiguity error expected: passing NULL. | 
|  | Class& cls = Class::Handle(core_lib.LookupClass(cls_name)); | 
|  | ASSERT(!cls.IsNull()); | 
|  | // There are no parameterized error types, so no need to set type arguments. | 
|  | return Instance::New(cls); | 
|  | } | 
|  |  | 
|  | // Allocate, initialize, and throw a TypeError or CastError. | 
|  | // If error_msg is not null, throw a TypeError, even for a type cast. | 
|  | void Exceptions::CreateAndThrowTypeError(TokenPosition location, | 
|  | const AbstractType& src_type, | 
|  | const AbstractType& dst_type, | 
|  | const String& dst_name, | 
|  | const String& bound_error_msg) { | 
|  | ASSERT(!dst_name.IsNull());  // Pass Symbols::Empty() instead. | 
|  | Thread* thread = Thread::Current(); | 
|  | Zone* zone = thread->zone(); | 
|  | const Array& args = Array::Handle(zone, Array::New(4)); | 
|  |  | 
|  | ExceptionType exception_type = | 
|  | (bound_error_msg.IsNull() && | 
|  | (dst_name.raw() == Symbols::InTypeCast().raw())) | 
|  | ? kCast | 
|  | : kType; | 
|  |  | 
|  | DartFrameIterator iterator(thread, | 
|  | StackFrameIterator::kNoCrossThreadIteration); | 
|  | const Script& script = Script::Handle(zone, GetCallerScript(&iterator)); | 
|  | intptr_t line = -1; | 
|  | intptr_t column = -1; | 
|  | ASSERT(!script.IsNull()); | 
|  | if (location.IsReal()) { | 
|  | if (script.HasSource() || script.kind() == RawScript::kKernelTag) { | 
|  | script.GetTokenLocation(location, &line, &column); | 
|  | } else { | 
|  | script.GetTokenLocation(location, &line, NULL); | 
|  | } | 
|  | } | 
|  | // Initialize '_url', '_line', and '_column' arguments. | 
|  | args.SetAt(0, String::Handle(zone, script.url())); | 
|  | args.SetAt(1, Smi::Handle(zone, Smi::New(line))); | 
|  | args.SetAt(2, Smi::Handle(zone, Smi::New(column))); | 
|  |  | 
|  | // Construct '_errorMsg'. | 
|  | const GrowableObjectArray& pieces = | 
|  | GrowableObjectArray::Handle(zone, GrowableObjectArray::New(20)); | 
|  |  | 
|  | // Print bound error first, if any. | 
|  | if (!bound_error_msg.IsNull() && (bound_error_msg.Length() > 0)) { | 
|  | pieces.Add(bound_error_msg); | 
|  | pieces.Add(Symbols::NewLine()); | 
|  | } | 
|  |  | 
|  | // If dst_type is malformed or malbounded, only print the embedded error. | 
|  | if (!dst_type.IsNull()) { | 
|  | const LanguageError& error = LanguageError::Handle(zone, dst_type.error()); | 
|  | if (!error.IsNull()) { | 
|  | // Print the embedded error only. | 
|  | pieces.Add(String::Handle(zone, String::New(error.ToErrorCString()))); | 
|  | pieces.Add(Symbols::NewLine()); | 
|  | } else { | 
|  | // Describe the type error. | 
|  | if (!src_type.IsNull()) { | 
|  | pieces.Add(Symbols::TypeQuote()); | 
|  | pieces.Add(String::Handle(zone, src_type.UserVisibleName())); | 
|  | pieces.Add(Symbols::QuoteIsNotASubtypeOf()); | 
|  | } | 
|  | pieces.Add(Symbols::TypeQuote()); | 
|  | pieces.Add(String::Handle(zone, dst_type.UserVisibleName())); | 
|  | pieces.Add(Symbols::SingleQuote()); | 
|  | if (exception_type == kCast) { | 
|  | pieces.Add(dst_name); | 
|  | } else if (dst_name.Length() > 0) { | 
|  | pieces.Add(Symbols::SpaceOfSpace()); | 
|  | pieces.Add(Symbols::SingleQuote()); | 
|  | pieces.Add(dst_name); | 
|  | pieces.Add(Symbols::SingleQuote()); | 
|  | } | 
|  | // Print ambiguous URIs of src and dst types. | 
|  | URIs uris(zone, 12); | 
|  | if (!src_type.IsNull()) { | 
|  | src_type.EnumerateURIs(&uris); | 
|  | } | 
|  | if (!dst_type.IsDynamicType() && !dst_type.IsVoidType()) { | 
|  | dst_type.EnumerateURIs(&uris); | 
|  | } | 
|  | const String& formatted_uris = | 
|  | String::Handle(zone, AbstractType::PrintURIs(&uris)); | 
|  | if (formatted_uris.Length() > 0) { | 
|  | pieces.Add(Symbols::SpaceWhereNewLine()); | 
|  | pieces.Add(formatted_uris); | 
|  | } | 
|  | } | 
|  | } | 
|  | const Array& arr = Array::Handle(zone, Array::MakeFixedLength(pieces)); | 
|  | const String& error_msg = String::Handle(zone, String::ConcatAll(arr)); | 
|  | args.SetAt(3, error_msg); | 
|  |  | 
|  | // Type errors in the core library may be difficult to diagnose. | 
|  | // Print type error information before throwing the error when debugging. | 
|  | if (FLAG_print_stacktrace_at_throw) { | 
|  | THR_Print("'%s': Failed type check: line %" Pd " pos %" Pd ": ", | 
|  | String::Handle(zone, script.url()).ToCString(), line, column); | 
|  | THR_Print("%s\n", error_msg.ToCString()); | 
|  | } | 
|  |  | 
|  | // Throw TypeError or CastError instance. | 
|  | Exceptions::ThrowByType(exception_type, args); | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void Exceptions::Throw(Thread* thread, const Instance& exception) { | 
|  | // Do not notify debugger on stack overflow and out of memory exceptions. | 
|  | // The VM would crash when the debugger calls back into the VM to | 
|  | // get values of variables. | 
|  | #if !defined(PRODUCT) | 
|  | Isolate* isolate = thread->isolate(); | 
|  | if (exception.raw() != isolate->object_store()->out_of_memory() && | 
|  | exception.raw() != isolate->object_store()->stack_overflow()) { | 
|  | isolate->debugger()->PauseException(exception); | 
|  | } | 
|  | #endif | 
|  | // Null object is a valid exception object. | 
|  | ThrowExceptionHelper(thread, exception, StackTrace::Handle(thread->zone()), | 
|  | false); | 
|  | } | 
|  |  | 
|  | void Exceptions::ReThrow(Thread* thread, | 
|  | const Instance& exception, | 
|  | const Instance& stacktrace) { | 
|  | // Null object is a valid exception object. | 
|  | ThrowExceptionHelper(thread, exception, stacktrace, true); | 
|  | } | 
|  |  | 
|  | void Exceptions::PropagateError(const Error& error) { | 
|  | Thread* thread = Thread::Current(); | 
|  | Zone* zone = thread->zone(); | 
|  | ASSERT(thread->top_exit_frame_info() != 0); | 
|  | if (error.IsUnhandledException()) { | 
|  | // If the error object represents an unhandled exception, then | 
|  | // rethrow the exception in the normal fashion. | 
|  | const UnhandledException& uhe = UnhandledException::Cast(error); | 
|  | const Instance& exc = Instance::Handle(zone, uhe.exception()); | 
|  | const Instance& stk = Instance::Handle(zone, uhe.stacktrace()); | 
|  | Exceptions::ReThrow(thread, exc, stk); | 
|  | } else { | 
|  | // Return to the invocation stub and return this error object.  The | 
|  | // C++ code which invoked this dart sequence can check and do the | 
|  | // appropriate thing. | 
|  | uword handler_pc = 0; | 
|  | uword handler_sp = 0; | 
|  | uword handler_fp = 0; | 
|  | FindErrorHandler(&handler_pc, &handler_sp, &handler_fp); | 
|  | JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp, error, | 
|  | StackTrace::Handle(zone));  // Null stacktrace. | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void Exceptions::PropagateToEntry(const Error& error) { | 
|  | Thread* thread = Thread::Current(); | 
|  | Zone* zone = thread->zone(); | 
|  | ASSERT(thread->top_exit_frame_info() != 0); | 
|  | Instance& stacktrace = Instance::Handle(zone); | 
|  | if (error.IsUnhandledException()) { | 
|  | const UnhandledException& uhe = UnhandledException::Cast(error); | 
|  | stacktrace = uhe.stacktrace(); | 
|  | } else { | 
|  | stacktrace = Exceptions::CurrentStackTrace(); | 
|  | } | 
|  | uword handler_pc = 0; | 
|  | uword handler_sp = 0; | 
|  | uword handler_fp = 0; | 
|  | FindErrorHandler(&handler_pc, &handler_sp, &handler_fp); | 
|  | JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp, error, | 
|  | stacktrace); | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | void Exceptions::ThrowByType(ExceptionType type, const Array& arguments) { | 
|  | Thread* thread = Thread::Current(); | 
|  | const Object& result = | 
|  | Object::Handle(thread->zone(), Create(type, arguments)); | 
|  | if (result.IsError()) { | 
|  | // We got an error while constructing the exception object. | 
|  | // Propagate the error instead of throwing the exception. | 
|  | PropagateError(Error::Cast(result)); | 
|  | } else { | 
|  | ASSERT(result.IsInstance()); | 
|  | Throw(thread, Instance::Cast(result)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Exceptions::ThrowOOM() { | 
|  | Thread* thread = Thread::Current(); | 
|  | Isolate* isolate = thread->isolate(); | 
|  | const Instance& oom = Instance::Handle( | 
|  | thread->zone(), isolate->object_store()->out_of_memory()); | 
|  | Throw(thread, oom); | 
|  | } | 
|  |  | 
|  | void Exceptions::ThrowStackOverflow() { | 
|  | Thread* thread = Thread::Current(); | 
|  | Isolate* isolate = thread->isolate(); | 
|  | const Instance& stack_overflow = Instance::Handle( | 
|  | thread->zone(), isolate->object_store()->stack_overflow()); | 
|  | Throw(thread, stack_overflow); | 
|  | } | 
|  |  | 
|  | void Exceptions::ThrowArgumentError(const Instance& arg) { | 
|  | const Array& args = Array::Handle(Array::New(1)); | 
|  | args.SetAt(0, arg); | 
|  | Exceptions::ThrowByType(Exceptions::kArgument, args); | 
|  | } | 
|  |  | 
|  | void Exceptions::ThrowRangeError(const char* argument_name, | 
|  | const Integer& argument_value, | 
|  | intptr_t expected_from, | 
|  | intptr_t expected_to) { | 
|  | const Array& args = Array::Handle(Array::New(4)); | 
|  | args.SetAt(0, argument_value); | 
|  | args.SetAt(1, Integer::Handle(Integer::New(expected_from))); | 
|  | args.SetAt(2, Integer::Handle(Integer::New(expected_to))); | 
|  | args.SetAt(3, String::Handle(String::New(argument_name))); | 
|  | Exceptions::ThrowByType(Exceptions::kRange, args); | 
|  | } | 
|  |  | 
|  | void Exceptions::ThrowRangeErrorMsg(const char* msg) { | 
|  | const Array& args = Array::Handle(Array::New(1)); | 
|  | args.SetAt(0, String::Handle(String::New(msg))); | 
|  | Exceptions::ThrowByType(Exceptions::kRangeMsg, args); | 
|  | } | 
|  |  | 
|  | void Exceptions::ThrowCompileTimeError(const LanguageError& error) { | 
|  | const Array& args = Array::Handle(Array::New(1)); | 
|  | args.SetAt(0, String::Handle(error.FormatMessage())); | 
|  | Exceptions::ThrowByType(Exceptions::kCompileTimeError, args); | 
|  | } | 
|  |  | 
|  | RawObject* Exceptions::Create(ExceptionType type, const Array& arguments) { | 
|  | Library& library = Library::Handle(); | 
|  | const String* class_name = NULL; | 
|  | const String* constructor_name = &Symbols::Dot(); | 
|  | switch (type) { | 
|  | case kNone: | 
|  | case kStackOverflow: | 
|  | case kOutOfMemory: | 
|  | UNREACHABLE(); | 
|  | break; | 
|  | case kRange: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::RangeError(); | 
|  | constructor_name = &Symbols::DotRange(); | 
|  | break; | 
|  | case kRangeMsg: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::RangeError(); | 
|  | constructor_name = &Symbols::Dot(); | 
|  | break; | 
|  | case kArgument: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::ArgumentError(); | 
|  | break; | 
|  | case kArgumentValue: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::ArgumentError(); | 
|  | constructor_name = &Symbols::DotValue(); | 
|  | break; | 
|  | case kIntegerDivisionByZeroException: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::IntegerDivisionByZeroException(); | 
|  | break; | 
|  | case kNoSuchMethod: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::NoSuchMethodError(); | 
|  | constructor_name = &Symbols::DotWithType(); | 
|  | break; | 
|  | case kFormat: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::FormatException(); | 
|  | break; | 
|  | case kUnsupported: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::UnsupportedError(); | 
|  | break; | 
|  | case kNullThrown: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::NullThrownError(); | 
|  | break; | 
|  | case kIsolateSpawn: | 
|  | library = Library::IsolateLibrary(); | 
|  | class_name = &Symbols::IsolateSpawnException(); | 
|  | break; | 
|  | case kAssertion: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::AssertionError(); | 
|  | constructor_name = &Symbols::DotCreate(); | 
|  | break; | 
|  | case kCast: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::CastError(); | 
|  | constructor_name = &Symbols::DotCreate(); | 
|  | break; | 
|  | case kType: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::TypeError(); | 
|  | constructor_name = &Symbols::DotCreate(); | 
|  | break; | 
|  | case kFallThrough: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::FallThroughError(); | 
|  | constructor_name = &Symbols::DotCreate(); | 
|  | break; | 
|  | case kAbstractClassInstantiation: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::AbstractClassInstantiationError(); | 
|  | constructor_name = &Symbols::DotCreate(); | 
|  | break; | 
|  | case kCyclicInitializationError: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::CyclicInitializationError(); | 
|  | break; | 
|  | case kCompileTimeError: | 
|  | library = Library::CoreLibrary(); | 
|  | class_name = &Symbols::_CompileTimeError(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | Thread* thread = Thread::Current(); | 
|  | NoReloadScope no_reload_scope(thread->isolate(), thread); | 
|  | return DartLibraryCalls::InstanceCreate(library, *class_name, | 
|  | *constructor_name, arguments); | 
|  | } | 
|  |  | 
|  | }  // namespace dart |