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