1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/code_descriptors.h"
6
7#include "platform/utils.h"
8#include "vm/compiler/api/deopt_id.h"
9#include "vm/flags.h"
10#include "vm/log.h"
11#include "vm/object.h"
12#include "vm/object_store.h"
13#include "vm/zone_text_buffer.h"
14
15namespace dart {
16
17DescriptorList::DescriptorList(
18 Zone* zone,
19 const GrowableArray<const Function*>* inline_id_to_function)
20 : function_(Function::Handle(
21 zone,
22 FLAG_check_token_positions && (inline_id_to_function != nullptr)
23 ? inline_id_to_function->At(0)->ptr()
24 : Function::null())),
25 script_(Script::Handle(
26 zone,
27 ptr: function_.IsNull() ? Script::null() : function_.script())),
28 encoded_data_(zone, kInitialStreamSize),
29 prev_pc_offset(0),
30 prev_deopt_id(0),
31 prev_token_pos(0) {}
32
33void DescriptorList::AddDescriptor(UntaggedPcDescriptors::Kind kind,
34 intptr_t pc_offset,
35 intptr_t deopt_id,
36 const TokenPosition token_pos,
37 intptr_t try_index,
38 intptr_t yield_index) {
39 // yield index 0 is reserved for normal entry.
40 RELEASE_ASSERT(yield_index != 0);
41
42 ASSERT((kind == UntaggedPcDescriptors::kRuntimeCall) ||
43 (kind == UntaggedPcDescriptors::kBSSRelocation) ||
44 (kind == UntaggedPcDescriptors::kOther) ||
45 (yield_index != UntaggedPcDescriptors::kInvalidYieldIndex) ||
46 (deopt_id != DeoptId::kNone));
47
48 // When precompiling, we only use pc descriptors for exceptions,
49 // relocations and yield indices.
50 if (!FLAG_precompiled_mode || try_index != -1 ||
51 yield_index != UntaggedPcDescriptors::kInvalidYieldIndex ||
52 kind == UntaggedPcDescriptors::kBSSRelocation) {
53 const int32_t kind_and_metadata =
54 UntaggedPcDescriptors::KindAndMetadata::Encode(kind, try_index,
55 yield_index);
56
57 encoded_data_.WriteSLEB128(kind_and_metadata);
58 encoded_data_.WriteSLEB128(pc_offset - prev_pc_offset);
59 prev_pc_offset = pc_offset;
60
61 if (!FLAG_precompiled_mode) {
62 if (FLAG_check_token_positions && token_pos.IsReal()) {
63 if (!function_.IsNull() &&
64 !token_pos.IsWithin(a: function_.token_pos(),
65 b: function_.end_token_pos())) {
66 FATAL("Token position %s for PC descriptor %s at offset 0x%" Px
67 " invalid for function %s (%s, %s)",
68 token_pos.ToCString(),
69 UntaggedPcDescriptors::KindToCString(kind), pc_offset,
70 function_.ToFullyQualifiedCString(),
71 function_.token_pos().ToCString(),
72 function_.end_token_pos().ToCString());
73 }
74 if (!script_.IsNull() && !script_.IsValidTokenPosition(token_pos)) {
75 FATAL("Token position %s for PC descriptor %s at offset 0x%" Px
76 " invalid for script %s of function %s",
77 token_pos.ToCString(),
78 UntaggedPcDescriptors::KindToCString(kind), pc_offset,
79 script_.ToCString(), function_.ToFullyQualifiedCString());
80 }
81 }
82 const int32_t encoded_pos = token_pos.Serialize();
83 encoded_data_.WriteSLEB128(deopt_id - prev_deopt_id);
84 encoded_data_.WriteSLEB128(
85 Utils::SubWithWrapAround(a: encoded_pos, b: prev_token_pos));
86 prev_deopt_id = deopt_id;
87 prev_token_pos = encoded_pos;
88 }
89 }
90}
91
92PcDescriptorsPtr DescriptorList::FinalizePcDescriptors(uword entry_point) {
93 if (encoded_data_.bytes_written() == 0) {
94 return Object::empty_descriptors().ptr();
95 }
96 return PcDescriptors::New(delta_encoded_data: encoded_data_.buffer(),
97 size: encoded_data_.bytes_written());
98}
99
100void CompressedStackMapsBuilder::AddEntry(intptr_t pc_offset,
101 BitmapBuilder* bitmap,
102 intptr_t spill_slot_bit_count) {
103 ASSERT(bitmap != nullptr);
104 ASSERT(pc_offset > last_pc_offset_);
105 ASSERT(spill_slot_bit_count >= 0 && spill_slot_bit_count <= bitmap->Length());
106 const uword pc_delta = pc_offset - last_pc_offset_;
107 const uword non_spill_slot_bit_count =
108 bitmap->Length() - spill_slot_bit_count;
109 encoded_bytes_.WriteLEB128(pc_delta);
110 encoded_bytes_.WriteLEB128(spill_slot_bit_count);
111 encoded_bytes_.WriteLEB128(non_spill_slot_bit_count);
112 bitmap->AppendAsBytesTo(stream: &encoded_bytes_);
113 last_pc_offset_ = pc_offset;
114}
115
116CompressedStackMapsPtr CompressedStackMapsBuilder::Finalize() const {
117 if (encoded_bytes_.bytes_written() == 0) {
118 return Object::empty_compressed_stackmaps().ptr();
119 }
120 return CompressedStackMaps::NewInlined(payload: encoded_bytes_.buffer(),
121 size: encoded_bytes_.bytes_written());
122}
123
124ExceptionHandlersPtr ExceptionHandlerList::FinalizeExceptionHandlers(
125 uword entry_point) const {
126 intptr_t num_handlers = Length();
127 if (num_handlers == 0) {
128 return has_async_handler_ ? Object::empty_async_exception_handlers().ptr()
129 : Object::empty_exception_handlers().ptr();
130 }
131 const ExceptionHandlers& handlers =
132 ExceptionHandlers::Handle(ptr: ExceptionHandlers::New(num_handlers));
133 handlers.set_has_async_handler(has_async_handler_);
134 for (intptr_t i = 0; i < num_handlers; i++) {
135 // Assert that every element in the array has been initialized.
136 if (list_[i].handler_types == nullptr) {
137 // Unreachable handler, entry not computed.
138 // Initialize it to some meaningful value.
139 const bool has_catch_all = false;
140 // Check it is uninitialized.
141 ASSERT((list_[i].outer_try_index == -1) &&
142 (list_[i].pc_offset == ExceptionHandlers::kInvalidPcOffset));
143 handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
144 list_[i].needs_stacktrace, has_catch_all,
145 list_[i].is_generated);
146 handlers.SetHandledTypes(try_index: i, handled_types: Array::empty_array());
147 } else {
148 const bool has_catch_all = ContainsCatchAllType(*list_[i].handler_types);
149 handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
150 list_[i].needs_stacktrace, has_catch_all,
151 list_[i].is_generated);
152 handlers.SetHandledTypes(i, *list_[i].handler_types);
153 }
154 }
155 return handlers.ptr();
156}
157
158#if !defined(DART_PRECOMPILED_RUNTIME)
159class CatchEntryMovesMapBuilder::TrieNode : public ZoneAllocated {
160 public:
161 TrieNode() : move_(), entry_state_offset_(-1) {}
162 TrieNode(CatchEntryMove move, intptr_t index)
163 : move_(move), entry_state_offset_(index) {}
164
165 intptr_t Offset() { return entry_state_offset_; }
166
167 TrieNode* Insert(TrieNode* node) {
168 children_.Add(node);
169 return node;
170 }
171
172 TrieNode* Follow(CatchEntryMove next) {
173 for (intptr_t i = 0; i < children_.length(); i++) {
174 if (children_[i]->move_ == next) return children_[i];
175 }
176 return nullptr;
177 }
178
179 private:
180 CatchEntryMove move_;
181 const intptr_t entry_state_offset_;
182 GrowableArray<TrieNode*> children_;
183};
184
185CatchEntryMovesMapBuilder::CatchEntryMovesMapBuilder()
186 : zone_(Thread::Current()->zone()),
187 root_(new TrieNode()),
188 current_pc_offset_(0),
189 stream_(zone_, 64) {}
190
191void CatchEntryMovesMapBuilder::Append(const CatchEntryMove& move) {
192 moves_.Add(move);
193}
194
195void CatchEntryMovesMapBuilder::NewMapping(intptr_t pc_offset) {
196 moves_.Clear();
197 current_pc_offset_ = pc_offset;
198}
199
200void CatchEntryMovesMapBuilder::EndMapping() {
201 intptr_t suffix_length = 0;
202 TrieNode* suffix = root_;
203 // Find the largest common suffix, get the last node of the path.
204 for (intptr_t i = moves_.length() - 1; i >= 0; i--) {
205 TrieNode* n = suffix->Follow(moves_[i]);
206 if (n == nullptr) break;
207 suffix_length++;
208 suffix = n;
209 }
210 intptr_t length = moves_.length() - suffix_length;
211 intptr_t current_offset = stream_.bytes_written();
212
213 typedef ZoneWriteStream::Raw<sizeof(intptr_t), intptr_t> Writer;
214 Writer::Write(st: &stream_, value: current_pc_offset_);
215 Writer::Write(st: &stream_, value: length);
216 Writer::Write(st: &stream_, value: suffix_length);
217 Writer::Write(st: &stream_, value: suffix->Offset());
218
219 // Write the unshared part, adding it to the trie.
220 TrieNode* node = suffix;
221 for (intptr_t i = length - 1; i >= 0; i--) {
222 moves_[i].WriteTo(&stream_);
223
224 TrieNode* child = new (zone_) TrieNode(moves_[i], current_offset);
225 node->Insert(node: child);
226 node = child;
227 }
228}
229
230TypedDataPtr CatchEntryMovesMapBuilder::FinalizeCatchEntryMovesMap() {
231 TypedData& td = TypedData::Handle(ptr: TypedData::New(
232 class_id: kTypedDataInt8ArrayCid, len: stream_.bytes_written(), space: Heap::kOld));
233 NoSafepointScope no_safepoint;
234 uint8_t* dest = reinterpret_cast<uint8_t*>(td.DataAddr(byte_offset: 0));
235 uint8_t* src = stream_.buffer();
236 for (intptr_t i = 0; i < stream_.bytes_written(); i++) {
237 dest[i] = src[i];
238 }
239 return td.ptr();
240}
241#endif // !defined(DART_PRECOMPILED_RUNTIME)
242
243uint8_t CodeSourceMapOps::Read(ReadStream* stream,
244 int32_t* arg1,
245 int32_t* arg2) {
246 ASSERT(stream != nullptr && arg1 != nullptr);
247 const int32_t n = stream->Read<int32_t>();
248 const uint8_t op = OpField::decode(n);
249 *arg1 = ArgField::decode(n);
250 if (*arg1 > kMaxArgValue) {
251 *arg1 |= kSignBits;
252 }
253#if defined(DART_PRECOMPILER)
254 // The special handling for non-symbolic stack trace mode only needs to
255 // happen in the precompiler, because those CSMs are not serialized in
256 // precompiled snapshots.
257 if (op == kChangePosition && FLAG_dwarf_stack_traces_mode) {
258 const int32_t m = stream->Read<int32_t>();
259 if (arg2 != nullptr) {
260 *arg2 = m;
261 }
262 }
263#endif
264 return op;
265}
266
267void CodeSourceMapOps::Write(BaseWriteStream* stream,
268 uint8_t op,
269 int32_t arg1,
270 int32_t arg2) {
271 ASSERT(stream != nullptr);
272 ASSERT(arg1 >= kMinArgValue && arg1 <= kMaxArgValue);
273 if (arg1 < 0) {
274 arg1 &= ~kSignBits;
275 }
276 const int32_t n = OpField::encode(value: op) | ArgField::encode(value: arg1);
277 stream->Write(value: n);
278#if defined(DART_PRECOMPILER)
279 if (op == kChangePosition && FLAG_dwarf_stack_traces_mode) {
280 // For non-symbolic stack traces, the CodeSourceMaps are not serialized,
281 // so we need not worry about increasing snapshot size by including more
282 // information here.
283 stream->Write(arg2);
284 }
285#endif
286}
287
288const TokenPosition& CodeSourceMapBuilder::kInitialPosition =
289 TokenPosition::kDartCodePrologue;
290
291CodeSourceMapBuilder::CodeSourceMapBuilder(
292 Zone* zone,
293 bool stack_traces_only,
294 const GrowableArray<intptr_t>& caller_inline_id,
295 const GrowableArray<TokenPosition>& inline_id_to_token_pos,
296 const GrowableArray<const Function*>& inline_id_to_function)
297 : zone_(zone),
298 buffered_pc_offset_(0),
299 buffered_inline_id_stack_(),
300 buffered_token_pos_stack_(),
301 written_pc_offset_(0),
302 written_inline_id_stack_(),
303 written_token_pos_stack_(),
304 caller_inline_id_(caller_inline_id),
305 inline_id_to_token_pos_(inline_id_to_token_pos),
306 inline_id_to_function_(inline_id_to_function),
307 inlined_functions_(
308 GrowableObjectArray::Handle(ptr: GrowableObjectArray::New(space: Heap::kOld))),
309 script_(Script::Handle(zone, ptr: Script::null())),
310 stream_(zone, 64),
311 stack_traces_only_(stack_traces_only) {
312 buffered_inline_id_stack_.Add(0);
313 buffered_token_pos_stack_.Add(kInitialPosition);
314 written_inline_id_stack_.Add(0);
315 written_token_pos_stack_.Add(kInitialPosition);
316}
317
318void CodeSourceMapBuilder::FlushBuffer() {
319 // 1. Flush the inlining stack.
320 //
321 // The top-most position where the buffered and written stack match.
322 intptr_t common_index;
323 for (common_index = buffered_inline_id_stack_.length() - 1; common_index >= 0;
324 common_index--) {
325 intptr_t buffered_id = buffered_inline_id_stack_[common_index];
326 if (common_index < written_inline_id_stack_.length()) {
327 intptr_t written_id = written_inline_id_stack_[common_index];
328 if (buffered_id == written_id) {
329 break;
330 }
331 }
332 }
333 if (common_index < 0) {
334 // The base, which is the root function, should _always_ match.
335 UNREACHABLE();
336 }
337 while (written_inline_id_stack_.length() > common_index + 1) {
338 WritePop();
339 }
340 for (intptr_t j = common_index + 1; j < buffered_inline_id_stack_.length();
341 j++) {
342 const auto& buffered_pos = buffered_token_pos_stack_[j - 1];
343 const auto& written_pos = written_token_pos_stack_[j - 1];
344 if (buffered_pos != written_pos) {
345 WriteChangePosition(pos: buffered_pos);
346 }
347 WritePush(buffered_inline_id_stack_[j]);
348 }
349
350 ASSERT_EQUAL(buffered_token_pos_stack_.length(),
351 written_token_pos_stack_.length());
352
353 // 2. Flush the current token position.
354 intptr_t top = buffered_token_pos_stack_.length() - 1;
355 const auto& buffered_pos = buffered_token_pos_stack_[top];
356 const auto& written_pos = written_token_pos_stack_[top];
357 if (buffered_pos != written_pos) {
358 WriteChangePosition(pos: buffered_pos);
359 }
360
361 // 3. Flush the current PC offset.
362 if (buffered_pc_offset_ != written_pc_offset_) {
363 WriteAdvancePC(distance: buffered_pc_offset_ - written_pc_offset_);
364 }
365}
366
367void CodeSourceMapBuilder::StartInliningInterval(
368 int32_t pc_offset,
369 const InstructionSource& source) {
370 if (!source.token_pos.IsReal() && !source.token_pos.IsSynthetic()) {
371 // Only record inlining intervals for token positions that might need
372 // to be checked against the appropriate function and/or script.
373 return;
374 }
375
376 if (buffered_inline_id_stack_.Last() == source.inlining_id) {
377 // No change in function stack.
378 return;
379 }
380
381 if (source.inlining_id < 0) {
382 // Inlining ID is unset for this source, so assume the current inlining ID.
383 return;
384 }
385
386 if (!stack_traces_only_) {
387 FlushBuffer();
388 }
389
390 // Find a minimal set of pops and pushes to bring us to the new function
391 // stack.
392
393 // Pop to a common ancestor.
394 intptr_t common_parent = source.inlining_id;
395 while (!IsOnBufferedStack(inline_id: common_parent)) {
396 common_parent = caller_inline_id_[common_parent];
397 }
398 while (buffered_inline_id_stack_.Last() != common_parent) {
399 BufferPop();
400 }
401
402 // Push to the new top-of-stack function.
403 GrowableArray<intptr_t> to_push;
404 for (intptr_t id = source.inlining_id; id != common_parent;
405 id = caller_inline_id_[id]) {
406 to_push.Add(id);
407 }
408 for (intptr_t i = to_push.length() - 1; i >= 0; i--) {
409 intptr_t callee_id = to_push[i];
410 // We should never push the root function or its "caller".
411 ASSERT(callee_id > 0);
412 BufferChangePosition(inline_id_to_token_pos_[callee_id - 1]);
413 BufferPush(inline_id: callee_id);
414 }
415 if (FLAG_check_token_positions) {
416 // Only update the cached script_ on inlining interval changes, since it's
417 // a non-trivial computation.
418 script_ = inline_id_to_function_[source.inlining_id]->script();
419 }
420}
421
422void CodeSourceMapBuilder::WriteFunctionEntrySourcePosition(
423 const InstructionSource& source) {
424 ASSERT(written_pc_offset_ == 0 && buffered_pc_offset_ == 0);
425 ASSERT(stream_.bytes_written() == 0);
426 WriteChangePosition(pos: source.token_pos);
427 WriteAdvancePC(distance: 0);
428}
429
430void CodeSourceMapBuilder::BeginCodeSourceRange(
431 int32_t pc_offset,
432 const InstructionSource& source) {
433 StartInliningInterval(pc_offset, source);
434}
435
436void CodeSourceMapBuilder::EndCodeSourceRange(int32_t pc_offset,
437 const InstructionSource& source) {
438 if (pc_offset == buffered_pc_offset_) {
439 return; // Empty intermediate instruction.
440 }
441 StartInliningInterval(pc_offset, source);
442 if (source.token_pos != buffered_token_pos_stack_.Last()) {
443 if (!stack_traces_only_) {
444 FlushBuffer();
445 }
446 BufferChangePosition(pos: source.token_pos);
447 }
448 BufferAdvancePC(distance: pc_offset - buffered_pc_offset_);
449}
450
451void CodeSourceMapBuilder::NoteDescriptor(UntaggedPcDescriptors::Kind kind,
452 int32_t pc_offset,
453 const InstructionSource& source) {
454 const uint8_t kCanThrow =
455 UntaggedPcDescriptors::kIcCall | UntaggedPcDescriptors::kUnoptStaticCall |
456 UntaggedPcDescriptors::kRuntimeCall | UntaggedPcDescriptors::kOther;
457 if ((kind & kCanThrow) != 0) {
458 StartInliningInterval(pc_offset, source);
459 BufferChangePosition(pos: source.token_pos);
460 BufferAdvancePC(distance: pc_offset - buffered_pc_offset_);
461 FlushBuffer();
462 }
463}
464
465void CodeSourceMapBuilder::NoteNullCheck(int32_t pc_offset,
466 const InstructionSource& source,
467 intptr_t name_index) {
468 StartInliningInterval(pc_offset, source);
469 BufferChangePosition(pos: source.token_pos);
470 BufferAdvancePC(distance: pc_offset - buffered_pc_offset_);
471 FlushBuffer();
472 WriteNullCheck(name_index);
473}
474
475intptr_t CodeSourceMapBuilder::GetFunctionId(intptr_t inline_id) {
476 const Function& function = *inline_id_to_function_[inline_id];
477 for (intptr_t i = 0; i < inlined_functions_.Length(); i++) {
478 if (inlined_functions_.At(index: i) == function.ptr()) {
479 return i;
480 }
481 }
482 RELEASE_ASSERT(!function.IsNull());
483 inlined_functions_.Add(value: function, space: Heap::kOld);
484 return inlined_functions_.Length() - 1;
485}
486
487TokenPosition CodeSourceMapBuilder::RootPosition(
488 const InstructionSource& source) {
489 if (source.inlining_id <= 0) return source.token_pos;
490
491 intptr_t id = source.inlining_id;
492 while (caller_inline_id_[id] != 0) {
493 id = caller_inline_id_[id];
494 }
495 return inline_id_to_token_pos_[id - 1];
496}
497
498ArrayPtr CodeSourceMapBuilder::InliningIdToFunction() {
499 if (inlined_functions_.Length() == 0) {
500 return Object::empty_array().ptr();
501 }
502 return Array::MakeFixedLength(growable_array: inlined_functions_);
503}
504
505CodeSourceMapPtr CodeSourceMapBuilder::Finalize() {
506 if (!stack_traces_only_) {
507 FlushBuffer();
508 }
509 intptr_t length = stream_.bytes_written();
510 const auto& map = CodeSourceMap::Handle(zone: zone_, ptr: CodeSourceMap::New(length));
511 NoSafepointScope no_safepoint;
512 if (length > 0) {
513 ASSERT(stream_.buffer() != nullptr);
514 memmove(map.Data(), stream_.buffer(), length);
515 }
516 return map.ptr();
517}
518
519void CodeSourceMapBuilder::BufferChangePosition(TokenPosition pos) {
520 if (FLAG_check_token_positions && pos.IsReal()) {
521 const intptr_t inline_id = buffered_inline_id_stack_.Last();
522 const auto& function = *inline_id_to_function_[inline_id];
523 if (function.end_token_pos().IsReal() &&
524 !pos.IsWithin(a: function.token_pos(), b: function.end_token_pos())) {
525 TextBuffer buffer(256);
526 buffer.Printf(format: "Token position %s is invalid for function %s (%s, %s)",
527 pos.ToCString(), function.ToFullyQualifiedCString(),
528 function.token_pos().ToCString(),
529 function.end_token_pos().ToCString());
530 if (inline_id > 0) {
531 buffer.Printf(" while compiling function %s",
532 inline_id_to_function_[0]->ToFullyQualifiedCString());
533 }
534 FATAL("%s", buffer.buffer());
535 }
536 script_ = function.script();
537 if (!script_.IsNull() && !script_.IsValidTokenPosition(token_pos: pos)) {
538 TextBuffer buffer(256);
539 buffer.Printf(format: "Token position %s is invalid for script %s of function %s",
540 pos.ToCString(), script_.ToCString(),
541 function.ToFullyQualifiedCString());
542 if (inline_id != 0) {
543 buffer.Printf(" inlined into function %s",
544 inline_id_to_function_[0]->ToFullyQualifiedCString());
545 }
546 FATAL("%s", buffer.buffer());
547 }
548 }
549 buffered_token_pos_stack_.Last() = pos;
550}
551
552void CodeSourceMapBuilder::WriteChangePosition(const TokenPosition pos) {
553 const TokenPosition& last_written = written_token_pos_stack_.Last();
554 intptr_t position_or_line =
555 Utils::SubWithWrapAround(a: pos.Serialize(), b: last_written.Serialize());
556 intptr_t column = TokenPosition::kNoSource.Serialize();
557#if defined(DART_PRECOMPILER)
558 if (FLAG_precompiled_mode) {
559 // Don't use the raw position value directly in precompiled mode. Instead,
560 // use the value of kNoSource as a fallback when no line or column
561 // information is found.
562 position_or_line = TokenPosition::kNoSource.Serialize();
563 const intptr_t inline_id = written_inline_id_stack_.Last();
564 ASSERT(inline_id < inline_id_to_function_.length());
565 script_ = inline_id_to_function_[inline_id]->script();
566 script_.GetTokenLocation(pos, &position_or_line, &column);
567 intptr_t old_line = TokenPosition::kNoSource.Serialize();
568 script_.GetTokenLocation(last_written, &old_line);
569 position_or_line =
570 Utils::SubWithWrapAround<int32_t>(position_or_line, old_line);
571 }
572#endif
573 CodeSourceMapOps::Write(stream: &stream_, op: CodeSourceMapOps::kChangePosition,
574 arg1: position_or_line, arg2: column);
575 written_token_pos_stack_.Last() = pos;
576}
577
578void CodeSourceMapReader::GetInlinedFunctionsAt(
579 int32_t pc_offset,
580 GrowableArray<const Function*>* function_stack,
581 GrowableArray<TokenPosition>* token_positions) {
582 function_stack->Clear();
583 token_positions->Clear();
584
585 NoSafepointScope no_safepoint;
586 ReadStream stream(map_.Data(), map_.Length());
587
588 int32_t current_pc_offset = 0;
589 function_stack->Add(&root_);
590 token_positions->Add(InitialPosition());
591
592 while (stream.PendingBytes() > 0) {
593 int32_t arg;
594 const uint8_t opcode = CodeSourceMapOps::Read(stream: &stream, arg1: &arg);
595 switch (opcode) {
596 case CodeSourceMapOps::kChangePosition: {
597 const TokenPosition& old_token =
598 (*token_positions)[token_positions->length() - 1];
599 (*token_positions)[token_positions->length() - 1] =
600 TokenPosition::Deserialize(
601 value: Utils::AddWithWrapAround(a: arg, b: old_token.Serialize()));
602 break;
603 }
604 case CodeSourceMapOps::kAdvancePC: {
605 current_pc_offset += arg;
606 if (current_pc_offset > pc_offset) {
607 return;
608 }
609 break;
610 }
611 case CodeSourceMapOps::kPushFunction: {
612 function_stack->Add(
613 &Function::Handle(ptr: Function::RawCast(raw: functions_.At(arg))));
614 token_positions->Add(InitialPosition());
615 break;
616 }
617 case CodeSourceMapOps::kPopFunction: {
618 // We never pop the root function.
619 ASSERT(function_stack->length() > 1);
620 ASSERT(token_positions->length() > 1);
621 function_stack->RemoveLast();
622 token_positions->RemoveLast();
623 break;
624 }
625 case CodeSourceMapOps::kNullCheck: {
626 break;
627 }
628 default:
629 UNREACHABLE();
630 }
631 }
632}
633
634#ifndef PRODUCT
635void CodeSourceMapReader::PrintJSONInlineIntervals(JSONObject* jsobj) {
636 {
637 JSONArray inlined_functions(jsobj, "_inlinedFunctions");
638 Function& function = Function::Handle();
639 for (intptr_t i = 0; i < functions_.Length(); i++) {
640 function ^= functions_.At(i);
641 ASSERT(!function.IsNull());
642 inlined_functions.AddValue(obj: function);
643 }
644 }
645
646 GrowableArray<intptr_t> function_stack;
647 JSONArray inline_intervals(jsobj, "_inlinedIntervals");
648 NoSafepointScope no_safepoint;
649 ReadStream stream(map_.Data(), map_.Length());
650
651 int32_t current_pc_offset = 0;
652 function_stack.Add(0);
653
654 while (stream.PendingBytes() > 0) {
655 int32_t arg;
656 const uint8_t opcode = CodeSourceMapOps::Read(stream: &stream, arg1: &arg);
657 switch (opcode) {
658 case CodeSourceMapOps::kChangePosition: {
659 break;
660 }
661 case CodeSourceMapOps::kAdvancePC: {
662 // Format: [start, end, inline functions...]
663 JSONArray inline_interval(&inline_intervals);
664 inline_interval.AddValue(i: static_cast<intptr_t>(current_pc_offset));
665 inline_interval.AddValue(
666 i: static_cast<intptr_t>(current_pc_offset + arg - 1));
667 for (intptr_t i = 0; i < function_stack.length(); i++) {
668 inline_interval.AddValue(function_stack[i]);
669 }
670 current_pc_offset += arg;
671 break;
672 }
673 case CodeSourceMapOps::kPushFunction: {
674 function_stack.Add(arg);
675 break;
676 }
677 case CodeSourceMapOps::kPopFunction: {
678 // We never pop the root function.
679 ASSERT(function_stack.length() > 1);
680 function_stack.RemoveLast();
681 break;
682 }
683 case CodeSourceMapOps::kNullCheck: {
684 break;
685 }
686 default:
687 UNREACHABLE();
688 }
689 }
690}
691#endif // !PRODUCT
692
693void CodeSourceMapReader::DumpInlineIntervals(uword start) {
694 GrowableArray<const Function*> function_stack;
695 LogBlock lb;
696 NoSafepointScope no_safepoint;
697 ReadStream stream(map_.Data(), map_.Length());
698
699 int32_t current_pc_offset = 0;
700 function_stack.Add(&root_);
701
702 THR_Print("Inline intervals for function '%s' {\n",
703 root_.ToFullyQualifiedCString());
704 while (stream.PendingBytes() > 0) {
705 int32_t arg;
706 const uint8_t opcode = CodeSourceMapOps::Read(stream: &stream, arg1: &arg);
707 switch (opcode) {
708 case CodeSourceMapOps::kChangePosition: {
709 break;
710 }
711 case CodeSourceMapOps::kAdvancePC: {
712 THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
713 start + current_pc_offset + arg - 1);
714 for (intptr_t i = 0; i < function_stack.length(); i++) {
715 THR_Print("%s ", function_stack[i]->ToCString());
716 }
717 THR_Print("\n");
718 current_pc_offset += arg;
719 break;
720 }
721 case CodeSourceMapOps::kPushFunction: {
722 function_stack.Add(
723 &Function::Handle(ptr: Function::RawCast(raw: functions_.At(arg))));
724 break;
725 }
726 case CodeSourceMapOps::kPopFunction: {
727 // We never pop the root function.
728 ASSERT(function_stack.length() > 1);
729 function_stack.RemoveLast();
730 break;
731 }
732 case CodeSourceMapOps::kNullCheck: {
733 break;
734 }
735 default:
736 UNREACHABLE();
737 }
738 }
739 THR_Print("}\n");
740}
741
742void CodeSourceMapReader::DumpSourcePositions(uword start) {
743 GrowableArray<const Function*> function_stack;
744 GrowableArray<TokenPosition> token_positions;
745 LogBlock lb;
746 NoSafepointScope no_safepoint;
747 ReadStream stream(map_.Data(), map_.Length());
748
749 int32_t current_pc_offset = 0;
750 function_stack.Add(&root_);
751 token_positions.Add(InitialPosition());
752
753 THR_Print("Source positions for function '%s' {\n",
754 root_.ToFullyQualifiedCString());
755 while (stream.PendingBytes() > 0) {
756 int32_t arg;
757 const uint8_t opcode = CodeSourceMapOps::Read(stream: &stream, arg1: &arg);
758 switch (opcode) {
759 case CodeSourceMapOps::kChangePosition: {
760 const TokenPosition& old_token =
761 token_positions[token_positions.length() - 1];
762 token_positions[token_positions.length() - 1] =
763 TokenPosition::Deserialize(
764 value: Utils::AddWithWrapAround(a: arg, b: old_token.Serialize()));
765 break;
766 }
767 case CodeSourceMapOps::kAdvancePC: {
768 THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
769 start + current_pc_offset + arg - 1);
770 for (intptr_t i = 0; i < function_stack.length(); i++) {
771 THR_Print("%s@%s", function_stack[i]->ToCString(),
772 token_positions[i].ToCString());
773 }
774 THR_Print("\n");
775 current_pc_offset += arg;
776 break;
777 }
778 case CodeSourceMapOps::kPushFunction: {
779 function_stack.Add(
780 &Function::Handle(ptr: Function::RawCast(raw: functions_.At(arg))));
781 token_positions.Add(InitialPosition());
782 break;
783 }
784 case CodeSourceMapOps::kPopFunction: {
785 // We never pop the root function.
786 ASSERT(function_stack.length() > 1);
787 ASSERT(token_positions.length() > 1);
788 function_stack.RemoveLast();
789 token_positions.RemoveLast();
790 break;
791 }
792 case CodeSourceMapOps::kNullCheck: {
793 THR_Print("%" Px "-%" Px ": null check PP#%" Pd32 "\n",
794 start + current_pc_offset, start + current_pc_offset, arg);
795 break;
796 }
797 default:
798 UNREACHABLE();
799 }
800 }
801 THR_Print("}\n");
802}
803
804intptr_t CodeSourceMapReader::GetNullCheckNameIndexAt(int32_t pc_offset) {
805 NoSafepointScope no_safepoint;
806 ReadStream stream(map_.Data(), map_.Length());
807
808 int32_t current_pc_offset = 0;
809
810 while (stream.PendingBytes() > 0) {
811 int32_t arg;
812 const uint8_t opcode = CodeSourceMapOps::Read(stream: &stream, arg1: &arg);
813 switch (opcode) {
814 case CodeSourceMapOps::kChangePosition: {
815 break;
816 }
817 case CodeSourceMapOps::kAdvancePC: {
818 current_pc_offset += arg;
819 RELEASE_ASSERT(current_pc_offset <= pc_offset);
820 break;
821 }
822 case CodeSourceMapOps::kPushFunction: {
823 break;
824 }
825 case CodeSourceMapOps::kPopFunction: {
826 break;
827 }
828 case CodeSourceMapOps::kNullCheck: {
829 if (current_pc_offset == pc_offset) {
830 return arg;
831 }
832 break;
833 }
834 default:
835 UNREACHABLE();
836 }
837 }
838
839 UNREACHABLE();
840 return -1;
841}
842
843} // namespace dart
844

source code of dart_sdk/runtime/vm/code_descriptors.cc