| 1 | // Copyright (c) 2018, 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 | #include "vm/compiler/frontend/constant_reader.h" |
| 6 | |
| 7 | namespace dart { |
| 8 | namespace kernel { |
| 9 | |
| 10 | #define Z (zone_) |
| 11 | #define H (translation_helper_) |
| 12 | |
| 13 | ConstantReader::ConstantReader(KernelReaderHelper* helper, |
| 14 | ActiveClass* active_class) |
| 15 | : helper_(helper), |
| 16 | zone_(helper->zone_), |
| 17 | translation_helper_(helper->translation_helper_), |
| 18 | active_class_(active_class), |
| 19 | script_(helper->script()), |
| 20 | result_(Instance::Handle(zone_)) {} |
| 21 | |
| 22 | InstancePtr ConstantReader::ReadConstantExpression() { |
| 23 | Tag tag = helper_->ReadTag(); // read tag. |
| 24 | switch (tag) { |
| 25 | case kConstantExpression: |
| 26 | helper_->ReadPosition(); |
| 27 | helper_->SkipDartType(); |
| 28 | result_ = ReadConstant(helper_->ReadUInt()); |
| 29 | break; |
| 30 | case kInvalidExpression: { |
| 31 | helper_->ReadPosition(); // Skip position. |
| 32 | const String& message = H.DartString(helper_->ReadStringReference()); |
| 33 | // Invalid expression message has pointer to the source code, no need to |
| 34 | // report it twice. |
| 35 | H.ReportError(helper_->script(), TokenPosition::kNoSource, "%s" , |
| 36 | message.ToCString()); |
| 37 | break; |
| 38 | } |
| 39 | default: |
| 40 | H.ReportError(script_, TokenPosition::kNoSource, |
| 41 | "Not a constant expression: unexpected kernel tag %s (%d)" , |
| 42 | Reader::TagName(tag), tag); |
| 43 | } |
| 44 | return result_.raw(); |
| 45 | } |
| 46 | |
| 47 | ObjectPtr ConstantReader::ReadAnnotations() { |
| 48 | intptr_t list_length = helper_->ReadListLength(); // read list length. |
| 49 | const Array& metadata_values = |
| 50 | Array::Handle(Z, Array::New(list_length, H.allocation_space())); |
| 51 | Instance& value = Instance::Handle(Z); |
| 52 | for (intptr_t i = 0; i < list_length; ++i) { |
| 53 | // This will read the expression. |
| 54 | value = ReadConstantExpression(); |
| 55 | metadata_values.SetAt(i, value); |
| 56 | } |
| 57 | return metadata_values.raw(); |
| 58 | } |
| 59 | |
| 60 | InstancePtr ConstantReader::ReadConstant(intptr_t constant_offset) { |
| 61 | ASSERT(!H.constants().IsNull()); |
| 62 | ASSERT(!H.constants_table().IsNull()); // raw bytes |
| 63 | |
| 64 | // For kernel-level cache (in contrast with script-level caching), |
| 65 | // we need to access the raw constants array inside the shared |
| 66 | // KernelProgramInfo directly, so that all scripts will see the |
| 67 | // results after new insertions. These accesses at kernel-level |
| 68 | // must be locked since mutator and background compiler can |
| 69 | // access the array at the same time. |
| 70 | { |
| 71 | SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex()); |
| 72 | KernelConstantsMap constant_map(H.info().constants()); |
| 73 | result_ ^= constant_map.GetOrNull(constant_offset); |
| 74 | ASSERT(constant_map.Release().raw() == H.info().constants()); |
| 75 | } |
| 76 | |
| 77 | // On miss, evaluate, and insert value. |
| 78 | if (result_.IsNull()) { |
| 79 | result_ = ReadConstantInternal(constant_offset); |
| 80 | SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex()); |
| 81 | KernelConstantsMap constant_map(H.info().constants()); |
| 82 | auto insert = constant_map.InsertNewOrGetValue(constant_offset, result_); |
| 83 | ASSERT(insert == result_.raw()); |
| 84 | H.info().set_constants(constant_map.Release()); // update! |
| 85 | } |
| 86 | return result_.raw(); |
| 87 | } |
| 88 | |
| 89 | bool ConstantReader::IsInstanceConstant(intptr_t constant_offset, |
| 90 | const Class& clazz) { |
| 91 | // Get reader directly into raw bytes of constant table. |
| 92 | KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0); |
| 93 | reader.ReadUInt(); // skip variable-sized int for adjusted constant offset |
| 94 | reader.SetOffset(reader.ReaderOffset() + constant_offset); |
| 95 | // Peek for an instance of the given clazz. |
| 96 | if (reader.ReadByte() == kInstanceConstant) { |
| 97 | const NameIndex index = reader.ReadCanonicalNameReference(); |
| 98 | return H.LookupClassByKernelClass(index) == clazz.raw(); |
| 99 | } |
| 100 | return false; |
| 101 | } |
| 102 | |
| 103 | InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_offset) { |
| 104 | // Get reader directly into raw bytes of constant table. |
| 105 | bool null_safety = H.thread()->isolate()->null_safety(); |
| 106 | KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0); |
| 107 | reader.ReadUInt(); // skip variable-sized int for adjusted constant offset |
| 108 | reader.SetOffset(reader.ReaderOffset() + constant_offset); |
| 109 | // Construct constant from raw bytes. |
| 110 | Instance& instance = Instance::Handle(Z); |
| 111 | const intptr_t constant_tag = reader.ReadByte(); |
| 112 | switch (constant_tag) { |
| 113 | case kNullConstant: |
| 114 | instance = Instance::null(); |
| 115 | break; |
| 116 | case kBoolConstant: |
| 117 | instance = reader.ReadByte() == 1 ? Object::bool_true().raw() |
| 118 | : Object::bool_false().raw(); |
| 119 | break; |
| 120 | case kIntConstant: { |
| 121 | uint8_t payload = 0; |
| 122 | Tag integer_tag = reader.ReadTag(&payload); // read tag. |
| 123 | switch (integer_tag) { |
| 124 | case kBigIntLiteral: { |
| 125 | const String& value = H.DartString(reader.ReadStringReference()); |
| 126 | instance = Integer::New(value, Heap::kOld); |
| 127 | break; |
| 128 | } |
| 129 | case kSpecializedIntLiteral: { |
| 130 | const int64_t value = |
| 131 | static_cast<int32_t>(payload) - SpecializedIntLiteralBias; |
| 132 | instance = Integer::New(value, Heap::kOld); |
| 133 | break; |
| 134 | } |
| 135 | case kNegativeIntLiteral: { |
| 136 | const int64_t value = -static_cast<int64_t>(reader.ReadUInt()); |
| 137 | instance = Integer::New(value, Heap::kOld); |
| 138 | break; |
| 139 | } |
| 140 | case kPositiveIntLiteral: { |
| 141 | const int64_t value = reader.ReadUInt(); |
| 142 | instance = Integer::New(value, Heap::kOld); |
| 143 | break; |
| 144 | } |
| 145 | default: |
| 146 | H.ReportError( |
| 147 | script_, TokenPosition::kNoSource, |
| 148 | "Cannot lazily read integer: unexpected kernel tag %s (%d)" , |
| 149 | Reader::TagName(integer_tag), integer_tag); |
| 150 | } |
| 151 | break; |
| 152 | } |
| 153 | case kDoubleConstant: |
| 154 | instance = Double::New(reader.ReadDouble(), Heap::kOld); |
| 155 | break; |
| 156 | case kStringConstant: |
| 157 | instance = H.DartSymbolPlain(reader.ReadStringReference()).raw(); |
| 158 | break; |
| 159 | case kSymbolConstant: { |
| 160 | Library& library = Library::Handle(Z); |
| 161 | library = Library::InternalLibrary(); |
| 162 | const auto& symbol_class = |
| 163 | Class::Handle(Z, library.LookupClass(Symbols::Symbol())); |
| 164 | const auto& symbol_name_field = Field::Handle( |
| 165 | Z, symbol_class.LookupInstanceFieldAllowPrivate(Symbols::_name())); |
| 166 | ASSERT(!symbol_name_field.IsNull()); |
| 167 | const NameIndex index = reader.ReadCanonicalNameReference(); |
| 168 | if (index == -1) { |
| 169 | library = Library::null(); |
| 170 | } else { |
| 171 | library = H.LookupLibraryByKernelLibrary(index); |
| 172 | } |
| 173 | const String& symbol = |
| 174 | H.DartIdentifier(library, reader.ReadStringReference()); |
| 175 | instance = Instance::New(symbol_class, Heap::kOld); |
| 176 | instance.SetField(symbol_name_field, symbol); |
| 177 | break; |
| 178 | } |
| 179 | case kListConstant: { |
| 180 | const auto& corelib = Library::Handle(Z, Library::CoreLibrary()); |
| 181 | const auto& list_class = |
| 182 | Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List())); |
| 183 | // Build type from the raw bytes (needs temporary translator). |
| 184 | TypeTranslator type_translator( |
| 185 | &reader, this, active_class_, true, |
| 186 | active_class_->RequireLegacyErasure(null_safety)); |
| 187 | auto& type_arguments = |
| 188 | TypeArguments::Handle(Z, TypeArguments::New(1, Heap::kOld)); |
| 189 | AbstractType& type = type_translator.BuildType(); |
| 190 | type_arguments.SetTypeAt(0, type); |
| 191 | // Instantiate class. |
| 192 | type = Type::New(list_class, type_arguments, TokenPosition::kNoSource); |
| 193 | type = ClassFinalizer::FinalizeType(*active_class_->klass, type, |
| 194 | ClassFinalizer::kCanonicalize); |
| 195 | type_arguments = type.arguments(); |
| 196 | // Fill array with constant elements. |
| 197 | const intptr_t length = reader.ReadUInt(); |
| 198 | const Array& array = |
| 199 | Array::Handle(Z, ImmutableArray::New(length, Heap::kOld)); |
| 200 | array.SetTypeArguments(type_arguments); |
| 201 | Instance& constant = Instance::Handle(Z); |
| 202 | for (intptr_t j = 0; j < length; ++j) { |
| 203 | // Recurse into lazily evaluating all "sub" constants |
| 204 | // needed to evaluate the current constant. |
| 205 | const intptr_t entry_offset = reader.ReadUInt(); |
| 206 | ASSERT(entry_offset < constant_offset); // DAG! |
| 207 | constant = ReadConstant(entry_offset); |
| 208 | array.SetAt(j, constant); |
| 209 | } |
| 210 | instance = array.raw(); |
| 211 | break; |
| 212 | } |
| 213 | case kInstanceConstant: { |
| 214 | const NameIndex index = reader.ReadCanonicalNameReference(); |
| 215 | const auto& klass = Class::Handle(Z, H.LookupClassByKernelClass(index)); |
| 216 | if (!klass.is_declaration_loaded() && !klass.is_declared_in_bytecode()) { |
| 217 | FATAL1( |
| 218 | "Trying to evaluate an instance constant whose references class " |
| 219 | "%s is not loaded yet." , |
| 220 | klass.ToCString()); |
| 221 | } |
| 222 | const auto& obj = Object::Handle(Z, klass.EnsureIsFinalized(H.thread())); |
| 223 | ASSERT(obj.IsNull()); |
| 224 | ASSERT(klass.is_enum_class() || klass.is_const()); |
| 225 | instance = Instance::New(klass, Heap::kOld); |
| 226 | // Build type from the raw bytes (needs temporary translator). |
| 227 | TypeTranslator type_translator( |
| 228 | &reader, this, active_class_, true, |
| 229 | active_class_->RequireLegacyErasure(null_safety)); |
| 230 | const intptr_t number_of_type_arguments = reader.ReadUInt(); |
| 231 | if (klass.NumTypeArguments() > 0) { |
| 232 | auto& type_arguments = TypeArguments::Handle( |
| 233 | Z, TypeArguments::New(number_of_type_arguments, Heap::kOld)); |
| 234 | for (intptr_t j = 0; j < number_of_type_arguments; ++j) { |
| 235 | type_arguments.SetTypeAt(j, type_translator.BuildType()); |
| 236 | } |
| 237 | // Instantiate class. |
| 238 | auto& type = AbstractType::Handle( |
| 239 | Z, Type::New(klass, type_arguments, TokenPosition::kNoSource)); |
| 240 | type = ClassFinalizer::FinalizeType(*active_class_->klass, type, |
| 241 | ClassFinalizer::kCanonicalize); |
| 242 | type_arguments = type.arguments(); |
| 243 | instance.SetTypeArguments(type_arguments); |
| 244 | } else { |
| 245 | ASSERT(number_of_type_arguments == 0); |
| 246 | } |
| 247 | // Set the fields. |
| 248 | const intptr_t number_of_fields = reader.ReadUInt(); |
| 249 | Field& field = Field::Handle(Z); |
| 250 | Instance& constant = Instance::Handle(Z); |
| 251 | for (intptr_t j = 0; j < number_of_fields; ++j) { |
| 252 | field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference()); |
| 253 | // Recurse into lazily evaluating all "sub" constants |
| 254 | // needed to evaluate the current constant. |
| 255 | const intptr_t entry_offset = reader.ReadUInt(); |
| 256 | ASSERT(entry_offset < constant_offset); // DAG! |
| 257 | constant = ReadConstant(entry_offset); |
| 258 | instance.SetField(field, constant); |
| 259 | } |
| 260 | break; |
| 261 | } |
| 262 | case kPartialInstantiationConstant: { |
| 263 | // Recurse into lazily evaluating the "sub" constant |
| 264 | // needed to evaluate the current constant. |
| 265 | const intptr_t entry_offset = reader.ReadUInt(); |
| 266 | ASSERT(entry_offset < constant_offset); // DAG! |
| 267 | const auto& constant = Instance::Handle(Z, ReadConstant(entry_offset)); |
| 268 | ASSERT(!constant.IsNull()); |
| 269 | |
| 270 | // Build type from the raw bytes (needs temporary translator). |
| 271 | TypeTranslator type_translator( |
| 272 | &reader, this, active_class_, true, |
| 273 | active_class_->RequireLegacyErasure(null_safety)); |
| 274 | const intptr_t number_of_type_arguments = reader.ReadUInt(); |
| 275 | ASSERT(number_of_type_arguments > 0); |
| 276 | auto& type_arguments = TypeArguments::Handle( |
| 277 | Z, TypeArguments::New(number_of_type_arguments, Heap::kOld)); |
| 278 | for (intptr_t j = 0; j < number_of_type_arguments; ++j) { |
| 279 | type_arguments.SetTypeAt(j, type_translator.BuildType()); |
| 280 | } |
| 281 | type_arguments = type_arguments.Canonicalize(); |
| 282 | // Make a copy of the old closure, and set delayed type arguments. |
| 283 | Closure& closure = Closure::Handle(Z, Closure::RawCast(constant.raw())); |
| 284 | Function& function = Function::Handle(Z, closure.function()); |
| 285 | const auto& type_arguments2 = |
| 286 | TypeArguments::Handle(Z, closure.instantiator_type_arguments()); |
| 287 | // The function type arguments are used for type parameters from enclosing |
| 288 | // closures. Though inner closures cannot be constants. We should |
| 289 | // therefore see `null here. |
| 290 | ASSERT(closure.function_type_arguments() == TypeArguments::null()); |
| 291 | Context& context = Context::Handle(Z, closure.context()); |
| 292 | instance = Closure::New(type_arguments2, Object::null_type_arguments(), |
| 293 | type_arguments, function, context, Heap::kOld); |
| 294 | break; |
| 295 | } |
| 296 | case kTearOffConstant: { |
| 297 | const NameIndex index = reader.ReadCanonicalNameReference(); |
| 298 | Function& function = |
| 299 | Function::Handle(Z, H.LookupStaticMethodByKernelProcedure(index)); |
| 300 | function = function.ImplicitClosureFunction(); |
| 301 | instance = function.ImplicitStaticClosure(); |
| 302 | break; |
| 303 | } |
| 304 | case kTypeLiteralConstant: { |
| 305 | // Build type from the raw bytes (needs temporary translator). |
| 306 | // Legacy erasure is not applied to type literals. See issue #42262. |
| 307 | TypeTranslator type_translator(&reader, this, active_class_, true); |
| 308 | instance = type_translator.BuildType().raw(); |
| 309 | break; |
| 310 | } |
| 311 | default: |
| 312 | // Set literals (kSetConstant) are currently desugared in the frontend |
| 313 | // and will not reach the VM. See http://dartbug.com/35124 for some |
| 314 | // discussion. Map constants (kMapConstant ) are already lowered to |
| 315 | // InstanceConstant or ListConstant. We should never see unevaluated |
| 316 | // constants (kUnevaluatedConstant) in the constant table, they should |
| 317 | // have been fully evaluated before we get them. |
| 318 | H.ReportError(script_, TokenPosition::kNoSource, |
| 319 | "Cannot lazily read constant: unexpected kernel tag (%" Pd |
| 320 | ")" , |
| 321 | constant_tag); |
| 322 | } |
| 323 | return H.Canonicalize(instance); |
| 324 | } |
| 325 | |
| 326 | } // namespace kernel |
| 327 | } // namespace dart |
| 328 | |