| 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 | #ifndef RUNTIME_VM_KERNEL_LOADER_H_ |
| 6 | #define RUNTIME_VM_KERNEL_LOADER_H_ |
| 7 | |
| 8 | #if !defined(DART_PRECOMPILED_RUNTIME) |
| 9 | |
| 10 | #include "vm/bit_vector.h" |
| 11 | #include "vm/compiler/frontend/bytecode_reader.h" |
| 12 | #include "vm/compiler/frontend/constant_reader.h" |
| 13 | #include "vm/compiler/frontend/kernel_translation_helper.h" |
| 14 | #include "vm/hash_map.h" |
| 15 | #include "vm/kernel.h" |
| 16 | #include "vm/object.h" |
| 17 | #include "vm/symbols.h" |
| 18 | |
| 19 | namespace dart { |
| 20 | namespace kernel { |
| 21 | |
| 22 | class KernelLoader; |
| 23 | |
| 24 | class BuildingTranslationHelper : public TranslationHelper { |
| 25 | public: |
| 26 | BuildingTranslationHelper(KernelLoader* loader, |
| 27 | Thread* thread, |
| 28 | Heap::Space space) |
| 29 | : TranslationHelper(thread, space), |
| 30 | loader_(loader), |
| 31 | library_lookup_handle_(Library::Handle(thread->zone())) {} |
| 32 | virtual ~BuildingTranslationHelper() {} |
| 33 | |
| 34 | virtual LibraryPtr LookupLibraryByKernelLibrary(NameIndex library); |
| 35 | virtual ClassPtr LookupClassByKernelClass(NameIndex klass); |
| 36 | |
| 37 | private: |
| 38 | KernelLoader* loader_; |
| 39 | |
| 40 | #if defined(DEBUG) |
| 41 | class LibraryLookupHandleScope { |
| 42 | public: |
| 43 | explicit LibraryLookupHandleScope(Library& lib) : lib_(lib) { |
| 44 | ASSERT(lib_.IsNull()); |
| 45 | } |
| 46 | |
| 47 | ~LibraryLookupHandleScope() { lib_ = Library::null(); } |
| 48 | |
| 49 | private: |
| 50 | Library& lib_; |
| 51 | |
| 52 | DISALLOW_COPY_AND_ASSIGN(LibraryLookupHandleScope); |
| 53 | }; |
| 54 | #endif // defined(DEBUG) |
| 55 | |
| 56 | // Preallocated handle for use in LookupClassByKernelClass(). |
| 57 | Library& library_lookup_handle_; |
| 58 | |
| 59 | DISALLOW_COPY_AND_ASSIGN(BuildingTranslationHelper); |
| 60 | }; |
| 61 | |
| 62 | template <typename VmType> |
| 63 | class Mapping { |
| 64 | public: |
| 65 | bool Lookup(intptr_t canonical_name, VmType** handle) { |
| 66 | typename MapType::Pair* pair = map_.LookupPair(canonical_name); |
| 67 | if (pair != NULL) { |
| 68 | *handle = pair->value; |
| 69 | return true; |
| 70 | } |
| 71 | return false; |
| 72 | } |
| 73 | |
| 74 | void Insert(intptr_t canonical_name, VmType* object) { |
| 75 | map_.Insert(canonical_name, object); |
| 76 | } |
| 77 | |
| 78 | private: |
| 79 | typedef IntMap<VmType*> MapType; |
| 80 | MapType map_; |
| 81 | }; |
| 82 | |
| 83 | class LibraryIndex { |
| 84 | public: |
| 85 | // |kernel_data| is the kernel data for one library alone. |
| 86 | explicit LibraryIndex(const ExternalTypedData& kernel_data, |
| 87 | uint32_t binary_version); |
| 88 | |
| 89 | intptr_t class_count() const { return class_count_; } |
| 90 | intptr_t procedure_count() const { return procedure_count_; } |
| 91 | |
| 92 | intptr_t ClassOffset(intptr_t index) const { |
| 93 | return reader_.ReadUInt32At(class_index_offset_ + index * 4); |
| 94 | } |
| 95 | |
| 96 | intptr_t ProcedureOffset(intptr_t index) const { |
| 97 | return reader_.ReadUInt32At(procedure_index_offset_ + index * 4); |
| 98 | } |
| 99 | |
| 100 | intptr_t SizeOfClassAtOffset(intptr_t class_offset) const { |
| 101 | for (intptr_t i = 0, offset = class_index_offset_; i < class_count_; |
| 102 | ++i, offset += 4) { |
| 103 | if (static_cast<intptr_t>(reader_.ReadUInt32At(offset)) == class_offset) { |
| 104 | return reader_.ReadUInt32At(offset + 4) - class_offset; |
| 105 | } |
| 106 | } |
| 107 | UNREACHABLE(); |
| 108 | return -1; |
| 109 | } |
| 110 | |
| 111 | intptr_t SourceReferencesOffset() { return source_references_offset_; } |
| 112 | |
| 113 | private: |
| 114 | Reader reader_; |
| 115 | uint32_t binary_version_; |
| 116 | intptr_t source_references_offset_; |
| 117 | intptr_t class_index_offset_; |
| 118 | intptr_t class_count_; |
| 119 | intptr_t procedure_index_offset_; |
| 120 | intptr_t procedure_count_; |
| 121 | |
| 122 | DISALLOW_COPY_AND_ASSIGN(LibraryIndex); |
| 123 | }; |
| 124 | |
| 125 | class ClassIndex { |
| 126 | public: |
| 127 | // |class_offset| is the offset of class' kernel data in |buffer| of |
| 128 | // size |size|. The size of the class' kernel data is |class_size|. |
| 129 | ClassIndex(const uint8_t* buffer, |
| 130 | intptr_t buffer_size, |
| 131 | intptr_t class_offset, |
| 132 | intptr_t class_size); |
| 133 | |
| 134 | // |class_offset| is the offset of class' kernel data in |kernel_data|. |
| 135 | // The size of the class' kernel data is |class_size|. |
| 136 | ClassIndex(const ExternalTypedData& kernel_data, |
| 137 | intptr_t class_offset, |
| 138 | intptr_t class_size); |
| 139 | |
| 140 | intptr_t procedure_count() const { return procedure_count_; } |
| 141 | |
| 142 | intptr_t ProcedureOffset(intptr_t index) const { |
| 143 | return reader_.ReadUInt32At(procedure_index_offset_ + index * 4); |
| 144 | } |
| 145 | |
| 146 | private: |
| 147 | void Init(intptr_t class_offset, intptr_t class_size); |
| 148 | |
| 149 | Reader reader_; |
| 150 | intptr_t procedure_count_; |
| 151 | intptr_t procedure_index_offset_; |
| 152 | |
| 153 | DISALLOW_COPY_AND_ASSIGN(ClassIndex); |
| 154 | }; |
| 155 | |
| 156 | struct UriToSourceTableEntry : public ZoneAllocated { |
| 157 | UriToSourceTableEntry() {} |
| 158 | |
| 159 | const String* uri = nullptr; |
| 160 | const String* sources = nullptr; |
| 161 | const TypedData* line_starts = nullptr; |
| 162 | }; |
| 163 | |
| 164 | struct UriToSourceTableTrait { |
| 165 | typedef UriToSourceTableEntry* Value; |
| 166 | typedef const UriToSourceTableEntry* Key; |
| 167 | typedef UriToSourceTableEntry* Pair; |
| 168 | |
| 169 | static Key KeyOf(Pair kv) { return kv; } |
| 170 | |
| 171 | static Value ValueOf(Pair kv) { return kv; } |
| 172 | |
| 173 | static inline intptr_t Hashcode(Key key) { return key->uri->Hash(); } |
| 174 | |
| 175 | static inline bool IsKeyEqual(Pair kv, Key key) { |
| 176 | // Only compare uri. |
| 177 | return kv->uri->CompareTo(*key->uri) == 0; |
| 178 | } |
| 179 | }; |
| 180 | |
| 181 | class KernelLoader : public ValueObject { |
| 182 | public: |
| 183 | explicit KernelLoader( |
| 184 | Program* program, |
| 185 | DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table); |
| 186 | static Object& LoadEntireProgram(Program* program, |
| 187 | bool process_pending_classes = true); |
| 188 | |
| 189 | // Returns the library containing the main procedure, null if there |
| 190 | // was no main procedure, or a failure object if there was an error. |
| 191 | ObjectPtr LoadProgram(bool process_pending_classes = true); |
| 192 | |
| 193 | // Load given library. |
| 194 | void LoadLibrary(const Library& library); |
| 195 | |
| 196 | // Returns the function which will evaluate the expression, or a failure |
| 197 | // object if there was an error. |
| 198 | ObjectPtr LoadExpressionEvaluationFunction(const String& library_url, |
| 199 | const String& klass); |
| 200 | |
| 201 | // Finds all libraries that have been modified in this incremental |
| 202 | // version of the kernel program file. |
| 203 | // |
| 204 | // When [force_reload] is false and if [p_num_classes], [p_num_procedures] are |
| 205 | // not nullptr, then they are populated with number of classes and top-level |
| 206 | // procedures in [program]. |
| 207 | static void FindModifiedLibraries(Program* program, |
| 208 | Isolate* isolate, |
| 209 | BitVector* modified_libs, |
| 210 | bool force_reload, |
| 211 | bool* is_empty_program, |
| 212 | intptr_t* p_num_classes, |
| 213 | intptr_t* p_num_procedures); |
| 214 | |
| 215 | static StringPtr FindSourceForScript(const uint8_t* kernel_buffer, |
| 216 | intptr_t kernel_buffer_length, |
| 217 | const String& url); |
| 218 | |
| 219 | void FinishTopLevelClassLoading(const Class& toplevel_class, |
| 220 | const Library& library, |
| 221 | const LibraryIndex& library_index); |
| 222 | |
| 223 | static void FinishLoading(const Class& klass); |
| 224 | |
| 225 | void ReadObfuscationProhibitions(); |
| 226 | void ReadLoadingUnits(); |
| 227 | |
| 228 | private: |
| 229 | // Check for the presence of a (possibly const) constructor for the |
| 230 | // 'ExternalName' class. If found, returns the name parameter to the |
| 231 | // constructor. |
| 232 | StringPtr DetectExternalNameCtor(); |
| 233 | |
| 234 | // Check for the presence of a (possibly const) constructor for the 'pragma' |
| 235 | // class. Returns whether it was found (no details about the type of pragma). |
| 236 | bool DetectPragmaCtor(); |
| 237 | |
| 238 | bool IsClassName(NameIndex name, const String& library, const String& klass); |
| 239 | |
| 240 | void AnnotateNativeProcedures(); |
| 241 | void LoadNativeExtensionLibraries(); |
| 242 | void LoadNativeExtension(const Library& library, const String& uri_path); |
| 243 | void EvaluateDelayedPragmas(); |
| 244 | |
| 245 | void ReadVMAnnotations(const Library& library, |
| 246 | intptr_t annotation_count, |
| 247 | String* native_name, |
| 248 | bool* is_potential_native, |
| 249 | bool* has_pragma_annotation); |
| 250 | |
| 251 | KernelLoader(const Script& script, |
| 252 | const ExternalTypedData& kernel_data, |
| 253 | intptr_t data_program_offset, |
| 254 | uint32_t kernel_binary_version); |
| 255 | |
| 256 | void InitializeFields( |
| 257 | DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table); |
| 258 | |
| 259 | LibraryPtr LoadLibrary(intptr_t index); |
| 260 | |
| 261 | const String& LibraryUri(intptr_t library_index) { |
| 262 | return translation_helper_.DartSymbolPlain( |
| 263 | translation_helper_.CanonicalNameString( |
| 264 | library_canonical_name(library_index))); |
| 265 | } |
| 266 | |
| 267 | intptr_t library_offset(intptr_t index) { |
| 268 | kernel::Reader reader(program_->kernel_data(), |
| 269 | program_->kernel_data_size()); |
| 270 | return reader.ReadFromIndexNoReset(reader.size(), |
| 271 | LibraryCountFieldCountFromEnd + 1, |
| 272 | program_->library_count() + 1, index); |
| 273 | } |
| 274 | |
| 275 | NameIndex library_canonical_name(intptr_t index) { |
| 276 | kernel::Reader reader(program_->kernel_data(), |
| 277 | program_->kernel_data_size()); |
| 278 | reader.set_offset(library_offset(index)); |
| 279 | |
| 280 | // Start reading library. |
| 281 | // Note that this needs to be keep in sync with LibraryHelper. |
| 282 | reader.ReadFlags(); |
| 283 | reader.ReadUInt(); // Read major language version. |
| 284 | reader.ReadUInt(); // Read minor language version. |
| 285 | return reader.ReadCanonicalNameReference(); |
| 286 | } |
| 287 | |
| 288 | uint8_t CharacterAt(StringIndex string_index, intptr_t index); |
| 289 | |
| 290 | static void index_programs(kernel::Reader* reader, |
| 291 | GrowableArray<intptr_t>* subprogram_file_starts); |
| 292 | void walk_incremental_kernel(BitVector* modified_libs, |
| 293 | bool* is_empty_program, |
| 294 | intptr_t* p_num_classes, |
| 295 | intptr_t* p_num_procedures); |
| 296 | |
| 297 | void LoadPreliminaryClass(ClassHelper* class_helper, |
| 298 | intptr_t type_parameter_count); |
| 299 | |
| 300 | void ReadInferredType(const Field& field, intptr_t kernel_offset); |
| 301 | void CheckForInitializer(const Field& field); |
| 302 | |
| 303 | void LoadClass(const Library& library, |
| 304 | const Class& toplevel_class, |
| 305 | intptr_t class_end, |
| 306 | Class* out_class); |
| 307 | |
| 308 | void FinishClassLoading(const Class& klass, |
| 309 | const Library& library, |
| 310 | const Class& toplevel_class, |
| 311 | intptr_t class_offset, |
| 312 | const ClassIndex& class_index, |
| 313 | ClassHelper* class_helper); |
| 314 | |
| 315 | void LoadProcedure(const Library& library, |
| 316 | const Class& owner, |
| 317 | bool in_class, |
| 318 | intptr_t procedure_end); |
| 319 | |
| 320 | ArrayPtr MakeFieldsArray(); |
| 321 | ArrayPtr MakeFunctionsArray(); |
| 322 | |
| 323 | ScriptPtr LoadScriptAt( |
| 324 | intptr_t index, |
| 325 | DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table); |
| 326 | |
| 327 | // If klass's script is not the script at the uri index, return a PatchClass |
| 328 | // for klass whose script corresponds to the uri index. |
| 329 | // Otherwise return klass. |
| 330 | const Object& ClassForScriptAt(const Class& klass, intptr_t source_uri_index); |
| 331 | ScriptPtr ScriptAt(intptr_t source_uri_index) { |
| 332 | return kernel_program_info_.ScriptAt(source_uri_index); |
| 333 | } |
| 334 | |
| 335 | void GenerateFieldAccessors(const Class& klass, |
| 336 | const Field& field, |
| 337 | FieldHelper* field_helper); |
| 338 | bool FieldNeedsSetter(FieldHelper* field_helper); |
| 339 | |
| 340 | void LoadLibraryImportsAndExports(Library* library, |
| 341 | const Class& toplevel_class); |
| 342 | |
| 343 | LibraryPtr LookupLibraryOrNull(NameIndex library); |
| 344 | LibraryPtr LookupLibrary(NameIndex library); |
| 345 | LibraryPtr LookupLibraryFromClass(NameIndex klass); |
| 346 | ClassPtr LookupClass(const Library& library, NameIndex klass); |
| 347 | |
| 348 | FunctionLayout::Kind GetFunctionType(ProcedureHelper::Kind procedure_kind); |
| 349 | |
| 350 | void EnsureExternalClassIsLookedUp() { |
| 351 | if (external_name_class_.IsNull()) { |
| 352 | ASSERT(external_name_field_.IsNull()); |
| 353 | const Library& internal_lib = |
| 354 | Library::Handle(zone_, dart::Library::InternalLibrary()); |
| 355 | external_name_class_ = internal_lib.LookupClass(Symbols::ExternalName()); |
| 356 | external_name_field_ = external_name_class_.LookupField(Symbols::name()); |
| 357 | } |
| 358 | ASSERT(!external_name_class_.IsNull()); |
| 359 | ASSERT(!external_name_field_.IsNull()); |
| 360 | ASSERT(external_name_class_.is_declaration_loaded()); |
| 361 | } |
| 362 | |
| 363 | void EnsurePragmaClassIsLookedUp() { |
| 364 | if (pragma_class_.IsNull()) { |
| 365 | const Library& core_lib = |
| 366 | Library::Handle(zone_, dart::Library::CoreLibrary()); |
| 367 | pragma_class_ = core_lib.LookupLocalClass(Symbols::Pragma()); |
| 368 | } |
| 369 | ASSERT(!pragma_class_.IsNull()); |
| 370 | ASSERT(pragma_class_.is_declaration_loaded()); |
| 371 | } |
| 372 | |
| 373 | void EnsurePotentialNatives() { |
| 374 | potential_natives_ = kernel_program_info_.potential_natives(); |
| 375 | if (potential_natives_.IsNull()) { |
| 376 | // To avoid too many grows in this array, we'll set it's initial size to |
| 377 | // something close to the actual number of potential native functions. |
| 378 | potential_natives_ = GrowableObjectArray::New(100, Heap::kNew); |
| 379 | kernel_program_info_.set_potential_natives(potential_natives_); |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | void EnsurePotentialPragmaFunctions() { |
| 384 | potential_pragma_functions_ = |
| 385 | translation_helper_.EnsurePotentialPragmaFunctions(); |
| 386 | } |
| 387 | |
| 388 | Program* program_; |
| 389 | |
| 390 | Thread* thread_; |
| 391 | Zone* zone_; |
| 392 | Isolate* isolate_; |
| 393 | Array& patch_classes_; |
| 394 | ActiveClass active_class_; |
| 395 | // This is the offset of the current library within |
| 396 | // the whole kernel program. |
| 397 | intptr_t library_kernel_offset_; |
| 398 | uint32_t kernel_binary_version_; |
| 399 | // This is the offset by which offsets, which are set relative |
| 400 | // to their library's kernel data, have to be corrected. |
| 401 | intptr_t correction_offset_; |
| 402 | bool loading_native_wrappers_library_; |
| 403 | |
| 404 | NameIndex skip_vmservice_library_; |
| 405 | |
| 406 | ExternalTypedData& library_kernel_data_; |
| 407 | KernelProgramInfo& kernel_program_info_; |
| 408 | BuildingTranslationHelper translation_helper_; |
| 409 | KernelReaderHelper helper_; |
| 410 | ConstantReader constant_reader_; |
| 411 | TypeTranslator type_translator_; |
| 412 | InferredTypeMetadataHelper inferred_type_metadata_helper_; |
| 413 | BytecodeMetadataHelper bytecode_metadata_helper_; |
| 414 | |
| 415 | Class& external_name_class_; |
| 416 | Field& external_name_field_; |
| 417 | GrowableObjectArray& potential_natives_; |
| 418 | GrowableObjectArray& potential_pragma_functions_; |
| 419 | |
| 420 | Class& pragma_class_; |
| 421 | |
| 422 | Smi& name_index_handle_; |
| 423 | |
| 424 | // We "re-use" the normal .dill file format for encoding compiled evaluation |
| 425 | // expressions from the debugger. This allows us to also reuse the normal |
| 426 | // a) kernel loader b) flow graph building code. The encoding is either one |
| 427 | // of the following two options: |
| 428 | // |
| 429 | // * Option a) The expression is evaluated inside an instance method call |
| 430 | // context: |
| 431 | // |
| 432 | // Program: |
| 433 | // |> library "evaluate:source" |
| 434 | // |> class "#DebugClass" |
| 435 | // |> procedure ":Eval" |
| 436 | // |
| 437 | // * Option b) The expression is evaluated outside an instance method call |
| 438 | // context: |
| 439 | // |
| 440 | // Program: |
| 441 | // |> library "evaluate:source" |
| 442 | // |> procedure ":Eval" |
| 443 | // |
| 444 | // See |
| 445 | // * pkg/front_end/lib/src/fasta/incremental_compiler.dart, |
| 446 | // compileExpression |
| 447 | // * pkg/front_end/lib/src/fasta/kernel/utils.dart, |
| 448 | // createExpressionEvaluationComponent |
| 449 | // |
| 450 | Library& expression_evaluation_library_; |
| 451 | |
| 452 | GrowableArray<const Function*> functions_; |
| 453 | GrowableArray<const Field*> fields_; |
| 454 | |
| 455 | friend class BuildingTranslationHelper; |
| 456 | |
| 457 | DISALLOW_COPY_AND_ASSIGN(KernelLoader); |
| 458 | }; |
| 459 | |
| 460 | FunctionPtr CreateFieldInitializerFunction(Thread* thread, |
| 461 | Zone* zone, |
| 462 | const Field& field); |
| 463 | |
| 464 | } // namespace kernel |
| 465 | } // namespace dart |
| 466 | |
| 467 | #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| 468 | #endif // RUNTIME_VM_KERNEL_LOADER_H_ |
| 469 | |