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