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
7namespace dart {
8namespace kernel {
9
10#define Z (zone_)
11#define H (translation_helper_)
12
13ConstantReader::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
22InstancePtr 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
47ObjectPtr 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
60InstancePtr 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
89bool 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
103InstancePtr 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