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 | |
20 | namespace dart { |
21 | namespace kernel { |
22 | |
23 | KernelLineStartsReader::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 | |
39 | void 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 | |
72 | void 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 | |
91 | int32_t KernelLineStartsReader::KernelInt8LineStartsHelper::At( |
92 | const dart::TypedData& data, |
93 | intptr_t index) const { |
94 | return data.GetInt8(index); |
95 | } |
96 | |
97 | int32_t KernelLineStartsReader::KernelInt16LineStartsHelper::At( |
98 | const dart::TypedData& data, |
99 | intptr_t index) const { |
100 | return data.GetInt16(index << 1); |
101 | } |
102 | |
103 | int32_t KernelLineStartsReader::KernelInt32LineStartsHelper::At( |
104 | const dart::TypedData& data, |
105 | intptr_t index) const { |
106 | return data.GetInt32(index << 2); |
107 | } |
108 | |
109 | class 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 | |
143 | void 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 | |
169 | void 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 | |
176 | static 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 | */ |
188 | static 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 | |
212 | static 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 | |
233 | static void CollectTokenPosition(TokenPosition position, |
234 | GrowableArray<intptr_t>* token_positions) { |
235 | if (position.IsReal()) { |
236 | token_positions->Add(position.value()); |
237 | } |
238 | } |
239 | |
240 | static 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 | |
250 | static 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 | |
298 | void 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 | |
460 | class 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 | |
511 | ObjectPtr 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 | |
538 | class 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 | |
561 | ObjectPtr 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 | |
621 | ObjectPtr 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 | |
654 | void 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 | |
718 | bool 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 | |
794 | static 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 | |
812 | static 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 | |
819 | static 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 | |
852 | ProcedureAttributesMetadata 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 | |
867 | ProcedureAttributesMetadata 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 | |
879 | TableSelectorMetadata* 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 | |