| // Copyright (c) 2012, 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_CODE_DESCRIPTORS_H_ | 
 | #define RUNTIME_VM_CODE_DESCRIPTORS_H_ | 
 |  | 
 | #include "vm/datastream.h" | 
 | #include "vm/globals.h" | 
 | #include "vm/growable_array.h" | 
 | #include "vm/log.h" | 
 | #include "vm/runtime_entry.h" | 
 | #include "vm/token_position.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | static const intptr_t kInvalidTryIndex = -1; | 
 |  | 
 | class DescriptorList : public ZoneAllocated { | 
 |  public: | 
 |   explicit DescriptorList( | 
 |       Zone* zone, | 
 |       const GrowableArray<const Function*>* inline_id_to_function = nullptr); | 
 |  | 
 |   ~DescriptorList() {} | 
 |  | 
 |   void AddDescriptor(UntaggedPcDescriptors::Kind kind, | 
 |                      intptr_t pc_offset, | 
 |                      intptr_t deopt_id, | 
 |                      TokenPosition token_pos, | 
 |                      intptr_t try_index, | 
 |                      intptr_t yield_index); | 
 |  | 
 |   PcDescriptorsPtr FinalizePcDescriptors(uword entry_point); | 
 |  | 
 |  private: | 
 |   static constexpr intptr_t kInitialStreamSize = 64; | 
 |  | 
 |   const Function& function_; | 
 |   const Script& script_; | 
 |   ZoneWriteStream encoded_data_; | 
 |  | 
 |   intptr_t prev_pc_offset; | 
 |   intptr_t prev_deopt_id; | 
 |   int32_t prev_token_pos; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(DescriptorList); | 
 | }; | 
 |  | 
 | class CompressedStackMapsBuilder : public ZoneAllocated { | 
 |  public: | 
 |   explicit CompressedStackMapsBuilder(Zone* zone) | 
 |       : encoded_bytes_(zone, kInitialStreamSize) {} | 
 |  | 
 |   void AddEntry(intptr_t pc_offset, | 
 |                 BitmapBuilder* bitmap, | 
 |                 intptr_t spill_slot_bit_count); | 
 |  | 
 |   CompressedStackMapsPtr Finalize() const; | 
 |  | 
 |  private: | 
 |   static constexpr intptr_t kInitialStreamSize = 16; | 
 |  | 
 |   ZoneWriteStream encoded_bytes_; | 
 |   intptr_t last_pc_offset_ = 0; | 
 |   DISALLOW_COPY_AND_ASSIGN(CompressedStackMapsBuilder); | 
 | }; | 
 |  | 
 | class ExceptionHandlerList : public ZoneAllocated { | 
 |  public: | 
 |   struct HandlerDesc { | 
 |     intptr_t outer_try_index;    // Try block in which this try block is nested. | 
 |     intptr_t pc_offset;          // Handler PC offset value. | 
 |     bool is_generated;           // False if this is directly from Dart code. | 
 |     const Array* handler_types;  // Catch clause guards. | 
 |     bool needs_stacktrace; | 
 |   }; | 
 |  | 
 |   explicit ExceptionHandlerList(const Function& function) | 
 |       : list_(), | 
 |         has_async_handler_(function.IsAsyncFunction() || | 
 |                            function.IsAsyncGenerator()) {} | 
 |  | 
 |   intptr_t Length() const { return list_.length(); } | 
 |  | 
 |   void AddPlaceHolder() { | 
 |     struct HandlerDesc data; | 
 |     data.outer_try_index = -1; | 
 |     data.pc_offset = ExceptionHandlers::kInvalidPcOffset; | 
 |     data.is_generated = true; | 
 |     data.handler_types = NULL; | 
 |     data.needs_stacktrace = false; | 
 |     list_.Add(data); | 
 |   } | 
 |  | 
 |   void AddHandler(intptr_t try_index, | 
 |                   intptr_t outer_try_index, | 
 |                   intptr_t pc_offset, | 
 |                   bool is_generated, | 
 |                   const Array& handler_types, | 
 |                   bool needs_stacktrace) { | 
 |     ASSERT(try_index >= 0); | 
 |     while (Length() <= try_index) { | 
 |       AddPlaceHolder(); | 
 |     } | 
 |     list_[try_index].outer_try_index = outer_try_index; | 
 |     ASSERT(list_[try_index].pc_offset == ExceptionHandlers::kInvalidPcOffset); | 
 |     list_[try_index].pc_offset = pc_offset; | 
 |     list_[try_index].is_generated = is_generated; | 
 |     DEBUG_ASSERT(handler_types.IsNotTemporaryScopedHandle()); | 
 |     list_[try_index].handler_types = &handler_types; | 
 |     list_[try_index].needs_stacktrace |= needs_stacktrace; | 
 |   } | 
 |  | 
 |   // Called by rethrows, to mark their enclosing handlers. | 
 |   void SetNeedsStackTrace(intptr_t try_index) { | 
 |     // Rethrows can be generated outside a try by the compiler. | 
 |     if (try_index == kInvalidTryIndex) { | 
 |       return; | 
 |     } | 
 |     ASSERT(try_index >= 0); | 
 |     while (Length() <= try_index) { | 
 |       AddPlaceHolder(); | 
 |     } | 
 |     list_[try_index].needs_stacktrace = true; | 
 |   } | 
 |  | 
 |   static bool ContainsCatchAllType(const Array& array) { | 
 |     auto& type = AbstractType::Handle(); | 
 |     for (intptr_t i = 0; i < array.Length(); i++) { | 
 |       type ^= array.At(i); | 
 |       if (type.IsCatchAllType()) { | 
 |         return true; | 
 |       } | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   ExceptionHandlersPtr FinalizeExceptionHandlers(uword entry_point) const; | 
 |  | 
 |  private: | 
 |   GrowableArray<struct HandlerDesc> list_; | 
 |   const bool has_async_handler_; | 
 |   DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerList); | 
 | }; | 
 |  | 
 | #if !defined(DART_PRECOMPILED_RUNTIME) | 
 | // Used to construct CatchEntryMoves for the AOT mode of compilation. | 
 | class CatchEntryMovesMapBuilder : public ZoneAllocated { | 
 |  public: | 
 |   CatchEntryMovesMapBuilder(); | 
 |  | 
 |   void NewMapping(intptr_t pc_offset); | 
 |   void Append(const CatchEntryMove& move); | 
 |   void EndMapping(); | 
 |   TypedDataPtr FinalizeCatchEntryMovesMap(); | 
 |  | 
 |  private: | 
 |   class TrieNode; | 
 |  | 
 |   Zone* zone_; | 
 |   TrieNode* root_; | 
 |   intptr_t current_pc_offset_; | 
 |   GrowableArray<CatchEntryMove> moves_; | 
 |   ZoneWriteStream stream_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CatchEntryMovesMapBuilder); | 
 | }; | 
 | #endif  // !defined(DART_PRECOMPILED_RUNTIME) | 
 |  | 
 | // Instructions have two pieces of information needed to get accurate source | 
 | // locations: the token position and the inlining id. The inlining id tells us | 
 | // which function, and thus which script, to use for this instruction and the | 
 | // token position, when real, tells us the position in the source for the | 
 | // script for the instruction. | 
 | // | 
 | // Thus, we bundle the two pieces of information in InstructionSource structs | 
 | // when copying or retrieving to lower the likelihood that the token position | 
 | // is used without the appropriate inlining id. | 
 | struct InstructionSource { | 
 |   // Treat an instruction source without inlining id information as unset. | 
 |   InstructionSource() : InstructionSource(TokenPosition::kNoSource) {} | 
 |   explicit InstructionSource(TokenPosition pos) : InstructionSource(pos, -1) {} | 
 |   InstructionSource(TokenPosition pos, intptr_t id) | 
 |       : token_pos(pos), inlining_id(id) {} | 
 |  | 
 |   const TokenPosition token_pos; | 
 |   const intptr_t inlining_id; | 
 |  | 
 |   DISALLOW_ALLOCATION(); | 
 | }; | 
 |  | 
 | struct CodeSourceMapOps : AllStatic { | 
 |   static const uint8_t kChangePosition = 0; | 
 |   static const uint8_t kAdvancePC = 1; | 
 |   static const uint8_t kPushFunction = 2; | 
 |   static const uint8_t kPopFunction = 3; | 
 |   static const uint8_t kNullCheck = 4; | 
 |  | 
 |   static uint8_t Read(ReadStream* stream, | 
 |                       int32_t* arg1, | 
 |                       int32_t* arg2 = nullptr); | 
 |  | 
 |   static void Write(BaseWriteStream* stream, | 
 |                     uint8_t op, | 
 |                     int32_t arg1 = 0, | 
 |                     int32_t arg2 = 0); | 
 |  | 
 |  private: | 
 |   static constexpr intptr_t kOpBits = 3; | 
 |  | 
 |   using OpField = BitField<int32_t, uint8_t, 0, kOpBits>; | 
 |   using ArgField = BitField<int32_t, int32_t, OpField::kNextBit>; | 
 |  | 
 |   static constexpr int32_t kMaxArgValue = | 
 |       Utils::NBitMask<int32_t>(ArgField::bitsize() - 1); | 
 |   static constexpr int32_t kMinArgValue = ~kMaxArgValue; | 
 |   static constexpr int32_t kSignBits = static_cast<uint32_t>(kMinArgValue) << 1; | 
 | }; | 
 |  | 
 | // A CodeSourceMap maps from pc offsets to a stack of inlined functions and | 
 | // their positions. This is encoded as a little bytecode that pushes and pops | 
 | // functions and changes the top function's position as the PC advances. | 
 | // Decoding happens by running this bytecode until we reach the desired PC. | 
 | // | 
 | // The implementation keeps track of two sets of state: one written to the byte | 
 | // stream and one that is buffered. On the JIT, this buffering effectively gives | 
 | // us a peephole optimization that merges adjacent advance PC bytecodes. On AOT, | 
 | // this allows to skip encoding our position until we reach a PC where we might | 
 | // throw. | 
 | class CodeSourceMapBuilder : public ZoneAllocated { | 
 |  public: | 
 |   CodeSourceMapBuilder( | 
 |       Zone* zone, | 
 |       bool stack_traces_only, | 
 |       const GrowableArray<intptr_t>& caller_inline_id, | 
 |       const GrowableArray<TokenPosition>& inline_id_to_token_pos, | 
 |       const GrowableArray<const Function*>& inline_id_to_function); | 
 |  | 
 |   // The position at which a function implicitly starts, for both the root and | 
 |   // after a push bytecode. We use the classifying position kDartCodePrologue | 
 |   // since it is the most common. | 
 |   static const TokenPosition& kInitialPosition; | 
 |  | 
 |   void BeginCodeSourceRange(int32_t pc_offset, const InstructionSource& source); | 
 |   void EndCodeSourceRange(int32_t pc_offset, const InstructionSource& source); | 
 |   void NoteDescriptor(UntaggedPcDescriptors::Kind kind, | 
 |                       int32_t pc_offset, | 
 |                       const InstructionSource& source); | 
 |   void NoteNullCheck(int32_t pc_offset, | 
 |                      const InstructionSource& source, | 
 |                      intptr_t name_index); | 
 |   void WriteFunctionEntrySourcePosition(const InstructionSource& source); | 
 |  | 
 |   // If source is from an inlined call, returns the token position of the | 
 |   // original call in the root function, otherwise the source's token position. | 
 |   TokenPosition RootPosition(const InstructionSource& source); | 
 |   ArrayPtr InliningIdToFunction(); | 
 |   CodeSourceMapPtr Finalize(); | 
 |  | 
 |   const GrowableArray<const Function*>& inline_id_to_function() const { | 
 |     return inline_id_to_function_; | 
 |   } | 
 |  | 
 |  private: | 
 |   intptr_t GetFunctionId(intptr_t inline_id); | 
 |   void StartInliningInterval(int32_t pc_offset, | 
 |                              const InstructionSource& source); | 
 |  | 
 |   void BufferChangePosition(TokenPosition pos); | 
 |   void WriteChangePosition(TokenPosition pos); | 
 |   void BufferAdvancePC(int32_t distance) { buffered_pc_offset_ += distance; } | 
 |   void WriteAdvancePC(int32_t distance) { | 
 |     CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kAdvancePC, distance); | 
 |     written_pc_offset_ += distance; | 
 |   } | 
 |   void BufferPush(intptr_t inline_id) { | 
 |     buffered_inline_id_stack_.Add(inline_id); | 
 |     buffered_token_pos_stack_.Add(kInitialPosition); | 
 |   } | 
 |   void WritePush(intptr_t inline_id) { | 
 |     CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kPushFunction, | 
 |                             GetFunctionId(inline_id)); | 
 |     written_inline_id_stack_.Add(inline_id); | 
 |     written_token_pos_stack_.Add(kInitialPosition); | 
 |   } | 
 |   void BufferPop() { | 
 |     buffered_inline_id_stack_.RemoveLast(); | 
 |     buffered_token_pos_stack_.RemoveLast(); | 
 |   } | 
 |   void WritePop() { | 
 |     CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kPopFunction); | 
 |     written_inline_id_stack_.RemoveLast(); | 
 |     written_token_pos_stack_.RemoveLast(); | 
 |   } | 
 |   void WriteNullCheck(int32_t name_index) { | 
 |     CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kNullCheck, name_index); | 
 |   } | 
 |  | 
 |   void FlushBuffer(); | 
 |  | 
 |   bool IsOnBufferedStack(intptr_t inline_id) { | 
 |     for (intptr_t i = 0; i < buffered_inline_id_stack_.length(); i++) { | 
 |       if (buffered_inline_id_stack_[i] == inline_id) return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   Zone* const zone_; | 
 |   intptr_t buffered_pc_offset_; | 
 |   GrowableArray<intptr_t> buffered_inline_id_stack_; | 
 |   GrowableArray<TokenPosition> buffered_token_pos_stack_; | 
 |  | 
 |   intptr_t written_pc_offset_; | 
 |   GrowableArray<intptr_t> written_inline_id_stack_; | 
 |   GrowableArray<TokenPosition> written_token_pos_stack_; | 
 |  | 
 |   const GrowableArray<intptr_t>& caller_inline_id_; | 
 |   const GrowableArray<TokenPosition>& inline_id_to_token_pos_; | 
 |   const GrowableArray<const Function*>& inline_id_to_function_; | 
 |  | 
 |   const GrowableObjectArray& inlined_functions_; | 
 |  | 
 |   Script& script_; | 
 |   ZoneWriteStream stream_; | 
 |  | 
 |   const bool stack_traces_only_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CodeSourceMapBuilder); | 
 | }; | 
 |  | 
 | class CodeSourceMapReader : public ValueObject { | 
 |  public: | 
 |   CodeSourceMapReader(const CodeSourceMap& map, | 
 |                       const Array& functions, | 
 |                       const Function& root) | 
 |       : map_(map), functions_(functions), root_(root) {} | 
 |  | 
 |   void GetInlinedFunctionsAt(int32_t pc_offset, | 
 |                              GrowableArray<const Function*>* function_stack, | 
 |                              GrowableArray<TokenPosition>* token_positions); | 
 |   NOT_IN_PRODUCT(void PrintJSONInlineIntervals(JSONObject* jsobj)); | 
 |   void DumpInlineIntervals(uword start); | 
 |   void DumpSourcePositions(uword start); | 
 |  | 
 |   intptr_t GetNullCheckNameIndexAt(int32_t pc_offset); | 
 |  | 
 |  private: | 
 |   static const TokenPosition& InitialPosition() { | 
 |     if (FLAG_precompiled_mode) { | 
 |       // In precompiled mode, the CodeSourceMap stores lines instead of | 
 |       // real token positions and uses kNoSourcePos for no line information. | 
 |       return TokenPosition::kNoSource; | 
 |     } else { | 
 |       return CodeSourceMapBuilder::kInitialPosition; | 
 |     } | 
 |   } | 
 |  | 
 |   // Reads a TokenPosition value from a CSM, handling the different encoding for | 
 |   // when non-symbolic stack traces are enabled. | 
 |   static TokenPosition ReadPosition(ReadStream* stream); | 
 |  | 
 |   const CodeSourceMap& map_; | 
 |   const Array& functions_; | 
 |   const Function& root_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CodeSourceMapReader); | 
 | }; | 
 |  | 
 | }  // namespace dart | 
 |  | 
 | #endif  // RUNTIME_VM_CODE_DESCRIPTORS_H_ |