|  | // Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "vm/compiler/frontend/constant_reader.h" | 
|  |  | 
|  | namespace dart { | 
|  | namespace kernel { | 
|  |  | 
|  | #define Z (zone_) | 
|  | #define H (translation_helper_) | 
|  |  | 
|  | ConstantReader::ConstantReader(KernelReaderHelper* helper, | 
|  | ActiveClass* active_class) | 
|  | : helper_(helper), | 
|  | zone_(helper->zone_), | 
|  | translation_helper_(helper->translation_helper_), | 
|  | active_class_(active_class), | 
|  | script_(helper->script()), | 
|  | result_(Instance::Handle(zone_)) {} | 
|  |  | 
|  | InstancePtr ConstantReader::ReadConstantInitializer() { | 
|  | Tag tag = helper_->ReadTag();  // read tag. | 
|  | switch (tag) { | 
|  | case kSomething: | 
|  | return ReadConstantExpression(); | 
|  | default: | 
|  | H.ReportError(script_, TokenPosition::kNoSource, | 
|  | "Not a constant expression: unexpected kernel tag %s (%d)", | 
|  | Reader::TagName(tag), tag); | 
|  | } | 
|  | return result_.ptr(); | 
|  | } | 
|  |  | 
|  | InstancePtr ConstantReader::ReadConstantExpression() { | 
|  | Tag tag = helper_->ReadTag();  // read tag. | 
|  | switch (tag) { | 
|  | case kConstantExpression: | 
|  | helper_->ReadPosition(); | 
|  | helper_->SkipDartType(); | 
|  | result_ = ReadConstant(helper_->ReadUInt()); | 
|  | break; | 
|  | case kInvalidExpression: { | 
|  | helper_->ReadPosition();  // Skip position. | 
|  | const String& message = H.DartString(helper_->ReadStringReference()); | 
|  | // Invalid expression message has pointer to the source code, no need to | 
|  | // report it twice. | 
|  | H.ReportError(helper_->script(), TokenPosition::kNoSource, "%s", | 
|  | message.ToCString()); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | H.ReportError(script_, TokenPosition::kNoSource, | 
|  | "Not a constant expression: unexpected kernel tag %s (%d)", | 
|  | Reader::TagName(tag), tag); | 
|  | } | 
|  | return result_.ptr(); | 
|  | } | 
|  |  | 
|  | ObjectPtr ConstantReader::ReadAnnotations() { | 
|  | intptr_t list_length = helper_->ReadListLength();  // read list length. | 
|  | const auto& metadata_values = | 
|  | Array::Handle(Z, ImmutableArray::New(list_length, H.allocation_space())); | 
|  | Instance& value = Instance::Handle(Z); | 
|  | for (intptr_t i = 0; i < list_length; ++i) { | 
|  | // This will read the expression. | 
|  | value = ReadConstantExpression(); | 
|  | metadata_values.SetAt(i, value); | 
|  | } | 
|  | return H.Canonicalize(metadata_values); | 
|  | } | 
|  |  | 
|  | InstancePtr ConstantReader::ReadConstant(intptr_t constant_offset) { | 
|  | ASSERT(!H.constants().IsNull()); | 
|  | ASSERT(!H.constants_table().IsNull());  // raw bytes | 
|  |  | 
|  | // For kernel-level cache (in contrast with script-level caching), | 
|  | // we need to access the raw constants array inside the shared | 
|  | // KernelProgramInfo directly, so that all scripts will see the | 
|  | // results after new insertions. These accesses at kernel-level | 
|  | // must be locked since mutator and background compiler can | 
|  | // access the array at the same time. | 
|  | { | 
|  | SafepointMutexLocker ml( | 
|  | H.thread()->isolate_group()->kernel_constants_mutex()); | 
|  | KernelConstantsMap constant_map(H.info().constants()); | 
|  | result_ ^= constant_map.GetOrNull(constant_offset); | 
|  | ASSERT(constant_map.Release().ptr() == H.info().constants()); | 
|  | } | 
|  |  | 
|  | // On miss, evaluate, and insert value. | 
|  | if (result_.IsNull()) { | 
|  | LeaveCompilerScope cs(H.thread()); | 
|  | result_ = ReadConstantInternal(constant_offset); | 
|  | SafepointMutexLocker ml( | 
|  | H.thread()->isolate_group()->kernel_constants_mutex()); | 
|  | KernelConstantsMap constant_map(H.info().constants()); | 
|  | auto insert = constant_map.InsertNewOrGetValue(constant_offset, result_); | 
|  | ASSERT(insert == result_.ptr()); | 
|  | H.info().set_constants(constant_map.Release());  // update! | 
|  | } | 
|  | return result_.ptr(); | 
|  | } | 
|  |  | 
|  | bool ConstantReader::IsInstanceConstant(intptr_t constant_offset, | 
|  | const Class& clazz) { | 
|  | // Get reader directly into raw bytes of constant table. | 
|  | KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0); | 
|  | reader.ReadUInt();  // skip variable-sized int for adjusted constant offset | 
|  | reader.SetOffset(reader.ReaderOffset() + constant_offset); | 
|  | // Peek for an instance of the given clazz. | 
|  | if (reader.ReadByte() == kInstanceConstant) { | 
|  | const NameIndex index = reader.ReadCanonicalNameReference(); | 
|  | return H.LookupClassByKernelClass(index) == clazz.ptr(); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_offset) { | 
|  | // Get reader directly into raw bytes of constant table. | 
|  | bool null_safety = H.thread()->isolate_group()->null_safety(); | 
|  | KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0); | 
|  | reader.ReadUInt();  // skip variable-sized int for adjusted constant offset | 
|  | reader.SetOffset(reader.ReaderOffset() + constant_offset); | 
|  | // Construct constant from raw bytes. | 
|  | Instance& instance = Instance::Handle(Z); | 
|  | const intptr_t constant_tag = reader.ReadByte(); | 
|  | switch (constant_tag) { | 
|  | case kNullConstant: | 
|  | instance = Instance::null(); | 
|  | break; | 
|  | case kBoolConstant: | 
|  | instance = reader.ReadByte() == 1 ? Object::bool_true().ptr() | 
|  | : Object::bool_false().ptr(); | 
|  | break; | 
|  | case kIntConstant: { | 
|  | uint8_t payload = 0; | 
|  | Tag integer_tag = reader.ReadTag(&payload);  // read tag. | 
|  | switch (integer_tag) { | 
|  | case kBigIntLiteral: { | 
|  | const String& value = H.DartString(reader.ReadStringReference()); | 
|  | instance = Integer::New(value, Heap::kOld); | 
|  | break; | 
|  | } | 
|  | case kSpecializedIntLiteral: { | 
|  | const int64_t value = | 
|  | static_cast<int32_t>(payload) - SpecializedIntLiteralBias; | 
|  | instance = Integer::New(value, Heap::kOld); | 
|  | break; | 
|  | } | 
|  | case kNegativeIntLiteral: { | 
|  | const int64_t value = -static_cast<int64_t>(reader.ReadUInt()); | 
|  | instance = Integer::New(value, Heap::kOld); | 
|  | break; | 
|  | } | 
|  | case kPositiveIntLiteral: { | 
|  | const int64_t value = reader.ReadUInt(); | 
|  | instance = Integer::New(value, Heap::kOld); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | H.ReportError( | 
|  | script_, TokenPosition::kNoSource, | 
|  | "Cannot lazily read integer: unexpected kernel tag %s (%d)", | 
|  | Reader::TagName(integer_tag), integer_tag); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case kDoubleConstant: | 
|  | instance = Double::New(reader.ReadDouble(), Heap::kOld); | 
|  | break; | 
|  | case kStringConstant: | 
|  | instance = H.DartSymbolPlain(reader.ReadStringReference()).ptr(); | 
|  | break; | 
|  | case kSymbolConstant: { | 
|  | Library& library = Library::Handle(Z); | 
|  | library = Library::InternalLibrary(); | 
|  | const auto& symbol_class = | 
|  | Class::Handle(Z, library.LookupClass(Symbols::Symbol())); | 
|  | const auto& symbol_name_field = Field::Handle( | 
|  | Z, symbol_class.LookupInstanceFieldAllowPrivate(Symbols::_name())); | 
|  | ASSERT(!symbol_name_field.IsNull()); | 
|  | const NameIndex index = reader.ReadCanonicalNameReference(); | 
|  | if (index == -1) { | 
|  | library = Library::null(); | 
|  | } else { | 
|  | library = H.LookupLibraryByKernelLibrary(index); | 
|  | } | 
|  | const String& symbol = | 
|  | H.DartIdentifier(library, reader.ReadStringReference()); | 
|  | instance = Instance::New(symbol_class, Heap::kOld); | 
|  | instance.SetField(symbol_name_field, symbol); | 
|  | break; | 
|  | } | 
|  | case kListConstant: { | 
|  | const auto& corelib = Library::Handle(Z, Library::CoreLibrary()); | 
|  | const auto& list_class = | 
|  | Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List())); | 
|  | // Build type from the raw bytes (needs temporary translator). | 
|  | TypeTranslator type_translator( | 
|  | &reader, this, active_class_, true, | 
|  | active_class_->RequireConstCanonicalTypeErasure(null_safety)); | 
|  | auto& type_arguments = | 
|  | TypeArguments::Handle(Z, TypeArguments::New(1, Heap::kOld)); | 
|  | AbstractType& type = type_translator.BuildType(); | 
|  | type_arguments.SetTypeAt(0, type); | 
|  | // Instantiate class. | 
|  | type = Type::New(list_class, type_arguments); | 
|  | type = ClassFinalizer::FinalizeType(type, ClassFinalizer::kCanonicalize); | 
|  | type_arguments = type.arguments(); | 
|  | // Fill array with constant elements. | 
|  | const intptr_t length = reader.ReadUInt(); | 
|  | const Array& array = | 
|  | Array::Handle(Z, ImmutableArray::New(length, Heap::kOld)); | 
|  | array.SetTypeArguments(type_arguments); | 
|  | Instance& constant = Instance::Handle(Z); | 
|  | for (intptr_t j = 0; j < length; ++j) { | 
|  | // Recurse into lazily evaluating all "sub" constants | 
|  | // needed to evaluate the current constant. | 
|  | const intptr_t entry_offset = reader.ReadUInt(); | 
|  | ASSERT(entry_offset < constant_offset);  // DAG! | 
|  | constant = ReadConstant(entry_offset); | 
|  | array.SetAt(j, constant); | 
|  | } | 
|  | instance = array.ptr(); | 
|  | break; | 
|  | } | 
|  | case kInstanceConstant: { | 
|  | const NameIndex index = reader.ReadCanonicalNameReference(); | 
|  | const auto& klass = Class::Handle(Z, H.LookupClassByKernelClass(index)); | 
|  | if (!klass.is_declaration_loaded()) { | 
|  | FATAL1( | 
|  | "Trying to evaluate an instance constant whose references class " | 
|  | "%s is not loaded yet.", | 
|  | klass.ToCString()); | 
|  | } | 
|  | const auto& obj = | 
|  | Object::Handle(Z, klass.EnsureIsAllocateFinalized(H.thread())); | 
|  | ASSERT(obj.IsNull()); | 
|  | ASSERT(klass.is_enum_class() || klass.is_const()); | 
|  | instance = Instance::New(klass, Heap::kOld); | 
|  | // Build type from the raw bytes (needs temporary translator). | 
|  | TypeTranslator type_translator( | 
|  | &reader, this, active_class_, true, | 
|  | active_class_->RequireConstCanonicalTypeErasure(null_safety)); | 
|  | const intptr_t number_of_type_arguments = reader.ReadUInt(); | 
|  | if (klass.NumTypeArguments() > 0) { | 
|  | auto& type_arguments = TypeArguments::Handle( | 
|  | Z, TypeArguments::New(number_of_type_arguments, Heap::kOld)); | 
|  | for (intptr_t j = 0; j < number_of_type_arguments; ++j) { | 
|  | type_arguments.SetTypeAt(j, type_translator.BuildType()); | 
|  | } | 
|  | // Instantiate class. | 
|  | auto& type = AbstractType::Handle(Z, Type::New(klass, type_arguments)); | 
|  | type = | 
|  | ClassFinalizer::FinalizeType(type, ClassFinalizer::kCanonicalize); | 
|  | type_arguments = type.arguments(); | 
|  | instance.SetTypeArguments(type_arguments); | 
|  | } else { | 
|  | ASSERT(number_of_type_arguments == 0); | 
|  | } | 
|  | // Set the fields. | 
|  | const intptr_t number_of_fields = reader.ReadUInt(); | 
|  | Field& field = Field::Handle(Z); | 
|  | Instance& constant = Instance::Handle(Z); | 
|  | for (intptr_t j = 0; j < number_of_fields; ++j) { | 
|  | field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference()); | 
|  | // Recurse into lazily evaluating all "sub" constants | 
|  | // needed to evaluate the current constant. | 
|  | const intptr_t entry_offset = reader.ReadUInt(); | 
|  | ASSERT(entry_offset < constant_offset);  // DAG! | 
|  | constant = ReadConstant(entry_offset); | 
|  | instance.SetField(field, constant); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case kPartialInstantiationConstant: { | 
|  | // Recurse into lazily evaluating the "sub" constant | 
|  | // needed to evaluate the current constant. | 
|  | const intptr_t entry_offset = reader.ReadUInt(); | 
|  | ASSERT(entry_offset < constant_offset);  // DAG! | 
|  | const auto& constant = Instance::Handle(Z, ReadConstant(entry_offset)); | 
|  | ASSERT(!constant.IsNull()); | 
|  |  | 
|  | // Build type from the raw bytes (needs temporary translator). | 
|  | TypeTranslator type_translator( | 
|  | &reader, this, active_class_, true, | 
|  | active_class_->RequireConstCanonicalTypeErasure(null_safety)); | 
|  | const intptr_t number_of_type_arguments = reader.ReadUInt(); | 
|  | ASSERT(number_of_type_arguments > 0); | 
|  | auto& type_arguments = TypeArguments::Handle( | 
|  | Z, TypeArguments::New(number_of_type_arguments, Heap::kOld)); | 
|  | for (intptr_t j = 0; j < number_of_type_arguments; ++j) { | 
|  | type_arguments.SetTypeAt(j, type_translator.BuildType()); | 
|  | } | 
|  | type_arguments = type_arguments.Canonicalize(Thread::Current(), nullptr); | 
|  | // Make a copy of the old closure, and set delayed type arguments. | 
|  | Closure& closure = Closure::Handle(Z, Closure::RawCast(constant.ptr())); | 
|  | Function& function = Function::Handle(Z, closure.function()); | 
|  | const auto& type_arguments2 = | 
|  | TypeArguments::Handle(Z, closure.instantiator_type_arguments()); | 
|  | // The function type arguments are used for type parameters from enclosing | 
|  | // closures. Though inner closures cannot be constants. We should | 
|  | // therefore see `null here. | 
|  | ASSERT(closure.function_type_arguments() == TypeArguments::null()); | 
|  | Context& context = Context::Handle(Z, closure.context()); | 
|  | instance = Closure::New(type_arguments2, Object::null_type_arguments(), | 
|  | type_arguments, function, context, Heap::kOld); | 
|  | break; | 
|  | } | 
|  | case kTearOffConstant: { | 
|  | const NameIndex index = reader.ReadCanonicalNameReference(); | 
|  | Function& function = | 
|  | Function::Handle(Z, H.LookupStaticMethodByKernelProcedure(index)); | 
|  | function = function.ImplicitClosureFunction(); | 
|  | instance = function.ImplicitStaticClosure(); | 
|  | break; | 
|  | } | 
|  | case kTypeLiteralConstant: { | 
|  | // Build type from the raw bytes (needs temporary translator). | 
|  | // Const canonical type erasure is not applied to constant type literals. | 
|  | // However, CFE must ensure that constant type literals can be | 
|  | // canonicalized to an identical representant independently of the null | 
|  | // safety mode currently in use (sound or unsound) or migration state of | 
|  | // the declaring library (legacy or opted-in). | 
|  | TypeTranslator type_translator(&reader, this, active_class_, true); | 
|  | instance = type_translator.BuildType().ptr(); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | // Set literals (kSetConstant) are currently desugared in the frontend | 
|  | // and will not reach the VM. See http://dartbug.com/35124 for some | 
|  | // discussion. Map constants (kMapConstant ) are already lowered to | 
|  | // InstanceConstant or ListConstant. We should never see unevaluated | 
|  | // constants (kUnevaluatedConstant) in the constant table, they should | 
|  | // have been fully evaluated before we get them. | 
|  | H.ReportError(script_, TokenPosition::kNoSource, | 
|  | "Cannot lazily read constant: unexpected kernel tag (%" Pd | 
|  | ")", | 
|  | constant_tag); | 
|  | } | 
|  | return H.Canonicalize(instance); | 
|  | } | 
|  |  | 
|  | }  // namespace kernel | 
|  | }  // namespace dart |