| 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 | |