1// Copyright (c) 2016, 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#if !defined(DART_PRECOMPILED_RUNTIME)
6
7#include "vm/kernel.h"
8
9#include "vm/bit_vector.h"
10#include "vm/compiler/frontend/bytecode_reader.h"
11#include "vm/compiler/frontend/constant_reader.h"
12#include "vm/compiler/frontend/kernel_translation_helper.h"
13#include "vm/compiler/jit/compiler.h"
14#include "vm/longjump.h"
15#include "vm/object_store.h"
16#include "vm/parser.h" // For Parser::kParameter* constants.
17#include "vm/stack_frame.h"
18
19
20namespace dart {
21namespace kernel {
22
23KernelLineStartsReader::KernelLineStartsReader(
24 const dart::TypedData& line_starts_data,
25 dart::Zone* zone)
26 : line_starts_data_(line_starts_data) {
27 TypedDataElementType type = line_starts_data_.ElementType();
28 if (type == kInt8ArrayElement) {
29 helper_ = new KernelInt8LineStartsHelper();
30 } else if (type == kInt16ArrayElement) {
31 helper_ = new KernelInt16LineStartsHelper();
32 } else if (type == kInt32ArrayElement) {
33 helper_ = new KernelInt32LineStartsHelper();
34 } else {
35 UNREACHABLE();
36 }
37}
38
39void KernelLineStartsReader::LocationForPosition(intptr_t position,
40 intptr_t* line,
41 intptr_t* col) const {
42 intptr_t line_count = line_starts_data_.Length();
43 intptr_t current_start = 0;
44 intptr_t previous_start = 0;
45 for (intptr_t i = 0; i < line_count; ++i) {
46 current_start += helper_->At(line_starts_data_, i);
47 if (current_start > position) {
48 *line = i;
49 if (col != NULL) {
50 *col = position - previous_start + 1;
51 }
52 return;
53 }
54 if (current_start == position) {
55 *line = i + 1;
56 if (col != NULL) {
57 *col = 1;
58 }
59 return;
60 }
61 previous_start = current_start;
62 }
63
64 // If the start of any of the lines did not cross |position|,
65 // then it means the position falls on the last line.
66 *line = line_count;
67 if (col != NULL) {
68 *col = position - current_start + 1;
69 }
70}
71
72void KernelLineStartsReader::TokenRangeAtLine(
73 intptr_t source_length,
74 intptr_t line_number,
75 TokenPosition* first_token_index,
76 TokenPosition* last_token_index) const {
77 ASSERT(line_number <= line_starts_data_.Length());
78 intptr_t cumulative = 0;
79 for (intptr_t i = 0; i < line_number; ++i) {
80 cumulative += helper_->At(line_starts_data_, i);
81 }
82 *first_token_index = dart::TokenPosition(cumulative);
83 if (line_number == line_starts_data_.Length()) {
84 *last_token_index = dart::TokenPosition(source_length);
85 } else {
86 *last_token_index = dart::TokenPosition(
87 cumulative + helper_->At(line_starts_data_, line_number) - 1);
88 }
89}
90
91int32_t KernelLineStartsReader::KernelInt8LineStartsHelper::At(
92 const dart::TypedData& data,
93 intptr_t index) const {
94 return data.GetInt8(index);
95}
96
97int32_t KernelLineStartsReader::KernelInt16LineStartsHelper::At(
98 const dart::TypedData& data,
99 intptr_t index) const {
100 return data.GetInt16(index << 1);
101}
102
103int32_t KernelLineStartsReader::KernelInt32LineStartsHelper::At(
104 const dart::TypedData& data,
105 intptr_t index) const {
106 return data.GetInt32(index << 2);
107}
108
109class KernelTokenPositionCollector : public KernelReaderHelper {
110 public:
111 KernelTokenPositionCollector(
112 Zone* zone,
113 TranslationHelper* translation_helper,
114 const Script& script,
115 const ExternalTypedData& data,
116 intptr_t data_program_offset,
117 intptr_t initial_script_index,
118 intptr_t record_for_script_id,
119 GrowableArray<intptr_t>* record_token_positions_into)
120 : KernelReaderHelper(zone,
121 translation_helper,
122 script,
123 data,
124 data_program_offset),
125 current_script_id_(initial_script_index),
126 record_for_script_id_(record_for_script_id),
127 record_token_positions_into_(record_token_positions_into) {}
128
129 void CollectTokenPositions(intptr_t kernel_offset);
130
131 void RecordTokenPosition(TokenPosition position) override;
132
133 void set_current_script_id(intptr_t id) override { current_script_id_ = id; }
134
135 private:
136 intptr_t current_script_id_;
137 intptr_t record_for_script_id_;
138 GrowableArray<intptr_t>* record_token_positions_into_;
139
140 DISALLOW_COPY_AND_ASSIGN(KernelTokenPositionCollector);
141};
142
143void KernelTokenPositionCollector::CollectTokenPositions(
144 intptr_t kernel_offset) {
145 SetOffset(kernel_offset);
146
147 const Tag tag = PeekTag();
148 if (tag == kProcedure) {
149 ProcedureHelper procedure_helper(this);
150 procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd);
151 } else if (tag == kConstructor) {
152 ConstructorHelper constructor_helper(this);
153 constructor_helper.ReadUntilExcluding(ConstructorHelper::kEnd);
154 } else if (tag == kFunctionNode) {
155 FunctionNodeHelper function_node_helper(this);
156 function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
157 } else if (tag == kField) {
158 FieldHelper field_helper(this);
159 field_helper.ReadUntilExcluding(FieldHelper::kEnd);
160 } else if (tag == kClass) {
161 ClassHelper class_helper(this);
162 class_helper.ReadUntilExcluding(ClassHelper::kEnd);
163 } else {
164 ReportUnexpectedTag("a class or a member", tag);
165 UNREACHABLE();
166 }
167}
168
169void KernelTokenPositionCollector::RecordTokenPosition(TokenPosition position) {
170 if (record_for_script_id_ == current_script_id_ &&
171 record_token_positions_into_ != NULL && position.IsReal()) {
172 record_token_positions_into_->Add(position.value());
173 }
174}
175
176static int LowestFirst(const intptr_t* a, const intptr_t* b) {
177 return *a - *b;
178}
179
180/**
181 * If index exists as sublist in list, sort the sublist from lowest to highest,
182 * then copy it, as Smis and without duplicates,
183 * to a new Array in Heap::kOld which is returned.
184 * Note that the source list is both sorted and de-duplicated as well, but will
185 * possibly contain duplicate and unsorted data at the end.
186 * Otherwise (when sublist doesn't exist in list) return new empty array.
187 */
188static ArrayPtr AsSortedDuplicateFreeArray(GrowableArray<intptr_t>* source) {
189 intptr_t size = source->length();
190 if (size == 0) {
191 return Object::empty_array().raw();
192 }
193
194 source->Sort(LowestFirst);
195
196 intptr_t last = 0;
197 for (intptr_t current = 1; current < size; ++current) {
198 if (source->At(last) != source->At(current)) {
199 (*source)[++last] = source->At(current);
200 }
201 }
202 Array& array_object = Array::Handle();
203 array_object = Array::New(last + 1, Heap::kOld);
204 Smi& smi_value = Smi::Handle();
205 for (intptr_t i = 0; i <= last; ++i) {
206 smi_value = Smi::New(source->At(i));
207 array_object.SetAt(i, smi_value);
208 }
209 return array_object.raw();
210}
211
212static void CollectKernelDataTokenPositions(
213 const ExternalTypedData& kernel_data,
214 const Script& script,
215 const Script& entry_script,
216 intptr_t kernel_offset,
217 intptr_t data_kernel_offset,
218 Zone* zone,
219 TranslationHelper* helper,
220 GrowableArray<intptr_t>* token_positions) {
221 if (kernel_data.IsNull()) {
222 return;
223 }
224
225 KernelTokenPositionCollector token_position_collector(
226 zone, helper, script, kernel_data, data_kernel_offset,
227 entry_script.kernel_script_index(), script.kernel_script_index(),
228 token_positions);
229
230 token_position_collector.CollectTokenPositions(kernel_offset);
231}
232
233static void CollectTokenPosition(TokenPosition position,
234 GrowableArray<intptr_t>* token_positions) {
235 if (position.IsReal()) {
236 token_positions->Add(position.value());
237 }
238}
239
240static void CollectBytecodeSourceTokenPositions(
241 const Bytecode& bytecode,
242 Zone* zone,
243 GrowableArray<intptr_t>* token_positions) {
244 BytecodeSourcePositionsIterator iter(zone, bytecode);
245 while (iter.MoveNext()) {
246 CollectTokenPosition(iter.TokenPos(), token_positions);
247 }
248}
249
250static void CollectBytecodeFunctionTokenPositions(
251 const Function& function,
252 GrowableArray<intptr_t>* token_positions) {
253 Thread* thread = Thread::Current();
254 Zone* zone = thread->zone();
255 ASSERT(function.is_declared_in_bytecode());
256 CollectTokenPosition(function.token_pos(), token_positions);
257 CollectTokenPosition(function.end_token_pos(), token_positions);
258 if (!function.HasBytecode()) {
259 const Object& result = Object::Handle(
260 zone, BytecodeReader::ReadFunctionBytecode(thread, function));
261 if (!result.IsNull()) {
262 Exceptions::PropagateError(Error::Cast(result));
263 }
264 }
265 Bytecode& bytecode = Bytecode::Handle(zone, function.bytecode());
266 if (bytecode.IsNull()) {
267 return;
268 }
269 if (bytecode.HasSourcePositions() && !function.IsLocalFunction()) {
270 CollectBytecodeSourceTokenPositions(bytecode, zone, token_positions);
271 // Find closure functions in the object pool.
272 const ObjectPool& pool = ObjectPool::Handle(zone, bytecode.object_pool());
273 Object& object = Object::Handle(zone);
274 Function& closure = Function::Handle(zone);
275 for (intptr_t i = 0; i < pool.Length(); i++) {
276 ObjectPool::EntryType entry_type = pool.TypeAt(i);
277 if (entry_type != ObjectPool::EntryType::kTaggedObject) {
278 continue;
279 }
280 object = pool.ObjectAt(i);
281 if (object.IsFunction()) {
282 closure ^= object.raw();
283 if (closure.kind() == FunctionLayout::kClosureFunction &&
284 closure.IsLocalFunction()) {
285 CollectTokenPosition(closure.token_pos(), token_positions);
286 CollectTokenPosition(closure.end_token_pos(), token_positions);
287 bytecode = closure.bytecode();
288 ASSERT(!bytecode.IsNull());
289 ASSERT(bytecode.function() != Function::null());
290 ASSERT(bytecode.HasSourcePositions());
291 CollectBytecodeSourceTokenPositions(bytecode, zone, token_positions);
292 }
293 }
294 }
295 }
296}
297
298void CollectTokenPositionsFor(const Script& interesting_script) {
299 Thread* thread = Thread::Current();
300 Zone* zone = thread->zone();
301 interesting_script.LookupSourceAndLineStarts(zone);
302 TranslationHelper helper(thread);
303 helper.InitFromScript(interesting_script);
304
305 GrowableArray<intptr_t> token_positions(10);
306
307 Isolate* isolate = thread->isolate();
308 const GrowableObjectArray& libs =
309 GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
310 Library& lib = Library::Handle(zone);
311 Object& entry = Object::Handle(zone);
312 Script& entry_script = Script::Handle(zone);
313 ExternalTypedData& data = ExternalTypedData::Handle(zone);
314
315 auto& temp_array = Array::Handle(zone);
316 auto& temp_field = Field::Handle(zone);
317 auto& temp_function = Function::Handle(zone);
318 for (intptr_t i = 0; i < libs.Length(); i++) {
319 lib ^= libs.At(i);
320 lib.EnsureTopLevelClassIsFinalized();
321 DictionaryIterator it(lib);
322 while (it.HasNext()) {
323 entry = it.GetNext();
324 data = ExternalTypedData::null();
325 if (entry.IsClass()) {
326 const Class& klass = Class::Cast(entry);
327 if (klass.script() == interesting_script.raw()) {
328 token_positions.Add(klass.token_pos().value());
329 token_positions.Add(klass.end_token_pos().value());
330 }
331 // If class is declared in bytecode, its members should be loaded
332 // (via class finalization) before their token positions could be
333 // collected.
334 if (klass.is_declared_in_bytecode() && !klass.is_finalized()) {
335 const Error& error =
336 Error::Handle(zone, klass.EnsureIsFinalized(thread));
337 if (!error.IsNull()) {
338 Exceptions::PropagateError(error);
339 }
340 }
341 if (klass.is_finalized()) {
342 temp_array = klass.fields();
343 for (intptr_t i = 0; i < temp_array.Length(); ++i) {
344 temp_field ^= temp_array.At(i);
345 if (!temp_field.is_declared_in_bytecode() &&
346 temp_field.kernel_offset() <= 0) {
347 // Skip artificially injected fields.
348 continue;
349 }
350 entry_script = temp_field.Script();
351 if (entry_script.raw() != interesting_script.raw()) {
352 continue;
353 }
354 if (temp_field.is_declared_in_bytecode()) {
355 token_positions.Add(temp_field.token_pos().value());
356 token_positions.Add(temp_field.end_token_pos().value());
357 if (temp_field.is_static() &&
358 temp_field.has_nontrivial_initializer()) {
359 temp_function = temp_field.EnsureInitializerFunction();
360 CollectBytecodeFunctionTokenPositions(temp_function,
361 &token_positions);
362 }
363 } else {
364 data = temp_field.KernelData();
365 CollectKernelDataTokenPositions(
366 data, interesting_script, entry_script,
367 temp_field.kernel_offset(),
368 temp_field.KernelDataProgramOffset(), zone, &helper,
369 &token_positions);
370 }
371 }
372 temp_array = klass.functions();
373 for (intptr_t i = 0; i < temp_array.Length(); ++i) {
374 temp_function ^= temp_array.At(i);
375 entry_script = temp_function.script();
376 if (entry_script.raw() != interesting_script.raw()) {
377 continue;
378 }
379 if (temp_function.is_declared_in_bytecode()) {
380 CollectBytecodeFunctionTokenPositions(temp_function,
381 &token_positions);
382 } else {
383 data = temp_function.KernelData();
384 CollectKernelDataTokenPositions(
385 data, interesting_script, entry_script,
386 temp_function.kernel_offset(),
387 temp_function.KernelDataProgramOffset(), zone, &helper,
388 &token_positions);
389 }
390 }
391 } else {
392 // Class isn't finalized yet: read the data attached to it.
393 ASSERT(!klass.is_declared_in_bytecode());
394 ASSERT(klass.kernel_offset() > 0);
395 data = lib.kernel_data();
396 ASSERT(!data.IsNull());
397 const intptr_t library_kernel_offset = lib.kernel_offset();
398 ASSERT(library_kernel_offset > 0);
399 const intptr_t class_offset = klass.kernel_offset();
400
401 entry_script = klass.script();
402 if (entry_script.raw() != interesting_script.raw()) {
403 continue;
404 }
405 CollectKernelDataTokenPositions(
406 data, interesting_script, entry_script, class_offset,
407 library_kernel_offset, zone, &helper, &token_positions);
408 }
409 } else if (entry.IsFunction()) {
410 temp_function ^= entry.raw();
411 entry_script = temp_function.script();
412 if (entry_script.raw() != interesting_script.raw()) {
413 continue;
414 }
415 if (temp_function.is_declared_in_bytecode()) {
416 CollectBytecodeFunctionTokenPositions(temp_function,
417 &token_positions);
418 } else {
419 data = temp_function.KernelData();
420 CollectKernelDataTokenPositions(
421 data, interesting_script, entry_script,
422 temp_function.kernel_offset(),
423 temp_function.KernelDataProgramOffset(), zone, &helper,
424 &token_positions);
425 }
426 } else if (entry.IsField()) {
427 const Field& field = Field::Cast(entry);
428 if (!field.is_declared_in_bytecode() && field.kernel_offset() <= 0) {
429 // Skip artificially injected fields.
430 continue;
431 }
432 entry_script = field.Script();
433 if (entry_script.raw() != interesting_script.raw()) {
434 continue;
435 }
436 if (field.is_declared_in_bytecode()) {
437 token_positions.Add(field.token_pos().value());
438 token_positions.Add(field.end_token_pos().value());
439 if (field.is_static() && field.has_nontrivial_initializer()) {
440 temp_function = field.EnsureInitializerFunction();
441 CollectBytecodeFunctionTokenPositions(temp_function,
442 &token_positions);
443 }
444 } else {
445 data = field.KernelData();
446 CollectKernelDataTokenPositions(
447 data, interesting_script, entry_script, field.kernel_offset(),
448 field.KernelDataProgramOffset(), zone, &helper, &token_positions);
449 }
450 }
451 }
452 }
453
454 Script& script = Script::Handle(zone, interesting_script.raw());
455 Array& array_object = Array::Handle(zone);
456 array_object = AsSortedDuplicateFreeArray(&token_positions);
457 script.set_debug_positions(array_object);
458}
459
460class MetadataEvaluator : public KernelReaderHelper {
461 public:
462 MetadataEvaluator(Zone* zone,
463 TranslationHelper* translation_helper,
464 const Script& script,
465 const ExternalTypedData& data,
466 intptr_t data_program_offset,
467 ActiveClass* active_class)
468 : KernelReaderHelper(zone,
469 translation_helper,
470 script,
471 data,
472 data_program_offset),
473 constant_reader_(this, active_class) {}
474
475 ObjectPtr EvaluateMetadata(intptr_t kernel_offset,
476 bool is_annotations_offset) {
477 SetOffset(kernel_offset);
478
479 // Library and LibraryDependency objects do not have a tag in kernel binary.
480 // Synthetic metadata fields corresponding to these objects keep kernel
481 // offset of annotations list instead of annotated object.
482 if (!is_annotations_offset) {
483 const Tag tag = PeekTag();
484
485 if (tag == kClass) {
486 ClassHelper class_helper(this);
487 class_helper.ReadUntilExcluding(ClassHelper::kAnnotations);
488 } else if (tag == kProcedure) {
489 ProcedureHelper procedure_helper(this);
490 procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
491 } else if (tag == kField) {
492 FieldHelper field_helper(this);
493 field_helper.ReadUntilExcluding(FieldHelper::kAnnotations);
494 } else if (tag == kConstructor) {
495 ConstructorHelper constructor_helper(this);
496 constructor_helper.ReadUntilExcluding(ConstructorHelper::kAnnotations);
497 } else {
498 FATAL("No support for metadata on this type of kernel node\n");
499 }
500 }
501
502 return constant_reader_.ReadAnnotations();
503 }
504
505 private:
506 ConstantReader constant_reader_;
507
508 DISALLOW_COPY_AND_ASSIGN(MetadataEvaluator);
509};
510
511ObjectPtr EvaluateMetadata(const Field& metadata_field,
512 bool is_annotations_offset) {
513 LongJumpScope jump;
514 if (setjmp(*jump.Set()) == 0) {
515 Thread* thread = Thread::Current();
516 Zone* zone = thread->zone();
517 TranslationHelper helper(thread);
518 Script& script = Script::Handle(zone, metadata_field.Script());
519 helper.InitFromScript(script);
520
521 const Class& owner_class = Class::Handle(zone, metadata_field.Owner());
522 ActiveClass active_class;
523 ActiveClassScope active_class_scope(&active_class, &owner_class);
524
525 MetadataEvaluator metadata_evaluator(
526 zone, &helper, script,
527 ExternalTypedData::Handle(zone, metadata_field.KernelData()),
528 metadata_field.KernelDataProgramOffset(), &active_class);
529
530 return metadata_evaluator.EvaluateMetadata(metadata_field.kernel_offset(),
531 is_annotations_offset);
532
533 } else {
534 return Thread::Current()->StealStickyError();
535 }
536}
537
538class ParameterDescriptorBuilder : public KernelReaderHelper {
539 public:
540 ParameterDescriptorBuilder(TranslationHelper* translation_helper,
541 const Script& script,
542 Zone* zone,
543 const ExternalTypedData& data,
544 intptr_t data_program_offset,
545 ActiveClass* active_class)
546 : KernelReaderHelper(zone,
547 translation_helper,
548 script,
549 data,
550 data_program_offset),
551 constant_reader_(this, active_class) {}
552
553 ObjectPtr BuildParameterDescriptor(const Function& function);
554
555 private:
556 ConstantReader constant_reader_;
557
558 DISALLOW_COPY_AND_ASSIGN(ParameterDescriptorBuilder);
559};
560
561ObjectPtr ParameterDescriptorBuilder::BuildParameterDescriptor(
562 const Function& function) {
563 SetOffset(function.kernel_offset());
564 ReadUntilFunctionNode();
565 FunctionNodeHelper function_node_helper(this);
566 function_node_helper.ReadUntilExcluding(
567 FunctionNodeHelper::kPositionalParameters);
568 intptr_t param_count = function_node_helper.total_parameter_count_;
569 intptr_t positional_count = ReadListLength(); // read list length.
570 intptr_t named_parameter_count = param_count - positional_count;
571
572 const Array& param_descriptor = Array::Handle(
573 Array::New(param_count * Parser::kParameterEntrySize, Heap::kOld));
574 for (intptr_t i = 0; i < param_count; ++i) {
575 const intptr_t entry_start = i * Parser::kParameterEntrySize;
576
577 if (i == positional_count) {
578 intptr_t named_parameter_count_check =
579 ReadListLength(); // read list length.
580 ASSERT(named_parameter_count_check == named_parameter_count);
581 }
582
583 // Read ith variable declaration.
584 intptr_t param_kernel_offset = reader_.offset();
585 VariableDeclarationHelper helper(this);
586 helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
587 param_descriptor.SetAt(entry_start + Parser::kParameterIsFinalOffset,
588 helper.IsFinal() ? Bool::True() : Bool::False());
589
590 Tag tag = ReadTag(); // read (first part of) initializer.
591 if ((tag == kSomething) && !function.is_abstract()) {
592 // This will read the initializer.
593 Instance& constant = Instance::ZoneHandle(
594 zone_, constant_reader_.ReadConstantExpression());
595 param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset,
596 constant);
597 } else {
598 if (tag == kSomething) {
599 SkipExpression(); // Skip initializer.
600 }
601 param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset,
602 Object::null_instance());
603 }
604
605 if (FLAG_enable_mirrors && (helper.annotation_count_ > 0)) {
606 AlternativeReadingScope alt(&reader_, param_kernel_offset);
607 VariableDeclarationHelper helper(this);
608 helper.ReadUntilExcluding(VariableDeclarationHelper::kAnnotations);
609 Object& metadata =
610 Object::ZoneHandle(zone_, constant_reader_.ReadAnnotations());
611 param_descriptor.SetAt(entry_start + Parser::kParameterMetadataOffset,
612 metadata);
613 } else {
614 param_descriptor.SetAt(entry_start + Parser::kParameterMetadataOffset,
615 Object::null_instance());
616 }
617 }
618 return param_descriptor.raw();
619}
620
621ObjectPtr BuildParameterDescriptor(const Function& function) {
622 LongJumpScope jump;
623 if (setjmp(*jump.Set()) == 0) {
624 Thread* thread = Thread::Current();
625 Zone* zone = thread->zone();
626 TranslationHelper helper(thread);
627 Script& script = Script::Handle(zone, function.script());
628 helper.InitFromScript(script);
629
630 if (function.is_declared_in_bytecode()) {
631 BytecodeComponentData bytecode_component(
632 &Array::Handle(zone, helper.GetBytecodeComponent()));
633 ActiveClass active_class;
634 BytecodeReaderHelper bytecode_reader_helper(&helper, &active_class,
635 &bytecode_component);
636 return bytecode_reader_helper.BuildParameterDescriptor(function);
637 }
638
639 const Class& owner_class = Class::Handle(zone, function.Owner());
640 ActiveClass active_class;
641 ActiveClassScope active_class_scope(&active_class, &owner_class);
642
643 ParameterDescriptorBuilder builder(
644 &helper, Script::Handle(zone, function.script()), zone,
645 ExternalTypedData::Handle(zone, function.KernelData()),
646 function.KernelDataProgramOffset(), &active_class);
647
648 return builder.BuildParameterDescriptor(function);
649 } else {
650 return Thread::Current()->StealStickyError();
651 }
652}
653
654void ReadParameterCovariance(const Function& function,
655 BitVector* is_covariant,
656 BitVector* is_generic_covariant_impl) {
657 Thread* thread = Thread::Current();
658 Zone* zone = thread->zone();
659
660 const intptr_t num_params = function.NumParameters();
661 ASSERT(is_covariant->length() == num_params);
662 ASSERT(is_generic_covariant_impl->length() == num_params);
663
664 const auto& script = Script::Handle(zone, function.script());
665 TranslationHelper translation_helper(thread);
666 translation_helper.InitFromScript(script);
667
668 if (function.is_declared_in_bytecode()) {
669 BytecodeReaderHelper bytecode_reader_helper(&translation_helper, nullptr,
670 nullptr);
671 bytecode_reader_helper.ReadParameterCovariance(function, is_covariant,
672 is_generic_covariant_impl);
673 return;
674 }
675
676 KernelReaderHelper reader_helper(
677 zone, &translation_helper, script,
678 ExternalTypedData::Handle(zone, function.KernelData()),
679 function.KernelDataProgramOffset());
680
681 reader_helper.SetOffset(function.kernel_offset());
682 reader_helper.ReadUntilFunctionNode();
683
684 FunctionNodeHelper function_node_helper(&reader_helper);
685 function_node_helper.ReadUntilExcluding(
686 FunctionNodeHelper::kPositionalParameters);
687
688 // Positional.
689 const intptr_t num_positional_params = reader_helper.ReadListLength();
690 intptr_t param_index = function.NumImplicitParameters();
691 for (intptr_t i = 0; i < num_positional_params; ++i, ++param_index) {
692 VariableDeclarationHelper helper(&reader_helper);
693 helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
694
695 if (helper.IsCovariant()) {
696 is_covariant->Add(param_index);
697 }
698 if (helper.IsGenericCovariantImpl()) {
699 is_generic_covariant_impl->Add(param_index);
700 }
701 }
702
703 // Named.
704 const intptr_t num_named_params = reader_helper.ReadListLength();
705 for (intptr_t i = 0; i < num_named_params; ++i, ++param_index) {
706 VariableDeclarationHelper helper(&reader_helper);
707 helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
708
709 if (helper.IsCovariant()) {
710 is_covariant->Add(param_index);
711 }
712 if (helper.IsGenericCovariantImpl()) {
713 is_generic_covariant_impl->Add(param_index);
714 }
715 }
716}
717
718bool NeedsDynamicInvocationForwarder(const Function& function) {
719 Zone* zone = Thread::Current()->zone();
720
721 // Right now closures do not need a dyn:* forwarder.
722 // See https://github.com/dart-lang/sdk/issues/40813
723 if (function.IsClosureFunction()) return false;
724
725 // Method extractors have no parameters to check and return value is a closure
726 // and therefore not an unboxed primitive type.
727 if (function.IsMethodExtractor()) {
728 return false;
729 }
730
731 // Invoke field dispatchers are dynamically generated, will invoke a getter to
732 // obtain the field value and then invoke ".call()" on the result.
733 // Those dynamically generated dispathers don't have proper kernel metadata
734 // associated with them - we can therefore not query if there are dynamic
735 // calls to them or not and are therefore conservative.
736 if (function.IsInvokeFieldDispatcher()) {
737 return true;
738 }
739
740 // The dyn:* forwarders perform unboxing of parameters before calling the
741 // actual target (which accepts unboxed parameters) and boxes return values
742 // of the return value.
743 if (function.HasUnboxedParameters() || function.HasUnboxedReturnValue()) {
744 return true;
745 }
746
747 // There are no parameters to type check for getters and if the return value
748 // is boxed, then the dyn:* forwarder is not needed.
749 if (function.IsImplicitGetterFunction()) {
750 return false;
751 }
752
753 // Covariant parameters (both explicitly covariant and generic-covariant-impl)
754 // are checked in the body of a function and therefore don't need checks in a
755 // dynamic invocation forwarder. So dynamic invocation forwarder is only
756 // needed if there are non-covariant parameters of non-top type.
757 if (function.IsImplicitSetterFunction()) {
758 const auto& field = Field::Handle(zone, function.accessor_field());
759 return !(field.is_covariant() || field.is_generic_covariant_impl());
760 }
761
762 const auto& type_params =
763 TypeArguments::Handle(zone, function.type_parameters());
764 if (!type_params.IsNull()) {
765 auto& type_param = TypeParameter::Handle(zone);
766 auto& bound = AbstractType::Handle(zone);
767 for (intptr_t i = 0, n = type_params.Length(); i < n; ++i) {
768 type_param ^= type_params.TypeAt(i);
769 bound = type_param.bound();
770 if (!bound.IsTopTypeForSubtyping() &&
771 !type_param.IsGenericCovariantImpl()) {
772 return true;
773 }
774 }
775 }
776
777 const intptr_t num_params = function.NumParameters();
778 BitVector is_covariant(zone, num_params);
779 BitVector is_generic_covariant_impl(zone, num_params);
780 ReadParameterCovariance(function, &is_covariant, &is_generic_covariant_impl);
781
782 auto& type = AbstractType::Handle(zone);
783 for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
784 type = function.ParameterTypeAt(i);
785 if (!type.IsTopTypeForSubtyping() &&
786 !is_generic_covariant_impl.Contains(i) && !is_covariant.Contains(i)) {
787 return true;
788 }
789 }
790
791 return false;
792}
793
794static ProcedureAttributesMetadata ProcedureAttributesOf(
795 Zone* zone,
796 const Script& script,
797 const ExternalTypedData& kernel_data,
798 intptr_t kernel_data_program_offset,
799 intptr_t kernel_offset) {
800 TranslationHelper translation_helper(Thread::Current());
801 translation_helper.InitFromScript(script);
802 KernelReaderHelper reader_helper(zone, &translation_helper, script,
803 kernel_data, kernel_data_program_offset);
804 ProcedureAttributesMetadataHelper procedure_attributes_metadata_helper(
805 &reader_helper);
806 ProcedureAttributesMetadata attrs =
807 procedure_attributes_metadata_helper.GetProcedureAttributes(
808 kernel_offset);
809 return attrs;
810}
811
812static void BytecodeProcedureAttributesError(const Object& function_or_field,
813 const Object& value) {
814 FATAL3("Unexpected value of %s bytecode attribute on %s: %s",
815 Symbols::vm_procedure_attributes_metadata().ToCString(),
816 function_or_field.ToCString(), value.ToCString());
817}
818
819static ProcedureAttributesMetadata ProcedureAttributesFromBytecodeAttribute(
820 Zone* zone,
821 const Object& function_or_field) {
822 ProcedureAttributesMetadata attrs;
823 const Object& value = Object::Handle(
824 zone,
825 BytecodeReader::GetBytecodeAttribute(
826 function_or_field, Symbols::vm_procedure_attributes_metadata()));
827 if (!value.IsNull()) {
828 const intptr_t kBytecodeAttributeLength = 3;
829 int32_t elements[kBytecodeAttributeLength];
830 if (!value.IsArray()) {
831 BytecodeProcedureAttributesError(function_or_field, value);
832 }
833 const Array& array = Array::Cast(value);
834 if (array.Length() != kBytecodeAttributeLength) {
835 BytecodeProcedureAttributesError(function_or_field, value);
836 }
837 Object& element = Object::Handle(zone);
838 for (intptr_t i = 0; i < kBytecodeAttributeLength; i++) {
839 element = array.At(i);
840 if (!element.IsSmi()) {
841 BytecodeProcedureAttributesError(function_or_field, value);
842 }
843 elements[i] = Smi::Cast(element).Value();
844 }
845 attrs.InitializeFromFlags(elements[0]);
846 attrs.method_or_setter_selector_id = elements[1];
847 attrs.getter_selector_id = elements[2];
848 }
849 return attrs;
850}
851
852ProcedureAttributesMetadata ProcedureAttributesOf(const Function& function,
853 Zone* zone) {
854 if (function.is_declared_in_bytecode()) {
855 if (function.IsImplicitGetterOrSetter()) {
856 const Field& field = Field::Handle(zone, function.accessor_field());
857 return ProcedureAttributesFromBytecodeAttribute(zone, field);
858 }
859 return ProcedureAttributesFromBytecodeAttribute(zone, function);
860 }
861 const Script& script = Script::Handle(zone, function.script());
862 return ProcedureAttributesOf(
863 zone, script, ExternalTypedData::Handle(zone, function.KernelData()),
864 function.KernelDataProgramOffset(), function.kernel_offset());
865}
866
867ProcedureAttributesMetadata ProcedureAttributesOf(const Field& field,
868 Zone* zone) {
869 if (field.is_declared_in_bytecode()) {
870 return ProcedureAttributesFromBytecodeAttribute(zone, field);
871 }
872 const Class& parent = Class::Handle(zone, field.Owner());
873 const Script& script = Script::Handle(zone, parent.script());
874 return ProcedureAttributesOf(
875 zone, script, ExternalTypedData::Handle(zone, field.KernelData()),
876 field.KernelDataProgramOffset(), field.kernel_offset());
877}
878
879TableSelectorMetadata* TableSelectorMetadataForProgram(
880 const KernelProgramInfo& info,
881 Zone* zone) {
882 TranslationHelper translation_helper(Thread::Current());
883 translation_helper.InitFromKernelProgramInfo(info);
884 const auto& data = ExternalTypedData::Handle(zone, info.metadata_payloads());
885 KernelReaderHelper reader_helper(zone, &translation_helper,
886 Script::Handle(zone), data, 0);
887 TableSelectorMetadataHelper table_selector_metadata_helper(&reader_helper);
888 return table_selector_metadata_helper.GetTableSelectorMetadata(zone);
889}
890
891} // namespace kernel
892} // namespace dart
893
894#endif // !defined(DART_PRECOMPILED_RUNTIME)
895