mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Liam Appelbe](/assets/img/avatar_default.png)
Second sub-CL of https://dart-review.googlesource.com/c/sdk/+/100644 The first was here https://dart-review.googlesource.com/c/sdk/+/103487 Now that offsets_extractor is checked in, and we have a big header full of hard coded constants, the next step is to make sure everything is using those constants. This is essentially everything in the original CL, except the new simarm_x64 architecture. Bug: https://github.com/dart-lang/sdk/issues/36839 Change-Id: I236e4f30aa1df6d92209891c983b792d1835b608 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104286 Commit-Queue: Liam Appelbe <liama@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
331 lines
10 KiB
C++
331 lines
10 KiB
C++
// 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.
|
|
|
|
#ifndef RUNTIME_VM_EXCEPTIONS_H_
|
|
#define RUNTIME_VM_EXCEPTIONS_H_
|
|
|
|
#include "vm/allocation.h"
|
|
#include "vm/bitfield.h"
|
|
#include "vm/token_position.h"
|
|
|
|
namespace dart {
|
|
|
|
// Forward declarations.
|
|
class AbstractType;
|
|
class Array;
|
|
class DartFrameIterator;
|
|
class Error;
|
|
class LanguageError;
|
|
class Instance;
|
|
class Integer;
|
|
class RawInstance;
|
|
class RawObject;
|
|
class RawScript;
|
|
class RawStackTrace;
|
|
class ReadStream;
|
|
class WriteStream;
|
|
class String;
|
|
class Thread;
|
|
class TypedData;
|
|
|
|
class Exceptions : AllStatic {
|
|
public:
|
|
DART_NORETURN static void Throw(Thread* thread, const Instance& exception);
|
|
DART_NORETURN static void ReThrow(Thread* thread,
|
|
const Instance& exception,
|
|
const Instance& stacktrace);
|
|
DART_NORETURN static void PropagateError(const Error& error);
|
|
|
|
// Propagate an error to the entry frame, skipping over Dart frames.
|
|
DART_NORETURN static void PropagateToEntry(const Error& error);
|
|
|
|
// Helpers to create and throw errors.
|
|
static RawStackTrace* CurrentStackTrace();
|
|
static RawScript* GetCallerScript(DartFrameIterator* iterator);
|
|
static RawInstance* NewInstance(const char* class_name);
|
|
static void CreateAndThrowTypeError(TokenPosition location,
|
|
const AbstractType& src_type,
|
|
const AbstractType& dst_type,
|
|
const String& dst_name);
|
|
|
|
enum ExceptionType {
|
|
kNone,
|
|
kRange,
|
|
kRangeMsg,
|
|
kArgument,
|
|
kArgumentValue,
|
|
kIntegerDivisionByZeroException,
|
|
kNoSuchMethod,
|
|
kFormat,
|
|
kUnsupported,
|
|
kStackOverflow,
|
|
kOutOfMemory,
|
|
kNullThrown,
|
|
kIsolateSpawn,
|
|
kAssertion,
|
|
kCast,
|
|
kType,
|
|
kFallThrough,
|
|
kAbstractClassInstantiation,
|
|
kCyclicInitializationError,
|
|
kCompileTimeError,
|
|
};
|
|
|
|
DART_NORETURN static void ThrowByType(ExceptionType type,
|
|
const Array& arguments);
|
|
// Uses the preallocated out of memory exception to avoid calling
|
|
// into Dart code or allocating any code.
|
|
DART_NORETURN static void ThrowOOM();
|
|
DART_NORETURN static void ThrowStackOverflow();
|
|
DART_NORETURN static void ThrowArgumentError(const Instance& arg);
|
|
DART_NORETURN static void ThrowRangeError(const char* argument_name,
|
|
const Integer& argument_value,
|
|
intptr_t expected_from,
|
|
intptr_t expected_to);
|
|
DART_NORETURN static void ThrowRangeErrorMsg(const char* msg);
|
|
DART_NORETURN static void ThrowUnsupportedError(const char* msg);
|
|
DART_NORETURN static void ThrowCompileTimeError(const LanguageError& error);
|
|
|
|
// Returns a RawInstance if the exception is successfully created,
|
|
// otherwise returns a RawError.
|
|
static RawObject* Create(ExceptionType type, const Array& arguments);
|
|
|
|
DART_NORETURN static void JumpToFrame(Thread* thread,
|
|
uword program_counter,
|
|
uword stack_pointer,
|
|
uword frame_pointer,
|
|
bool clear_deopt_at_target);
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(Exceptions);
|
|
};
|
|
|
|
// The index into the ExceptionHandlers table corresponds to
|
|
// the try_index of the handler.
|
|
struct ExceptionHandlerInfo {
|
|
uint32_t handler_pc_offset; // PC offset value of handler.
|
|
int16_t outer_try_index; // Try block index of enclosing try block.
|
|
int8_t needs_stacktrace; // True if a stacktrace is needed.
|
|
int8_t has_catch_all; // Catches all exceptions.
|
|
int8_t is_generated; // True if this is a generated handler.
|
|
};
|
|
|
|
//
|
|
// Support for try/catch in the optimized code.
|
|
//
|
|
// Optimizing compiler does not model exceptional control flow explicitly,
|
|
// instead we rely on the runtime system to create correct state at the
|
|
// entry into the catch block by reshuffling values in the frame into
|
|
// positions where they are expected to be at the beginning of the catch block.
|
|
//
|
|
// See runtime/docs/compiler/exceptions.md for more details.
|
|
//
|
|
|
|
// A single move from a stack slot or an object pool into another stack slot.
|
|
// Destination slot is expecting only tagged values, however source
|
|
// slot can contain an unboxed value (e.g. an unboxed double) - in this case
|
|
// we will box the value before executing the move.
|
|
class CatchEntryMove {
|
|
public:
|
|
CatchEntryMove()
|
|
: src_(0),
|
|
dest_and_kind_(static_cast<intptr_t>(SourceKind::kTaggedSlot)) {
|
|
ASSERT(IsRedundant());
|
|
}
|
|
|
|
enum class SourceKind {
|
|
kConstant,
|
|
kTaggedSlot,
|
|
kDoubleSlot,
|
|
kFloat32x4Slot,
|
|
kFloat64x2Slot,
|
|
kInt32x4Slot,
|
|
kInt64PairSlot,
|
|
kInt64Slot,
|
|
kInt32Slot,
|
|
kUint32Slot,
|
|
};
|
|
|
|
SourceKind source_kind() const {
|
|
return SourceKindField::decode(dest_and_kind_);
|
|
}
|
|
|
|
intptr_t src_slot() const {
|
|
ASSERT(source_kind() != SourceKind::kInt64PairSlot);
|
|
return src_;
|
|
}
|
|
|
|
intptr_t src_lo_slot() const {
|
|
ASSERT(source_kind() == SourceKind::kInt64PairSlot);
|
|
return LoSourceSlot::decode(src_);
|
|
}
|
|
|
|
intptr_t src_hi_slot() const {
|
|
ASSERT(source_kind() == SourceKind::kInt64PairSlot);
|
|
return HiSourceSlot::decode(src_);
|
|
}
|
|
|
|
intptr_t dest_slot() const {
|
|
return dest_and_kind_ >> SourceKindField::bitsize();
|
|
}
|
|
|
|
static CatchEntryMove FromConstant(intptr_t pool_id, intptr_t dest_slot) {
|
|
return FromSlot(SourceKind::kConstant, pool_id, dest_slot);
|
|
}
|
|
|
|
static CatchEntryMove FromSlot(SourceKind kind,
|
|
intptr_t src_slot,
|
|
intptr_t dest_slot) {
|
|
return CatchEntryMove(src_slot,
|
|
SourceKindField::encode(kind) |
|
|
(dest_slot << SourceKindField::bitsize()));
|
|
}
|
|
|
|
static intptr_t EncodePairSource(intptr_t src_lo_slot, intptr_t src_hi_slot) {
|
|
return LoSourceSlot::encode(src_lo_slot) |
|
|
HiSourceSlot::encode(src_hi_slot);
|
|
}
|
|
|
|
bool IsRedundant() const {
|
|
return (source_kind() == SourceKind::kTaggedSlot) &&
|
|
(dest_slot() == src_slot());
|
|
}
|
|
|
|
bool operator==(const CatchEntryMove& rhs) const {
|
|
return src_ == rhs.src_ && dest_and_kind_ == rhs.dest_and_kind_;
|
|
}
|
|
|
|
static CatchEntryMove ReadFrom(ReadStream* stream);
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
void WriteTo(WriteStream* stream);
|
|
#endif
|
|
|
|
private:
|
|
CatchEntryMove(int32_t src, int32_t dest_and_kind)
|
|
: src_(src), dest_and_kind_(dest_and_kind) {}
|
|
|
|
// Note: BitField helper does not work with signed values of size that does
|
|
// not match the destination size - thus we don't use BitField for declaring
|
|
// DestinationField and instead encode and decode it manually.
|
|
using SourceKindField = BitField<int32_t, SourceKind, 0, 4>;
|
|
|
|
static constexpr intptr_t kHalfSourceBits = 16;
|
|
using LoSourceSlot = BitField<int32_t, int32_t, 0, kHalfSourceBits>;
|
|
using HiSourceSlot =
|
|
BitField<int32_t, int32_t, kHalfSourceBits, kHalfSourceBits>;
|
|
|
|
int32_t src_;
|
|
int32_t dest_and_kind_;
|
|
};
|
|
|
|
// A sequence of moves that needs to be executed to create a state expected
|
|
// at the catch entry.
|
|
// Note: this is a deserialized representation that is used by the runtime
|
|
// system as a temporary representation and for caching. That is why this
|
|
// object is allocated in the malloced heap and not in the Dart heap.
|
|
class CatchEntryMoves {
|
|
public:
|
|
static CatchEntryMoves* Allocate(intptr_t num_moves) {
|
|
auto result = reinterpret_cast<CatchEntryMoves*>(
|
|
malloc(sizeof(CatchEntryMoves) + sizeof(CatchEntryMove) * num_moves));
|
|
result->count_ = num_moves;
|
|
return result;
|
|
}
|
|
|
|
static void Free(const CatchEntryMoves* moves) {
|
|
free(const_cast<CatchEntryMoves*>(moves));
|
|
}
|
|
|
|
intptr_t count() const { return count_; }
|
|
CatchEntryMove& At(intptr_t i) { return Moves()[i]; }
|
|
const CatchEntryMove& At(intptr_t i) const { return Moves()[i]; }
|
|
|
|
private:
|
|
CatchEntryMove* Moves() {
|
|
return reinterpret_cast<CatchEntryMove*>(this + 1);
|
|
}
|
|
|
|
const CatchEntryMove* Moves() const {
|
|
return reinterpret_cast<const CatchEntryMove*>(this + 1);
|
|
}
|
|
|
|
intptr_t count_;
|
|
// Followed by CatchEntryMove[count_]
|
|
};
|
|
|
|
// Used for reading the [CatchEntryMoves] from the compressed form.
|
|
class CatchEntryMovesMapReader : public ValueObject {
|
|
public:
|
|
explicit CatchEntryMovesMapReader(const TypedData& bytes) : bytes_(bytes) {}
|
|
|
|
// The returned [CatchEntryMoves] must be freed by the caller via [free].
|
|
CatchEntryMoves* ReadMovesForPcOffset(intptr_t pc_offset);
|
|
|
|
private:
|
|
// Given the [pc_offset] this function will find the [position] at which to
|
|
// read the catch entries and the [length] of the catch entry moves array.
|
|
void FindEntryForPc(ReadStream* stream,
|
|
intptr_t pc_offset,
|
|
intptr_t* position,
|
|
intptr_t* length);
|
|
|
|
// Reads the [length] catch entry moves from [offset] in the [stream].
|
|
CatchEntryMoves* ReadCompressedCatchEntryMovesSuffix(ReadStream* stream,
|
|
intptr_t offset,
|
|
intptr_t length);
|
|
|
|
const TypedData& bytes_;
|
|
};
|
|
|
|
// A simple reference counting wrapper for CatchEntryMoves.
|
|
//
|
|
// TODO(vegorov) switch this to intrusive reference counting.
|
|
class CatchEntryMovesRefPtr {
|
|
public:
|
|
CatchEntryMovesRefPtr() : moves_(nullptr), ref_count_(nullptr) {}
|
|
explicit CatchEntryMovesRefPtr(const CatchEntryMoves* moves)
|
|
: moves_(moves), ref_count_(new intptr_t(1)) {}
|
|
|
|
CatchEntryMovesRefPtr(const CatchEntryMovesRefPtr& state) { Copy(state); }
|
|
|
|
~CatchEntryMovesRefPtr() { Destroy(); }
|
|
|
|
CatchEntryMovesRefPtr& operator=(const CatchEntryMovesRefPtr& state) {
|
|
Destroy();
|
|
Copy(state);
|
|
return *this;
|
|
}
|
|
|
|
bool IsEmpty() { return ref_count_ == nullptr; }
|
|
|
|
const CatchEntryMoves& moves() { return *moves_; }
|
|
|
|
private:
|
|
void Destroy() {
|
|
if (ref_count_ != nullptr) {
|
|
(*ref_count_)--;
|
|
if (*ref_count_ == 0) {
|
|
delete ref_count_;
|
|
CatchEntryMoves::Free(moves_);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Copy(const CatchEntryMovesRefPtr& state) {
|
|
moves_ = state.moves_;
|
|
ref_count_ = state.ref_count_;
|
|
if (ref_count_ != nullptr) {
|
|
(*ref_count_)++;
|
|
}
|
|
}
|
|
|
|
const CatchEntryMoves* moves_;
|
|
intptr_t* ref_count_;
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_EXCEPTIONS_H_
|