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 | #include "vm/compiler/frontend/kernel_binary_flowgraph.h" |
6 | |
7 | #include "vm/compiler/ffi/callback.h" |
8 | #include "vm/compiler/frontend/bytecode_flow_graph_builder.h" |
9 | #include "vm/compiler/frontend/bytecode_reader.h" |
10 | #include "vm/compiler/frontend/flow_graph_builder.h" // For dart::FlowGraphBuilder::SimpleInstanceOfType. |
11 | #include "vm/compiler/frontend/prologue_builder.h" |
12 | #include "vm/compiler/jit/compiler.h" |
13 | #include "vm/object_store.h" |
14 | #include "vm/resolver.h" |
15 | #include "vm/stack_frame.h" |
16 | |
17 | namespace dart { |
18 | |
19 | DECLARE_FLAG(bool, enable_interpreter); |
20 | |
21 | namespace kernel { |
22 | |
23 | #define Z (zone_) |
24 | #define H (translation_helper_) |
25 | #define T (type_translator_) |
26 | #define I Isolate::Current() |
27 | #define B (flow_graph_builder_) |
28 | |
29 | Class& StreamingFlowGraphBuilder::GetSuperOrDie() { |
30 | Class& klass = Class::Handle(Z, parsed_function()->function().Owner()); |
31 | ASSERT(!klass.IsNull()); |
32 | klass = klass.SuperClass(); |
33 | ASSERT(!klass.IsNull()); |
34 | return klass; |
35 | } |
36 | |
37 | FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldInitializer() { |
38 | FieldHelper field_helper(this); |
39 | field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
40 | |
41 | Tag initializer_tag = ReadTag(); // read first part of initializer. |
42 | if (initializer_tag != kSomething) { |
43 | UNREACHABLE(); |
44 | } |
45 | |
46 | B->graph_entry_ = new (Z) GraphEntryInstr(*parsed_function(), B->osr_id_); |
47 | |
48 | auto normal_entry = B->BuildFunctionEntry(B->graph_entry_); |
49 | B->graph_entry_->set_normal_entry(normal_entry); |
50 | |
51 | Fragment body(normal_entry); |
52 | body += B->CheckStackOverflowInPrologue(field_helper.position_); |
53 | if (field_helper.IsConst()) { |
54 | // This will read the initializer. |
55 | body += Constant( |
56 | Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression())); |
57 | } else { |
58 | body += SetupCapturedParameters(parsed_function()->function()); |
59 | body += BuildExpression(); // read initializer. |
60 | } |
61 | body += Return(TokenPosition::kNoSource); |
62 | |
63 | PrologueInfo prologue_info(-1, -1); |
64 | if (B->IsCompiledForOsr()) { |
65 | B->graph_entry_->RelinkToOsrEntry(Z, B->last_used_block_id_ + 1); |
66 | } |
67 | return new (Z) FlowGraph(*parsed_function(), B->graph_entry_, |
68 | B->last_used_block_id_, prologue_info); |
69 | } |
70 | |
71 | void StreamingFlowGraphBuilder::EvaluateConstFieldValue(const Field& field) { |
72 | ASSERT(field.is_const() && field.IsUninitialized()); |
73 | |
74 | FieldHelper field_helper(this); |
75 | field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
76 | Tag initializer_tag = ReadTag(); // read first part of initializer. |
77 | |
78 | ASSERT(initializer_tag == kSomething); |
79 | |
80 | Instance& value = |
81 | Instance::Handle(Z, constant_reader_.ReadConstantExpression()); |
82 | field.SetStaticValue(value); |
83 | } |
84 | |
85 | void StreamingFlowGraphBuilder::SetupDefaultParameterValues() { |
86 | intptr_t optional_parameter_count = |
87 | parsed_function()->function().NumOptionalParameters(); |
88 | if (optional_parameter_count > 0) { |
89 | ZoneGrowableArray<const Instance*>* default_values = |
90 | new ZoneGrowableArray<const Instance*>(Z, optional_parameter_count); |
91 | |
92 | AlternativeReadingScope alt(&reader_); |
93 | FunctionNodeHelper function_node_helper(this); |
94 | function_node_helper.ReadUntilExcluding( |
95 | FunctionNodeHelper::kPositionalParameters); |
96 | |
97 | if (parsed_function()->function().HasOptionalNamedParameters()) { |
98 | // List of positional. |
99 | intptr_t list_length = ReadListLength(); // read list length. |
100 | for (intptr_t i = 0; i < list_length; ++i) { |
101 | SkipVariableDeclaration(); // read ith variable declaration. |
102 | } |
103 | |
104 | // List of named. |
105 | list_length = ReadListLength(); // read list length. |
106 | ASSERT(optional_parameter_count == list_length); |
107 | ASSERT(!parsed_function()->function().HasOptionalPositionalParameters()); |
108 | for (intptr_t i = 0; i < list_length; ++i) { |
109 | Instance* default_value; |
110 | |
111 | // Read ith variable declaration |
112 | VariableDeclarationHelper helper(this); |
113 | helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
114 | Tag tag = ReadTag(); // read (first part of) initializer. |
115 | if (tag == kSomething) { |
116 | // This will read the initializer. |
117 | default_value = &Instance::ZoneHandle( |
118 | Z, constant_reader_.ReadConstantExpression()); |
119 | } else { |
120 | default_value = &Instance::ZoneHandle(Z, Instance::null()); |
121 | } |
122 | default_values->Add(default_value); |
123 | } |
124 | } else { |
125 | // List of positional. |
126 | intptr_t list_length = ReadListLength(); // read list length. |
127 | ASSERT(list_length == function_node_helper.required_parameter_count_ + |
128 | optional_parameter_count); |
129 | ASSERT(parsed_function()->function().HasOptionalPositionalParameters()); |
130 | for (intptr_t i = 0; i < function_node_helper.required_parameter_count_; |
131 | ++i) { |
132 | SkipVariableDeclaration(); // read ith variable declaration. |
133 | } |
134 | for (intptr_t i = 0; i < optional_parameter_count; ++i) { |
135 | Instance* default_value; |
136 | |
137 | // Read ith variable declaration |
138 | VariableDeclarationHelper helper(this); |
139 | helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
140 | Tag tag = ReadTag(); // read (first part of) initializer. |
141 | if (tag == kSomething) { |
142 | // This will read the initializer. |
143 | default_value = &Instance::ZoneHandle( |
144 | Z, constant_reader_.ReadConstantExpression()); |
145 | } else { |
146 | default_value = &Instance::ZoneHandle(Z, Instance::null()); |
147 | } |
148 | default_values->Add(default_value); |
149 | } |
150 | |
151 | // List of named. |
152 | list_length = ReadListLength(); // read list length. |
153 | ASSERT(list_length == 0); |
154 | } |
155 | parsed_function()->set_default_parameter_values(default_values); |
156 | } |
157 | } |
158 | |
159 | Fragment StreamingFlowGraphBuilder::BuildFieldInitializer( |
160 | const Field& field, |
161 | bool only_for_side_effects) { |
162 | ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
163 | if (PeekTag() == kNullLiteral) { |
164 | SkipExpression(); // read past the null literal. |
165 | if (H.thread()->IsMutatorThread()) { |
166 | field.RecordStore(Object::null_object()); |
167 | } else { |
168 | ASSERT(field.is_nullable(/* silence_assert = */ true)); |
169 | } |
170 | return Fragment(); |
171 | } |
172 | |
173 | Fragment instructions; |
174 | if (!only_for_side_effects) { |
175 | instructions += LoadLocal(parsed_function()->receiver_var()); |
176 | } |
177 | // All closures created inside BuildExpression will have |
178 | // field.RawOwner() as its owner. |
179 | closure_owner_ = field.RawOwner(); |
180 | instructions += BuildExpression(); |
181 | closure_owner_ = Object::null(); |
182 | if (only_for_side_effects) { |
183 | instructions += Drop(); |
184 | } else { |
185 | instructions += flow_graph_builder_->StoreInstanceFieldGuarded( |
186 | field, StoreInstanceFieldInstr::Kind::kInitializing); |
187 | } |
188 | return instructions; |
189 | } |
190 | |
191 | Fragment StreamingFlowGraphBuilder::BuildLateFieldInitializer( |
192 | const Field& field, |
193 | bool has_initializer) { |
194 | if (has_initializer && PeekTag() == kNullLiteral) { |
195 | SkipExpression(); // read past the null literal. |
196 | if (H.thread()->IsMutatorThread()) { |
197 | field.RecordStore(Object::null_object()); |
198 | } else { |
199 | ASSERT(field.is_nullable(/* silence_assert = */ true)); |
200 | } |
201 | return Fragment(); |
202 | } |
203 | |
204 | Fragment instructions; |
205 | instructions += LoadLocal(parsed_function()->receiver_var()); |
206 | instructions += flow_graph_builder_->Constant(Object::sentinel()); |
207 | instructions += flow_graph_builder_->StoreInstanceField( |
208 | field, StoreInstanceFieldInstr::Kind::kInitializing); |
209 | return instructions; |
210 | } |
211 | |
212 | Fragment StreamingFlowGraphBuilder::BuildInitializers( |
213 | const Class& parent_class) { |
214 | ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
215 | Fragment instructions; |
216 | |
217 | // Start by getting the position of the constructors initializer. |
218 | intptr_t initializers_offset = -1; |
219 | { |
220 | AlternativeReadingScope alt(&reader_); |
221 | SkipFunctionNode(); // read constructors function node. |
222 | initializers_offset = ReaderOffset(); |
223 | } |
224 | |
225 | bool is_redirecting_constructor = false; |
226 | |
227 | // Field which will be initialized by the initializer with the given index. |
228 | GrowableArray<const Field*> initializer_fields(5); |
229 | |
230 | // Check if this is a redirecting constructor and collect all fields which |
231 | // will be initialized by the constructor initializer list. |
232 | { |
233 | AlternativeReadingScope alt(&reader_, initializers_offset); |
234 | |
235 | const intptr_t list_length = |
236 | ReadListLength(); // read initializers list length. |
237 | initializer_fields.EnsureLength(list_length, nullptr); |
238 | |
239 | bool has_field_initializers = false; |
240 | for (intptr_t i = 0; i < list_length; ++i) { |
241 | if (PeekTag() == kRedirectingInitializer) { |
242 | is_redirecting_constructor = true; |
243 | } else if (PeekTag() == kFieldInitializer) { |
244 | has_field_initializers = true; |
245 | ReadTag(); |
246 | ReadBool(); |
247 | const NameIndex field_name = ReadCanonicalNameReference(); |
248 | const Field& field = |
249 | Field::Handle(Z, H.LookupFieldByKernelField(field_name)); |
250 | initializer_fields[i] = &field; |
251 | SkipExpression(); |
252 | continue; |
253 | } |
254 | SkipInitializer(); |
255 | } |
256 | ASSERT(!is_redirecting_constructor || !has_field_initializers); |
257 | } |
258 | |
259 | // These come from: |
260 | // |
261 | // class A { |
262 | // var x = (expr); |
263 | // } |
264 | // |
265 | // We don't want to do that when this is a Redirecting Constructors though |
266 | // (i.e. has a single initializer being of type kRedirectingInitializer). |
267 | if (!is_redirecting_constructor) { |
268 | // Sort list of fields (represented as their kernel offsets) which will |
269 | // be initialized by the constructor initializer list. We will not emit |
270 | // StoreInstanceField instructions for those initializers though we will |
271 | // still evaluate initialization expression for its side effects. |
272 | GrowableArray<intptr_t> constructor_initialized_field_offsets( |
273 | initializer_fields.length()); |
274 | for (auto field : initializer_fields) { |
275 | if (field != nullptr) { |
276 | constructor_initialized_field_offsets.Add(field->kernel_offset()); |
277 | } |
278 | } |
279 | |
280 | constructor_initialized_field_offsets.Sort( |
281 | [](const intptr_t* a, const intptr_t* b) { |
282 | return static_cast<int>(*a) - static_cast<int>(*b); |
283 | }); |
284 | constructor_initialized_field_offsets.Add(-1); |
285 | |
286 | ExternalTypedData& kernel_data = ExternalTypedData::Handle(Z); |
287 | Array& class_fields = Array::Handle(Z, parent_class.fields()); |
288 | Field& class_field = Field::Handle(Z); |
289 | intptr_t next_constructor_initialized_field_index = 0; |
290 | for (intptr_t i = 0; i < class_fields.Length(); ++i) { |
291 | class_field ^= class_fields.At(i); |
292 | if (!class_field.is_static()) { |
293 | const intptr_t field_offset = class_field.kernel_offset(); |
294 | |
295 | // Check if this field will be initialized by the constructor |
296 | // initializer list. |
297 | // Note that both class_fields and the list of initialized fields |
298 | // are sorted by their kernel offset (by construction) - |
299 | // so we don't need to perform the search. |
300 | bool is_constructor_initialized = false; |
301 | const intptr_t constructor_initialized_field_offset = |
302 | constructor_initialized_field_offsets |
303 | [next_constructor_initialized_field_index]; |
304 | if (constructor_initialized_field_offset == field_offset) { |
305 | next_constructor_initialized_field_index++; |
306 | is_constructor_initialized = true; |
307 | } |
308 | |
309 | kernel_data = class_field.KernelData(); |
310 | ASSERT(!kernel_data.IsNull()); |
311 | AlternativeReadingScopeWithNewData alt(&reader_, &kernel_data, |
312 | field_offset); |
313 | FieldHelper field_helper(this); |
314 | field_helper.ReadUntilExcluding(FieldHelper::kInitializer); |
315 | const Tag initializer_tag = ReadTag(); |
316 | if (class_field.is_late()) { |
317 | if (!is_constructor_initialized) { |
318 | instructions += BuildLateFieldInitializer( |
319 | Field::ZoneHandle(Z, class_field.raw()), |
320 | initializer_tag == kSomething); |
321 | } |
322 | } else if (initializer_tag == kSomething) { |
323 | EnterScope(field_offset); |
324 | // If this field is initialized in constructor then we can ignore the |
325 | // value produced by the field initializer. However we still need to |
326 | // execute it for its side effects. |
327 | instructions += BuildFieldInitializer( |
328 | Field::ZoneHandle(Z, class_field.raw()), |
329 | /*only_for_side_effects=*/is_constructor_initialized); |
330 | ExitScope(field_offset); |
331 | } |
332 | } |
333 | } |
334 | } |
335 | |
336 | // These to come from: |
337 | // class A { |
338 | // var x; |
339 | // var y; |
340 | // A(this.x) : super(expr), y = (expr); |
341 | // } |
342 | { |
343 | AlternativeReadingScope alt(&reader_, initializers_offset); |
344 | intptr_t list_length = ReadListLength(); // read initializers list length. |
345 | for (intptr_t i = 0; i < list_length; ++i) { |
346 | Tag tag = ReadTag(); |
347 | bool isSynthetic = ReadBool(); // read isSynthetic flag. |
348 | switch (tag) { |
349 | case kInvalidInitializer: |
350 | UNIMPLEMENTED(); |
351 | return Fragment(); |
352 | case kFieldInitializer: { |
353 | ReadCanonicalNameReference(); |
354 | instructions += BuildFieldInitializer( |
355 | Field::ZoneHandle(Z, initializer_fields[i]->raw()), |
356 | /*only_for_size_effects=*/false); |
357 | break; |
358 | } |
359 | case kAssertInitializer: { |
360 | instructions += BuildStatement(); |
361 | break; |
362 | } |
363 | case kSuperInitializer: { |
364 | TokenPosition position = ReadPosition(); // read position. |
365 | NameIndex canonical_target = |
366 | ReadCanonicalNameReference(); // read target_reference. |
367 | |
368 | instructions += LoadLocal(parsed_function()->receiver_var()); |
369 | |
370 | // TODO(jensj): ASSERT(init->arguments()->types().length() == 0); |
371 | Array& argument_names = Array::ZoneHandle(Z); |
372 | intptr_t argument_count; |
373 | instructions += BuildArguments( |
374 | &argument_names, &argument_count, |
375 | /* positional_parameter_count = */ NULL); // read arguments. |
376 | argument_count += 1; |
377 | |
378 | Class& parent_klass = GetSuperOrDie(); |
379 | |
380 | const Function& target = Function::ZoneHandle( |
381 | Z, H.LookupConstructorByKernelConstructor( |
382 | parent_klass, H.CanonicalNameString(canonical_target))); |
383 | instructions += StaticCall( |
384 | isSynthetic ? TokenPosition::kNoSource : position, target, |
385 | argument_count, argument_names, ICData::kStatic); |
386 | instructions += Drop(); |
387 | break; |
388 | } |
389 | case kRedirectingInitializer: { |
390 | TokenPosition position = ReadPosition(); // read position. |
391 | NameIndex canonical_target = |
392 | ReadCanonicalNameReference(); // read target_reference. |
393 | |
394 | instructions += LoadLocal(parsed_function()->receiver_var()); |
395 | |
396 | // TODO(jensj): ASSERT(init->arguments()->types().length() == 0); |
397 | Array& argument_names = Array::ZoneHandle(Z); |
398 | intptr_t argument_count; |
399 | instructions += BuildArguments( |
400 | &argument_names, &argument_count, |
401 | /* positional_parameter_count = */ NULL); // read arguments. |
402 | argument_count += 1; |
403 | |
404 | const Function& target = Function::ZoneHandle( |
405 | Z, H.LookupConstructorByKernelConstructor(canonical_target)); |
406 | instructions += StaticCall( |
407 | isSynthetic ? TokenPosition::kNoSource : position, target, |
408 | argument_count, argument_names, ICData::kStatic); |
409 | instructions += Drop(); |
410 | break; |
411 | } |
412 | case kLocalInitializer: { |
413 | // The other initializers following this one might read the variable. |
414 | // This is used e.g. for evaluating the arguments to a super call |
415 | // first, run normal field initializers next and then make the actual |
416 | // super call: |
417 | // |
418 | // The frontend converts |
419 | // |
420 | // class A { |
421 | // var x; |
422 | // A(a, b) : super(a + b), x = 2*b {} |
423 | // } |
424 | // |
425 | // to |
426 | // |
427 | // class A { |
428 | // var x; |
429 | // A(a, b) : tmp = a + b, x = 2*b, super(tmp) {} |
430 | // } |
431 | // |
432 | // (This is strictly speaking not what one should do in terms of the |
433 | // specification but that is how it is currently implemented.) |
434 | LocalVariable* variable = |
435 | LookupVariable(ReaderOffset() + data_program_offset_); |
436 | |
437 | // Variable declaration |
438 | VariableDeclarationHelper helper(this); |
439 | helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer); |
440 | ASSERT(!helper.IsConst()); |
441 | Tag tag = ReadTag(); // read (first part of) initializer. |
442 | if (tag != kSomething) { |
443 | UNREACHABLE(); |
444 | } |
445 | |
446 | instructions += BuildExpression(); // read initializer. |
447 | instructions += StoreLocal(TokenPosition::kNoSource, variable); |
448 | instructions += Drop(); |
449 | break; |
450 | } |
451 | default: |
452 | ReportUnexpectedTag("initializer" , tag); |
453 | UNREACHABLE(); |
454 | } |
455 | } |
456 | } |
457 | return instructions; |
458 | } |
459 | |
460 | void StreamingFlowGraphBuilder::ReadDefaultFunctionTypeArguments( |
461 | const Function& function) { |
462 | if (!function.IsGeneric()) { |
463 | return; |
464 | } |
465 | AlternativeReadingScope alt(&reader_); |
466 | FunctionNodeHelper function_node_helper(this); |
467 | function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
468 | intptr_t num_type_params = ReadListLength(); |
469 | ASSERT(num_type_params == function.NumTypeParameters()); |
470 | TypeArguments& default_types = |
471 | TypeArguments::Handle(Z, TypeArguments::New(num_type_params)); |
472 | for (intptr_t i = 0; i < num_type_params; ++i) { |
473 | TypeParameterHelper helper(this); |
474 | helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType); |
475 | if (ReadTag() == kSomething) { |
476 | default_types.SetTypeAt(i, T.BuildType()); |
477 | } else { |
478 | default_types.SetTypeAt(i, Object::dynamic_type()); |
479 | } |
480 | helper.Finish(); |
481 | } |
482 | default_types = default_types.Canonicalize(); |
483 | parsed_function()->SetDefaultFunctionTypeArguments(default_types); |
484 | } |
485 | |
486 | Fragment StreamingFlowGraphBuilder::DebugStepCheckInPrologue( |
487 | const Function& dart_function, |
488 | TokenPosition position) { |
489 | if (!NeedsDebugStepCheck(dart_function, position)) { |
490 | return {}; |
491 | } |
492 | |
493 | // Place this check at the last parameter to ensure parameters |
494 | // are in scope in the debugger at method entry. |
495 | const int parameter_count = dart_function.NumParameters(); |
496 | TokenPosition check_pos = TokenPosition::kNoSource; |
497 | if (parameter_count > 0) { |
498 | const LocalVariable& parameter = |
499 | *parsed_function()->ParameterVariable(parameter_count - 1); |
500 | check_pos = parameter.token_pos(); |
501 | } |
502 | if (!check_pos.IsDebugPause()) { |
503 | // No parameters or synthetic parameters. |
504 | check_pos = position; |
505 | ASSERT(check_pos.IsDebugPause()); |
506 | } |
507 | |
508 | return DebugStepCheck(check_pos); |
509 | } |
510 | |
511 | Fragment StreamingFlowGraphBuilder::SetAsyncStackTrace( |
512 | const Function& dart_function) { |
513 | if (!FLAG_causal_async_stacks || |
514 | !(dart_function.IsAsyncClosure() || dart_function.IsAsyncGenClosure())) { |
515 | return {}; |
516 | } |
517 | |
518 | // The code we are building will be executed right after we enter |
519 | // the function and before any nested contexts are allocated. |
520 | ASSERT(B->context_depth_ == |
521 | scopes()->yield_jump_variable->owner()->context_level()); |
522 | |
523 | Fragment instructions; |
524 | LocalScope* scope = parsed_function()->scope(); |
525 | |
526 | const Function& target = Function::ZoneHandle( |
527 | Z, I->object_store()->async_set_thread_stack_trace()); |
528 | ASSERT(!target.IsNull()); |
529 | |
530 | // Fetch and load :async_stack_trace |
531 | LocalVariable* async_stack_trace_var = |
532 | scope->LookupVariable(Symbols::AsyncStackTraceVar(), false); |
533 | ASSERT((async_stack_trace_var != NULL) && |
534 | async_stack_trace_var->is_captured()); |
535 | |
536 | Fragment code; |
537 | code += LoadLocal(async_stack_trace_var); |
538 | // Call _asyncSetThreadStackTrace |
539 | code += StaticCall(TokenPosition::kNoSource, target, |
540 | /* argument_count = */ 1, ICData::kStatic); |
541 | code += Drop(); |
542 | return code; |
543 | } |
544 | |
545 | Fragment StreamingFlowGraphBuilder::TypeArgumentsHandling( |
546 | const Function& dart_function) { |
547 | Fragment prologue = B->BuildDefaultTypeHandling(dart_function); |
548 | |
549 | if (dart_function.IsClosureFunction() && |
550 | dart_function.NumParentTypeParameters() > 0) { |
551 | LocalVariable* closure = parsed_function()->ParameterVariable(0); |
552 | |
553 | // Function with yield points can not be generic itself but the outer |
554 | // function can be. |
555 | ASSERT(yield_continuations().is_empty() || !dart_function.IsGeneric()); |
556 | |
557 | LocalVariable* fn_type_args = parsed_function()->function_type_arguments(); |
558 | ASSERT(fn_type_args != NULL && closure != NULL); |
559 | |
560 | if (dart_function.IsGeneric()) { |
561 | prologue += LoadLocal(fn_type_args); |
562 | |
563 | prologue += LoadLocal(closure); |
564 | prologue += LoadNativeField(Slot::Closure_function_type_arguments()); |
565 | |
566 | prologue += IntConstant(dart_function.NumParentTypeParameters()); |
567 | |
568 | prologue += IntConstant(dart_function.NumTypeParameters() + |
569 | dart_function.NumParentTypeParameters()); |
570 | |
571 | const Library& dart_internal = |
572 | Library::Handle(Z, Library::InternalLibrary()); |
573 | const Function& prepend_function = |
574 | Function::ZoneHandle(Z, dart_internal.LookupFunctionAllowPrivate( |
575 | Symbols::PrependTypeArguments())); |
576 | ASSERT(!prepend_function.IsNull()); |
577 | |
578 | prologue += StaticCall(TokenPosition::kNoSource, prepend_function, 4, |
579 | ICData::kStatic); |
580 | prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args); |
581 | prologue += Drop(); |
582 | } else { |
583 | prologue += LoadLocal(closure); |
584 | prologue += LoadNativeField(Slot::Closure_function_type_arguments()); |
585 | prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args); |
586 | prologue += Drop(); |
587 | } |
588 | } |
589 | |
590 | return prologue; |
591 | } |
592 | |
593 | Fragment StreamingFlowGraphBuilder::CompleteBodyWithYieldContinuations( |
594 | Fragment body) { |
595 | // The code we are building will be executed right after we enter |
596 | // the function and before any nested contexts are allocated. |
597 | // Reset current context_depth_ to match this. |
598 | const intptr_t current_context_depth = B->context_depth_; |
599 | B->context_depth_ = scopes()->yield_jump_variable->owner()->context_level(); |
600 | |
601 | // Prepend an entry corresponding to normal entry to the function. |
602 | yield_continuations().InsertAt( |
603 | 0, YieldContinuation(new (Z) DropTempsInstr(0, NULL), kInvalidTryIndex)); |
604 | yield_continuations()[0].entry->LinkTo(body.entry); |
605 | |
606 | // Load :await_jump_var into a temporary. |
607 | Fragment dispatch; |
608 | dispatch += LoadLocal(scopes()->yield_jump_variable); |
609 | dispatch += StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable); |
610 | dispatch += Drop(); |
611 | |
612 | const intptr_t continuation_count = yield_continuations().length(); |
613 | |
614 | IndirectGotoInstr* indirect_goto; |
615 | if (FLAG_async_igoto_threshold >= 0 && |
616 | continuation_count >= FLAG_async_igoto_threshold) { |
617 | const auto& offsets = TypedData::ZoneHandle( |
618 | Z, TypedData::New(kTypedDataInt32ArrayCid, continuation_count, |
619 | Heap::kOld)); |
620 | |
621 | dispatch += Constant(offsets); |
622 | dispatch += LoadLocal(scopes()->switch_variable); |
623 | |
624 | // Ideally this would just be LoadIndexedTypedData(kTypedDataInt32ArrayCid), |
625 | // but that doesn't work in unoptimised code. |
626 | // The optimiser will turn this into that in any case. |
627 | dispatch += InstanceCall(TokenPosition::kNoSource, Symbols::IndexToken(), |
628 | Token::kINDEX, /*argument_count=*/2); |
629 | |
630 | Value* offset_from_start = Pop(); |
631 | |
632 | indirect_goto = new (Z) IndirectGotoInstr(&offsets, offset_from_start); |
633 | dispatch <<= indirect_goto; |
634 | |
635 | for (intptr_t i = 0; i < continuation_count; i++) { |
636 | if (i >= 1) { |
637 | Fragment resumption; |
638 | // Every continuation after the first is not a normal entry but a |
639 | // resumption. |
640 | // Restore :current_context_var from :await_ctx_var. |
641 | // Note: after this point context_depth_ does not match current context |
642 | // depth so we should not access any local variables anymore. |
643 | resumption += LoadLocal(scopes()->yield_context_variable); |
644 | resumption += StoreLocal(TokenPosition::kNoSource, |
645 | parsed_function()->current_context_var()); |
646 | resumption += Drop(); |
647 | |
648 | Instruction* next = yield_continuations()[i].entry->next(); |
649 | yield_continuations()[i].entry->LinkTo(resumption.entry); |
650 | resumption <<= next; |
651 | } |
652 | |
653 | IndirectEntryInstr* indirect_entry = B->BuildIndirectEntry( |
654 | /*indirect_id=*/i, yield_continuations()[i].try_index); |
655 | indirect_entry->LinkTo(yield_continuations()[i].entry->next()); |
656 | |
657 | TargetEntryInstr* target = B->BuildTargetEntry(); |
658 | Fragment(target) + Goto(indirect_entry); |
659 | |
660 | indirect_goto->AddSuccessor(target); |
661 | } |
662 | } else { |
663 | BlockEntryInstr* block = nullptr; |
664 | for (intptr_t i = 0; i < continuation_count; i++) { |
665 | if (i == 1) { |
666 | // This is not a normal entry but a resumption. Restore |
667 | // :current_context_var from :await_ctx_var. |
668 | // Note: after this point context_depth_ does not match current context |
669 | // depth so we should not access any local variables anymore. |
670 | dispatch += LoadLocal(scopes()->yield_context_variable); |
671 | dispatch += StoreLocal(TokenPosition::kNoSource, |
672 | parsed_function()->current_context_var()); |
673 | dispatch += Drop(); |
674 | } |
675 | if (i == (continuation_count - 1)) { |
676 | // We reached the last possibility, no need to build more ifs. |
677 | // Continue to the last continuation. |
678 | // Note: continuations start with nop DropTemps instruction |
679 | // which acts like an anchor, so we need to skip it. |
680 | block->set_try_index(yield_continuations()[i].try_index); |
681 | dispatch <<= yield_continuations()[i].entry->next(); |
682 | break; |
683 | } |
684 | |
685 | // Build comparison: |
686 | // |
687 | // if (:await_jump_var == i) { |
688 | // -> yield_continuations()[i] |
689 | // } else ... |
690 | // |
691 | TargetEntryInstr* then; |
692 | TargetEntryInstr* otherwise; |
693 | dispatch += LoadLocal(scopes()->switch_variable); |
694 | dispatch += IntConstant(i); |
695 | dispatch += B->BranchIfStrictEqual(&then, &otherwise); |
696 | |
697 | // True branch is linked to appropriate continuation point. |
698 | // Note: continuations start with nop DropTemps instruction |
699 | // which acts like an anchor, so we need to skip it. |
700 | then->LinkTo(yield_continuations()[i].entry->next()); |
701 | then->set_try_index(yield_continuations()[i].try_index); |
702 | // False branch will contain the next comparison. |
703 | dispatch = Fragment(dispatch.entry, otherwise); |
704 | block = otherwise; |
705 | } |
706 | } |
707 | B->context_depth_ = current_context_depth; |
708 | return dispatch; |
709 | } |
710 | |
711 | Fragment StreamingFlowGraphBuilder::CheckStackOverflowInPrologue( |
712 | const Function& dart_function) { |
713 | if (dart_function.is_native()) return {}; |
714 | return B->CheckStackOverflowInPrologue(dart_function.token_pos()); |
715 | } |
716 | |
717 | Fragment StreamingFlowGraphBuilder::SetupCapturedParameters( |
718 | const Function& dart_function) { |
719 | Fragment body; |
720 | const LocalScope* scope = parsed_function()->scope(); |
721 | if (scope->num_context_variables() > 0) { |
722 | body += flow_graph_builder_->PushContext(scope); |
723 | LocalVariable* context = MakeTemporary(); |
724 | |
725 | // Copy captured parameters from the stack into the context. |
726 | LocalScope* scope = parsed_function()->scope(); |
727 | intptr_t parameter_count = dart_function.NumParameters(); |
728 | const ParsedFunction& pf = *flow_graph_builder_->parsed_function_; |
729 | const Function& function = pf.function(); |
730 | |
731 | for (intptr_t i = 0; i < parameter_count; ++i) { |
732 | LocalVariable* variable = pf.ParameterVariable(i); |
733 | if (variable->is_captured()) { |
734 | LocalVariable& raw_parameter = *pf.RawParameterVariable(i); |
735 | ASSERT((function.HasOptionalParameters() && |
736 | raw_parameter.owner() == scope) || |
737 | (!function.HasOptionalParameters() && |
738 | raw_parameter.owner() == NULL)); |
739 | ASSERT(!raw_parameter.is_captured()); |
740 | |
741 | // Copy the parameter from the stack to the context. |
742 | body += LoadLocal(context); |
743 | body += LoadLocal(&raw_parameter); |
744 | body += flow_graph_builder_->StoreInstanceField( |
745 | TokenPosition::kNoSource, |
746 | Slot::GetContextVariableSlotFor(thread(), *variable), |
747 | StoreInstanceFieldInstr::Kind::kInitializing); |
748 | } |
749 | } |
750 | body += Drop(); // The context. |
751 | } |
752 | return body; |
753 | } |
754 | |
755 | // If we run in checked mode or strong mode, we have to check the type of the |
756 | // passed arguments. |
757 | // |
758 | // TODO(#34162): If we're building an extra entry-point to skip |
759 | // type checks, we should substitute Redefinition nodes for the AssertAssignable |
760 | // instructions to ensure that the argument types are known. |
761 | void StreamingFlowGraphBuilder::CheckArgumentTypesAsNecessary( |
762 | const Function& dart_function, |
763 | intptr_t type_parameters_offset, |
764 | Fragment* explicit_checks, |
765 | Fragment* implicit_checks, |
766 | Fragment* implicit_redefinitions) { |
767 | if (!dart_function.NeedsArgumentTypeChecks()) return; |
768 | |
769 | // Check if parent function was annotated with no-dynamic-invocations. |
770 | const ProcedureAttributesMetadata attrs = |
771 | procedure_attributes_metadata_helper_.GetProcedureAttributes( |
772 | dart_function.kernel_offset()); |
773 | |
774 | AlternativeReadingScope _(&reader_); |
775 | SetOffset(type_parameters_offset); |
776 | B->BuildArgumentTypeChecks( |
777 | MethodCanSkipTypeChecksForNonCovariantArguments(dart_function, attrs) |
778 | ? TypeChecksToBuild::kCheckCovariantTypeParameterBounds |
779 | : TypeChecksToBuild::kCheckAllTypeParameterBounds, |
780 | explicit_checks, implicit_checks, implicit_redefinitions); |
781 | } |
782 | |
783 | Fragment StreamingFlowGraphBuilder::ShortcutForUserDefinedEquals( |
784 | const Function& dart_function, |
785 | LocalVariable* first_parameter) { |
786 | // The specification defines the result of `a == b` to be: |
787 | // |
788 | // a) if either side is `null` then the result is `identical(a, b)`. |
789 | // b) else the result is `a.operator==(b)` |
790 | // |
791 | // For user-defined implementations of `operator==` we need therefore |
792 | // implement the handling of a). |
793 | // |
794 | // The default `operator==` implementation in `Object` is implemented in terms |
795 | // of identical (which we assume here!) which means that case a) is actually |
796 | // included in b). So we just use the normal implementation in the body. |
797 | Fragment body; |
798 | if ((dart_function.NumParameters() == 2) && |
799 | (dart_function.name() == Symbols::EqualOperator().raw()) && |
800 | (dart_function.Owner() != I->object_store()->object_class())) { |
801 | TargetEntryInstr* null_entry; |
802 | TargetEntryInstr* non_null_entry; |
803 | |
804 | body += LoadLocal(first_parameter); |
805 | body += BranchIfNull(&null_entry, &non_null_entry); |
806 | |
807 | // The argument was `null` and the receiver is not the null class (we only |
808 | // go into this branch for user-defined == operators) so we can return |
809 | // false. |
810 | Fragment null_fragment(null_entry); |
811 | null_fragment += Constant(Bool::False()); |
812 | null_fragment += Return(dart_function.end_token_pos()); |
813 | |
814 | body = Fragment(body.entry, non_null_entry); |
815 | } |
816 | return body; |
817 | } |
818 | |
819 | Fragment StreamingFlowGraphBuilder::BuildFunctionBody( |
820 | const Function& dart_function, |
821 | LocalVariable* first_parameter, |
822 | bool constructor) { |
823 | Fragment body; |
824 | |
825 | // TODO(27590): Currently the [VariableDeclaration]s from the |
826 | // initializers will be visible inside the entire body of the constructor. |
827 | // We should make a separate scope for them. |
828 | if (constructor) { |
829 | body += BuildInitializers(Class::Handle(Z, dart_function.Owner())); |
830 | } |
831 | |
832 | if (body.is_closed()) return body; |
833 | |
834 | FunctionNodeHelper function_node_helper(this); |
835 | function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody); |
836 | |
837 | const bool has_body = ReadTag() == kSomething; // read first part of body. |
838 | |
839 | if (dart_function.is_native()) { |
840 | body += B->NativeFunctionBody(dart_function, first_parameter); |
841 | } else if (has_body) { |
842 | body += BuildStatement(); |
843 | } |
844 | |
845 | if (body.is_open()) { |
846 | body += NullConstant(); |
847 | body += Return(dart_function.end_token_pos()); |
848 | } |
849 | |
850 | return body; |
851 | } |
852 | |
853 | Fragment StreamingFlowGraphBuilder::BuildEveryTimePrologue( |
854 | const Function& dart_function, |
855 | TokenPosition token_position, |
856 | intptr_t type_parameters_offset) { |
857 | Fragment F; |
858 | F += CheckStackOverflowInPrologue(dart_function); |
859 | F += DebugStepCheckInPrologue(dart_function, token_position); |
860 | F += SetAsyncStackTrace(dart_function); |
861 | F += B->InitConstantParameters(); |
862 | return F; |
863 | } |
864 | |
865 | Fragment StreamingFlowGraphBuilder::BuildFirstTimePrologue( |
866 | const Function& dart_function, |
867 | LocalVariable* first_parameter, |
868 | intptr_t type_parameters_offset) { |
869 | Fragment F; |
870 | F += SetupCapturedParameters(dart_function); |
871 | F += ShortcutForUserDefinedEquals(dart_function, first_parameter); |
872 | return F; |
873 | } |
874 | |
875 | Fragment StreamingFlowGraphBuilder::ClearRawParameters( |
876 | const Function& dart_function) { |
877 | const ParsedFunction& pf = *flow_graph_builder_->parsed_function_; |
878 | Fragment code; |
879 | for (intptr_t i = 0; i < dart_function.NumParameters(); ++i) { |
880 | LocalVariable* variable = pf.ParameterVariable(i); |
881 | |
882 | if (!variable->is_captured()) continue; |
883 | |
884 | // Captured 'this' is immutable, so within the outer method we don't need to |
885 | // load it from the context. Therefore we don't reset it to null. |
886 | if (pf.function().HasThisParameter() && pf.has_receiver_var() && |
887 | variable == pf.receiver_var()) { |
888 | ASSERT(i == 0); |
889 | continue; |
890 | } |
891 | |
892 | variable = pf.RawParameterVariable(i); |
893 | code += NullConstant(); |
894 | code += StoreLocal(TokenPosition::kNoSource, variable); |
895 | code += Drop(); |
896 | } |
897 | return code; |
898 | } |
899 | |
900 | UncheckedEntryPointStyle StreamingFlowGraphBuilder::ChooseEntryPointStyle( |
901 | const Function& dart_function, |
902 | const Fragment& implicit_type_checks, |
903 | const Fragment& first_time_prologue, |
904 | const Fragment& every_time_prologue, |
905 | const Fragment& type_args_handling) { |
906 | ASSERT(!dart_function.IsImplicitClosureFunction()); |
907 | if (!dart_function.MayHaveUncheckedEntryPoint() || |
908 | implicit_type_checks.is_empty()) { |
909 | return UncheckedEntryPointStyle::kNone; |
910 | } |
911 | |
912 | // Record which entry-point was taken into a variable and test it later if |
913 | // either: |
914 | // |
915 | // 1. There is a non-empty PrologueBuilder-prologue. |
916 | // |
917 | // 2. There is a non-empty "first-time" prologue. |
918 | // |
919 | // 3. The "every-time" prologue has more than two instructions (DebugStepCheck |
920 | // and CheckStackOverflow). |
921 | // |
922 | // TODO(#34162): For regular closures we can often avoid the |
923 | // PrologueBuilder-prologue on non-dynamic invocations. |
924 | if (!PrologueBuilder::HasEmptyPrologue(dart_function) || |
925 | !type_args_handling.is_empty() || !first_time_prologue.is_empty() || |
926 | !(every_time_prologue.entry == every_time_prologue.current || |
927 | every_time_prologue.current->previous() == every_time_prologue.entry)) { |
928 | return UncheckedEntryPointStyle::kSharedWithVariable; |
929 | } |
930 | |
931 | return UncheckedEntryPointStyle::kSeparate; |
932 | } |
933 | |
934 | FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction( |
935 | bool is_constructor) { |
936 | const Function& dart_function = parsed_function()->function(); |
937 | |
938 | intptr_t type_parameters_offset = 0; |
939 | LocalVariable* first_parameter = nullptr; |
940 | TokenPosition token_position = TokenPosition::kNoSource; |
941 | { |
942 | AlternativeReadingScope alt(&reader_); |
943 | FunctionNodeHelper function_node_helper(this); |
944 | function_node_helper.ReadUntilExcluding( |
945 | FunctionNodeHelper::kTypeParameters); |
946 | type_parameters_offset = ReaderOffset(); |
947 | function_node_helper.ReadUntilExcluding( |
948 | FunctionNodeHelper::kPositionalParameters); |
949 | intptr_t list_length = ReadListLength(); // read number of positionals. |
950 | if (list_length > 0) { |
951 | intptr_t first_parameter_offset = ReaderOffset() + data_program_offset_; |
952 | first_parameter = LookupVariable(first_parameter_offset); |
953 | } |
954 | token_position = function_node_helper.position_; |
955 | } |
956 | |
957 | auto graph_entry = flow_graph_builder_->graph_entry_ = |
958 | new (Z) GraphEntryInstr(*parsed_function(), flow_graph_builder_->osr_id_); |
959 | |
960 | auto normal_entry = flow_graph_builder_->BuildFunctionEntry(graph_entry); |
961 | graph_entry->set_normal_entry(normal_entry); |
962 | |
963 | PrologueInfo prologue_info(-1, -1); |
964 | BlockEntryInstr* instruction_cursor = |
965 | flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info); |
966 | |
967 | // The 'every_time_prologue' runs first and is run when resuming from yield |
968 | // points. |
969 | const Fragment every_time_prologue = BuildEveryTimePrologue( |
970 | dart_function, token_position, type_parameters_offset); |
971 | |
972 | // The 'first_time_prologue' run after 'every_time_prologue' and is *not* run |
973 | // when resuming from yield points. |
974 | const Fragment first_time_prologue = BuildFirstTimePrologue( |
975 | dart_function, first_parameter, type_parameters_offset); |
976 | |
977 | // TODO(#34162): We can remove the default type handling (and |
978 | // shorten the prologue type handling sequence) for non-dynamic invocations of |
979 | // regular methods. |
980 | const Fragment type_args_handling = TypeArgumentsHandling(dart_function); |
981 | |
982 | Fragment explicit_type_checks; |
983 | Fragment implicit_type_checks; |
984 | Fragment implicit_redefinitions; |
985 | CheckArgumentTypesAsNecessary(dart_function, type_parameters_offset, |
986 | &explicit_type_checks, &implicit_type_checks, |
987 | &implicit_redefinitions); |
988 | |
989 | // The RawParameter variables should be set to null to avoid retaining more |
990 | // objects than necessary during GC. |
991 | const Fragment body = |
992 | ClearRawParameters(dart_function) + B->BuildNullAssertions() + |
993 | BuildFunctionBody(dart_function, first_parameter, is_constructor); |
994 | |
995 | auto = ChooseEntryPointStyle( |
996 | dart_function, implicit_type_checks, first_time_prologue, |
997 | every_time_prologue, type_args_handling); |
998 | |
999 | Fragment function(instruction_cursor); |
1000 | if (yield_continuations().is_empty()) { |
1001 | FunctionEntryInstr* = nullptr; |
1002 | switch (extra_entry_point_style) { |
1003 | case UncheckedEntryPointStyle::kNone: { |
1004 | function += every_time_prologue + first_time_prologue + |
1005 | type_args_handling + implicit_type_checks + |
1006 | explicit_type_checks + body; |
1007 | break; |
1008 | } |
1009 | case UncheckedEntryPointStyle::kSeparate: { |
1010 | ASSERT(instruction_cursor == normal_entry); |
1011 | ASSERT(first_time_prologue.is_empty()); |
1012 | ASSERT(type_args_handling.is_empty()); |
1013 | |
1014 | const Fragment prologue_copy = BuildEveryTimePrologue( |
1015 | dart_function, token_position, type_parameters_offset); |
1016 | |
1017 | extra_entry = B->BuildSeparateUncheckedEntryPoint( |
1018 | normal_entry, |
1019 | /*normal_prologue=*/every_time_prologue + implicit_type_checks, |
1020 | /*extra_prologue=*/prologue_copy, |
1021 | /*shared_prologue=*/explicit_type_checks, |
1022 | /*body=*/body); |
1023 | break; |
1024 | } |
1025 | case UncheckedEntryPointStyle::kSharedWithVariable: { |
1026 | Fragment prologue(normal_entry, instruction_cursor); |
1027 | prologue += every_time_prologue; |
1028 | prologue += first_time_prologue; |
1029 | prologue += type_args_handling; |
1030 | prologue += explicit_type_checks; |
1031 | extra_entry = B->BuildSharedUncheckedEntryPoint( |
1032 | /*shared_prologue_linked_in=*/prologue, |
1033 | /*skippable_checks=*/implicit_type_checks, |
1034 | /*redefinitions_if_skipped=*/implicit_redefinitions, |
1035 | /*body=*/body); |
1036 | break; |
1037 | } |
1038 | } |
1039 | if (extra_entry != nullptr) { |
1040 | B->RecordUncheckedEntryPoint(graph_entry, extra_entry); |
1041 | } |
1042 | } else { |
1043 | // If the function's body contains any yield points, build switch statement |
1044 | // that selects a continuation point based on the value of :await_jump_var. |
1045 | ASSERT(explicit_type_checks.is_empty()); |
1046 | |
1047 | // If the function is generic, type_args_handling might require access to |
1048 | // (possibly captured) 'this' for preparing default type arguments, in which |
1049 | // case we can't run it before the 'first_time_prologue'. |
1050 | ASSERT(!dart_function.IsGeneric()); |
1051 | |
1052 | // TODO(#34162): We can probably ignore the implicit checks |
1053 | // here as well since the arguments are passed from generated code. |
1054 | function += every_time_prologue + type_args_handling + |
1055 | CompleteBodyWithYieldContinuations(first_time_prologue + |
1056 | implicit_type_checks + body); |
1057 | } |
1058 | |
1059 | // When compiling for OSR, use a depth first search to find the OSR |
1060 | // entry and make graph entry jump to it instead of normal entry. |
1061 | // Catch entries are always considered reachable, even if they |
1062 | // become unreachable after OSR. |
1063 | if (flow_graph_builder_->IsCompiledForOsr()) { |
1064 | graph_entry->RelinkToOsrEntry(Z, |
1065 | flow_graph_builder_->last_used_block_id_ + 1); |
1066 | } |
1067 | return new (Z) |
1068 | FlowGraph(*parsed_function(), graph_entry, |
1069 | flow_graph_builder_->last_used_block_id_, prologue_info); |
1070 | } |
1071 | |
1072 | FlowGraph* StreamingFlowGraphBuilder::BuildGraph() { |
1073 | ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
1074 | ASSERT(flow_graph_builder_ != nullptr); |
1075 | |
1076 | const Function& function = parsed_function()->function(); |
1077 | |
1078 | // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used |
1079 | // e.g. for type translation. |
1080 | const Class& klass = |
1081 | Class::Handle(zone_, parsed_function()->function().Owner()); |
1082 | Function& outermost_function = |
1083 | Function::Handle(Z, function.GetOutermostFunction()); |
1084 | |
1085 | ActiveClassScope active_class_scope(active_class(), &klass); |
1086 | ActiveMemberScope active_member(active_class(), &outermost_function); |
1087 | ActiveTypeParametersScope active_type_params(active_class(), function, Z); |
1088 | |
1089 | if (function.is_declared_in_bytecode()) { |
1090 | bytecode_metadata_helper_.ParseBytecodeFunction(parsed_function()); |
1091 | |
1092 | switch (function.kind()) { |
1093 | case FunctionLayout::kImplicitClosureFunction: |
1094 | return B->BuildGraphOfImplicitClosureFunction(function); |
1095 | case FunctionLayout::kImplicitGetter: |
1096 | case FunctionLayout::kImplicitSetter: |
1097 | return B->BuildGraphOfFieldAccessor(function); |
1098 | case FunctionLayout::kImplicitStaticGetter: { |
1099 | if (IsStaticFieldGetterGeneratedAsInitializer(function, Z)) { |
1100 | break; |
1101 | } |
1102 | return B->BuildGraphOfFieldAccessor(function); |
1103 | } |
1104 | case FunctionLayout::kDynamicInvocationForwarder: |
1105 | return B->BuildGraphOfDynamicInvocationForwarder(function); |
1106 | case FunctionLayout::kMethodExtractor: |
1107 | return B->BuildGraphOfMethodExtractor(function); |
1108 | case FunctionLayout::kNoSuchMethodDispatcher: |
1109 | return B->BuildGraphOfNoSuchMethodDispatcher(function); |
1110 | default: |
1111 | break; |
1112 | } |
1113 | |
1114 | ASSERT(function.HasBytecode()); |
1115 | |
1116 | BytecodeFlowGraphBuilder bytecode_compiler( |
1117 | flow_graph_builder_, parsed_function(), |
1118 | &(flow_graph_builder_->ic_data_array_)); |
1119 | |
1120 | if (B->IsRecognizedMethodForFlowGraph(function)) { |
1121 | bytecode_compiler.CreateParameterVariables(); |
1122 | return B->BuildGraphOfRecognizedMethod(function); |
1123 | } |
1124 | |
1125 | return bytecode_compiler.BuildGraph(); |
1126 | } |
1127 | |
1128 | // Certain special functions could have a VM-internal bytecode |
1129 | // attached to them. |
1130 | ASSERT((!function.HasBytecode()) || |
1131 | (function.kind() == FunctionLayout::kImplicitGetter) || |
1132 | (function.kind() == FunctionLayout::kImplicitSetter) || |
1133 | (function.kind() == FunctionLayout::kImplicitStaticGetter) || |
1134 | (function.kind() == FunctionLayout::kMethodExtractor) || |
1135 | (function.kind() == FunctionLayout::kInvokeFieldDispatcher) || |
1136 | (function.kind() == FunctionLayout::kNoSuchMethodDispatcher)); |
1137 | |
1138 | ParseKernelASTFunction(); |
1139 | |
1140 | switch (function.kind()) { |
1141 | case FunctionLayout::kRegularFunction: |
1142 | case FunctionLayout::kGetterFunction: |
1143 | case FunctionLayout::kSetterFunction: |
1144 | case FunctionLayout::kClosureFunction: |
1145 | case FunctionLayout::kConstructor: { |
1146 | if (B->IsRecognizedMethodForFlowGraph(function)) { |
1147 | return B->BuildGraphOfRecognizedMethod(function); |
1148 | } |
1149 | return BuildGraphOfFunction(function.IsGenerativeConstructor()); |
1150 | } |
1151 | case FunctionLayout::kImplicitGetter: |
1152 | case FunctionLayout::kImplicitStaticGetter: |
1153 | case FunctionLayout::kImplicitSetter: { |
1154 | const Field& field = Field::Handle(Z, function.accessor_field()); |
1155 | if (field.is_const() && field.IsUninitialized()) { |
1156 | EvaluateConstFieldValue(field); |
1157 | } |
1158 | return B->BuildGraphOfFieldAccessor(function); |
1159 | } |
1160 | case FunctionLayout::kFieldInitializer: |
1161 | return BuildGraphOfFieldInitializer(); |
1162 | case FunctionLayout::kDynamicInvocationForwarder: |
1163 | return B->BuildGraphOfDynamicInvocationForwarder(function); |
1164 | case FunctionLayout::kMethodExtractor: |
1165 | return flow_graph_builder_->BuildGraphOfMethodExtractor(function); |
1166 | case FunctionLayout::kNoSuchMethodDispatcher: |
1167 | return flow_graph_builder_->BuildGraphOfNoSuchMethodDispatcher(function); |
1168 | case FunctionLayout::kInvokeFieldDispatcher: |
1169 | return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function); |
1170 | case FunctionLayout::kImplicitClosureFunction: |
1171 | return flow_graph_builder_->BuildGraphOfImplicitClosureFunction(function); |
1172 | case FunctionLayout::kFfiTrampoline: |
1173 | return flow_graph_builder_->BuildGraphOfFfiTrampoline(function); |
1174 | case FunctionLayout::kSignatureFunction: |
1175 | case FunctionLayout::kIrregexpFunction: |
1176 | break; |
1177 | } |
1178 | UNREACHABLE(); |
1179 | return NULL; |
1180 | } |
1181 | |
1182 | void StreamingFlowGraphBuilder::ParseKernelASTFunction() { |
1183 | const Function& function = parsed_function()->function(); |
1184 | |
1185 | const intptr_t kernel_offset = function.kernel_offset(); |
1186 | ASSERT(kernel_offset >= 0); |
1187 | |
1188 | SetOffset(kernel_offset); |
1189 | |
1190 | // Mark forwarding stubs. |
1191 | switch (function.kind()) { |
1192 | case FunctionLayout::kRegularFunction: |
1193 | case FunctionLayout::kImplicitClosureFunction: |
1194 | case FunctionLayout::kGetterFunction: |
1195 | case FunctionLayout::kSetterFunction: |
1196 | case FunctionLayout::kClosureFunction: |
1197 | case FunctionLayout::kConstructor: |
1198 | case FunctionLayout::kDynamicInvocationForwarder: |
1199 | ReadForwardingStubTarget(function); |
1200 | break; |
1201 | default: |
1202 | break; |
1203 | } |
1204 | |
1205 | set_scopes(parsed_function()->EnsureKernelScopes()); |
1206 | |
1207 | switch (function.kind()) { |
1208 | case FunctionLayout::kRegularFunction: |
1209 | case FunctionLayout::kGetterFunction: |
1210 | case FunctionLayout::kSetterFunction: |
1211 | case FunctionLayout::kClosureFunction: |
1212 | case FunctionLayout::kConstructor: |
1213 | case FunctionLayout::kImplicitClosureFunction: |
1214 | ReadUntilFunctionNode(); |
1215 | SetupDefaultParameterValues(); |
1216 | ReadDefaultFunctionTypeArguments(function); |
1217 | break; |
1218 | case FunctionLayout::kImplicitGetter: |
1219 | case FunctionLayout::kImplicitStaticGetter: |
1220 | case FunctionLayout::kImplicitSetter: |
1221 | case FunctionLayout::kFieldInitializer: |
1222 | case FunctionLayout::kMethodExtractor: |
1223 | case FunctionLayout::kNoSuchMethodDispatcher: |
1224 | case FunctionLayout::kInvokeFieldDispatcher: |
1225 | case FunctionLayout::kFfiTrampoline: |
1226 | break; |
1227 | case FunctionLayout::kDynamicInvocationForwarder: |
1228 | if (PeekTag() != kField) { |
1229 | ReadUntilFunctionNode(); |
1230 | SetupDefaultParameterValues(); |
1231 | ReadDefaultFunctionTypeArguments(function); |
1232 | } |
1233 | break; |
1234 | case FunctionLayout::kSignatureFunction: |
1235 | case FunctionLayout::kIrregexpFunction: |
1236 | UNREACHABLE(); |
1237 | break; |
1238 | } |
1239 | } |
1240 | |
1241 | void StreamingFlowGraphBuilder::ReadForwardingStubTarget( |
1242 | const Function& function) { |
1243 | if (PeekTag() == kProcedure) { |
1244 | AlternativeReadingScope alt(&reader_); |
1245 | ProcedureHelper procedure_helper(this); |
1246 | procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction); |
1247 | if (procedure_helper.IsForwardingStub() && !procedure_helper.IsAbstract()) { |
1248 | const NameIndex target_name = |
1249 | procedure_helper.forwarding_stub_super_target_; |
1250 | ASSERT(target_name != NameIndex::kInvalidName); |
1251 | const String& name = function.IsSetterFunction() |
1252 | ? H.DartSetterName(target_name) |
1253 | : H.DartProcedureName(target_name); |
1254 | const Function* forwarding_target = |
1255 | &Function::ZoneHandle(Z, H.LookupMethodByMember(target_name, name)); |
1256 | ASSERT(!forwarding_target->IsNull()); |
1257 | parsed_function()->MarkForwardingStub(forwarding_target); |
1258 | } |
1259 | } |
1260 | } |
1261 | |
1262 | Fragment StreamingFlowGraphBuilder::BuildStatementAt(intptr_t kernel_offset) { |
1263 | SetOffset(kernel_offset); |
1264 | return BuildStatement(); // read statement. |
1265 | } |
1266 | |
1267 | Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) { |
1268 | uint8_t payload = 0; |
1269 | Tag tag = ReadTag(&payload); // read tag. |
1270 | switch (tag) { |
1271 | case kInvalidExpression: |
1272 | return BuildInvalidExpression(position); |
1273 | case kVariableGet: |
1274 | return BuildVariableGet(position); |
1275 | case kSpecializedVariableGet: |
1276 | return BuildVariableGet(payload, position); |
1277 | case kVariableSet: |
1278 | return BuildVariableSet(position); |
1279 | case kSpecializedVariableSet: |
1280 | return BuildVariableSet(payload, position); |
1281 | case kPropertyGet: |
1282 | return BuildPropertyGet(position); |
1283 | case kPropertySet: |
1284 | return BuildPropertySet(position); |
1285 | case kDirectPropertyGet: |
1286 | return BuildDirectPropertyGet(position); |
1287 | case kDirectPropertySet: |
1288 | return BuildDirectPropertySet(position); |
1289 | case kSuperPropertyGet: |
1290 | return BuildSuperPropertyGet(position); |
1291 | case kSuperPropertySet: |
1292 | return BuildSuperPropertySet(position); |
1293 | case kStaticGet: |
1294 | return BuildStaticGet(position); |
1295 | case kStaticSet: |
1296 | return BuildStaticSet(position); |
1297 | case kMethodInvocation: |
1298 | return BuildMethodInvocation(position); |
1299 | case kSuperMethodInvocation: |
1300 | return BuildSuperMethodInvocation(position); |
1301 | case kDirectMethodInvocation: |
1302 | return BuildDirectMethodInvocation(position); |
1303 | case kStaticInvocation: |
1304 | return BuildStaticInvocation(position); |
1305 | case kConstructorInvocation: |
1306 | return BuildConstructorInvocation(position); |
1307 | case kNot: |
1308 | return BuildNot(position); |
1309 | case kNullCheck: |
1310 | return BuildNullCheck(position); |
1311 | case kLogicalExpression: |
1312 | return BuildLogicalExpression(position); |
1313 | case kConditionalExpression: |
1314 | return BuildConditionalExpression(position); |
1315 | case kStringConcatenation: |
1316 | return BuildStringConcatenation(position); |
1317 | case kIsExpression: |
1318 | return BuildIsExpression(position); |
1319 | case kAsExpression: |
1320 | return BuildAsExpression(position); |
1321 | case kTypeLiteral: |
1322 | return BuildTypeLiteral(position); |
1323 | case kThisExpression: |
1324 | return BuildThisExpression(position); |
1325 | case kRethrow: |
1326 | return BuildRethrow(position); |
1327 | case kThrow: |
1328 | return BuildThrow(position); |
1329 | case kListLiteral: |
1330 | return BuildListLiteral(position); |
1331 | case kSetLiteral: |
1332 | // Set literals are currently desugared in the frontend and will not |
1333 | // reach the VM. See http://dartbug.com/35124 for discussion. |
1334 | UNREACHABLE(); |
1335 | break; |
1336 | case kMapLiteral: |
1337 | return BuildMapLiteral(position); |
1338 | case kFunctionExpression: |
1339 | return BuildFunctionExpression(); |
1340 | case kLet: |
1341 | return BuildLet(position); |
1342 | case kBlockExpression: |
1343 | return BuildBlockExpression(); |
1344 | case kBigIntLiteral: |
1345 | return BuildBigIntLiteral(position); |
1346 | case kStringLiteral: |
1347 | return BuildStringLiteral(position); |
1348 | case kSpecializedIntLiteral: |
1349 | return BuildIntLiteral(payload, position); |
1350 | case kNegativeIntLiteral: |
1351 | return BuildIntLiteral(true, position); |
1352 | case kPositiveIntLiteral: |
1353 | return BuildIntLiteral(false, position); |
1354 | case kDoubleLiteral: |
1355 | return BuildDoubleLiteral(position); |
1356 | case kTrueLiteral: |
1357 | return BuildBoolLiteral(true, position); |
1358 | case kFalseLiteral: |
1359 | return BuildBoolLiteral(false, position); |
1360 | case kNullLiteral: |
1361 | return BuildNullLiteral(position); |
1362 | case kConstantExpression: |
1363 | return BuildConstantExpression(position, tag); |
1364 | case kInstantiation: |
1365 | return BuildPartialTearoffInstantiation(position); |
1366 | case kLoadLibrary: |
1367 | return BuildLibraryPrefixAction(position, Symbols::LoadLibrary()); |
1368 | case kCheckLibraryIsLoaded: |
1369 | return BuildLibraryPrefixAction(position, Symbols::CheckLoaded()); |
1370 | case kConstStaticInvocation: |
1371 | case kConstConstructorInvocation: |
1372 | case kConstListLiteral: |
1373 | case kConstSetLiteral: |
1374 | case kConstMapLiteral: |
1375 | case kSymbolLiteral: |
1376 | // Const invocations and const literals are removed by the |
1377 | // constant evaluator. |
1378 | case kListConcatenation: |
1379 | case kSetConcatenation: |
1380 | case kMapConcatenation: |
1381 | case kInstanceCreation: |
1382 | case kFileUriExpression: |
1383 | // Collection concatenation, instance creation operations and |
1384 | // in-expression URI changes are internal to the front end and |
1385 | // removed by the constant evaluator. |
1386 | default: |
1387 | ReportUnexpectedTag("expression" , tag); |
1388 | UNREACHABLE(); |
1389 | } |
1390 | |
1391 | return Fragment(); |
1392 | } |
1393 | |
1394 | Fragment StreamingFlowGraphBuilder::BuildStatement() { |
1395 | Tag tag = ReadTag(); // read tag. |
1396 | switch (tag) { |
1397 | case kExpressionStatement: |
1398 | return BuildExpressionStatement(); |
1399 | case kBlock: |
1400 | return BuildBlock(); |
1401 | case kEmptyStatement: |
1402 | return BuildEmptyStatement(); |
1403 | case kAssertBlock: |
1404 | return BuildAssertBlock(); |
1405 | case kAssertStatement: |
1406 | return BuildAssertStatement(); |
1407 | case kLabeledStatement: |
1408 | return BuildLabeledStatement(); |
1409 | case kBreakStatement: |
1410 | return BuildBreakStatement(); |
1411 | case kWhileStatement: |
1412 | return BuildWhileStatement(); |
1413 | case kDoStatement: |
1414 | return BuildDoStatement(); |
1415 | case kForStatement: |
1416 | return BuildForStatement(); |
1417 | case kForInStatement: |
1418 | return BuildForInStatement(false); |
1419 | case kAsyncForInStatement: |
1420 | return BuildForInStatement(true); |
1421 | case kSwitchStatement: |
1422 | return BuildSwitchStatement(); |
1423 | case kContinueSwitchStatement: |
1424 | return BuildContinueSwitchStatement(); |
1425 | case kIfStatement: |
1426 | return BuildIfStatement(); |
1427 | case kReturnStatement: |
1428 | return BuildReturnStatement(); |
1429 | case kTryCatch: |
1430 | return BuildTryCatch(); |
1431 | case kTryFinally: |
1432 | return BuildTryFinally(); |
1433 | case kYieldStatement: |
1434 | return BuildYieldStatement(); |
1435 | case kVariableDeclaration: |
1436 | return BuildVariableDeclaration(); |
1437 | case kFunctionDeclaration: |
1438 | return BuildFunctionDeclaration(); |
1439 | default: |
1440 | ReportUnexpectedTag("statement" , tag); |
1441 | UNREACHABLE(); |
1442 | } |
1443 | return Fragment(); |
1444 | } |
1445 | |
1446 | void StreamingFlowGraphBuilder::ReportUnexpectedTag(const char* variant, |
1447 | Tag tag) { |
1448 | if ((flow_graph_builder_ == NULL) || (parsed_function() == NULL)) { |
1449 | KernelReaderHelper::ReportUnexpectedTag(variant, tag); |
1450 | } else { |
1451 | H.ReportError(script_, TokenPosition::kNoSource, |
1452 | "Unexpected tag %d (%s) in %s, expected %s" , tag, |
1453 | Reader::TagName(tag), |
1454 | parsed_function()->function().ToQualifiedCString(), variant); |
1455 | } |
1456 | } |
1457 | |
1458 | Tag KernelReaderHelper::ReadTag(uint8_t* payload) { |
1459 | return reader_.ReadTag(payload); |
1460 | } |
1461 | |
1462 | Tag KernelReaderHelper::PeekTag(uint8_t* payload) { |
1463 | return reader_.PeekTag(payload); |
1464 | } |
1465 | |
1466 | Nullability KernelReaderHelper::ReadNullability() { |
1467 | return reader_.ReadNullability(); |
1468 | } |
1469 | |
1470 | Variance KernelReaderHelper::ReadVariance() { |
1471 | if (translation_helper_.info().kernel_binary_version() >= 34) { |
1472 | return reader_.ReadVariance(); |
1473 | } |
1474 | return kCovariant; |
1475 | } |
1476 | |
1477 | void StreamingFlowGraphBuilder::loop_depth_inc() { |
1478 | ++flow_graph_builder_->loop_depth_; |
1479 | } |
1480 | |
1481 | void StreamingFlowGraphBuilder::loop_depth_dec() { |
1482 | --flow_graph_builder_->loop_depth_; |
1483 | } |
1484 | |
1485 | intptr_t StreamingFlowGraphBuilder::for_in_depth() { |
1486 | return flow_graph_builder_->for_in_depth_; |
1487 | } |
1488 | |
1489 | void StreamingFlowGraphBuilder::for_in_depth_inc() { |
1490 | ++flow_graph_builder_->for_in_depth_; |
1491 | } |
1492 | |
1493 | void StreamingFlowGraphBuilder::for_in_depth_dec() { |
1494 | --flow_graph_builder_->for_in_depth_; |
1495 | } |
1496 | |
1497 | void StreamingFlowGraphBuilder::catch_depth_inc() { |
1498 | ++flow_graph_builder_->catch_depth_; |
1499 | } |
1500 | |
1501 | void StreamingFlowGraphBuilder::catch_depth_dec() { |
1502 | --flow_graph_builder_->catch_depth_; |
1503 | } |
1504 | |
1505 | void StreamingFlowGraphBuilder::try_depth_inc() { |
1506 | ++flow_graph_builder_->try_depth_; |
1507 | } |
1508 | |
1509 | void StreamingFlowGraphBuilder::try_depth_dec() { |
1510 | --flow_graph_builder_->try_depth_; |
1511 | } |
1512 | |
1513 | intptr_t StreamingFlowGraphBuilder::block_expression_depth() { |
1514 | return flow_graph_builder_->block_expression_depth_; |
1515 | } |
1516 | |
1517 | void StreamingFlowGraphBuilder::block_expression_depth_inc() { |
1518 | ++flow_graph_builder_->block_expression_depth_; |
1519 | } |
1520 | |
1521 | void StreamingFlowGraphBuilder::block_expression_depth_dec() { |
1522 | --flow_graph_builder_->block_expression_depth_; |
1523 | } |
1524 | |
1525 | intptr_t StreamingFlowGraphBuilder::CurrentTryIndex() { |
1526 | return flow_graph_builder_->CurrentTryIndex(); |
1527 | } |
1528 | |
1529 | intptr_t StreamingFlowGraphBuilder::AllocateTryIndex() { |
1530 | return flow_graph_builder_->AllocateTryIndex(); |
1531 | } |
1532 | |
1533 | LocalVariable* StreamingFlowGraphBuilder::CurrentException() { |
1534 | return flow_graph_builder_->CurrentException(); |
1535 | } |
1536 | |
1537 | LocalVariable* StreamingFlowGraphBuilder::CurrentStackTrace() { |
1538 | return flow_graph_builder_->CurrentStackTrace(); |
1539 | } |
1540 | |
1541 | CatchBlock* StreamingFlowGraphBuilder::catch_block() { |
1542 | return flow_graph_builder_->catch_block_; |
1543 | } |
1544 | |
1545 | ActiveClass* StreamingFlowGraphBuilder::active_class() { |
1546 | return active_class_; |
1547 | } |
1548 | |
1549 | ScopeBuildingResult* StreamingFlowGraphBuilder::scopes() { |
1550 | return flow_graph_builder_->scopes_; |
1551 | } |
1552 | |
1553 | void StreamingFlowGraphBuilder::set_scopes(ScopeBuildingResult* scope) { |
1554 | flow_graph_builder_->scopes_ = scope; |
1555 | } |
1556 | |
1557 | ParsedFunction* StreamingFlowGraphBuilder::parsed_function() { |
1558 | return flow_graph_builder_->parsed_function_; |
1559 | } |
1560 | |
1561 | TryFinallyBlock* StreamingFlowGraphBuilder::try_finally_block() { |
1562 | return flow_graph_builder_->try_finally_block_; |
1563 | } |
1564 | |
1565 | SwitchBlock* StreamingFlowGraphBuilder::switch_block() { |
1566 | return flow_graph_builder_->switch_block_; |
1567 | } |
1568 | |
1569 | BreakableBlock* StreamingFlowGraphBuilder::breakable_block() { |
1570 | return flow_graph_builder_->breakable_block_; |
1571 | } |
1572 | |
1573 | GrowableArray<YieldContinuation>& |
1574 | StreamingFlowGraphBuilder::yield_continuations() { |
1575 | return flow_graph_builder_->yield_continuations_; |
1576 | } |
1577 | |
1578 | Value* StreamingFlowGraphBuilder::stack() { |
1579 | return flow_graph_builder_->stack_; |
1580 | } |
1581 | |
1582 | void StreamingFlowGraphBuilder::Push(Definition* definition) { |
1583 | flow_graph_builder_->Push(definition); |
1584 | } |
1585 | |
1586 | Value* StreamingFlowGraphBuilder::Pop() { |
1587 | return flow_graph_builder_->Pop(); |
1588 | } |
1589 | |
1590 | Tag StreamingFlowGraphBuilder::PeekArgumentsFirstPositionalTag() { |
1591 | // read parts of arguments, then go back to before doing so. |
1592 | AlternativeReadingScope alt(&reader_); |
1593 | ReadUInt(); // read number of arguments. |
1594 | |
1595 | SkipListOfDartTypes(); // Read list of types. |
1596 | |
1597 | // List of positional. |
1598 | intptr_t list_length = ReadListLength(); // read list length. |
1599 | if (list_length > 0) { |
1600 | return ReadTag(); // read first tag. |
1601 | } |
1602 | |
1603 | UNREACHABLE(); |
1604 | return kNothing; |
1605 | } |
1606 | |
1607 | const TypeArguments& StreamingFlowGraphBuilder::PeekArgumentsInstantiatedType( |
1608 | const Class& klass) { |
1609 | // read parts of arguments, then go back to before doing so. |
1610 | AlternativeReadingScope alt(&reader_); |
1611 | ReadUInt(); // read argument count. |
1612 | intptr_t list_length = ReadListLength(); // read types list length. |
1613 | return T.BuildInstantiatedTypeArguments(klass, list_length); // read types. |
1614 | } |
1615 | |
1616 | intptr_t StreamingFlowGraphBuilder::PeekArgumentsCount() { |
1617 | return PeekUInt(); |
1618 | } |
1619 | |
1620 | LocalVariable* StreamingFlowGraphBuilder::LookupVariable( |
1621 | intptr_t kernel_offset) { |
1622 | return flow_graph_builder_->LookupVariable(kernel_offset); |
1623 | } |
1624 | |
1625 | LocalVariable* StreamingFlowGraphBuilder::MakeTemporary() { |
1626 | return flow_graph_builder_->MakeTemporary(); |
1627 | } |
1628 | |
1629 | Function& StreamingFlowGraphBuilder::FindMatchingFunction( |
1630 | const Class& klass, |
1631 | const String& name, |
1632 | int type_args_len, |
1633 | int argument_count, |
1634 | const Array& argument_names) { |
1635 | // Search the superclass chain for the selector. |
1636 | ArgumentsDescriptor args_desc( |
1637 | Array::Handle(Z, ArgumentsDescriptor::NewBoxed( |
1638 | type_args_len, argument_count, argument_names))); |
1639 | Function& function = |
1640 | Function::Handle(Z, Resolver::ResolveDynamicForReceiverClassAllowPrivate( |
1641 | klass, name, args_desc, /*allow_add=*/false)); |
1642 | return function; |
1643 | } |
1644 | |
1645 | bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(const Function& function, |
1646 | TokenPosition position) { |
1647 | return flow_graph_builder_->NeedsDebugStepCheck(function, position); |
1648 | } |
1649 | |
1650 | bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(Value* value, |
1651 | TokenPosition position) { |
1652 | return flow_graph_builder_->NeedsDebugStepCheck(value, position); |
1653 | } |
1654 | |
1655 | void StreamingFlowGraphBuilder::InlineBailout(const char* reason) { |
1656 | flow_graph_builder_->InlineBailout(reason); |
1657 | } |
1658 | |
1659 | Fragment StreamingFlowGraphBuilder::DebugStepCheck(TokenPosition position) { |
1660 | return flow_graph_builder_->DebugStepCheck(position); |
1661 | } |
1662 | |
1663 | Fragment StreamingFlowGraphBuilder::LoadLocal(LocalVariable* variable) { |
1664 | return flow_graph_builder_->LoadLocal(variable); |
1665 | } |
1666 | |
1667 | Fragment StreamingFlowGraphBuilder::Return(TokenPosition position, |
1668 | intptr_t yield_index) { |
1669 | return flow_graph_builder_->Return(position, /*omit_result_type_check=*/false, |
1670 | yield_index); |
1671 | } |
1672 | |
1673 | Fragment StreamingFlowGraphBuilder::EvaluateAssertion() { |
1674 | return flow_graph_builder_->EvaluateAssertion(); |
1675 | } |
1676 | |
1677 | Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position, |
1678 | int catch_try_index) { |
1679 | return flow_graph_builder_->RethrowException(position, catch_try_index); |
1680 | } |
1681 | |
1682 | Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError( |
1683 | const Function& target) { |
1684 | return flow_graph_builder_->ThrowNoSuchMethodError(target); |
1685 | } |
1686 | |
1687 | Fragment StreamingFlowGraphBuilder::Constant(const Object& value) { |
1688 | return flow_graph_builder_->Constant(value); |
1689 | } |
1690 | |
1691 | Fragment StreamingFlowGraphBuilder::IntConstant(int64_t value) { |
1692 | return flow_graph_builder_->IntConstant(value); |
1693 | } |
1694 | |
1695 | Fragment StreamingFlowGraphBuilder::LoadStaticField(const Field& field, |
1696 | bool calls_initializer) { |
1697 | return flow_graph_builder_->LoadStaticField(field, calls_initializer); |
1698 | } |
1699 | |
1700 | Fragment StreamingFlowGraphBuilder::RedefinitionWithType( |
1701 | const AbstractType& type) { |
1702 | return flow_graph_builder_->RedefinitionWithType(type); |
1703 | } |
1704 | |
1705 | Fragment StreamingFlowGraphBuilder::CheckNull( |
1706 | TokenPosition position, |
1707 | LocalVariable* receiver, |
1708 | const String& function_name, |
1709 | bool clear_the_temp /* = true */) { |
1710 | return flow_graph_builder_->CheckNull(position, receiver, function_name, |
1711 | clear_the_temp); |
1712 | } |
1713 | |
1714 | Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position, |
1715 | const Function& target, |
1716 | intptr_t argument_count, |
1717 | ICData::RebindRule rebind_rule) { |
1718 | if (!target.AreValidArgumentCounts(0, argument_count, 0, nullptr)) { |
1719 | return flow_graph_builder_->ThrowNoSuchMethodError(target); |
1720 | } |
1721 | return flow_graph_builder_->StaticCall(position, target, argument_count, |
1722 | rebind_rule); |
1723 | } |
1724 | |
1725 | Fragment StreamingFlowGraphBuilder::StaticCall( |
1726 | TokenPosition position, |
1727 | const Function& target, |
1728 | intptr_t argument_count, |
1729 | const Array& argument_names, |
1730 | ICData::RebindRule rebind_rule, |
1731 | const InferredTypeMetadata* result_type, |
1732 | intptr_t type_args_count, |
1733 | bool use_unchecked_entry) { |
1734 | if (!target.AreValidArguments(type_args_count, argument_count, argument_names, |
1735 | nullptr)) { |
1736 | return flow_graph_builder_->ThrowNoSuchMethodError(target); |
1737 | } |
1738 | return flow_graph_builder_->StaticCall( |
1739 | position, target, argument_count, argument_names, rebind_rule, |
1740 | result_type, type_args_count, use_unchecked_entry); |
1741 | } |
1742 | |
1743 | Fragment StreamingFlowGraphBuilder::InstanceCall( |
1744 | TokenPosition position, |
1745 | const String& name, |
1746 | Token::Kind kind, |
1747 | intptr_t argument_count, |
1748 | intptr_t checked_argument_count) { |
1749 | const intptr_t kTypeArgsLen = 0; |
1750 | return flow_graph_builder_->InstanceCall(position, name, kind, kTypeArgsLen, |
1751 | argument_count, Array::null_array(), |
1752 | checked_argument_count); |
1753 | } |
1754 | |
1755 | Fragment StreamingFlowGraphBuilder::InstanceCall( |
1756 | TokenPosition position, |
1757 | const String& name, |
1758 | Token::Kind kind, |
1759 | intptr_t type_args_len, |
1760 | intptr_t argument_count, |
1761 | const Array& argument_names, |
1762 | intptr_t checked_argument_count, |
1763 | const Function& interface_target, |
1764 | const Function& tearoff_interface_target, |
1765 | const InferredTypeMetadata* result_type, |
1766 | bool use_unchecked_entry, |
1767 | const CallSiteAttributesMetadata* call_site_attrs, |
1768 | bool receiver_is_not_smi) { |
1769 | return flow_graph_builder_->InstanceCall( |
1770 | position, name, kind, type_args_len, argument_count, argument_names, |
1771 | checked_argument_count, interface_target, tearoff_interface_target, |
1772 | result_type, use_unchecked_entry, call_site_attrs, receiver_is_not_smi); |
1773 | } |
1774 | |
1775 | Fragment StreamingFlowGraphBuilder::ThrowException(TokenPosition position) { |
1776 | return flow_graph_builder_->ThrowException(position); |
1777 | } |
1778 | |
1779 | Fragment StreamingFlowGraphBuilder::BooleanNegate() { |
1780 | return flow_graph_builder_->BooleanNegate(); |
1781 | } |
1782 | |
1783 | Fragment StreamingFlowGraphBuilder::TranslateInstantiatedTypeArguments( |
1784 | const TypeArguments& type_arguments) { |
1785 | return flow_graph_builder_->TranslateInstantiatedTypeArguments( |
1786 | type_arguments); |
1787 | } |
1788 | |
1789 | Fragment StreamingFlowGraphBuilder::StrictCompare(TokenPosition position, |
1790 | Token::Kind kind, |
1791 | bool number_check) { |
1792 | return flow_graph_builder_->StrictCompare(position, kind, number_check); |
1793 | } |
1794 | |
1795 | Fragment StreamingFlowGraphBuilder::AllocateObject(TokenPosition position, |
1796 | const Class& klass, |
1797 | intptr_t argument_count) { |
1798 | return flow_graph_builder_->AllocateObject(position, klass, argument_count); |
1799 | } |
1800 | |
1801 | Fragment StreamingFlowGraphBuilder::AllocateContext( |
1802 | const ZoneGrowableArray<const Slot*>& context_slots) { |
1803 | return flow_graph_builder_->AllocateContext(context_slots); |
1804 | } |
1805 | |
1806 | Fragment StreamingFlowGraphBuilder::LoadNativeField(const Slot& field) { |
1807 | return flow_graph_builder_->LoadNativeField(field); |
1808 | } |
1809 | |
1810 | Fragment StreamingFlowGraphBuilder::StoreLocal(TokenPosition position, |
1811 | LocalVariable* variable) { |
1812 | return flow_graph_builder_->StoreLocal(position, variable); |
1813 | } |
1814 | |
1815 | Fragment StreamingFlowGraphBuilder::StoreStaticField(TokenPosition position, |
1816 | const Field& field) { |
1817 | return flow_graph_builder_->StoreStaticField(position, field); |
1818 | } |
1819 | |
1820 | Fragment StreamingFlowGraphBuilder::StringInterpolate(TokenPosition position) { |
1821 | return flow_graph_builder_->StringInterpolate(position); |
1822 | } |
1823 | |
1824 | Fragment StreamingFlowGraphBuilder::StringInterpolateSingle( |
1825 | TokenPosition position) { |
1826 | return flow_graph_builder_->StringInterpolateSingle(position); |
1827 | } |
1828 | |
1829 | Fragment StreamingFlowGraphBuilder::ThrowTypeError() { |
1830 | return flow_graph_builder_->ThrowTypeError(); |
1831 | } |
1832 | |
1833 | Fragment StreamingFlowGraphBuilder::LoadInstantiatorTypeArguments() { |
1834 | return flow_graph_builder_->LoadInstantiatorTypeArguments(); |
1835 | } |
1836 | |
1837 | Fragment StreamingFlowGraphBuilder::LoadFunctionTypeArguments() { |
1838 | return flow_graph_builder_->LoadFunctionTypeArguments(); |
1839 | } |
1840 | |
1841 | Fragment StreamingFlowGraphBuilder::InstantiateType(const AbstractType& type) { |
1842 | return flow_graph_builder_->InstantiateType(type); |
1843 | } |
1844 | |
1845 | Fragment StreamingFlowGraphBuilder::CreateArray() { |
1846 | return flow_graph_builder_->CreateArray(); |
1847 | } |
1848 | |
1849 | Fragment StreamingFlowGraphBuilder::StoreIndexed(intptr_t class_id) { |
1850 | return flow_graph_builder_->StoreIndexed(class_id); |
1851 | } |
1852 | |
1853 | Fragment StreamingFlowGraphBuilder::CheckStackOverflow(TokenPosition position) { |
1854 | return flow_graph_builder_->CheckStackOverflow( |
1855 | position, flow_graph_builder_->GetStackDepth(), |
1856 | flow_graph_builder_->loop_depth_); |
1857 | } |
1858 | |
1859 | Fragment StreamingFlowGraphBuilder::CloneContext( |
1860 | const ZoneGrowableArray<const Slot*>& context_slots) { |
1861 | return flow_graph_builder_->CloneContext(context_slots); |
1862 | } |
1863 | |
1864 | Fragment StreamingFlowGraphBuilder::TranslateFinallyFinalizers( |
1865 | TryFinallyBlock* outer_finally, |
1866 | intptr_t target_context_depth) { |
1867 | // TranslateFinallyFinalizers can move the readers offset. |
1868 | // Save the current position and restore it afterwards. |
1869 | AlternativeReadingScope alt(&reader_); |
1870 | |
1871 | // Save context. |
1872 | TryFinallyBlock* const saved_finally_block = B->try_finally_block_; |
1873 | TryCatchBlock* const saved_try_catch_block = B->CurrentTryCatchBlock(); |
1874 | const intptr_t saved_context_depth = B->context_depth_; |
1875 | const ProgramState state(B->breakable_block_, B->switch_block_, |
1876 | B->loop_depth_, B->for_in_depth_, B->try_depth_, |
1877 | B->catch_depth_, B->block_expression_depth_); |
1878 | |
1879 | Fragment instructions; |
1880 | |
1881 | // While translating the body of a finalizer we need to set the try-finally |
1882 | // block which is active when translating the body. |
1883 | while (B->try_finally_block_ != outer_finally) { |
1884 | ASSERT(B->try_finally_block_ != nullptr); |
1885 | // Adjust program context to finalizer's position. |
1886 | B->try_finally_block_->state().assignTo(B); |
1887 | |
1888 | // Potentially restore the context to what is expected for the finally |
1889 | // block. |
1890 | instructions += B->AdjustContextTo(B->try_finally_block_->context_depth()); |
1891 | |
1892 | // The to-be-translated finalizer has to have the correct try-index (namely |
1893 | // the one outside the try-finally block). |
1894 | bool changed_try_index = false; |
1895 | intptr_t target_try_index = B->try_finally_block_->try_index(); |
1896 | while (B->CurrentTryIndex() != target_try_index) { |
1897 | B->SetCurrentTryCatchBlock(B->CurrentTryCatchBlock()->outer()); |
1898 | changed_try_index = true; |
1899 | } |
1900 | if (changed_try_index) { |
1901 | JoinEntryInstr* entry = BuildJoinEntry(); |
1902 | instructions += Goto(entry); |
1903 | instructions = Fragment(instructions.entry, entry); |
1904 | } |
1905 | |
1906 | intptr_t finalizer_kernel_offset = |
1907 | B->try_finally_block_->finalizer_kernel_offset(); |
1908 | B->try_finally_block_ = B->try_finally_block_->outer(); |
1909 | instructions += BuildStatementAt(finalizer_kernel_offset); |
1910 | |
1911 | // We only need to make sure that if the finalizer ended normally, we |
1912 | // continue towards the next outer try-finally. |
1913 | if (!instructions.is_open()) break; |
1914 | } |
1915 | |
1916 | if (instructions.is_open() && target_context_depth != -1) { |
1917 | // A target context depth of -1 indicates that the code after this |
1918 | // will not care about the context chain so we can leave it any way we |
1919 | // want after the last finalizer. That is used when returning. |
1920 | instructions += B->AdjustContextTo(target_context_depth); |
1921 | } |
1922 | |
1923 | // Restore. |
1924 | B->try_finally_block_ = saved_finally_block; |
1925 | B->SetCurrentTryCatchBlock(saved_try_catch_block); |
1926 | B->context_depth_ = saved_context_depth; |
1927 | state.assignTo(B); |
1928 | |
1929 | return instructions; |
1930 | } |
1931 | |
1932 | Fragment StreamingFlowGraphBuilder::BranchIfTrue( |
1933 | TargetEntryInstr** then_entry, |
1934 | TargetEntryInstr** otherwise_entry, |
1935 | bool negate) { |
1936 | return flow_graph_builder_->BranchIfTrue(then_entry, otherwise_entry, negate); |
1937 | } |
1938 | |
1939 | Fragment StreamingFlowGraphBuilder::BranchIfEqual( |
1940 | TargetEntryInstr** then_entry, |
1941 | TargetEntryInstr** otherwise_entry, |
1942 | bool negate) { |
1943 | return flow_graph_builder_->BranchIfEqual(then_entry, otherwise_entry, |
1944 | negate); |
1945 | } |
1946 | |
1947 | Fragment StreamingFlowGraphBuilder::BranchIfNull( |
1948 | TargetEntryInstr** then_entry, |
1949 | TargetEntryInstr** otherwise_entry, |
1950 | bool negate) { |
1951 | return flow_graph_builder_->BranchIfNull(then_entry, otherwise_entry, negate); |
1952 | } |
1953 | |
1954 | Fragment StreamingFlowGraphBuilder::CatchBlockEntry(const Array& handler_types, |
1955 | intptr_t handler_index, |
1956 | bool needs_stacktrace, |
1957 | bool is_synthesized) { |
1958 | return flow_graph_builder_->CatchBlockEntry(handler_types, handler_index, |
1959 | needs_stacktrace, is_synthesized); |
1960 | } |
1961 | |
1962 | Fragment StreamingFlowGraphBuilder::TryCatch(int try_handler_index) { |
1963 | return flow_graph_builder_->TryCatch(try_handler_index); |
1964 | } |
1965 | |
1966 | Fragment StreamingFlowGraphBuilder::Drop() { |
1967 | return flow_graph_builder_->Drop(); |
1968 | } |
1969 | |
1970 | Fragment StreamingFlowGraphBuilder::DropTempsPreserveTop( |
1971 | intptr_t num_temps_to_drop) { |
1972 | return flow_graph_builder_->DropTempsPreserveTop(num_temps_to_drop); |
1973 | } |
1974 | |
1975 | Fragment StreamingFlowGraphBuilder::MakeTemp() { |
1976 | return flow_graph_builder_->MakeTemp(); |
1977 | } |
1978 | |
1979 | Fragment StreamingFlowGraphBuilder::NullConstant() { |
1980 | return flow_graph_builder_->NullConstant(); |
1981 | } |
1982 | |
1983 | JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry() { |
1984 | return flow_graph_builder_->BuildJoinEntry(); |
1985 | } |
1986 | |
1987 | JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) { |
1988 | return flow_graph_builder_->BuildJoinEntry(try_index); |
1989 | } |
1990 | |
1991 | Fragment StreamingFlowGraphBuilder::Goto(JoinEntryInstr* destination) { |
1992 | return flow_graph_builder_->Goto(destination); |
1993 | } |
1994 | |
1995 | Fragment StreamingFlowGraphBuilder::BuildImplicitClosureCreation( |
1996 | const Function& target) { |
1997 | return flow_graph_builder_->BuildImplicitClosureCreation(target); |
1998 | } |
1999 | |
2000 | Fragment StreamingFlowGraphBuilder::CheckBoolean(TokenPosition position) { |
2001 | return flow_graph_builder_->CheckBoolean(position); |
2002 | } |
2003 | |
2004 | Fragment StreamingFlowGraphBuilder::CheckArgumentType( |
2005 | LocalVariable* variable, |
2006 | const AbstractType& type) { |
2007 | return flow_graph_builder_->CheckAssignable( |
2008 | type, variable->name(), AssertAssignableInstr::kParameterCheck); |
2009 | } |
2010 | |
2011 | Fragment StreamingFlowGraphBuilder::EnterScope( |
2012 | intptr_t kernel_offset, |
2013 | const LocalScope** scope /* = nullptr */) { |
2014 | return flow_graph_builder_->EnterScope(kernel_offset, scope); |
2015 | } |
2016 | |
2017 | Fragment StreamingFlowGraphBuilder::ExitScope(intptr_t kernel_offset) { |
2018 | return flow_graph_builder_->ExitScope(kernel_offset); |
2019 | } |
2020 | |
2021 | TestFragment StreamingFlowGraphBuilder::TranslateConditionForControl() { |
2022 | // Skip all negations and go directly to the expression. |
2023 | bool negate = false; |
2024 | while (PeekTag() == kNot) { |
2025 | SkipBytes(1); |
2026 | negate = !negate; |
2027 | } |
2028 | |
2029 | TestFragment result; |
2030 | if (PeekTag() == kLogicalExpression) { |
2031 | // Handle '&&' and '||' operators specially to implement short circuit |
2032 | // evaluation. |
2033 | SkipBytes(1); // tag. |
2034 | |
2035 | TestFragment left = TranslateConditionForControl(); |
2036 | LogicalOperator op = static_cast<LogicalOperator>(ReadByte()); |
2037 | TestFragment right = TranslateConditionForControl(); |
2038 | |
2039 | result.entry = left.entry; |
2040 | if (op == kAnd) { |
2041 | left.CreateTrueSuccessor(flow_graph_builder_)->LinkTo(right.entry); |
2042 | result.true_successor_addresses = right.true_successor_addresses; |
2043 | result.false_successor_addresses = left.false_successor_addresses; |
2044 | result.false_successor_addresses->AddArray( |
2045 | *right.false_successor_addresses); |
2046 | } else { |
2047 | ASSERT(op == kOr); |
2048 | left.CreateFalseSuccessor(flow_graph_builder_)->LinkTo(right.entry); |
2049 | result.true_successor_addresses = left.true_successor_addresses; |
2050 | result.true_successor_addresses->AddArray( |
2051 | *right.true_successor_addresses); |
2052 | result.false_successor_addresses = right.false_successor_addresses; |
2053 | } |
2054 | } else { |
2055 | // Other expressions. |
2056 | TokenPosition position = TokenPosition::kNoSource; |
2057 | Fragment instructions = BuildExpression(&position); // read expression. |
2058 | |
2059 | // Check if the top of the stack is already a StrictCompare that |
2060 | // can be merged with a branch. Otherwise compare TOS with |
2061 | // true value and branch on that. |
2062 | BranchInstr* branch; |
2063 | if (stack()->definition()->IsStrictCompare() && |
2064 | stack()->definition() == instructions.current) { |
2065 | StrictCompareInstr* compare = Pop()->definition()->AsStrictCompare(); |
2066 | if (negate) { |
2067 | compare->NegateComparison(); |
2068 | negate = false; |
2069 | } |
2070 | branch = |
2071 | new (Z) BranchInstr(compare, flow_graph_builder_->GetNextDeoptId()); |
2072 | branch->comparison()->ClearTempIndex(); |
2073 | ASSERT(instructions.current->previous() != nullptr); |
2074 | instructions.current = instructions.current->previous(); |
2075 | } else { |
2076 | instructions += CheckBoolean(position); |
2077 | instructions += Constant(Bool::True()); |
2078 | Value* right_value = Pop(); |
2079 | Value* left_value = Pop(); |
2080 | StrictCompareInstr* compare = new (Z) StrictCompareInstr( |
2081 | TokenPosition::kNoSource, |
2082 | negate ? Token::kNE_STRICT : Token::kEQ_STRICT, left_value, |
2083 | right_value, false, flow_graph_builder_->GetNextDeoptId()); |
2084 | branch = |
2085 | new (Z) BranchInstr(compare, flow_graph_builder_->GetNextDeoptId()); |
2086 | negate = false; |
2087 | } |
2088 | instructions <<= branch; |
2089 | |
2090 | result = TestFragment(instructions.entry, branch); |
2091 | } |
2092 | |
2093 | return result.Negate(negate); |
2094 | } |
2095 | |
2096 | const TypeArguments& StreamingFlowGraphBuilder::BuildTypeArguments() { |
2097 | ReadUInt(); // read arguments count. |
2098 | intptr_t type_count = ReadListLength(); // read type count. |
2099 | return T.BuildTypeArguments(type_count); // read types. |
2100 | } |
2101 | |
2102 | Fragment StreamingFlowGraphBuilder::BuildArguments(Array* argument_names, |
2103 | intptr_t* argument_count, |
2104 | intptr_t* positional_count) { |
2105 | intptr_t dummy; |
2106 | if (argument_count == NULL) argument_count = &dummy; |
2107 | *argument_count = ReadUInt(); // read arguments count. |
2108 | |
2109 | // List of types. |
2110 | SkipListOfDartTypes(); // read list of types. |
2111 | |
2112 | { |
2113 | AlternativeReadingScope _(&reader_); |
2114 | if (positional_count == NULL) positional_count = &dummy; |
2115 | *positional_count = ReadListLength(); // read length of expression list |
2116 | } |
2117 | return BuildArgumentsFromActualArguments(argument_names); |
2118 | } |
2119 | |
2120 | Fragment StreamingFlowGraphBuilder::BuildArgumentsFromActualArguments( |
2121 | Array* argument_names) { |
2122 | Fragment instructions; |
2123 | |
2124 | // List of positional. |
2125 | intptr_t list_length = ReadListLength(); // read list length. |
2126 | for (intptr_t i = 0; i < list_length; ++i) { |
2127 | instructions += BuildExpression(); // read ith expression. |
2128 | } |
2129 | |
2130 | // List of named. |
2131 | list_length = ReadListLength(); // read list length. |
2132 | if (argument_names != NULL && list_length > 0) { |
2133 | *argument_names = Array::New(list_length, Heap::kOld); |
2134 | } |
2135 | for (intptr_t i = 0; i < list_length; ++i) { |
2136 | String& name = |
2137 | H.DartSymbolObfuscate(ReadStringReference()); // read ith name index. |
2138 | instructions += BuildExpression(); // read ith expression. |
2139 | if (argument_names != NULL) { |
2140 | argument_names->SetAt(i, name); |
2141 | } |
2142 | } |
2143 | |
2144 | return instructions; |
2145 | } |
2146 | |
2147 | Fragment StreamingFlowGraphBuilder::BuildInvalidExpression( |
2148 | TokenPosition* position) { |
2149 | // The frontend will take care of emitting normal errors (like |
2150 | // [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special |
2151 | // situations (e.g. an invalid annotation). |
2152 | TokenPosition pos = ReadPosition(); |
2153 | if (position != NULL) *position = pos; |
2154 | const String& message = H.DartString(ReadStringReference()); |
2155 | // Invalid expression message has pointer to the source code, no need to |
2156 | // report it twice. |
2157 | H.ReportError(script(), TokenPosition::kNoSource, "%s" , message.ToCString()); |
2158 | return Fragment(); |
2159 | } |
2160 | |
2161 | Fragment StreamingFlowGraphBuilder::BuildVariableGet(TokenPosition* position) { |
2162 | const TokenPosition pos = ReadPosition(); |
2163 | if (position != nullptr) *position = pos; |
2164 | intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
2165 | ReadUInt(); // read relative variable index. |
2166 | SkipOptionalDartType(); // read promoted type. |
2167 | return BuildVariableGetImpl(variable_kernel_position, pos); |
2168 | } |
2169 | |
2170 | Fragment StreamingFlowGraphBuilder::BuildVariableGet(uint8_t payload, |
2171 | TokenPosition* position) { |
2172 | const TokenPosition pos = ReadPosition(); |
2173 | if (position != nullptr) *position = pos; |
2174 | intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
2175 | return BuildVariableGetImpl(variable_kernel_position, pos); |
2176 | } |
2177 | |
2178 | Fragment StreamingFlowGraphBuilder::BuildVariableGetImpl( |
2179 | intptr_t variable_kernel_position, |
2180 | TokenPosition position) { |
2181 | LocalVariable* variable = LookupVariable(variable_kernel_position); |
2182 | if (!variable->is_late()) { |
2183 | return LoadLocal(variable); |
2184 | } |
2185 | |
2186 | // Late variable, so check whether it has been initialized already. |
2187 | Fragment instructions = LoadLocal(variable); |
2188 | TargetEntryInstr *is_uninitialized, *is_initialized; |
2189 | instructions += Constant(Object::sentinel()); |
2190 | instructions += flow_graph_builder_->BranchIfStrictEqual(&is_uninitialized, |
2191 | &is_initialized); |
2192 | JoinEntryInstr* join = BuildJoinEntry(); |
2193 | |
2194 | { |
2195 | AlternativeReadingScope alt(&reader_, variable->late_init_offset()); |
2196 | const bool has_initializer = (ReadTag() != kNothing); |
2197 | |
2198 | if (has_initializer) { |
2199 | // If the variable isn't initialized, call the initializer and set it. |
2200 | Fragment initialize(is_uninitialized); |
2201 | initialize += BuildExpression(); |
2202 | initialize += StoreLocal(position, variable); |
2203 | initialize += Drop(); |
2204 | initialize += Goto(join); |
2205 | } else { |
2206 | // The variable has no initializer, so throw a LateInitializationError. |
2207 | Fragment initialize(is_uninitialized); |
2208 | initialize += flow_graph_builder_->ThrowLateInitializationError( |
2209 | position, variable->name()); |
2210 | initialize += Goto(join); |
2211 | } |
2212 | } |
2213 | |
2214 | { |
2215 | // Already initialized, so there's nothing to do. |
2216 | Fragment already_initialized(is_initialized); |
2217 | already_initialized += Goto(join); |
2218 | } |
2219 | |
2220 | Fragment done = Fragment(instructions.entry, join); |
2221 | done += LoadLocal(variable); |
2222 | return done; |
2223 | } |
2224 | |
2225 | Fragment StreamingFlowGraphBuilder::BuildVariableSet(TokenPosition* p) { |
2226 | TokenPosition position = ReadPosition(); // read position. |
2227 | if (p != NULL) *p = position; |
2228 | |
2229 | intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
2230 | ReadUInt(); // read relative variable index. |
2231 | return BuildVariableSetImpl(position, variable_kernel_position); |
2232 | } |
2233 | |
2234 | Fragment StreamingFlowGraphBuilder::BuildVariableSet(uint8_t payload, |
2235 | TokenPosition* p) { |
2236 | TokenPosition position = ReadPosition(); // read position. |
2237 | if (p != NULL) *p = position; |
2238 | |
2239 | intptr_t variable_kernel_position = ReadUInt(); // read kernel position. |
2240 | return BuildVariableSetImpl(position, variable_kernel_position); |
2241 | } |
2242 | |
2243 | Fragment StreamingFlowGraphBuilder::BuildVariableSetImpl( |
2244 | TokenPosition position, |
2245 | intptr_t variable_kernel_position) { |
2246 | Fragment instructions = BuildExpression(); // read expression. |
2247 | if (NeedsDebugStepCheck(stack(), position)) { |
2248 | instructions = DebugStepCheck(position) + instructions; |
2249 | } |
2250 | |
2251 | LocalVariable* variable = LookupVariable(variable_kernel_position); |
2252 | if (variable->is_late() && variable->is_final()) { |
2253 | // Late final variable, so check whether it has been initialized. |
2254 | LocalVariable* expr_temp = MakeTemporary(); |
2255 | instructions += LoadLocal(variable); |
2256 | TargetEntryInstr *is_uninitialized, *is_initialized; |
2257 | instructions += Constant(Object::sentinel()); |
2258 | instructions += flow_graph_builder_->BranchIfStrictEqual(&is_uninitialized, |
2259 | &is_initialized); |
2260 | JoinEntryInstr* join = BuildJoinEntry(); |
2261 | |
2262 | { |
2263 | // The variable is uninitialized, so store the expression value. |
2264 | Fragment initialize(is_uninitialized); |
2265 | initialize += LoadLocal(expr_temp); |
2266 | initialize += StoreLocal(position, variable); |
2267 | initialize += Drop(); |
2268 | initialize += Goto(join); |
2269 | } |
2270 | |
2271 | { |
2272 | // Already initialized, so throw a LateInitializationError. |
2273 | Fragment already_initialized(is_initialized); |
2274 | already_initialized += flow_graph_builder_->ThrowLateInitializationError( |
2275 | position, variable->name()); |
2276 | already_initialized += Goto(join); |
2277 | } |
2278 | |
2279 | instructions = Fragment(instructions.entry, join); |
2280 | } else { |
2281 | instructions += StoreLocal(position, variable); |
2282 | } |
2283 | |
2284 | return instructions; |
2285 | } |
2286 | |
2287 | Fragment StreamingFlowGraphBuilder::BuildPropertyGet(TokenPosition* p) { |
2288 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
2289 | const TokenPosition position = ReadPosition(); // read position. |
2290 | if (p != NULL) *p = position; |
2291 | |
2292 | const DirectCallMetadata direct_call = |
2293 | direct_call_metadata_helper_.GetDirectTargetForPropertyGet(offset); |
2294 | const InferredTypeMetadata result_type = |
2295 | inferred_type_metadata_helper_.GetInferredType(offset); |
2296 | |
2297 | Fragment instructions = BuildExpression(); // read receiver. |
2298 | |
2299 | LocalVariable* receiver = NULL; |
2300 | if (direct_call.check_receiver_for_null_) { |
2301 | // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
2302 | receiver = MakeTemporary(); |
2303 | instructions += LoadLocal(receiver); |
2304 | } |
2305 | |
2306 | const String& getter_name = ReadNameAsGetterName(); // read name. |
2307 | |
2308 | const Function* interface_target = &Function::null_function(); |
2309 | const Function* tearoff_interface_target = &Function::null_function(); |
2310 | const NameIndex itarget_name = |
2311 | ReadInterfaceMemberNameReference(); // read interface_target_reference. |
2312 | if (!H.IsRoot(itarget_name) && |
2313 | (H.IsGetter(itarget_name) || H.IsField(itarget_name))) { |
2314 | interface_target = &Function::ZoneHandle( |
2315 | Z, |
2316 | H.LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name))); |
2317 | ASSERT(getter_name.raw() == interface_target->name()); |
2318 | } else if (!H.IsRoot(itarget_name) && H.IsMethod(itarget_name)) { |
2319 | tearoff_interface_target = &Function::ZoneHandle( |
2320 | Z, |
2321 | H.LookupMethodByMember(itarget_name, H.DartMethodName(itarget_name))); |
2322 | } |
2323 | |
2324 | if (direct_call.check_receiver_for_null_) { |
2325 | instructions += CheckNull(position, receiver, getter_name); |
2326 | } |
2327 | |
2328 | const String* mangled_name = &getter_name; |
2329 | const Function* direct_call_target = &direct_call.target_; |
2330 | if (H.IsRoot(itarget_name)) { |
2331 | mangled_name = &String::ZoneHandle( |
2332 | Z, Function::CreateDynamicInvocationForwarderName(getter_name)); |
2333 | if (!direct_call_target->IsNull()) { |
2334 | direct_call_target = &Function::ZoneHandle( |
2335 | direct_call.target_.GetDynamicInvocationForwarder(*mangled_name)); |
2336 | } |
2337 | } |
2338 | |
2339 | if (!direct_call_target->IsNull()) { |
2340 | ASSERT(CompilerState::Current().is_aot()); |
2341 | instructions += |
2342 | StaticCall(position, *direct_call_target, 1, Array::null_array(), |
2343 | ICData::kNoRebind, &result_type); |
2344 | } else { |
2345 | const intptr_t kTypeArgsLen = 0; |
2346 | const intptr_t kNumArgsChecked = 1; |
2347 | instructions += |
2348 | InstanceCall(position, *mangled_name, Token::kGET, kTypeArgsLen, 1, |
2349 | Array::null_array(), kNumArgsChecked, *interface_target, |
2350 | *tearoff_interface_target, &result_type); |
2351 | } |
2352 | |
2353 | if (direct_call.check_receiver_for_null_) { |
2354 | instructions += DropTempsPreserveTop(1); // Drop receiver, preserve result. |
2355 | } |
2356 | |
2357 | return instructions; |
2358 | } |
2359 | |
2360 | Fragment StreamingFlowGraphBuilder::BuildPropertySet(TokenPosition* p) { |
2361 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
2362 | |
2363 | const DirectCallMetadata direct_call = |
2364 | direct_call_metadata_helper_.GetDirectTargetForPropertySet(offset); |
2365 | const CallSiteAttributesMetadata call_site_attributes = |
2366 | call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset); |
2367 | const InferredTypeMetadata inferred_type = |
2368 | inferred_type_metadata_helper_.GetInferredType(offset); |
2369 | |
2370 | // True if callee can skip argument type checks. |
2371 | bool is_unchecked_call = inferred_type.IsSkipCheck(); |
2372 | if (call_site_attributes.receiver_type != nullptr && |
2373 | call_site_attributes.receiver_type->HasTypeClass() && |
2374 | !Class::Handle(call_site_attributes.receiver_type->type_class()) |
2375 | .IsGeneric()) { |
2376 | is_unchecked_call = true; |
2377 | } |
2378 | |
2379 | Fragment instructions(MakeTemp()); |
2380 | LocalVariable* variable = MakeTemporary(); |
2381 | |
2382 | const TokenPosition position = ReadPosition(); // read position. |
2383 | if (p != nullptr) *p = position; |
2384 | |
2385 | if (PeekTag() == kThisExpression) { |
2386 | is_unchecked_call = true; |
2387 | } |
2388 | instructions += BuildExpression(); // read receiver. |
2389 | |
2390 | LocalVariable* receiver = nullptr; |
2391 | if (direct_call.check_receiver_for_null_) { |
2392 | // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
2393 | receiver = MakeTemporary(); |
2394 | instructions += LoadLocal(receiver); |
2395 | } |
2396 | |
2397 | const String& setter_name = ReadNameAsSetterName(); // read name. |
2398 | |
2399 | instructions += BuildExpression(); // read value. |
2400 | instructions += StoreLocal(TokenPosition::kNoSource, variable); |
2401 | |
2402 | const Function* interface_target = &Function::null_function(); |
2403 | const NameIndex itarget_name = |
2404 | ReadInterfaceMemberNameReference(); // read interface_target_reference. |
2405 | if (!H.IsRoot(itarget_name)) { |
2406 | interface_target = &Function::ZoneHandle( |
2407 | Z, |
2408 | H.LookupMethodByMember(itarget_name, H.DartSetterName(itarget_name))); |
2409 | ASSERT(setter_name.raw() == interface_target->name()); |
2410 | } |
2411 | |
2412 | if (direct_call.check_receiver_for_null_) { |
2413 | instructions += CheckNull(position, receiver, setter_name); |
2414 | } |
2415 | |
2416 | const String* mangled_name = &setter_name; |
2417 | const Function* direct_call_target = &direct_call.target_; |
2418 | if (H.IsRoot(itarget_name)) { |
2419 | mangled_name = &String::ZoneHandle( |
2420 | Z, Function::CreateDynamicInvocationForwarderName(setter_name)); |
2421 | if (!direct_call_target->IsNull()) { |
2422 | direct_call_target = &Function::ZoneHandle( |
2423 | direct_call.target_.GetDynamicInvocationForwarder(*mangled_name)); |
2424 | } |
2425 | } |
2426 | |
2427 | if (!direct_call_target->IsNull()) { |
2428 | ASSERT(CompilerState::Current().is_aot()); |
2429 | instructions += |
2430 | StaticCall(position, *direct_call_target, 2, Array::null_array(), |
2431 | ICData::kNoRebind, /*result_type=*/nullptr, |
2432 | /*type_args_count=*/0, |
2433 | /*use_unchecked_entry=*/is_unchecked_call); |
2434 | } else { |
2435 | const intptr_t kTypeArgsLen = 0; |
2436 | const intptr_t kNumArgsChecked = 1; |
2437 | |
2438 | instructions += InstanceCall( |
2439 | position, *mangled_name, Token::kSET, kTypeArgsLen, 2, |
2440 | Array::null_array(), kNumArgsChecked, *interface_target, |
2441 | Function::null_function(), |
2442 | /*result_type=*/nullptr, |
2443 | /*use_unchecked_entry=*/is_unchecked_call, &call_site_attributes); |
2444 | } |
2445 | |
2446 | instructions += Drop(); // Drop result of the setter invocation. |
2447 | |
2448 | if (direct_call.check_receiver_for_null_) { |
2449 | instructions += Drop(); // Drop receiver. |
2450 | } |
2451 | |
2452 | return instructions; |
2453 | } |
2454 | |
2455 | static Function& GetNoSuchMethodOrDie(Zone* zone, const Class& klass) { |
2456 | Function& nsm_function = Function::Handle(zone); |
2457 | Class& iterate_klass = Class::Handle(zone, klass.raw()); |
2458 | while (!iterate_klass.IsNull()) { |
2459 | nsm_function = iterate_klass.LookupDynamicFunction(Symbols::NoSuchMethod()); |
2460 | if (!nsm_function.IsNull() && nsm_function.NumParameters() == 2 && |
2461 | nsm_function.NumTypeParameters() == 0) { |
2462 | break; |
2463 | } |
2464 | iterate_klass = iterate_klass.SuperClass(); |
2465 | } |
2466 | // We are guaranteed to find noSuchMethod of class Object. |
2467 | ASSERT(!nsm_function.IsNull()); |
2468 | |
2469 | return nsm_function; |
2470 | } |
2471 | |
2472 | // Note, that this will always mark `super` flag to true. |
2473 | Fragment StreamingFlowGraphBuilder::BuildAllocateInvocationMirrorCall( |
2474 | TokenPosition position, |
2475 | const String& name, |
2476 | intptr_t num_type_arguments, |
2477 | intptr_t num_arguments, |
2478 | const Array& argument_names, |
2479 | LocalVariable* actuals_array, |
2480 | Fragment build_rest_of_actuals) { |
2481 | Fragment instructions; |
2482 | |
2483 | // Populate array containing the actual arguments. Just add [this] here. |
2484 | instructions += LoadLocal(actuals_array); // array |
2485 | instructions += IntConstant(num_type_arguments == 0 ? 0 : 1); // index |
2486 | instructions += LoadLocal(parsed_function()->receiver_var()); // receiver |
2487 | instructions += StoreIndexed(kArrayCid); |
2488 | instructions += build_rest_of_actuals; |
2489 | |
2490 | // First argument is receiver. |
2491 | instructions += LoadLocal(parsed_function()->receiver_var()); |
2492 | |
2493 | // Push the arguments for allocating the invocation mirror: |
2494 | // - the name. |
2495 | instructions += Constant(String::ZoneHandle(Z, name.raw())); |
2496 | |
2497 | // - the arguments descriptor. |
2498 | const Array& args_descriptor = |
2499 | Array::Handle(Z, ArgumentsDescriptor::NewBoxed( |
2500 | num_type_arguments, num_arguments, argument_names)); |
2501 | instructions += Constant(Array::ZoneHandle(Z, args_descriptor.raw())); |
2502 | |
2503 | // - an array containing the actual arguments. |
2504 | instructions += LoadLocal(actuals_array); |
2505 | |
2506 | // - [true] indicating this is a `super` NoSuchMethod. |
2507 | instructions += Constant(Bool::True()); |
2508 | |
2509 | const Class& mirror_class = |
2510 | Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror())); |
2511 | ASSERT(!mirror_class.IsNull()); |
2512 | const Function& allocation_function = Function::ZoneHandle( |
2513 | Z, mirror_class.LookupStaticFunction( |
2514 | Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror()))); |
2515 | ASSERT(!allocation_function.IsNull()); |
2516 | instructions += StaticCall(position, allocation_function, |
2517 | /* argument_count = */ 4, ICData::kStatic); |
2518 | return instructions; |
2519 | } |
2520 | |
2521 | Fragment StreamingFlowGraphBuilder::BuildSuperPropertyGet(TokenPosition* p) { |
2522 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
2523 | const TokenPosition position = ReadPosition(); // read position. |
2524 | if (p != NULL) *p = position; |
2525 | |
2526 | const InferredTypeMetadata result_type = |
2527 | inferred_type_metadata_helper_.GetInferredType(offset); |
2528 | |
2529 | Class& klass = GetSuperOrDie(); |
2530 | |
2531 | StringIndex name_index = ReadStringReference(); // read name index. |
2532 | NameIndex library_reference = |
2533 | ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_') |
2534 | ? ReadCanonicalNameReference() // read library index. |
2535 | : NameIndex(); |
2536 | const String& getter_name = H.DartGetterName(library_reference, name_index); |
2537 | const String& method_name = H.DartMethodName(library_reference, name_index); |
2538 | |
2539 | SkipInterfaceMemberNameReference(); // skip target_reference. |
2540 | |
2541 | // Search the superclass chain for the selector looking for either getter or |
2542 | // method. |
2543 | Function& function = Function::Handle(Z); |
2544 | while (!klass.IsNull()) { |
2545 | function = klass.LookupDynamicFunction(method_name); |
2546 | if (!function.IsNull()) { |
2547 | Function& target = |
2548 | Function::ZoneHandle(Z, function.ImplicitClosureFunction()); |
2549 | ASSERT(!target.IsNull()); |
2550 | // Generate inline code for allocation closure object with context |
2551 | // which captures `this`. |
2552 | return BuildImplicitClosureCreation(target); |
2553 | } |
2554 | function = klass.LookupDynamicFunction(getter_name); |
2555 | if (!function.IsNull()) break; |
2556 | klass = klass.SuperClass(); |
2557 | } |
2558 | |
2559 | Fragment instructions; |
2560 | if (klass.IsNull()) { |
2561 | instructions += |
2562 | Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
2563 | instructions += IntConstant(1); // array size |
2564 | instructions += CreateArray(); |
2565 | LocalVariable* actuals_array = MakeTemporary(); |
2566 | |
2567 | Class& parent_klass = GetSuperOrDie(); |
2568 | |
2569 | instructions += BuildAllocateInvocationMirrorCall( |
2570 | position, getter_name, |
2571 | /* num_type_arguments = */ 0, |
2572 | /* num_arguments = */ 1, |
2573 | /* argument_names = */ Object::empty_array(), actuals_array, |
2574 | /* build_rest_of_actuals = */ Fragment()); |
2575 | |
2576 | Function& nsm_function = GetNoSuchMethodOrDie(Z, parent_klass); |
2577 | instructions += |
2578 | StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()), |
2579 | /* argument_count = */ 2, ICData::kNSMDispatch); |
2580 | instructions += DropTempsPreserveTop(1); // Drop array |
2581 | } else { |
2582 | ASSERT(!klass.IsNull()); |
2583 | ASSERT(!function.IsNull()); |
2584 | |
2585 | instructions += LoadLocal(parsed_function()->receiver_var()); |
2586 | |
2587 | instructions += |
2588 | StaticCall(position, Function::ZoneHandle(Z, function.raw()), |
2589 | /* argument_count = */ 1, Array::null_array(), |
2590 | ICData::kSuper, &result_type); |
2591 | } |
2592 | |
2593 | return instructions; |
2594 | } |
2595 | |
2596 | Fragment StreamingFlowGraphBuilder::BuildSuperPropertySet(TokenPosition* p) { |
2597 | const TokenPosition position = ReadPosition(); // read position. |
2598 | if (p != NULL) *p = position; |
2599 | |
2600 | Class& klass = GetSuperOrDie(); |
2601 | |
2602 | const String& setter_name = ReadNameAsSetterName(); // read name. |
2603 | |
2604 | Function& function = |
2605 | Function::Handle(Z, H.LookupDynamicFunction(klass, setter_name)); |
2606 | |
2607 | Fragment instructions(MakeTemp()); |
2608 | LocalVariable* value = MakeTemporary(); // this holds RHS value |
2609 | |
2610 | if (function.IsNull()) { |
2611 | instructions += |
2612 | Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
2613 | instructions += IntConstant(2); // array size |
2614 | instructions += CreateArray(); |
2615 | LocalVariable* actuals_array = MakeTemporary(); |
2616 | |
2617 | Fragment build_rest_of_actuals; |
2618 | build_rest_of_actuals += LoadLocal(actuals_array); // array |
2619 | build_rest_of_actuals += IntConstant(1); // index |
2620 | build_rest_of_actuals += BuildExpression(); // value. |
2621 | build_rest_of_actuals += StoreLocal(position, value); |
2622 | build_rest_of_actuals += StoreIndexed(kArrayCid); |
2623 | |
2624 | instructions += BuildAllocateInvocationMirrorCall( |
2625 | position, setter_name, /* num_type_arguments = */ 0, |
2626 | /* num_arguments = */ 2, |
2627 | /* argument_names = */ Object::empty_array(), actuals_array, |
2628 | build_rest_of_actuals); |
2629 | |
2630 | SkipInterfaceMemberNameReference(); // skip target_reference. |
2631 | |
2632 | Function& nsm_function = GetNoSuchMethodOrDie(Z, klass); |
2633 | instructions += |
2634 | StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()), |
2635 | /* argument_count = */ 2, ICData::kNSMDispatch); |
2636 | instructions += Drop(); // Drop result of NoSuchMethod invocation |
2637 | instructions += Drop(); // Drop array |
2638 | } else { |
2639 | // receiver |
2640 | instructions += LoadLocal(parsed_function()->receiver_var()); |
2641 | |
2642 | instructions += BuildExpression(); // read value. |
2643 | instructions += StoreLocal(position, value); |
2644 | |
2645 | SkipInterfaceMemberNameReference(); // skip target_reference. |
2646 | |
2647 | instructions += StaticCall( |
2648 | position, Function::ZoneHandle(Z, function.raw()), |
2649 | /* argument_count = */ 2, Array::null_array(), ICData::kSuper, |
2650 | /*result_type=*/nullptr, /*type_args_len=*/0, |
2651 | /*use_unchecked_entry=*/true); |
2652 | instructions += Drop(); // Drop result of the setter invocation. |
2653 | } |
2654 | |
2655 | return instructions; |
2656 | } |
2657 | |
2658 | Fragment StreamingFlowGraphBuilder::BuildDirectPropertyGet(TokenPosition* p) { |
2659 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
2660 | const TokenPosition position = ReadPosition(); // read position. |
2661 | if (p != NULL) *p = position; |
2662 | |
2663 | const InferredTypeMetadata result_type = |
2664 | inferred_type_metadata_helper_.GetInferredType(offset); |
2665 | |
2666 | const Tag receiver_tag = PeekTag(); // peek tag for receiver. |
2667 | Fragment instructions = BuildExpression(); // read receiver. |
2668 | const NameIndex kernel_name = |
2669 | ReadInterfaceMemberNameReference(); // read target_reference. |
2670 | |
2671 | Function& target = Function::ZoneHandle(Z); |
2672 | if (H.IsProcedure(kernel_name)) { |
2673 | if (H.IsGetter(kernel_name)) { |
2674 | target = |
2675 | H.LookupMethodByMember(kernel_name, H.DartGetterName(kernel_name)); |
2676 | } else if (receiver_tag == kThisExpression) { |
2677 | // Undo stack change for the BuildExpression. |
2678 | Pop(); |
2679 | |
2680 | target = |
2681 | H.LookupMethodByMember(kernel_name, H.DartMethodName(kernel_name)); |
2682 | target = target.ImplicitClosureFunction(); |
2683 | ASSERT(!target.IsNull()); |
2684 | |
2685 | // Generate inline code for allocating closure object with context which |
2686 | // captures `this`. |
2687 | return BuildImplicitClosureCreation(target); |
2688 | } else { |
2689 | // Need to create implicit closure (tear-off), receiver != this. |
2690 | // Ensure method extractor exists and call it directly. |
2691 | const Function& target_method = Function::ZoneHandle( |
2692 | Z, |
2693 | H.LookupMethodByMember(kernel_name, H.DartMethodName(kernel_name))); |
2694 | const String& getter_name = H.DartGetterName(kernel_name); |
2695 | target = target_method.GetMethodExtractor(getter_name); |
2696 | } |
2697 | } else { |
2698 | ASSERT(H.IsField(kernel_name)); |
2699 | const String& getter_name = H.DartGetterName(kernel_name); |
2700 | target = H.LookupMethodByMember(kernel_name, getter_name); |
2701 | ASSERT(target.IsGetterFunction() || target.IsImplicitGetterFunction()); |
2702 | } |
2703 | |
2704 | // Static calls are marked as "no-rebind", which is currently safe because |
2705 | // DirectPropertyGet are only used in enums (index in toString) and enums |
2706 | // can't change their structure during hot reload. |
2707 | // If there are other sources of DirectPropertyGet in the future, this code |
2708 | // have to be adjusted. |
2709 | return instructions + StaticCall(position, target, 1, Array::null_array(), |
2710 | ICData::kNoRebind, &result_type); |
2711 | } |
2712 | |
2713 | Fragment StreamingFlowGraphBuilder::BuildDirectPropertySet(TokenPosition* p) { |
2714 | const TokenPosition position = ReadPosition(); // read position. |
2715 | if (p != NULL) *p = position; |
2716 | |
2717 | Fragment instructions(MakeTemp()); |
2718 | LocalVariable* value = MakeTemporary(); |
2719 | |
2720 | instructions += BuildExpression(); // read receiver. |
2721 | |
2722 | const NameIndex target_reference = |
2723 | ReadInterfaceMemberNameReference(); // read target_reference. |
2724 | const String& method_name = H.DartSetterName(target_reference); |
2725 | const Function& target = Function::ZoneHandle( |
2726 | Z, H.LookupMethodByMember(target_reference, method_name)); |
2727 | ASSERT(target.IsSetterFunction() || target.IsImplicitSetterFunction()); |
2728 | |
2729 | instructions += BuildExpression(); // read value. |
2730 | instructions += StoreLocal(TokenPosition::kNoSource, value); |
2731 | |
2732 | // Static calls are marked as "no-rebind", which is currently safe because |
2733 | // DirectPropertyGet are only used in enums (index in toString) and enums |
2734 | // can't change their structure during hot reload. |
2735 | // If there are other sources of DirectPropertyGet in the future, this code |
2736 | // have to be adjusted. |
2737 | instructions += |
2738 | StaticCall(position, target, 2, Array::null_array(), ICData::kNoRebind, |
2739 | /* result_type = */ NULL); |
2740 | |
2741 | return instructions + Drop(); |
2742 | } |
2743 | |
2744 | Fragment StreamingFlowGraphBuilder::BuildStaticGet(TokenPosition* p) { |
2745 | ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull()); |
2746 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
2747 | |
2748 | TokenPosition position = ReadPosition(); // read position. |
2749 | if (p != NULL) *p = position; |
2750 | |
2751 | const InferredTypeMetadata result_type = |
2752 | inferred_type_metadata_helper_.GetInferredType(offset); |
2753 | |
2754 | NameIndex target = ReadCanonicalNameReference(); // read target_reference. |
2755 | |
2756 | if (H.IsField(target)) { |
2757 | const Field& field = |
2758 | Field::ZoneHandle(Z, H.LookupFieldByKernelField(target)); |
2759 | if (field.is_const()) { |
2760 | // Since the CFE inlines all references to const variables and fields, |
2761 | // it never emits a StaticGet of a const field. |
2762 | // This situation only arises because of the static const fields in |
2763 | // the ClassID class, which are generated internally in the VM |
2764 | // during loading. See also Class::InjectCIDFields. |
2765 | ASSERT(Class::Handle(field.Owner()).library() == |
2766 | Library::InternalLibrary() && |
2767 | Class::Handle(field.Owner()).Name() == Symbols::ClassID().raw()); |
2768 | return Constant(Instance::ZoneHandle(Z, field.StaticValue())); |
2769 | } else { |
2770 | const Class& owner = Class::Handle(Z, field.Owner()); |
2771 | const String& getter_name = H.DartGetterName(target); |
2772 | const Function& getter = |
2773 | Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name)); |
2774 | if (!getter.IsNull() && field.NeedsGetter()) { |
2775 | return StaticCall(position, getter, 0, Array::null_array(), |
2776 | ICData::kStatic, &result_type); |
2777 | } else { |
2778 | if (result_type.IsConstant()) { |
2779 | return Constant(result_type.constant_value); |
2780 | } |
2781 | return LoadStaticField(field, /*calls_initializer=*/false); |
2782 | } |
2783 | } |
2784 | } else { |
2785 | const Function& function = |
2786 | Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
2787 | |
2788 | if (H.IsGetter(target)) { |
2789 | return StaticCall(position, function, 0, Array::null_array(), |
2790 | ICData::kStatic, &result_type); |
2791 | } else if (H.IsMethod(target)) { |
2792 | const auto& closure_function = |
2793 | Function::Handle(Z, function.ImplicitClosureFunction()); |
2794 | const auto& static_closure = |
2795 | Instance::Handle(Z, closure_function.ImplicitStaticClosure()); |
2796 | return Constant(Instance::ZoneHandle(Z, H.Canonicalize(static_closure))); |
2797 | } else { |
2798 | UNIMPLEMENTED(); |
2799 | } |
2800 | } |
2801 | |
2802 | return Fragment(); |
2803 | } |
2804 | |
2805 | Fragment StreamingFlowGraphBuilder::BuildStaticSet(TokenPosition* p) { |
2806 | TokenPosition position = ReadPosition(); // read position. |
2807 | if (p != NULL) *p = position; |
2808 | |
2809 | NameIndex target = ReadCanonicalNameReference(); // read target_reference. |
2810 | |
2811 | if (H.IsField(target)) { |
2812 | const Field& field = |
2813 | Field::ZoneHandle(Z, H.LookupFieldByKernelField(target)); |
2814 | const Class& owner = Class::Handle(Z, field.Owner()); |
2815 | const String& setter_name = H.DartSetterName(target); |
2816 | const Function& setter = |
2817 | Function::ZoneHandle(Z, owner.LookupStaticFunction(setter_name)); |
2818 | Fragment instructions = BuildExpression(); // read expression. |
2819 | if (NeedsDebugStepCheck(stack(), position)) { |
2820 | instructions = DebugStepCheck(position) + instructions; |
2821 | } |
2822 | LocalVariable* variable = MakeTemporary(); |
2823 | instructions += LoadLocal(variable); |
2824 | if (!setter.IsNull() && field.NeedsSetter()) { |
2825 | instructions += StaticCall(position, setter, 1, ICData::kStatic); |
2826 | instructions += Drop(); |
2827 | } else { |
2828 | instructions += StoreStaticField(position, field); |
2829 | } |
2830 | return instructions; |
2831 | } else { |
2832 | ASSERT(H.IsProcedure(target)); |
2833 | |
2834 | // Evaluate the expression on the right hand side. |
2835 | Fragment instructions = BuildExpression(); // read expression. |
2836 | LocalVariable* variable = MakeTemporary(); |
2837 | |
2838 | // Prepare argument. |
2839 | instructions += LoadLocal(variable); |
2840 | |
2841 | // Invoke the setter function. |
2842 | const Function& function = |
2843 | Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
2844 | instructions += StaticCall(position, function, 1, ICData::kStatic); |
2845 | |
2846 | // Drop the unused result & leave the stored value on the stack. |
2847 | return instructions + Drop(); |
2848 | } |
2849 | } |
2850 | |
2851 | Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) { |
2852 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
2853 | const TokenPosition position = ReadPosition(); // read position. |
2854 | if (p != NULL) *p = position; |
2855 | |
2856 | const DirectCallMetadata direct_call = |
2857 | direct_call_metadata_helper_.GetDirectTargetForMethodInvocation(offset); |
2858 | const InferredTypeMetadata result_type = |
2859 | inferred_type_metadata_helper_.GetInferredType(offset); |
2860 | const CallSiteAttributesMetadata call_site_attributes = |
2861 | call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset); |
2862 | |
2863 | const Tag receiver_tag = PeekTag(); // peek tag for receiver. |
2864 | |
2865 | bool is_unchecked_closure_call = false; |
2866 | bool is_unchecked_call = result_type.IsSkipCheck(); |
2867 | if (call_site_attributes.receiver_type != nullptr) { |
2868 | if (call_site_attributes.receiver_type->IsFunctionType()) { |
2869 | AlternativeReadingScope alt(&reader_); |
2870 | SkipExpression(); // skip receiver |
2871 | is_unchecked_closure_call = |
2872 | ReadNameAsMethodName().Equals(Symbols::Call()); |
2873 | } else if (call_site_attributes.receiver_type->HasTypeClass() && |
2874 | !call_site_attributes.receiver_type->IsDynamicType() && |
2875 | !Class::Handle(call_site_attributes.receiver_type->type_class()) |
2876 | .IsGeneric()) { |
2877 | is_unchecked_call = true; |
2878 | } |
2879 | } |
2880 | |
2881 | Fragment instructions; |
2882 | |
2883 | intptr_t type_args_len = 0; |
2884 | LocalVariable* type_arguments_temp = NULL; |
2885 | { |
2886 | AlternativeReadingScope alt(&reader_); |
2887 | SkipExpression(); // skip receiver |
2888 | SkipName(); // skip method name |
2889 | ReadUInt(); // read argument count. |
2890 | intptr_t list_length = ReadListLength(); // read types list length. |
2891 | if (list_length > 0) { |
2892 | const TypeArguments& type_arguments = |
2893 | T.BuildTypeArguments(list_length); // read types. |
2894 | instructions += TranslateInstantiatedTypeArguments(type_arguments); |
2895 | if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) { |
2896 | // Don't yet push type arguments if we need to check receiver for null. |
2897 | // In this case receiver will be duplicated so instead of pushing |
2898 | // type arguments here we need to push it between receiver_temp |
2899 | // and actual receiver. See the code below. |
2900 | type_arguments_temp = MakeTemporary(); |
2901 | } |
2902 | } |
2903 | type_args_len = list_length; |
2904 | } |
2905 | |
2906 | // Take note of whether the invocation is against the receiver of the current |
2907 | // function: in this case, we may skip some type checks in the callee. |
2908 | if (PeekTag() == kThisExpression) { |
2909 | is_unchecked_call = true; |
2910 | } |
2911 | instructions += BuildExpression(); // read receiver. |
2912 | |
2913 | const String& name = ReadNameAsMethodName(); // read name. |
2914 | const Token::Kind token_kind = |
2915 | MethodTokenRecognizer::RecognizeTokenKind(name); |
2916 | |
2917 | // Detect comparison with null. |
2918 | if ((token_kind == Token::kEQ || token_kind == Token::kNE) && |
2919 | PeekArgumentsCount() == 1 && |
2920 | (receiver_tag == kNullLiteral || |
2921 | PeekArgumentsFirstPositionalTag() == kNullLiteral)) { |
2922 | ASSERT(type_args_len == 0); |
2923 | // "==" or "!=" with null on either side. |
2924 | instructions += |
2925 | BuildArguments(NULL /* named */, NULL /* arg count */, |
2926 | NULL /* positional arg count */); // read arguments. |
2927 | SkipInterfaceMemberNameReference(); // read interface_target_reference. |
2928 | Token::Kind strict_cmp_kind = |
2929 | token_kind == Token::kEQ ? Token::kEQ_STRICT : Token::kNE_STRICT; |
2930 | return instructions + |
2931 | StrictCompare(position, strict_cmp_kind, /*number_check = */ true); |
2932 | } |
2933 | |
2934 | LocalVariable* receiver_temp = NULL; |
2935 | if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) { |
2936 | // Duplicate receiver for CheckNull before it is consumed by PushArgument. |
2937 | receiver_temp = MakeTemporary(); |
2938 | if (type_arguments_temp != NULL) { |
2939 | // If call has type arguments then push them before pushing the receiver. |
2940 | // The stack will contain: |
2941 | // |
2942 | // [type_arguments_temp][receiver_temp][type_arguments][receiver] ... |
2943 | // |
2944 | instructions += LoadLocal(type_arguments_temp); |
2945 | } |
2946 | instructions += LoadLocal(receiver_temp); |
2947 | } |
2948 | |
2949 | intptr_t argument_count; |
2950 | intptr_t positional_argument_count; |
2951 | Array& argument_names = Array::ZoneHandle(Z); |
2952 | instructions += |
2953 | BuildArguments(&argument_names, &argument_count, |
2954 | &positional_argument_count); // read arguments. |
2955 | ++argument_count; // include receiver |
2956 | |
2957 | intptr_t checked_argument_count = 1; |
2958 | // If we have a special operation (e.g. +/-/==) we mark both arguments as |
2959 | // to be checked. |
2960 | if (token_kind != Token::kILLEGAL) { |
2961 | ASSERT(argument_count <= 2); |
2962 | checked_argument_count = argument_count; |
2963 | } |
2964 | |
2965 | const Function* interface_target = &Function::null_function(); |
2966 | const NameIndex itarget_name = |
2967 | ReadInterfaceMemberNameReference(); // read interface_target_reference. |
2968 | // TODO(dartbug.com/34497): Once front-end desugars calls via |
2969 | // fields/getters, filtering of field and getter interface targets here |
2970 | // can be turned into assertions. |
2971 | if (!H.IsRoot(itarget_name) && !H.IsField(itarget_name) && |
2972 | !H.IsGetter(itarget_name)) { |
2973 | interface_target = &Function::ZoneHandle( |
2974 | Z, H.LookupMethodByMember(itarget_name, |
2975 | H.DartProcedureName(itarget_name))); |
2976 | ASSERT(name.raw() == interface_target->name()); |
2977 | ASSERT(!interface_target->IsGetterFunction()); |
2978 | } |
2979 | |
2980 | // TODO(sjindel): Avoid the check for null on unchecked closure calls if TFA |
2981 | // allows. |
2982 | if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) { |
2983 | // Receiver temp is needed to load the function to call from the closure. |
2984 | instructions += CheckNull(position, receiver_temp, name, |
2985 | /*clear_temp=*/!is_unchecked_closure_call); |
2986 | } |
2987 | |
2988 | const String* mangled_name = &name; |
2989 | // Do not mangle ==: |
2990 | // * operator == takes an Object so its either not checked or checked |
2991 | // at the entry because the parameter is marked covariant, neither of |
2992 | // those cases require a dynamic invocation forwarder. |
2993 | const Function* direct_call_target = &direct_call.target_; |
2994 | if (H.IsRoot(itarget_name) && |
2995 | (name.raw() != Symbols::EqualOperator().raw())) { |
2996 | mangled_name = &String::ZoneHandle( |
2997 | Z, Function::CreateDynamicInvocationForwarderName(name)); |
2998 | if (!direct_call_target->IsNull()) { |
2999 | direct_call_target = &Function::ZoneHandle( |
3000 | direct_call_target->GetDynamicInvocationForwarder(*mangled_name)); |
3001 | } |
3002 | } |
3003 | |
3004 | if (is_unchecked_closure_call) { |
3005 | // Lookup the function in the closure. |
3006 | instructions += LoadLocal(receiver_temp); |
3007 | instructions += LoadNativeField(Slot::Closure_function()); |
3008 | if (parsed_function()->function().is_debuggable()) { |
3009 | ASSERT(!parsed_function()->function().is_native()); |
3010 | instructions += DebugStepCheck(position); |
3011 | } |
3012 | instructions += |
3013 | B->ClosureCall(position, type_args_len, argument_count, argument_names, |
3014 | /*use_unchecked_entry=*/true); |
3015 | } else if (!direct_call_target->IsNull()) { |
3016 | // Even if TFA infers a concrete receiver type, the static type of the |
3017 | // call-site may still be dynamic and we need to call the dynamic invocation |
3018 | // forwarder to ensure type-checks are performed. |
3019 | ASSERT(CompilerState::Current().is_aot()); |
3020 | instructions += |
3021 | StaticCall(position, *direct_call_target, argument_count, |
3022 | argument_names, ICData::kNoRebind, &result_type, |
3023 | type_args_len, /*use_unchecked_entry=*/is_unchecked_call); |
3024 | } else { |
3025 | instructions += |
3026 | InstanceCall(position, *mangled_name, token_kind, type_args_len, |
3027 | argument_count, argument_names, checked_argument_count, |
3028 | *interface_target, Function::null_function(), &result_type, |
3029 | /*use_unchecked_entry=*/is_unchecked_call, |
3030 | &call_site_attributes, result_type.ReceiverNotInt()); |
3031 | } |
3032 | |
3033 | // Drop temporaries preserving result on the top of the stack. |
3034 | ASSERT((receiver_temp != NULL) || (type_arguments_temp == NULL)); |
3035 | if (receiver_temp != NULL) { |
3036 | const intptr_t num_temps = |
3037 | (receiver_temp != NULL ? 1 : 0) + (type_arguments_temp != NULL ? 1 : 0); |
3038 | instructions += DropTempsPreserveTop(num_temps); |
3039 | } |
3040 | |
3041 | // Later optimization passes assume that result of a x.[]=(...) call is not |
3042 | // used. We must guarantee this invariant because violation will lead to an |
3043 | // illegal IL once we replace x.[]=(...) with a sequence that does not |
3044 | // actually produce any value. See http://dartbug.com/29135 for more details. |
3045 | if (name.raw() == Symbols::AssignIndexToken().raw()) { |
3046 | instructions += Drop(); |
3047 | instructions += NullConstant(); |
3048 | } |
3049 | |
3050 | return instructions; |
3051 | } |
3052 | |
3053 | Fragment StreamingFlowGraphBuilder::BuildDirectMethodInvocation( |
3054 | TokenPosition* p) { |
3055 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
3056 | TokenPosition position = ReadPosition(); // read offset. |
3057 | if (p != NULL) *p = position; |
3058 | |
3059 | const InferredTypeMetadata result_type = |
3060 | inferred_type_metadata_helper_.GetInferredType(offset); |
3061 | |
3062 | Tag receiver_tag = PeekTag(); // peek tag for receiver. |
3063 | |
3064 | Fragment instructions; |
3065 | intptr_t type_args_len = 0; |
3066 | { |
3067 | AlternativeReadingScope alt(&reader_); |
3068 | SkipExpression(); // skip receiver |
3069 | ReadInterfaceMemberNameReference(); // skip target reference |
3070 | ReadUInt(); // read argument count. |
3071 | intptr_t list_length = ReadListLength(); // read types list length. |
3072 | if (list_length > 0) { |
3073 | const TypeArguments& type_arguments = |
3074 | T.BuildTypeArguments(list_length); // read types. |
3075 | instructions += TranslateInstantiatedTypeArguments(type_arguments); |
3076 | } |
3077 | type_args_len = list_length; |
3078 | } |
3079 | |
3080 | instructions += BuildExpression(); // read receiver. |
3081 | |
3082 | NameIndex kernel_name = |
3083 | ReadInterfaceMemberNameReference(); // read target_reference. |
3084 | const String& method_name = H.DartProcedureName(kernel_name); |
3085 | const Token::Kind token_kind = |
3086 | MethodTokenRecognizer::RecognizeTokenKind(method_name); |
3087 | |
3088 | // Detect comparison with null. |
3089 | if ((token_kind == Token::kEQ || token_kind == Token::kNE) && |
3090 | PeekArgumentsCount() == 1 && |
3091 | (receiver_tag == kNullLiteral || |
3092 | PeekArgumentsFirstPositionalTag() == kNullLiteral)) { |
3093 | ASSERT(type_args_len == 0); |
3094 | // "==" or "!=" with null on either side. |
3095 | instructions += |
3096 | BuildArguments(NULL /* names */, NULL /* arg count */, |
3097 | NULL /* positional arg count */); // read arguments. |
3098 | Token::Kind strict_cmp_kind = |
3099 | token_kind == Token::kEQ ? Token::kEQ_STRICT : Token::kNE_STRICT; |
3100 | return instructions + |
3101 | StrictCompare(position, strict_cmp_kind, /*number_check = */ true); |
3102 | } |
3103 | |
3104 | const Function& target = |
3105 | Function::ZoneHandle(Z, H.LookupMethodByMember(kernel_name, method_name)); |
3106 | |
3107 | Array& argument_names = Array::ZoneHandle(Z); |
3108 | intptr_t argument_count, positional_argument_count; |
3109 | instructions += |
3110 | BuildArguments(&argument_names, &argument_count, |
3111 | &positional_argument_count); // read arguments. |
3112 | ++argument_count; |
3113 | |
3114 | return instructions + StaticCall(position, target, argument_count, |
3115 | argument_names, ICData::kNoRebind, |
3116 | &result_type, type_args_len); |
3117 | } |
3118 | |
3119 | Fragment StreamingFlowGraphBuilder::BuildSuperMethodInvocation( |
3120 | TokenPosition* p) { |
3121 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
3122 | const TokenPosition position = ReadPosition(); // read position. |
3123 | if (p != NULL) *p = position; |
3124 | |
3125 | const InferredTypeMetadata result_type = |
3126 | inferred_type_metadata_helper_.GetInferredType(offset); |
3127 | |
3128 | intptr_t type_args_len = 0; |
3129 | { |
3130 | AlternativeReadingScope alt(&reader_); |
3131 | SkipName(); // skip method name |
3132 | ReadUInt(); // read argument count. |
3133 | type_args_len = ReadListLength(); // read types list length. |
3134 | } |
3135 | |
3136 | Class& klass = GetSuperOrDie(); |
3137 | |
3138 | // Search the superclass chain for the selector. |
3139 | const String& method_name = ReadNameAsMethodName(); // read name. |
3140 | |
3141 | // Figure out selector signature. |
3142 | intptr_t argument_count; |
3143 | Array& argument_names = Array::Handle(Z); |
3144 | { |
3145 | AlternativeReadingScope alt(&reader_); |
3146 | argument_count = ReadUInt(); |
3147 | SkipListOfDartTypes(); |
3148 | |
3149 | SkipListOfExpressions(); |
3150 | intptr_t named_list_length = ReadListLength(); |
3151 | argument_names = Array::New(named_list_length, H.allocation_space()); |
3152 | for (intptr_t i = 0; i < named_list_length; i++) { |
3153 | const String& arg_name = H.DartSymbolObfuscate(ReadStringReference()); |
3154 | argument_names.SetAt(i, arg_name); |
3155 | SkipExpression(); |
3156 | } |
3157 | } |
3158 | |
3159 | Function& function = FindMatchingFunction( |
3160 | klass, method_name, type_args_len, |
3161 | argument_count + 1 /* account for 'this' */, argument_names); |
3162 | |
3163 | if (function.IsNull()) { |
3164 | ReadUInt(); // argument count |
3165 | intptr_t type_list_length = ReadListLength(); |
3166 | |
3167 | Fragment instructions; |
3168 | instructions += |
3169 | Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null())); |
3170 | instructions += IntConstant(argument_count + 1 /* this */ + |
3171 | (type_list_length == 0 ? 0 : 1)); // array size |
3172 | instructions += CreateArray(); |
3173 | LocalVariable* actuals_array = MakeTemporary(); |
3174 | |
3175 | // Call allocationInvocationMirror to get instance of Invocation. |
3176 | Fragment build_rest_of_actuals; |
3177 | intptr_t actuals_array_index = 0; |
3178 | if (type_list_length > 0) { |
3179 | const TypeArguments& type_arguments = |
3180 | T.BuildTypeArguments(type_list_length); |
3181 | build_rest_of_actuals += LoadLocal(actuals_array); |
3182 | build_rest_of_actuals += IntConstant(actuals_array_index); |
3183 | build_rest_of_actuals += |
3184 | TranslateInstantiatedTypeArguments(type_arguments); |
3185 | build_rest_of_actuals += StoreIndexed(kArrayCid); |
3186 | ++actuals_array_index; |
3187 | } |
3188 | |
3189 | ++actuals_array_index; // account for 'this'. |
3190 | // Read arguments |
3191 | intptr_t list_length = ReadListLength(); |
3192 | intptr_t i = 0; |
3193 | while (i < list_length) { |
3194 | build_rest_of_actuals += LoadLocal(actuals_array); // array |
3195 | build_rest_of_actuals += IntConstant(actuals_array_index + i); // index |
3196 | build_rest_of_actuals += BuildExpression(); // value. |
3197 | build_rest_of_actuals += StoreIndexed(kArrayCid); |
3198 | ++i; |
3199 | } |
3200 | // Read named arguments |
3201 | intptr_t named_list_length = ReadListLength(); |
3202 | if (named_list_length > 0) { |
3203 | ASSERT(argument_count == list_length + named_list_length); |
3204 | while ((i - list_length) < named_list_length) { |
3205 | SkipStringReference(); |
3206 | build_rest_of_actuals += LoadLocal(actuals_array); // array |
3207 | build_rest_of_actuals += IntConstant(i + actuals_array_index); // index |
3208 | build_rest_of_actuals += BuildExpression(); // value. |
3209 | build_rest_of_actuals += StoreIndexed(kArrayCid); |
3210 | ++i; |
3211 | } |
3212 | } |
3213 | instructions += BuildAllocateInvocationMirrorCall( |
3214 | position, method_name, type_list_length, |
3215 | /* num_arguments = */ argument_count + 1, argument_names, actuals_array, |
3216 | build_rest_of_actuals); |
3217 | |
3218 | SkipInterfaceMemberNameReference(); // skip target_reference. |
3219 | |
3220 | Function& nsm_function = GetNoSuchMethodOrDie(Z, klass); |
3221 | instructions += StaticCall(TokenPosition::kNoSource, |
3222 | Function::ZoneHandle(Z, nsm_function.raw()), |
3223 | /* argument_count = */ 2, ICData::kNSMDispatch); |
3224 | instructions += DropTempsPreserveTop(1); // Drop actuals_array temp. |
3225 | return instructions; |
3226 | } else { |
3227 | Fragment instructions; |
3228 | |
3229 | { |
3230 | AlternativeReadingScope alt(&reader_); |
3231 | ReadUInt(); // read argument count. |
3232 | intptr_t list_length = ReadListLength(); // read types list length. |
3233 | if (list_length > 0) { |
3234 | const TypeArguments& type_arguments = |
3235 | T.BuildTypeArguments(list_length); // read types. |
3236 | instructions += TranslateInstantiatedTypeArguments(type_arguments); |
3237 | } |
3238 | } |
3239 | |
3240 | // receiver |
3241 | instructions += LoadLocal(parsed_function()->receiver_var()); |
3242 | |
3243 | Array& argument_names = Array::ZoneHandle(Z); |
3244 | intptr_t argument_count; |
3245 | instructions += BuildArguments( |
3246 | &argument_names, &argument_count, |
3247 | /* positional_argument_count = */ NULL); // read arguments. |
3248 | ++argument_count; // include receiver |
3249 | SkipInterfaceMemberNameReference(); // interfaceTargetReference |
3250 | return instructions + |
3251 | StaticCall(position, Function::ZoneHandle(Z, function.raw()), |
3252 | argument_count, argument_names, ICData::kSuper, |
3253 | &result_type, type_args_len, |
3254 | /*use_unchecked_entry_point=*/true); |
3255 | } |
3256 | } |
3257 | |
3258 | Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) { |
3259 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
3260 | TokenPosition position = ReadPosition(); // read position. |
3261 | if (p != NULL) *p = position; |
3262 | |
3263 | const InferredTypeMetadata result_type = |
3264 | inferred_type_metadata_helper_.GetInferredType(offset); |
3265 | |
3266 | NameIndex procedure_reference = |
3267 | ReadCanonicalNameReference(); // read procedure reference. |
3268 | intptr_t argument_count = PeekArgumentsCount(); |
3269 | const Function& target = Function::ZoneHandle( |
3270 | Z, H.LookupStaticMethodByKernelProcedure(procedure_reference)); |
3271 | const Class& klass = Class::ZoneHandle(Z, target.Owner()); |
3272 | if (target.IsGenerativeConstructor() || target.IsFactory()) { |
3273 | // The VM requires a TypeArguments object as first parameter for |
3274 | // every factory constructor. |
3275 | ++argument_count; |
3276 | } |
3277 | |
3278 | const auto recognized_kind = target.recognized_kind(); |
3279 | if (recognized_kind == MethodRecognizer::kFfiAsFunctionInternal) { |
3280 | return BuildFfiAsFunctionInternal(); |
3281 | } else if (CompilerState::Current().is_aot() && |
3282 | recognized_kind == MethodRecognizer::kFfiNativeCallbackFunction) { |
3283 | return BuildFfiNativeCallbackFunction(); |
3284 | } |
3285 | |
3286 | Fragment instructions; |
3287 | LocalVariable* instance_variable = NULL; |
3288 | |
3289 | const bool special_case_nop_async_stack_trace_helper = |
3290 | !FLAG_causal_async_stacks && |
3291 | recognized_kind == MethodRecognizer::kAsyncStackTraceHelper; |
3292 | |
3293 | const bool special_case_unchecked_cast = |
3294 | klass.IsTopLevel() && (klass.library() == Library::InternalLibrary()) && |
3295 | (target.name() == Symbols::UnsafeCast().raw()); |
3296 | |
3297 | const bool special_case_identical = |
3298 | klass.IsTopLevel() && (klass.library() == Library::CoreLibrary()) && |
3299 | (target.name() == Symbols::Identical().raw()); |
3300 | |
3301 | const bool special_case = special_case_identical || |
3302 | special_case_unchecked_cast || |
3303 | special_case_nop_async_stack_trace_helper; |
3304 | |
3305 | // If we cross the Kernel -> VM core library boundary, a [StaticInvocation] |
3306 | // can appear, but the thing we're calling is not a static method, but a |
3307 | // factory constructor. |
3308 | // The `H.LookupStaticmethodByKernelProcedure` will potentially resolve to the |
3309 | // forwarded constructor. |
3310 | // In that case we'll make an instance and pass it as first argument. |
3311 | // |
3312 | // TODO(27590): Get rid of this after we're using core libraries compiled |
3313 | // into Kernel. |
3314 | intptr_t type_args_len = 0; |
3315 | if (target.IsGenerativeConstructor()) { |
3316 | if (klass.NumTypeArguments() > 0) { |
3317 | const TypeArguments& type_arguments = |
3318 | PeekArgumentsInstantiatedType(klass); |
3319 | instructions += TranslateInstantiatedTypeArguments(type_arguments); |
3320 | instructions += AllocateObject(position, klass, 1); |
3321 | } else { |
3322 | instructions += AllocateObject(position, klass, 0); |
3323 | } |
3324 | |
3325 | instance_variable = MakeTemporary(); |
3326 | |
3327 | instructions += LoadLocal(instance_variable); |
3328 | } else if (target.IsFactory()) { |
3329 | // The VM requires currently a TypeArguments object as first parameter for |
3330 | // every factory constructor :-/ ! |
3331 | // |
3332 | // TODO(27590): Get rid of this after we're using core libraries compiled |
3333 | // into Kernel. |
3334 | const TypeArguments& type_arguments = PeekArgumentsInstantiatedType(klass); |
3335 | instructions += TranslateInstantiatedTypeArguments(type_arguments); |
3336 | } else if (!special_case) { |
3337 | AlternativeReadingScope alt(&reader_); |
3338 | ReadUInt(); // read argument count. |
3339 | intptr_t list_length = ReadListLength(); // read types list length. |
3340 | if (list_length > 0) { |
3341 | const TypeArguments& type_arguments = |
3342 | T.BuildTypeArguments(list_length); // read types. |
3343 | instructions += TranslateInstantiatedTypeArguments(type_arguments); |
3344 | } |
3345 | type_args_len = list_length; |
3346 | } |
3347 | |
3348 | Array& argument_names = Array::ZoneHandle(Z); |
3349 | instructions += |
3350 | BuildArguments(&argument_names, NULL /* arg count */, |
3351 | NULL /* positional arg count */); // read arguments. |
3352 | ASSERT(!special_case || |
3353 | target.AreValidArguments(type_args_len, argument_count, argument_names, |
3354 | NULL)); |
3355 | |
3356 | // Special case identical(x, y) call. |
3357 | // Note: similar optimization is performed in bytecode flow graph builder - |
3358 | // see BytecodeFlowGraphBuilder::BuildDirectCall(). |
3359 | // TODO(27590) consider moving this into the inliner and force inline it |
3360 | // there. |
3361 | if (special_case_identical) { |
3362 | ASSERT(argument_count == 2); |
3363 | instructions += |
3364 | StrictCompare(position, Token::kEQ_STRICT, /*number_check=*/true); |
3365 | } else if (special_case_nop_async_stack_trace_helper) { |
3366 | ASSERT(argument_count == 1); |
3367 | instructions += Drop(); |
3368 | instructions += NullConstant(); |
3369 | } else if (special_case_unchecked_cast) { |
3370 | // Simply do nothing: the result value is already pushed on the stack. |
3371 | } else { |
3372 | instructions += StaticCall(position, target, argument_count, argument_names, |
3373 | ICData::kStatic, &result_type, type_args_len); |
3374 | if (target.IsGenerativeConstructor()) { |
3375 | // Drop the result of the constructor call and leave [instance_variable] |
3376 | // on top-of-stack. |
3377 | instructions += Drop(); |
3378 | } |
3379 | } |
3380 | |
3381 | return instructions; |
3382 | } |
3383 | |
3384 | Fragment StreamingFlowGraphBuilder::BuildConstructorInvocation( |
3385 | TokenPosition* p) { |
3386 | TokenPosition position = ReadPosition(); // read position. |
3387 | if (p != NULL) *p = position; |
3388 | |
3389 | NameIndex kernel_name = |
3390 | ReadCanonicalNameReference(); // read target_reference. |
3391 | |
3392 | Class& klass = Class::ZoneHandle( |
3393 | Z, H.LookupClassByKernelClass(H.EnclosingName(kernel_name))); |
3394 | |
3395 | Fragment instructions; |
3396 | |
3397 | if (klass.NumTypeArguments() > 0) { |
3398 | if (!klass.IsGeneric()) { |
3399 | Type& type = Type::ZoneHandle(Z, T.ReceiverType(klass).raw()); |
3400 | |
3401 | // TODO(27590): Can we move this code into [ReceiverType]? |
3402 | type ^= ClassFinalizer::FinalizeType(*active_class()->klass, type, |
3403 | ClassFinalizer::kFinalize); |
3404 | TypeArguments& canonicalized_type_arguments = |
3405 | TypeArguments::ZoneHandle(Z, type.arguments()); |
3406 | canonicalized_type_arguments = |
3407 | canonicalized_type_arguments.Canonicalize(); |
3408 | instructions += Constant(canonicalized_type_arguments); |
3409 | } else { |
3410 | const TypeArguments& type_arguments = |
3411 | PeekArgumentsInstantiatedType(klass); |
3412 | instructions += TranslateInstantiatedTypeArguments(type_arguments); |
3413 | } |
3414 | |
3415 | instructions += AllocateObject(position, klass, 1); |
3416 | } else { |
3417 | instructions += AllocateObject(position, klass, 0); |
3418 | } |
3419 | LocalVariable* variable = MakeTemporary(); |
3420 | |
3421 | instructions += LoadLocal(variable); |
3422 | |
3423 | Array& argument_names = Array::ZoneHandle(Z); |
3424 | intptr_t argument_count; |
3425 | instructions += BuildArguments( |
3426 | &argument_names, &argument_count, |
3427 | /* positional_argument_count = */ NULL); // read arguments. |
3428 | |
3429 | const Function& target = Function::ZoneHandle( |
3430 | Z, H.LookupConstructorByKernelConstructor(klass, kernel_name)); |
3431 | ++argument_count; |
3432 | instructions += StaticCall(position, target, argument_count, argument_names, |
3433 | ICData::kStatic, /* result_type = */ NULL); |
3434 | return instructions + Drop(); |
3435 | } |
3436 | |
3437 | Fragment StreamingFlowGraphBuilder::BuildNot(TokenPosition* position) { |
3438 | if (position != NULL) *position = TokenPosition::kNoSource; |
3439 | |
3440 | TokenPosition operand_position = TokenPosition::kNoSource; |
3441 | Fragment instructions = |
3442 | BuildExpression(&operand_position); // read expression. |
3443 | instructions += CheckBoolean(operand_position); |
3444 | instructions += BooleanNegate(); |
3445 | return instructions; |
3446 | } |
3447 | |
3448 | Fragment StreamingFlowGraphBuilder::BuildNullCheck(TokenPosition* p) { |
3449 | const TokenPosition position = ReadPosition(); // read position. |
3450 | if (p != nullptr) *p = position; |
3451 | |
3452 | TokenPosition operand_position = TokenPosition::kNoSource; |
3453 | Fragment instructions = BuildExpression(&operand_position); |
3454 | LocalVariable* expr_temp = MakeTemporary(); |
3455 | instructions += CheckNull(position, expr_temp, String::null_string(), |
3456 | /* clear_the_temp = */ false); |
3457 | |
3458 | return instructions; |
3459 | } |
3460 | |
3461 | // Translate the logical expression (lhs && rhs or lhs || rhs) in a context |
3462 | // where a value is required. |
3463 | // |
3464 | // Translation accumulates short-circuit exits from logical |
3465 | // subexpressions in the side_exits. These exits are expected to store |
3466 | // true and false into :expr_temp. |
3467 | // |
3468 | // The result of evaluating the last |
3469 | // expression in chain would be stored in :expr_temp directly to avoid |
3470 | // generating graph like: |
3471 | // |
3472 | // if (v) :expr_temp = true; else :expr_temp = false; |
3473 | // |
3474 | // Outer negations are stripped and instead negation is passed down via |
3475 | // negated parameter. |
3476 | Fragment StreamingFlowGraphBuilder::TranslateLogicalExpressionForValue( |
3477 | bool negated, |
3478 | TestFragment* side_exits) { |
3479 | TestFragment left = TranslateConditionForControl().Negate(negated); |
3480 | LogicalOperator op = static_cast<LogicalOperator>(ReadByte()); |
3481 | if (negated) { |
3482 | op = (op == kAnd) ? kOr : kAnd; |
3483 | } |
3484 | |
3485 | // Short circuit the control flow after the left hand side condition. |
3486 | if (op == kAnd) { |
3487 | side_exits->false_successor_addresses->AddArray( |
3488 | *left.false_successor_addresses); |
3489 | } else { |
3490 | side_exits->true_successor_addresses->AddArray( |
3491 | *left.true_successor_addresses); |
3492 | } |
3493 | |
3494 | // Skip negations of the right hand side. |
3495 | while (PeekTag() == kNot) { |
3496 | SkipBytes(1); |
3497 | negated = !negated; |
3498 | } |
3499 | |
3500 | Fragment right_value(op == kAnd |
3501 | ? left.CreateTrueSuccessor(flow_graph_builder_) |
3502 | : left.CreateFalseSuccessor(flow_graph_builder_)); |
3503 | |
3504 | if (PeekTag() == kLogicalExpression) { |
3505 | SkipBytes(1); |
3506 | // Handle nested logical expressions specially to avoid materializing |
3507 | // intermediate boolean values. |
3508 | right_value += TranslateLogicalExpressionForValue(negated, side_exits); |
3509 | } else { |
3510 | // Arbitrary expression on the right hand side. Translate it for value. |
3511 | TokenPosition position = TokenPosition::kNoSource; |
3512 | right_value += BuildExpression(&position); // read expression. |
3513 | |
3514 | // Check if the top of the stack is known to be a non-nullable boolean. |
3515 | // Note that in strong mode we know that any value that reaches here |
3516 | // is at least a nullable boolean - so there is no need to compare |
3517 | // with true like in Dart 1. |
3518 | Definition* top = stack()->definition(); |
3519 | const bool is_bool = top->IsStrictCompare() || top->IsBooleanNegate(); |
3520 | if (!is_bool) { |
3521 | right_value += CheckBoolean(position); |
3522 | } |
3523 | if (negated) { |
3524 | right_value += BooleanNegate(); |
3525 | } |
3526 | right_value += StoreLocal(TokenPosition::kNoSource, |
3527 | parsed_function()->expression_temp_var()); |
3528 | right_value += Drop(); |
3529 | } |
3530 | |
3531 | return Fragment(left.entry, right_value.current); |
3532 | } |
3533 | |
3534 | Fragment StreamingFlowGraphBuilder::BuildLogicalExpression( |
3535 | TokenPosition* position) { |
3536 | if (position != NULL) *position = TokenPosition::kNoSource; |
3537 | |
3538 | TestFragment exits; |
3539 | exits.true_successor_addresses = new TestFragment::SuccessorAddressArray(2); |
3540 | exits.false_successor_addresses = new TestFragment::SuccessorAddressArray(2); |
3541 | |
3542 | JoinEntryInstr* join = BuildJoinEntry(); |
3543 | Fragment instructions = |
3544 | TranslateLogicalExpressionForValue(/*negated=*/false, &exits); |
3545 | instructions += Goto(join); |
3546 | |
3547 | // Generate :expr_temp = true if needed and connect it to true side-exits. |
3548 | if (!exits.true_successor_addresses->is_empty()) { |
3549 | Fragment constant_fragment(exits.CreateTrueSuccessor(flow_graph_builder_)); |
3550 | constant_fragment += Constant(Bool::Get(true)); |
3551 | constant_fragment += StoreLocal(TokenPosition::kNoSource, |
3552 | parsed_function()->expression_temp_var()); |
3553 | constant_fragment += Drop(); |
3554 | constant_fragment += Goto(join); |
3555 | } |
3556 | |
3557 | // Generate :expr_temp = false if needed and connect it to false side-exits. |
3558 | if (!exits.false_successor_addresses->is_empty()) { |
3559 | Fragment constant_fragment(exits.CreateFalseSuccessor(flow_graph_builder_)); |
3560 | constant_fragment += Constant(Bool::Get(false)); |
3561 | constant_fragment += StoreLocal(TokenPosition::kNoSource, |
3562 | parsed_function()->expression_temp_var()); |
3563 | constant_fragment += Drop(); |
3564 | constant_fragment += Goto(join); |
3565 | } |
3566 | |
3567 | return Fragment(instructions.entry, join) + |
3568 | LoadLocal(parsed_function()->expression_temp_var()); |
3569 | } |
3570 | |
3571 | Fragment StreamingFlowGraphBuilder::BuildConditionalExpression( |
3572 | TokenPosition* position) { |
3573 | if (position != NULL) *position = TokenPosition::kNoSource; |
3574 | |
3575 | TestFragment condition = TranslateConditionForControl(); // read condition. |
3576 | |
3577 | Value* top = stack(); |
3578 | Fragment then_fragment(condition.CreateTrueSuccessor(flow_graph_builder_)); |
3579 | then_fragment += BuildExpression(); // read then. |
3580 | then_fragment += StoreLocal(TokenPosition::kNoSource, |
3581 | parsed_function()->expression_temp_var()); |
3582 | then_fragment += Drop(); |
3583 | ASSERT(stack() == top); |
3584 | |
3585 | Fragment otherwise_fragment( |
3586 | condition.CreateFalseSuccessor(flow_graph_builder_)); |
3587 | otherwise_fragment += BuildExpression(); // read otherwise. |
3588 | otherwise_fragment += StoreLocal(TokenPosition::kNoSource, |
3589 | parsed_function()->expression_temp_var()); |
3590 | otherwise_fragment += Drop(); |
3591 | ASSERT(stack() == top); |
3592 | |
3593 | JoinEntryInstr* join = BuildJoinEntry(); |
3594 | then_fragment += Goto(join); |
3595 | otherwise_fragment += Goto(join); |
3596 | |
3597 | SkipOptionalDartType(); // read unused static type. |
3598 | |
3599 | return Fragment(condition.entry, join) + |
3600 | LoadLocal(parsed_function()->expression_temp_var()); |
3601 | } |
3602 | |
3603 | Fragment StreamingFlowGraphBuilder::BuildStringConcatenation(TokenPosition* p) { |
3604 | TokenPosition position = ReadPosition(); // read position. |
3605 | if (p != NULL) *p = position; |
3606 | |
3607 | intptr_t length = ReadListLength(); // read list length. |
3608 | // Note: there will be "length" expressions. |
3609 | |
3610 | Fragment instructions; |
3611 | if (length == 1) { |
3612 | instructions += BuildExpression(); // read expression. |
3613 | instructions += StringInterpolateSingle(position); |
3614 | } else { |
3615 | // The type arguments for CreateArray. |
3616 | instructions += Constant(TypeArguments::ZoneHandle(Z)); |
3617 | instructions += IntConstant(length); |
3618 | instructions += CreateArray(); |
3619 | LocalVariable* array = MakeTemporary(); |
3620 | |
3621 | for (intptr_t i = 0; i < length; ++i) { |
3622 | instructions += LoadLocal(array); |
3623 | instructions += IntConstant(i); |
3624 | instructions += BuildExpression(); // read ith expression. |
3625 | instructions += StoreIndexed(kArrayCid); |
3626 | } |
3627 | |
3628 | instructions += StringInterpolate(position); |
3629 | } |
3630 | |
3631 | return instructions; |
3632 | } |
3633 | |
3634 | Fragment StreamingFlowGraphBuilder::BuildIsExpression(TokenPosition* p) { |
3635 | TokenPosition position = ReadPosition(); // read position. |
3636 | if (p != NULL) *p = position; |
3637 | |
3638 | if (translation_helper_.info().kernel_binary_version() >= 38) { |
3639 | // We do not use the library mode for the type test, which is indicated by |
3640 | // the flag kIsExpressionFlagForNonNullableByDefault. |
3641 | ReadFlags(); |
3642 | } |
3643 | |
3644 | Fragment instructions = BuildExpression(); // read operand. |
3645 | |
3646 | const AbstractType& type = T.BuildType(); // read type. |
3647 | |
3648 | // The VM does not like an instanceOf call with a dynamic type. We need to |
3649 | // special case this situation by detecting a top type. |
3650 | if (type.IsTopTypeForInstanceOf()) { |
3651 | // Evaluate the expression on the left but ignore its result. |
3652 | instructions += Drop(); |
3653 | |
3654 | // Let condition be always true. |
3655 | instructions += Constant(Bool::True()); |
3656 | } else { |
3657 | // See if simple instanceOf is applicable. |
3658 | if (dart::SimpleInstanceOfType(type)) { |
3659 | instructions += Constant(type); |
3660 | instructions += InstanceCall( |
3661 | position, Library::PrivateCoreLibName(Symbols::_simpleInstanceOf()), |
3662 | Token::kIS, 2, 2); // 2 checked arguments. |
3663 | return instructions; |
3664 | } |
3665 | |
3666 | if (!type.IsInstantiated(kCurrentClass)) { |
3667 | instructions += LoadInstantiatorTypeArguments(); |
3668 | } else { |
3669 | instructions += NullConstant(); |
3670 | } |
3671 | |
3672 | if (!type.IsInstantiated(kFunctions)) { |
3673 | instructions += LoadFunctionTypeArguments(); |
3674 | } else { |
3675 | instructions += NullConstant(); |
3676 | } |
3677 | |
3678 | instructions += Constant(type); |
3679 | |
3680 | instructions += InstanceCall( |
3681 | position, Library::PrivateCoreLibName(Symbols::_instanceOf()), |
3682 | Token::kIS, 4); |
3683 | } |
3684 | return instructions; |
3685 | } |
3686 | |
3687 | Fragment StreamingFlowGraphBuilder::BuildAsExpression(TokenPosition* p) { |
3688 | TokenPosition position = ReadPosition(); // read position. |
3689 | if (p != NULL) *p = position; |
3690 | |
3691 | const uint8_t flags = ReadFlags(); // read flags. |
3692 | const bool is_type_error = (flags & kAsExpressionFlagTypeError) != 0; |
3693 | |
3694 | Fragment instructions = BuildExpression(); // read operand. |
3695 | |
3696 | const AbstractType& type = T.BuildType(); // read type. |
3697 | if (type.IsInstantiated() && type.IsTopTypeForSubtyping()) { |
3698 | // We already evaluated the operand on the left and just leave it there as |
3699 | // the result of the `obj as dynamic` expression. |
3700 | } else { |
3701 | // We do not care whether the 'as' cast as implicitly added by the frontend |
3702 | // or explicitly written by the user, in both cases we use an assert |
3703 | // assignable. |
3704 | instructions += LoadLocal(MakeTemporary()); |
3705 | instructions += B->AssertAssignableLoadTypeArguments( |
3706 | position, type, |
3707 | is_type_error ? Symbols::Empty() : Symbols::InTypeCast(), |
3708 | AssertAssignableInstr::kInsertedByFrontend); |
3709 | instructions += Drop(); |
3710 | } |
3711 | return instructions; |
3712 | } |
3713 | |
3714 | Fragment StreamingFlowGraphBuilder::BuildTypeLiteral(TokenPosition* position) { |
3715 | if (position != NULL) *position = TokenPosition::kNoSource; |
3716 | |
3717 | const AbstractType& type = T.BuildType(); // read type. |
3718 | Fragment instructions; |
3719 | if (type.IsInstantiated()) { |
3720 | instructions += Constant(type); |
3721 | } else { |
3722 | if (!type.IsInstantiated(kCurrentClass)) { |
3723 | instructions += LoadInstantiatorTypeArguments(); |
3724 | } else { |
3725 | instructions += NullConstant(); |
3726 | } |
3727 | if (!type.IsInstantiated(kFunctions)) { |
3728 | instructions += LoadFunctionTypeArguments(); |
3729 | } else { |
3730 | instructions += NullConstant(); |
3731 | } |
3732 | instructions += InstantiateType(type); |
3733 | } |
3734 | return instructions; |
3735 | } |
3736 | |
3737 | Fragment StreamingFlowGraphBuilder::BuildThisExpression( |
3738 | TokenPosition* position) { |
3739 | if (position != NULL) *position = TokenPosition::kNoSource; |
3740 | |
3741 | return LoadLocal(parsed_function()->receiver_var()); |
3742 | } |
3743 | |
3744 | Fragment StreamingFlowGraphBuilder::BuildRethrow(TokenPosition* p) { |
3745 | TokenPosition position = ReadPosition(); // read position. |
3746 | if (p != NULL) *p = position; |
3747 | |
3748 | Fragment instructions = DebugStepCheck(position); |
3749 | instructions += LoadLocal(catch_block()->exception_var()); |
3750 | instructions += LoadLocal(catch_block()->stack_trace_var()); |
3751 | instructions += RethrowException(position, catch_block()->catch_try_index()); |
3752 | |
3753 | return instructions; |
3754 | } |
3755 | |
3756 | Fragment StreamingFlowGraphBuilder::BuildThrow(TokenPosition* p) { |
3757 | TokenPosition position = ReadPosition(); // read position. |
3758 | if (p != NULL) *p = position; |
3759 | |
3760 | Fragment instructions; |
3761 | |
3762 | instructions += BuildExpression(); // read expression. |
3763 | |
3764 | if (NeedsDebugStepCheck(stack(), position)) { |
3765 | instructions = DebugStepCheck(position) + instructions; |
3766 | } |
3767 | instructions += ThrowException(position); |
3768 | ASSERT(instructions.is_closed()); |
3769 | |
3770 | return instructions; |
3771 | } |
3772 | |
3773 | Fragment StreamingFlowGraphBuilder::BuildListLiteral(TokenPosition* p) { |
3774 | TokenPosition position = ReadPosition(); // read position. |
3775 | if (p != NULL) *p = position; |
3776 | |
3777 | const TypeArguments& type_arguments = T.BuildTypeArguments(1); // read type. |
3778 | intptr_t length = ReadListLength(); // read list length. |
3779 | // Note: there will be "length" expressions. |
3780 | |
3781 | // The type argument for the factory call. |
3782 | Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments); |
3783 | LocalVariable* type = MakeTemporary(); |
3784 | |
3785 | instructions += LoadLocal(type); |
3786 | if (length == 0) { |
3787 | instructions += Constant(Object::empty_array()); |
3788 | } else { |
3789 | // The type arguments for CreateArray. |
3790 | instructions += LoadLocal(type); |
3791 | instructions += IntConstant(length); |
3792 | instructions += CreateArray(); |
3793 | |
3794 | LocalVariable* array = MakeTemporary(); |
3795 | for (intptr_t i = 0; i < length; ++i) { |
3796 | instructions += LoadLocal(array); |
3797 | instructions += IntConstant(i); |
3798 | instructions += BuildExpression(); // read ith expression. |
3799 | instructions += StoreIndexed(kArrayCid); |
3800 | } |
3801 | } |
3802 | |
3803 | const Class& factory_class = |
3804 | Class::Handle(Z, Library::LookupCoreClass(Symbols::List())); |
3805 | const Function& factory_method = Function::ZoneHandle( |
3806 | Z, factory_class.LookupFactory( |
3807 | Library::PrivateCoreLibName(Symbols::ListLiteralFactory()))); |
3808 | |
3809 | instructions += StaticCall(position, factory_method, 2, ICData::kStatic); |
3810 | instructions += DropTempsPreserveTop(1); // Instantiated type_arguments. |
3811 | return instructions; |
3812 | } |
3813 | |
3814 | Fragment StreamingFlowGraphBuilder::BuildMapLiteral(TokenPosition* p) { |
3815 | TokenPosition position = ReadPosition(); // read position. |
3816 | if (p != NULL) *p = position; |
3817 | |
3818 | const TypeArguments& type_arguments = |
3819 | T.BuildTypeArguments(2); // read key_type and value_type. |
3820 | |
3821 | // The type argument for the factory call `new Map<K, V>._fromLiteral(List)`. |
3822 | Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments); |
3823 | |
3824 | intptr_t length = ReadListLength(); // read list length. |
3825 | // Note: there will be "length" map entries (i.e. key and value expressions). |
3826 | |
3827 | if (length == 0) { |
3828 | instructions += Constant(Object::empty_array()); |
3829 | } else { |
3830 | // The type arguments for `new List<X>(int len)`. |
3831 | instructions += Constant(TypeArguments::ZoneHandle(Z)); |
3832 | |
3833 | // We generate a list of tuples, i.e. [key1, value1, ..., keyN, valueN]. |
3834 | instructions += IntConstant(2 * length); |
3835 | instructions += CreateArray(); |
3836 | |
3837 | LocalVariable* array = MakeTemporary(); |
3838 | for (intptr_t i = 0; i < length; ++i) { |
3839 | instructions += LoadLocal(array); |
3840 | instructions += IntConstant(2 * i); |
3841 | instructions += BuildExpression(); // read ith key. |
3842 | instructions += StoreIndexed(kArrayCid); |
3843 | |
3844 | instructions += LoadLocal(array); |
3845 | instructions += IntConstant(2 * i + 1); |
3846 | instructions += BuildExpression(); // read ith value. |
3847 | instructions += StoreIndexed(kArrayCid); |
3848 | } |
3849 | } |
3850 | |
3851 | const Class& map_class = |
3852 | Class::Handle(Z, Library::LookupCoreClass(Symbols::Map())); |
3853 | const Function& factory_method = Function::ZoneHandle( |
3854 | Z, map_class.LookupFactory( |
3855 | Library::PrivateCoreLibName(Symbols::MapLiteralFactory()))); |
3856 | |
3857 | return instructions + |
3858 | StaticCall(position, factory_method, 2, ICData::kStatic); |
3859 | } |
3860 | |
3861 | Fragment StreamingFlowGraphBuilder::BuildFunctionExpression() { |
3862 | ReadPosition(); // read position. |
3863 | return BuildFunctionNode(TokenPosition::kNoSource, StringIndex()); |
3864 | } |
3865 | |
3866 | Fragment StreamingFlowGraphBuilder::BuildLet(TokenPosition* position) { |
3867 | if (position != NULL) *position = TokenPosition::kNoSource; |
3868 | |
3869 | Fragment instructions = BuildVariableDeclaration(); // read variable. |
3870 | instructions += BuildExpression(); // read body. |
3871 | return instructions; |
3872 | } |
3873 | |
3874 | Fragment StreamingFlowGraphBuilder::BuildBlockExpression() { |
3875 | block_expression_depth_inc(); |
3876 | const intptr_t offset = ReaderOffset() - 1; // Include the tag. |
3877 | |
3878 | Fragment instructions; |
3879 | |
3880 | instructions += EnterScope(offset); |
3881 | const intptr_t list_length = ReadListLength(); // read number of statements. |
3882 | for (intptr_t i = 0; i < list_length; ++i) { |
3883 | instructions += BuildStatement(); // read ith statement. |
3884 | } |
3885 | instructions += BuildExpression(); // read expression (inside scope). |
3886 | instructions += ExitScope(offset); |
3887 | |
3888 | block_expression_depth_dec(); |
3889 | return instructions; |
3890 | } |
3891 | |
3892 | Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral( |
3893 | TokenPosition* position) { |
3894 | if (position != NULL) *position = TokenPosition::kNoSource; |
3895 | |
3896 | const String& value = |
3897 | H.DartString(ReadStringReference()); // read index into string table. |
3898 | const Integer& integer = Integer::ZoneHandle(Z, Integer::NewCanonical(value)); |
3899 | if (integer.IsNull()) { |
3900 | H.ReportError(script_, TokenPosition::kNoSource, |
3901 | "Integer literal %s is out of range" , value.ToCString()); |
3902 | UNREACHABLE(); |
3903 | } |
3904 | return Constant(integer); |
3905 | } |
3906 | |
3907 | Fragment StreamingFlowGraphBuilder::BuildStringLiteral( |
3908 | TokenPosition* position) { |
3909 | if (position != NULL) *position = TokenPosition::kNoSource; |
3910 | |
3911 | return Constant(H.DartSymbolPlain( |
3912 | ReadStringReference())); // read index into string table. |
3913 | } |
3914 | |
3915 | Fragment StreamingFlowGraphBuilder::BuildIntLiteral(uint8_t payload, |
3916 | TokenPosition* position) { |
3917 | if (position != NULL) *position = TokenPosition::kNoSource; |
3918 | |
3919 | int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias; |
3920 | return IntConstant(value); |
3921 | } |
3922 | |
3923 | Fragment StreamingFlowGraphBuilder::BuildIntLiteral(bool is_negative, |
3924 | TokenPosition* position) { |
3925 | if (position != NULL) *position = TokenPosition::kNoSource; |
3926 | |
3927 | int64_t value = is_negative ? -static_cast<int64_t>(ReadUInt()) |
3928 | : ReadUInt(); // read value. |
3929 | return IntConstant(value); |
3930 | } |
3931 | |
3932 | Fragment StreamingFlowGraphBuilder::BuildDoubleLiteral( |
3933 | TokenPosition* position) { |
3934 | if (position != NULL) *position = TokenPosition::kNoSource; |
3935 | |
3936 | Double& constant = Double::ZoneHandle( |
3937 | Z, Double::NewCanonical(ReadDouble())); // read double. |
3938 | return Constant(constant); |
3939 | } |
3940 | |
3941 | Fragment StreamingFlowGraphBuilder::BuildBoolLiteral(bool value, |
3942 | TokenPosition* position) { |
3943 | if (position != NULL) *position = TokenPosition::kNoSource; |
3944 | |
3945 | return Constant(Bool::Get(value)); |
3946 | } |
3947 | |
3948 | Fragment StreamingFlowGraphBuilder::BuildNullLiteral(TokenPosition* position) { |
3949 | if (position != NULL) *position = TokenPosition::kNoSource; |
3950 | |
3951 | return Constant(Instance::ZoneHandle(Z, Instance::null())); |
3952 | } |
3953 | |
3954 | Fragment StreamingFlowGraphBuilder::BuildFutureNullValue( |
3955 | TokenPosition* position) { |
3956 | if (position != NULL) *position = TokenPosition::kNoSource; |
3957 | const Class& future = Class::Handle(Z, I->object_store()->future_class()); |
3958 | ASSERT(!future.IsNull()); |
3959 | const Function& constructor = |
3960 | Function::ZoneHandle(Z, future.LookupFunction(Symbols::FutureValue())); |
3961 | ASSERT(!constructor.IsNull()); |
3962 | |
3963 | Fragment instructions; |
3964 | instructions += BuildNullLiteral(position); |
3965 | instructions += StaticCall(TokenPosition::kNoSource, constructor, |
3966 | /* argument_count = */ 1, ICData::kStatic); |
3967 | return instructions; |
3968 | } |
3969 | |
3970 | Fragment StreamingFlowGraphBuilder::BuildConstantExpression( |
3971 | TokenPosition* position, |
3972 | Tag tag) { |
3973 | TokenPosition p = TokenPosition::kNoSource; |
3974 | if (tag == kConstantExpression) { |
3975 | p = ReadPosition(); |
3976 | SkipDartType(); |
3977 | } |
3978 | if (position != nullptr) *position = p; |
3979 | const intptr_t constant_offset = ReadUInt(); |
3980 | Fragment result = Constant( |
3981 | Object::ZoneHandle(Z, constant_reader_.ReadConstant(constant_offset))); |
3982 | return result; |
3983 | } |
3984 | |
3985 | Fragment StreamingFlowGraphBuilder::BuildPartialTearoffInstantiation( |
3986 | TokenPosition* position) { |
3987 | if (position != NULL) *position = TokenPosition::kNoSource; |
3988 | |
3989 | // Create a copy of the closure. |
3990 | |
3991 | Fragment instructions = BuildExpression(); |
3992 | LocalVariable* original_closure = MakeTemporary(); |
3993 | |
3994 | instructions += AllocateObject( |
3995 | TokenPosition::kNoSource, |
3996 | Class::ZoneHandle(Z, I->object_store()->closure_class()), 0); |
3997 | LocalVariable* new_closure = MakeTemporary(); |
3998 | |
3999 | intptr_t num_type_args = ReadListLength(); |
4000 | const TypeArguments& type_args = T.BuildTypeArguments(num_type_args); |
4001 | instructions += TranslateInstantiatedTypeArguments(type_args); |
4002 | LocalVariable* type_args_vec = MakeTemporary(); |
4003 | |
4004 | // Check the bounds. |
4005 | // |
4006 | // TODO(sjindel): We should be able to skip this check in many cases, e.g. |
4007 | // when the closure is coming from a tearoff of a top-level method or from a |
4008 | // local closure. |
4009 | instructions += LoadLocal(original_closure); |
4010 | instructions += LoadLocal(type_args_vec); |
4011 | const Library& dart_internal = Library::Handle(Z, Library::InternalLibrary()); |
4012 | const Function& bounds_check_function = Function::ZoneHandle( |
4013 | Z, dart_internal.LookupFunctionAllowPrivate( |
4014 | Symbols::BoundsCheckForPartialInstantiation())); |
4015 | ASSERT(!bounds_check_function.IsNull()); |
4016 | instructions += StaticCall(TokenPosition::kNoSource, bounds_check_function, 2, |
4017 | ICData::kStatic); |
4018 | instructions += Drop(); |
4019 | |
4020 | instructions += LoadLocal(new_closure); |
4021 | instructions += LoadLocal(type_args_vec); |
4022 | instructions += flow_graph_builder_->StoreInstanceField( |
4023 | TokenPosition::kNoSource, Slot::Closure_delayed_type_arguments(), |
4024 | StoreInstanceFieldInstr::Kind::kInitializing); |
4025 | instructions += Drop(); // Drop type args. |
4026 | |
4027 | // Copy over the target function. |
4028 | instructions += LoadLocal(new_closure); |
4029 | instructions += LoadLocal(original_closure); |
4030 | instructions += |
4031 | flow_graph_builder_->LoadNativeField(Slot::Closure_function()); |
4032 | instructions += flow_graph_builder_->StoreInstanceField( |
4033 | TokenPosition::kNoSource, Slot::Closure_function(), |
4034 | StoreInstanceFieldInstr::Kind::kInitializing); |
4035 | |
4036 | // Copy over the instantiator type arguments. |
4037 | instructions += LoadLocal(new_closure); |
4038 | instructions += LoadLocal(original_closure); |
4039 | instructions += flow_graph_builder_->LoadNativeField( |
4040 | Slot::Closure_instantiator_type_arguments()); |
4041 | instructions += flow_graph_builder_->StoreInstanceField( |
4042 | TokenPosition::kNoSource, Slot::Closure_instantiator_type_arguments(), |
4043 | StoreInstanceFieldInstr::Kind::kInitializing); |
4044 | |
4045 | // Copy over the function type arguments. |
4046 | instructions += LoadLocal(new_closure); |
4047 | instructions += LoadLocal(original_closure); |
4048 | instructions += flow_graph_builder_->LoadNativeField( |
4049 | Slot::Closure_function_type_arguments()); |
4050 | instructions += flow_graph_builder_->StoreInstanceField( |
4051 | TokenPosition::kNoSource, Slot::Closure_function_type_arguments(), |
4052 | StoreInstanceFieldInstr::Kind::kInitializing); |
4053 | |
4054 | // Copy over the context. |
4055 | instructions += LoadLocal(new_closure); |
4056 | instructions += LoadLocal(original_closure); |
4057 | instructions += flow_graph_builder_->LoadNativeField(Slot::Closure_context()); |
4058 | instructions += flow_graph_builder_->StoreInstanceField( |
4059 | TokenPosition::kNoSource, Slot::Closure_context(), |
4060 | StoreInstanceFieldInstr::Kind::kInitializing); |
4061 | |
4062 | instructions += DropTempsPreserveTop(1); // Drop old closure. |
4063 | |
4064 | return instructions; |
4065 | } |
4066 | |
4067 | Fragment StreamingFlowGraphBuilder::BuildLibraryPrefixAction( |
4068 | TokenPosition* position, |
4069 | const String& selector) { |
4070 | const intptr_t dependency_index = ReadUInt(); |
4071 | const Library& current_library = Library::Handle( |
4072 | Z, Class::Handle(Z, parsed_function()->function().origin()).library()); |
4073 | const Array& dependencies = Array::Handle(Z, current_library.dependencies()); |
4074 | const LibraryPrefix& prefix = |
4075 | LibraryPrefix::CheckedZoneHandle(Z, dependencies.At(dependency_index)); |
4076 | const Function& function = |
4077 | Function::ZoneHandle(Z, Library::Handle(Z, Library::CoreLibrary()) |
4078 | .LookupFunctionAllowPrivate(selector)); |
4079 | ASSERT(!function.IsNull()); |
4080 | Fragment instructions; |
4081 | instructions += Constant(prefix); |
4082 | instructions += |
4083 | StaticCall(TokenPosition::kNoSource, function, 1, ICData::kStatic); |
4084 | return instructions; |
4085 | } |
4086 | |
4087 | Fragment StreamingFlowGraphBuilder::BuildExpressionStatement() { |
4088 | Fragment instructions = BuildExpression(); // read expression. |
4089 | instructions += Drop(); |
4090 | return instructions; |
4091 | } |
4092 | |
4093 | Fragment StreamingFlowGraphBuilder::BuildBlock() { |
4094 | intptr_t offset = ReaderOffset() - 1; // Include the tag. |
4095 | |
4096 | Fragment instructions; |
4097 | |
4098 | instructions += EnterScope(offset); |
4099 | intptr_t list_length = ReadListLength(); // read number of statements. |
4100 | for (intptr_t i = 0; i < list_length; ++i) { |
4101 | if (instructions.is_open()) { |
4102 | instructions += BuildStatement(); // read ith statement. |
4103 | } else { |
4104 | SkipStatement(); // read ith statement. |
4105 | } |
4106 | } |
4107 | instructions += ExitScope(offset); |
4108 | |
4109 | return instructions; |
4110 | } |
4111 | |
4112 | Fragment StreamingFlowGraphBuilder::BuildEmptyStatement() { |
4113 | return Fragment(); |
4114 | } |
4115 | |
4116 | Fragment StreamingFlowGraphBuilder::BuildAssertBlock() { |
4117 | if (!I->asserts()) { |
4118 | SkipStatementList(); |
4119 | return Fragment(); |
4120 | } |
4121 | |
4122 | intptr_t offset = ReaderOffset() - 1; // Include the tag. |
4123 | |
4124 | Fragment instructions; |
4125 | |
4126 | instructions += EnterScope(offset); |
4127 | intptr_t list_length = ReadListLength(); // read number of statements. |
4128 | for (intptr_t i = 0; i < list_length; ++i) { |
4129 | if (instructions.is_open()) { |
4130 | instructions += BuildStatement(); // read ith statement. |
4131 | } else { |
4132 | SkipStatement(); // read ith statement. |
4133 | } |
4134 | } |
4135 | instructions += ExitScope(offset); |
4136 | |
4137 | return instructions; |
4138 | } |
4139 | |
4140 | Fragment StreamingFlowGraphBuilder::BuildAssertStatement() { |
4141 | if (!I->asserts()) { |
4142 | SetOffset(ReaderOffset() - 1); // Include the tag. |
4143 | SkipStatement(); // read this statement. |
4144 | return Fragment(); |
4145 | } |
4146 | |
4147 | TargetEntryInstr* then; |
4148 | TargetEntryInstr* otherwise; |
4149 | |
4150 | Fragment instructions; |
4151 | // Asserts can be of the following two kinds: |
4152 | // |
4153 | // * `assert(expr)` |
4154 | // * `assert(() { ... })` |
4155 | // |
4156 | // The call to `_AssertionError._evaluateAssertion()` will take care of both |
4157 | // and returns a boolean. |
4158 | instructions += BuildExpression(); // read condition. |
4159 | |
4160 | const TokenPosition condition_start_offset = |
4161 | ReadPosition(); // read condition start offset. |
4162 | const TokenPosition condition_end_offset = |
4163 | ReadPosition(); // read condition end offset. |
4164 | |
4165 | instructions += EvaluateAssertion(); |
4166 | instructions += CheckBoolean(condition_start_offset); |
4167 | instructions += Constant(Bool::True()); |
4168 | instructions += BranchIfEqual(&then, &otherwise, false); |
4169 | |
4170 | const Class& klass = |
4171 | Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError())); |
4172 | ASSERT(!klass.IsNull()); |
4173 | const Function& target = Function::ZoneHandle( |
4174 | Z, klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNew())); |
4175 | ASSERT(!target.IsNull()); |
4176 | |
4177 | // Build equivalent of `throw _AssertionError._throwNew(start, end, message)` |
4178 | // expression. We build throw (even through _throwNew already throws) because |
4179 | // call is not a valid last instruction for the block. Blocks can only |
4180 | // terminate with explicit control flow instructions (Branch, Goto, Return |
4181 | // or Throw). |
4182 | Fragment otherwise_fragment(otherwise); |
4183 | otherwise_fragment += IntConstant(condition_start_offset.Pos()); |
4184 | otherwise_fragment += IntConstant(condition_end_offset.Pos()); |
4185 | Tag tag = ReadTag(); // read (first part of) message. |
4186 | if (tag == kSomething) { |
4187 | otherwise_fragment += BuildExpression(); // read (rest of) message. |
4188 | } else { |
4189 | otherwise_fragment += Constant(Instance::ZoneHandle(Z)); // null. |
4190 | } |
4191 | |
4192 | // Note: condition_start_offset points to the first token after the opening |
4193 | // paren, not the beginning of 'assert'. |
4194 | otherwise_fragment += |
4195 | StaticCall(condition_start_offset, target, 3, ICData::kStatic); |
4196 | otherwise_fragment += ThrowException(TokenPosition::kNoSource); |
4197 | otherwise_fragment += Drop(); |
4198 | |
4199 | return Fragment(instructions.entry, then); |
4200 | } |
4201 | |
4202 | Fragment StreamingFlowGraphBuilder::BuildLabeledStatement() { |
4203 | // There can be serveral cases: |
4204 | // |
4205 | // * the body contains a break |
4206 | // * the body doesn't contain a break |
4207 | // |
4208 | // * translating the body results in a closed fragment |
4209 | // * translating the body results in a open fragment |
4210 | // |
4211 | // => We will only know which case we are in after the body has been |
4212 | // traversed. |
4213 | |
4214 | BreakableBlock block(flow_graph_builder_); |
4215 | Fragment instructions = BuildStatement(); // read body. |
4216 | if (block.HadJumper()) { |
4217 | if (instructions.is_open()) { |
4218 | instructions += Goto(block.destination()); |
4219 | } |
4220 | return Fragment(instructions.entry, block.destination()); |
4221 | } else { |
4222 | return instructions; |
4223 | } |
4224 | } |
4225 | |
4226 | Fragment StreamingFlowGraphBuilder::BuildBreakStatement() { |
4227 | TokenPosition position = ReadPosition(); // read position. |
4228 | intptr_t target_index = ReadUInt(); // read target index. |
4229 | |
4230 | TryFinallyBlock* outer_finally = NULL; |
4231 | intptr_t target_context_depth = -1; |
4232 | JoinEntryInstr* destination = breakable_block()->BreakDestination( |
4233 | target_index, &outer_finally, &target_context_depth); |
4234 | |
4235 | Fragment instructions; |
4236 | // Break statement should pause before manipulation of context, which |
4237 | // will possibly cause debugger having incorrect context object. |
4238 | if (NeedsDebugStepCheck(parsed_function()->function(), position)) { |
4239 | instructions += DebugStepCheck(position); |
4240 | } |
4241 | instructions += |
4242 | TranslateFinallyFinalizers(outer_finally, target_context_depth); |
4243 | if (instructions.is_open()) { |
4244 | instructions += Goto(destination); |
4245 | } |
4246 | return instructions; |
4247 | } |
4248 | |
4249 | Fragment StreamingFlowGraphBuilder::BuildWhileStatement() { |
4250 | ASSERT(block_expression_depth() == 0); // no while in block-expr |
4251 | loop_depth_inc(); |
4252 | const TokenPosition position = ReadPosition(); // read position. |
4253 | TestFragment condition = TranslateConditionForControl(); // read condition. |
4254 | const Fragment body = BuildStatement(); // read body |
4255 | |
4256 | Fragment body_entry(condition.CreateTrueSuccessor(flow_graph_builder_)); |
4257 | body_entry += body; |
4258 | |
4259 | Instruction* entry; |
4260 | if (body_entry.is_open()) { |
4261 | JoinEntryInstr* join = BuildJoinEntry(); |
4262 | body_entry += Goto(join); |
4263 | |
4264 | Fragment loop(join); |
4265 | ASSERT(B->GetStackDepth() == 0); |
4266 | loop += CheckStackOverflow(position); |
4267 | loop.current->LinkTo(condition.entry); |
4268 | |
4269 | entry = Goto(join).entry; |
4270 | } else { |
4271 | entry = condition.entry; |
4272 | } |
4273 | |
4274 | loop_depth_dec(); |
4275 | return Fragment(entry, condition.CreateFalseSuccessor(flow_graph_builder_)); |
4276 | } |
4277 | |
4278 | Fragment StreamingFlowGraphBuilder::BuildDoStatement() { |
4279 | ASSERT(block_expression_depth() == 0); // no do-while in block-expr |
4280 | loop_depth_inc(); |
4281 | const TokenPosition position = ReadPosition(); // read position. |
4282 | Fragment body = BuildStatement(); // read body. |
4283 | |
4284 | if (body.is_closed()) { |
4285 | SkipExpression(); // read condition. |
4286 | loop_depth_dec(); |
4287 | return body; |
4288 | } |
4289 | |
4290 | TestFragment condition = TranslateConditionForControl(); |
4291 | |
4292 | JoinEntryInstr* join = BuildJoinEntry(); |
4293 | Fragment loop(join); |
4294 | ASSERT(B->GetStackDepth() == 0); |
4295 | loop += CheckStackOverflow(position); |
4296 | loop += body; |
4297 | loop <<= condition.entry; |
4298 | |
4299 | condition.IfTrueGoto(flow_graph_builder_, join); |
4300 | |
4301 | loop_depth_dec(); |
4302 | return Fragment( |
4303 | new (Z) GotoInstr(join, CompilerState::Current().GetNextDeoptId()), |
4304 | condition.CreateFalseSuccessor(flow_graph_builder_)); |
4305 | } |
4306 | |
4307 | Fragment StreamingFlowGraphBuilder::BuildForStatement() { |
4308 | intptr_t offset = ReaderOffset() - 1; // Include the tag. |
4309 | |
4310 | const TokenPosition position = ReadPosition(); // read position. |
4311 | |
4312 | Fragment declarations; |
4313 | |
4314 | loop_depth_inc(); |
4315 | |
4316 | const LocalScope* context_scope = nullptr; |
4317 | declarations += EnterScope(offset, &context_scope); |
4318 | |
4319 | intptr_t list_length = ReadListLength(); // read number of variables. |
4320 | for (intptr_t i = 0; i < list_length; ++i) { |
4321 | declarations += BuildVariableDeclaration(); // read ith variable. |
4322 | } |
4323 | |
4324 | Tag tag = ReadTag(); // Read first part of condition. |
4325 | TestFragment condition; |
4326 | BlockEntryInstr* body_entry; |
4327 | BlockEntryInstr* loop_exit; |
4328 | if (tag != kNothing) { |
4329 | condition = TranslateConditionForControl(); |
4330 | body_entry = condition.CreateTrueSuccessor(flow_graph_builder_); |
4331 | loop_exit = condition.CreateFalseSuccessor(flow_graph_builder_); |
4332 | } else { |
4333 | body_entry = BuildJoinEntry(); |
4334 | loop_exit = BuildJoinEntry(); |
4335 | } |
4336 | |
4337 | Fragment updates; |
4338 | list_length = ReadListLength(); // read number of updates. |
4339 | for (intptr_t i = 0; i < list_length; ++i) { |
4340 | updates += BuildExpression(); // read ith update. |
4341 | updates += Drop(); |
4342 | } |
4343 | |
4344 | Fragment body(body_entry); |
4345 | body += BuildStatement(); // read body. |
4346 | |
4347 | if (body.is_open()) { |
4348 | // We allocated a fresh context before the loop which contains captured |
4349 | // [ForStatement] variables. Before jumping back to the loop entry we clone |
4350 | // the context object (at same depth) which ensures the next iteration of |
4351 | // the body gets a fresh set of [ForStatement] variables (with the old |
4352 | // (possibly updated) values). |
4353 | if (context_scope->num_context_variables() > 0) { |
4354 | body += CloneContext(context_scope->context_slots()); |
4355 | } |
4356 | |
4357 | body += updates; |
4358 | JoinEntryInstr* join = BuildJoinEntry(); |
4359 | declarations += Goto(join); |
4360 | body += Goto(join); |
4361 | |
4362 | Fragment loop(join); |
4363 | loop += CheckStackOverflow(position); // may have non-empty stack |
4364 | if (condition.entry != nullptr) { |
4365 | loop <<= condition.entry; |
4366 | } else { |
4367 | loop += Goto(body_entry->AsJoinEntry()); |
4368 | } |
4369 | } else { |
4370 | if (condition.entry != nullptr) { |
4371 | declarations <<= condition.entry; |
4372 | } else { |
4373 | declarations += Goto(body_entry->AsJoinEntry()); |
4374 | } |
4375 | } |
4376 | |
4377 | Fragment loop(declarations.entry, loop_exit); |
4378 | |
4379 | loop += ExitScope(offset); |
4380 | |
4381 | loop_depth_dec(); |
4382 | |
4383 | return loop; |
4384 | } |
4385 | |
4386 | Fragment StreamingFlowGraphBuilder::BuildForInStatement(bool async) { |
4387 | intptr_t offset = ReaderOffset() - 1; // Include the tag. |
4388 | |
4389 | const TokenPosition position = ReadPosition(); // read position. |
4390 | TokenPosition body_position = ReadPosition(); // read body position. |
4391 | intptr_t variable_kernel_position = ReaderOffset() + data_program_offset_; |
4392 | SkipVariableDeclaration(); // read variable. |
4393 | |
4394 | TokenPosition iterable_position = TokenPosition::kNoSource; |
4395 | Fragment instructions = |
4396 | BuildExpression(&iterable_position); // read iterable. |
4397 | |
4398 | const String& iterator_getter = |
4399 | String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Iterator())); |
4400 | instructions += |
4401 | InstanceCall(iterable_position, iterator_getter, Token::kGET, 1); |
4402 | LocalVariable* iterator = scopes()->iterator_variables[for_in_depth()]; |
4403 | instructions += StoreLocal(TokenPosition::kNoSource, iterator); |
4404 | instructions += Drop(); |
4405 | |
4406 | for_in_depth_inc(); |
4407 | loop_depth_inc(); |
4408 | Fragment condition = LoadLocal(iterator); |
4409 | condition += |
4410 | InstanceCall(iterable_position, Symbols::MoveNext(), Token::kILLEGAL, 1); |
4411 | TargetEntryInstr* body_entry; |
4412 | TargetEntryInstr* loop_exit; |
4413 | condition += BranchIfTrue(&body_entry, &loop_exit, false); |
4414 | |
4415 | Fragment body(body_entry); |
4416 | body += EnterScope(offset); |
4417 | body += LoadLocal(iterator); |
4418 | const String& current_getter = |
4419 | String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Current())); |
4420 | body += InstanceCall(body_position, current_getter, Token::kGET, 1); |
4421 | body += StoreLocal(TokenPosition::kNoSource, |
4422 | LookupVariable(variable_kernel_position)); |
4423 | body += Drop(); |
4424 | body += BuildStatement(); // read body. |
4425 | body += ExitScope(offset); |
4426 | |
4427 | if (body.is_open()) { |
4428 | JoinEntryInstr* join = BuildJoinEntry(); |
4429 | instructions += Goto(join); |
4430 | body += Goto(join); |
4431 | |
4432 | Fragment loop(join); |
4433 | loop += CheckStackOverflow(position); // may have non-empty stack |
4434 | loop += condition; |
4435 | } else { |
4436 | instructions += condition; |
4437 | } |
4438 | |
4439 | loop_depth_dec(); |
4440 | for_in_depth_dec(); |
4441 | return Fragment(instructions.entry, loop_exit); |
4442 | } |
4443 | |
4444 | Fragment StreamingFlowGraphBuilder::BuildSwitchStatement() { |
4445 | ReadPosition(); // read position. |
4446 | // We need the number of cases. So start by getting that, then go back. |
4447 | intptr_t offset = ReaderOffset(); |
4448 | SkipExpression(); // temporarily skip condition |
4449 | int case_count = ReadListLength(); // read number of cases. |
4450 | SetOffset(offset); |
4451 | |
4452 | SwitchBlock block(flow_graph_builder_, case_count); |
4453 | |
4454 | // Instead of using a variable we should reuse the expression on the stack, |
4455 | // since it won't be assigned again, we don't need phi nodes. |
4456 | Fragment head_instructions = BuildExpression(); // read condition. |
4457 | head_instructions += |
4458 | StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable); |
4459 | head_instructions += Drop(); |
4460 | |
4461 | case_count = ReadListLength(); // read number of cases. |
4462 | |
4463 | // Phase 1: Generate bodies and try to find out whether a body will be target |
4464 | // of a jump due to: |
4465 | // * `continue case_label` |
4466 | // * `case e1: case e2: body` |
4467 | Fragment* body_fragments = Z->Alloc<Fragment>(case_count); |
4468 | intptr_t* case_expression_offsets = Z->Alloc<intptr_t>(case_count); |
4469 | int default_case = -1; |
4470 | |
4471 | for (intptr_t i = 0; i < case_count; ++i) { |
4472 | case_expression_offsets[i] = ReaderOffset(); |
4473 | int expression_count = ReadListLength(); // read number of expressions. |
4474 | for (intptr_t j = 0; j < expression_count; ++j) { |
4475 | ReadPosition(); // read jth position. |
4476 | SkipExpression(); // read jth expression. |
4477 | } |
4478 | bool is_default = ReadBool(); // read is_default. |
4479 | if (is_default) default_case = i; |
4480 | Fragment& body_fragment = body_fragments[i] = |
4481 | BuildStatement(); // read body. |
4482 | |
4483 | if (body_fragment.entry == NULL) { |
4484 | // Make a NOP in order to ensure linking works properly. |
4485 | body_fragment = NullConstant(); |
4486 | body_fragment += Drop(); |
4487 | } |
4488 | |
4489 | // The Dart language specification mandates fall-throughs in [SwitchCase]es |
4490 | // to be runtime errors. |
4491 | if (!is_default && body_fragment.is_open() && (i < (case_count - 1))) { |
4492 | const Class& klass = Class::ZoneHandle( |
4493 | Z, Library::LookupCoreClass(Symbols::FallThroughError())); |
4494 | ASSERT(!klass.IsNull()); |
4495 | |
4496 | GrowableHandlePtrArray<const String> pieces(Z, 3); |
4497 | pieces.Add(Symbols::FallThroughError()); |
4498 | pieces.Add(Symbols::Dot()); |
4499 | pieces.Add(H.DartSymbolObfuscate("_create" )); |
4500 | |
4501 | const Function& constructor = Function::ZoneHandle( |
4502 | Z, klass.LookupConstructorAllowPrivate(String::ZoneHandle( |
4503 | Z, Symbols::FromConcatAll(H.thread(), pieces)))); |
4504 | ASSERT(!constructor.IsNull()); |
4505 | const String& url = H.DartSymbolPlain( |
4506 | parsed_function()->function().ToLibNamePrefixedQualifiedCString()); |
4507 | |
4508 | // Create instance of _FallThroughError |
4509 | body_fragment += AllocateObject(TokenPosition::kNoSource, klass, 0); |
4510 | LocalVariable* instance = MakeTemporary(); |
4511 | |
4512 | // Call _FallThroughError._create constructor. |
4513 | body_fragment += LoadLocal(instance); // this |
4514 | body_fragment += Constant(url); // url |
4515 | body_fragment += NullConstant(); // line |
4516 | |
4517 | body_fragment += |
4518 | StaticCall(TokenPosition::kNoSource, constructor, 3, ICData::kStatic); |
4519 | body_fragment += Drop(); |
4520 | |
4521 | // Throw the exception |
4522 | body_fragment += ThrowException(TokenPosition::kNoSource); |
4523 | body_fragment += Drop(); |
4524 | } |
4525 | |
4526 | // If there is an implicit fall-through we have one [SwitchCase] and |
4527 | // multiple expressions, e.g. |
4528 | // |
4529 | // switch(expr) { |
4530 | // case a: |
4531 | // case b: |
4532 | // <stmt-body> |
4533 | // } |
4534 | // |
4535 | // This means that the <stmt-body> will have more than 1 incoming edge (one |
4536 | // from `a == expr` and one from `a != expr && b == expr`). The |
4537 | // `block.Destination()` records the additional jump. |
4538 | if (expression_count > 1) { |
4539 | block.DestinationDirect(i); |
4540 | } |
4541 | } |
4542 | |
4543 | intptr_t end_offset = ReaderOffset(); |
4544 | |
4545 | // Phase 2: Generate everything except the real bodies: |
4546 | // * jump directly to a body (if there is no jumper) |
4547 | // * jump to a wrapper block which jumps to the body (if there is a jumper) |
4548 | Fragment current_instructions = head_instructions; |
4549 | for (intptr_t i = 0; i < case_count; ++i) { |
4550 | SetOffset(case_expression_offsets[i]); |
4551 | int expression_count = ReadListLength(); // read length of expressions. |
4552 | |
4553 | if (i == default_case) { |
4554 | ASSERT(i == (case_count - 1)); |
4555 | |
4556 | if (block.HadJumper(i)) { |
4557 | // There are several branches to the body, so we will make a goto to |
4558 | // the join block (and prepend a join instruction to the real body). |
4559 | JoinEntryInstr* join = block.DestinationDirect(i); |
4560 | current_instructions += Goto(join); |
4561 | |
4562 | current_instructions = Fragment(current_instructions.entry, join); |
4563 | current_instructions += body_fragments[i]; |
4564 | } else { |
4565 | current_instructions += body_fragments[i]; |
4566 | } |
4567 | } else { |
4568 | JoinEntryInstr* body_join = NULL; |
4569 | if (block.HadJumper(i)) { |
4570 | body_join = block.DestinationDirect(i); |
4571 | body_fragments[i] = Fragment(body_join) + body_fragments[i]; |
4572 | } |
4573 | |
4574 | for (intptr_t j = 0; j < expression_count; ++j) { |
4575 | TargetEntryInstr* then; |
4576 | TargetEntryInstr* otherwise; |
4577 | |
4578 | TokenPosition position = ReadPosition(); // read jth position. |
4579 | current_instructions += Constant( |
4580 | Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression())); |
4581 | current_instructions += LoadLocal(scopes()->switch_variable); |
4582 | current_instructions += |
4583 | InstanceCall(position, Symbols::EqualOperator(), Token::kEQ, |
4584 | /*argument_count=*/2, |
4585 | /*checked_argument_count=*/2); |
4586 | current_instructions += BranchIfTrue(&then, &otherwise, false); |
4587 | |
4588 | Fragment then_fragment(then); |
4589 | |
4590 | if (body_join != NULL) { |
4591 | // There are several branches to the body, so we will make a goto to |
4592 | // the join block (the real body has already been prepended with a |
4593 | // join instruction). |
4594 | then_fragment += Goto(body_join); |
4595 | } else { |
4596 | // There is only a signle branch to the body, so we will just append |
4597 | // the body fragment. |
4598 | then_fragment += body_fragments[i]; |
4599 | } |
4600 | |
4601 | current_instructions = Fragment(otherwise); |
4602 | } |
4603 | } |
4604 | } |
4605 | |
4606 | if (case_count > 0 && default_case < 0) { |
4607 | // There is no default, which means we have an open [current_instructions] |
4608 | // (which is a [TargetEntryInstruction] for the last "otherwise" branch). |
4609 | // |
4610 | // Furthermore the last [SwitchCase] can be open as well. If so, we need |
4611 | // to join these two. |
4612 | Fragment& last_body = body_fragments[case_count - 1]; |
4613 | if (last_body.is_open()) { |
4614 | ASSERT(current_instructions.is_open()); |
4615 | ASSERT(current_instructions.current->IsTargetEntry()); |
4616 | |
4617 | // Join the last "otherwise" branch and the last [SwitchCase] fragment. |
4618 | JoinEntryInstr* join = BuildJoinEntry(); |
4619 | current_instructions += Goto(join); |
4620 | last_body += Goto(join); |
4621 | |
4622 | current_instructions = Fragment(join); |
4623 | } |
4624 | } else { |
4625 | // All non-default cases will be closed (i.e. break/continue/throw/return) |
4626 | // So it is fine to just let more statements after the switch append to the |
4627 | // default case. |
4628 | } |
4629 | |
4630 | SetOffset(end_offset); |
4631 | return Fragment(head_instructions.entry, current_instructions.current); |
4632 | } |
4633 | |
4634 | Fragment StreamingFlowGraphBuilder::BuildContinueSwitchStatement() { |
4635 | TokenPosition position = ReadPosition(); // read position. |
4636 | intptr_t target_index = ReadUInt(); // read target index. |
4637 | |
4638 | TryFinallyBlock* outer_finally = NULL; |
4639 | intptr_t target_context_depth = -1; |
4640 | JoinEntryInstr* entry = switch_block()->Destination( |
4641 | target_index, &outer_finally, &target_context_depth); |
4642 | |
4643 | Fragment instructions; |
4644 | instructions += |
4645 | TranslateFinallyFinalizers(outer_finally, target_context_depth); |
4646 | if (instructions.is_open()) { |
4647 | if (NeedsDebugStepCheck(parsed_function()->function(), position)) { |
4648 | instructions += DebugStepCheck(position); |
4649 | } |
4650 | instructions += Goto(entry); |
4651 | } |
4652 | return instructions; |
4653 | } |
4654 | |
4655 | Fragment StreamingFlowGraphBuilder::BuildIfStatement() { |
4656 | ReadPosition(); // read position. |
4657 | |
4658 | TestFragment condition = TranslateConditionForControl(); |
4659 | |
4660 | Fragment then_fragment(condition.CreateTrueSuccessor(flow_graph_builder_)); |
4661 | then_fragment += BuildStatement(); // read then. |
4662 | |
4663 | Fragment otherwise_fragment( |
4664 | condition.CreateFalseSuccessor(flow_graph_builder_)); |
4665 | otherwise_fragment += BuildStatement(); // read otherwise. |
4666 | |
4667 | if (then_fragment.is_open()) { |
4668 | if (otherwise_fragment.is_open()) { |
4669 | JoinEntryInstr* join = BuildJoinEntry(); |
4670 | then_fragment += Goto(join); |
4671 | otherwise_fragment += Goto(join); |
4672 | return Fragment(condition.entry, join); |
4673 | } else { |
4674 | return Fragment(condition.entry, then_fragment.current); |
4675 | } |
4676 | } else if (otherwise_fragment.is_open()) { |
4677 | return Fragment(condition.entry, otherwise_fragment.current); |
4678 | } else { |
4679 | return Fragment(condition.entry, nullptr); |
4680 | } |
4681 | } |
4682 | |
4683 | Fragment StreamingFlowGraphBuilder::BuildReturnStatement() { |
4684 | TokenPosition position = ReadPosition(); // read position. |
4685 | Tag tag = ReadTag(); // read first part of expression. |
4686 | |
4687 | bool inside_try_finally = try_finally_block() != NULL; |
4688 | |
4689 | Fragment instructions = tag == kNothing |
4690 | ? NullConstant() |
4691 | : BuildExpression(); // read rest of expression. |
4692 | |
4693 | if (instructions.is_open()) { |
4694 | if (inside_try_finally) { |
4695 | ASSERT(scopes()->finally_return_variable != NULL); |
4696 | const Function& function = parsed_function()->function(); |
4697 | if (NeedsDebugStepCheck(function, position)) { |
4698 | instructions += DebugStepCheck(position); |
4699 | } |
4700 | instructions += StoreLocal(position, scopes()->finally_return_variable); |
4701 | instructions += Drop(); |
4702 | instructions += TranslateFinallyFinalizers(NULL, -1); |
4703 | if (instructions.is_open()) { |
4704 | instructions += LoadLocal(scopes()->finally_return_variable); |
4705 | instructions += Return(TokenPosition::kNoSource); |
4706 | } |
4707 | } else { |
4708 | instructions += Return(position); |
4709 | } |
4710 | } else { |
4711 | Pop(); |
4712 | } |
4713 | |
4714 | return instructions; |
4715 | } |
4716 | |
4717 | Fragment StreamingFlowGraphBuilder::BuildTryCatch() { |
4718 | ASSERT(block_expression_depth() == 0); // no try-catch in block-expr |
4719 | InlineBailout("kernel::FlowgraphBuilder::VisitTryCatch" ); |
4720 | |
4721 | intptr_t try_handler_index = AllocateTryIndex(); |
4722 | Fragment try_body = TryCatch(try_handler_index); |
4723 | JoinEntryInstr* after_try = BuildJoinEntry(); |
4724 | |
4725 | // Fill in the body of the try. |
4726 | try_depth_inc(); |
4727 | { |
4728 | TryCatchBlock block(flow_graph_builder_, try_handler_index); |
4729 | try_body += BuildStatement(); // read body. |
4730 | try_body += Goto(after_try); |
4731 | } |
4732 | try_depth_dec(); |
4733 | |
4734 | const int kNeedsStracktraceBit = 1 << 0; |
4735 | const int kIsSyntheticBit = 1 << 1; |
4736 | |
4737 | uint8_t flags = ReadByte(); |
4738 | bool needs_stacktrace = |
4739 | (flags & kNeedsStracktraceBit) == kNeedsStracktraceBit; |
4740 | bool is_synthetic = (flags & kIsSyntheticBit) == kIsSyntheticBit; |
4741 | |
4742 | catch_depth_inc(); |
4743 | intptr_t catch_count = ReadListLength(); // read number of catches. |
4744 | const Array& handler_types = |
4745 | Array::ZoneHandle(Z, Array::New(catch_count, Heap::kOld)); |
4746 | |
4747 | Fragment catch_body = CatchBlockEntry(handler_types, try_handler_index, |
4748 | needs_stacktrace, is_synthetic); |
4749 | // Fill in the body of the catch. |
4750 | for (intptr_t i = 0; i < catch_count; ++i) { |
4751 | intptr_t catch_offset = ReaderOffset(); // Catch has no tag. |
4752 | TokenPosition position = ReadPosition(); // read position. |
4753 | const AbstractType& type_guard = T.BuildType(); // read guard. |
4754 | handler_types.SetAt(i, type_guard); |
4755 | |
4756 | Fragment catch_handler_body = EnterScope(catch_offset); |
4757 | |
4758 | Tag tag = ReadTag(); // read first part of exception. |
4759 | if (tag == kSomething) { |
4760 | catch_handler_body += LoadLocal(CurrentException()); |
4761 | catch_handler_body += |
4762 | StoreLocal(TokenPosition::kNoSource, |
4763 | LookupVariable(ReaderOffset() + data_program_offset_)); |
4764 | catch_handler_body += Drop(); |
4765 | SkipVariableDeclaration(); // read exception. |
4766 | } |
4767 | |
4768 | tag = ReadTag(); // read first part of stack trace. |
4769 | if (tag == kSomething) { |
4770 | catch_handler_body += LoadLocal(CurrentStackTrace()); |
4771 | catch_handler_body += |
4772 | StoreLocal(TokenPosition::kNoSource, |
4773 | LookupVariable(ReaderOffset() + data_program_offset_)); |
4774 | catch_handler_body += Drop(); |
4775 | SkipVariableDeclaration(); // read stack trace. |
4776 | } |
4777 | |
4778 | { |
4779 | CatchBlock block(flow_graph_builder_, CurrentException(), |
4780 | CurrentStackTrace(), try_handler_index); |
4781 | |
4782 | catch_handler_body += BuildStatement(); // read body. |
4783 | |
4784 | // Note: ExitScope adjusts context_depth_ so even if catch_handler_body |
4785 | // is closed we still need to execute ExitScope for its side effect. |
4786 | catch_handler_body += ExitScope(catch_offset); |
4787 | if (catch_handler_body.is_open()) { |
4788 | catch_handler_body += Goto(after_try); |
4789 | } |
4790 | } |
4791 | |
4792 | if (!type_guard.IsCatchAllType()) { |
4793 | catch_body += LoadLocal(CurrentException()); |
4794 | |
4795 | if (!type_guard.IsInstantiated(kCurrentClass)) { |
4796 | catch_body += LoadInstantiatorTypeArguments(); |
4797 | } else { |
4798 | catch_body += NullConstant(); |
4799 | } |
4800 | |
4801 | if (!type_guard.IsInstantiated(kFunctions)) { |
4802 | catch_body += LoadFunctionTypeArguments(); |
4803 | } else { |
4804 | catch_body += NullConstant(); |
4805 | } |
4806 | |
4807 | catch_body += Constant(type_guard); |
4808 | |
4809 | catch_body += InstanceCall( |
4810 | position, Library::PrivateCoreLibName(Symbols::_instanceOf()), |
4811 | Token::kIS, 4); |
4812 | |
4813 | TargetEntryInstr* catch_entry; |
4814 | TargetEntryInstr* next_catch_entry; |
4815 | catch_body += BranchIfTrue(&catch_entry, &next_catch_entry, false); |
4816 | |
4817 | Fragment(catch_entry) + catch_handler_body; |
4818 | catch_body = Fragment(next_catch_entry); |
4819 | } else { |
4820 | catch_body += catch_handler_body; |
4821 | } |
4822 | } |
4823 | |
4824 | // In case the last catch body was not handling the exception and branching to |
4825 | // after the try block, we will rethrow the exception (i.e. no default catch |
4826 | // handler). |
4827 | if (catch_body.is_open()) { |
4828 | catch_body += LoadLocal(CurrentException()); |
4829 | catch_body += LoadLocal(CurrentStackTrace()); |
4830 | catch_body += RethrowException(TokenPosition::kNoSource, try_handler_index); |
4831 | Drop(); |
4832 | } |
4833 | catch_depth_dec(); |
4834 | |
4835 | return Fragment(try_body.entry, after_try); |
4836 | } |
4837 | |
4838 | Fragment StreamingFlowGraphBuilder::BuildTryFinally() { |
4839 | ASSERT(block_expression_depth() == 0); // no try-finally in block-expr |
4840 | // Note on streaming: |
4841 | // We only stream this TryFinally if we can stream everything inside it, |
4842 | // so creating a "TryFinallyBlock" with a kernel binary offset instead of an |
4843 | // AST node isn't a problem. |
4844 | |
4845 | InlineBailout("kernel::FlowgraphBuilder::VisitTryFinally" ); |
4846 | |
4847 | // There are 5 different cases where we need to execute the finally block: |
4848 | // |
4849 | // a) 1/2/3th case: Special control flow going out of `node->body()`: |
4850 | // |
4851 | // * [BreakStatement] transfers control to a [LabeledStatement] |
4852 | // * [ContinueSwitchStatement] transfers control to a [SwitchCase] |
4853 | // * [ReturnStatement] returns a value |
4854 | // |
4855 | // => All three cases will automatically append all finally blocks |
4856 | // between the branching point and the destination (so we don't need to |
4857 | // do anything here). |
4858 | // |
4859 | // b) 4th case: Translating the body resulted in an open fragment (i.e. body |
4860 | // executes without any control flow out of it) |
4861 | // |
4862 | // => We are responsible for jumping out of the body to a new block (with |
4863 | // different try index) and execute the finalizer. |
4864 | // |
4865 | // c) 5th case: An exception occurred inside the body. |
4866 | // |
4867 | // => We are responsible for catching it, executing the finally block and |
4868 | // rethrowing the exception. |
4869 | intptr_t try_handler_index = AllocateTryIndex(); |
4870 | Fragment try_body = TryCatch(try_handler_index); |
4871 | JoinEntryInstr* after_try = BuildJoinEntry(); |
4872 | |
4873 | intptr_t offset = ReaderOffset(); |
4874 | SkipStatement(); // temporarily read body. |
4875 | intptr_t finalizer_offset = ReaderOffset(); |
4876 | SetOffset(offset); |
4877 | |
4878 | // Fill in the body of the try. |
4879 | try_depth_inc(); |
4880 | { |
4881 | TryFinallyBlock tfb(flow_graph_builder_, finalizer_offset); |
4882 | TryCatchBlock tcb(flow_graph_builder_, try_handler_index); |
4883 | try_body += BuildStatement(); // read body. |
4884 | } |
4885 | try_depth_dec(); |
4886 | |
4887 | if (try_body.is_open()) { |
4888 | // Please note: The try index will be on level out of this block, |
4889 | // thereby ensuring if there's an exception in the finally block we |
4890 | // won't run it twice. |
4891 | JoinEntryInstr* finally_entry = BuildJoinEntry(); |
4892 | |
4893 | try_body += Goto(finally_entry); |
4894 | |
4895 | Fragment finally_body(finally_entry); |
4896 | finally_body += BuildStatement(); // read finalizer. |
4897 | finally_body += Goto(after_try); |
4898 | } |
4899 | |
4900 | // Fill in the body of the catch. |
4901 | catch_depth_inc(); |
4902 | const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld)); |
4903 | handler_types.SetAt(0, Object::dynamic_type()); |
4904 | // Note: rethrow will actually force mark the handler as needing a stacktrace. |
4905 | Fragment finally_body = CatchBlockEntry(handler_types, try_handler_index, |
4906 | /* needs_stacktrace = */ false, |
4907 | /* is_synthesized = */ true); |
4908 | SetOffset(finalizer_offset); |
4909 | finally_body += BuildStatement(); // read finalizer |
4910 | if (finally_body.is_open()) { |
4911 | finally_body += LoadLocal(CurrentException()); |
4912 | finally_body += LoadLocal(CurrentStackTrace()); |
4913 | finally_body += |
4914 | RethrowException(TokenPosition::kNoSource, try_handler_index); |
4915 | Drop(); |
4916 | } |
4917 | catch_depth_dec(); |
4918 | |
4919 | return Fragment(try_body.entry, after_try); |
4920 | } |
4921 | |
4922 | Fragment StreamingFlowGraphBuilder::BuildYieldStatement() { |
4923 | TokenPosition position = ReadPosition(); // read position. |
4924 | uint8_t flags = ReadByte(); // read flags. |
4925 | |
4926 | ASSERT(flags == kNativeYieldFlags); // Must have been desugared. |
4927 | |
4928 | // Setup yield/continue point: |
4929 | // |
4930 | // ... |
4931 | // :await_jump_var = index; |
4932 | // :await_ctx_var = :current_context_var |
4933 | // return <expr> |
4934 | // |
4935 | // Continuation<index>: |
4936 | // Drop(1) |
4937 | // ... |
4938 | // |
4939 | // BuildGraphOfFunction will create a dispatch that jumps to |
4940 | // Continuation<:await_jump_var> upon entry to the function. |
4941 | // |
4942 | const intptr_t new_yield_pos = yield_continuations().length() + 1; |
4943 | Fragment instructions = IntConstant(new_yield_pos); |
4944 | instructions += |
4945 | StoreLocal(TokenPosition::kNoSource, scopes()->yield_jump_variable); |
4946 | instructions += Drop(); |
4947 | instructions += LoadLocal(parsed_function()->current_context_var()); |
4948 | instructions += |
4949 | StoreLocal(TokenPosition::kNoSource, scopes()->yield_context_variable); |
4950 | instructions += Drop(); |
4951 | instructions += BuildExpression(); // read expression. |
4952 | instructions += Return(position, new_yield_pos); |
4953 | |
4954 | // Note: DropTempsInstr serves as an anchor instruction. It will not |
4955 | // be linked into the resulting graph. |
4956 | DropTempsInstr* anchor = new (Z) DropTempsInstr(0, NULL); |
4957 | yield_continuations().Add(YieldContinuation(anchor, CurrentTryIndex())); |
4958 | |
4959 | Fragment continuation(instructions.entry, anchor); |
4960 | |
4961 | if (parsed_function()->function().IsAsyncClosure() || |
4962 | parsed_function()->function().IsAsyncGenClosure()) { |
4963 | // If function is async closure or async gen closure it takes three |
4964 | // parameters where the second and the third are exception and stack_trace. |
4965 | // Check if exception is non-null and rethrow it. |
4966 | // |
4967 | // :async_op([:result, :exception, :stack_trace]) { |
4968 | // ... |
4969 | // Continuation<index>: |
4970 | // if (:exception != null) rethrow(:exception, :stack_trace); |
4971 | // ... |
4972 | // } |
4973 | // |
4974 | LocalVariable* exception_var = parsed_function()->ParameterVariable(2); |
4975 | LocalVariable* stack_trace_var = parsed_function()->ParameterVariable(3); |
4976 | ASSERT(exception_var->name().raw() == Symbols::ExceptionParameter().raw()); |
4977 | ASSERT(stack_trace_var->name().raw() == |
4978 | Symbols::StackTraceParameter().raw()); |
4979 | |
4980 | TargetEntryInstr* no_error; |
4981 | TargetEntryInstr* error; |
4982 | |
4983 | continuation += LoadLocal(exception_var); |
4984 | continuation += BranchIfNull(&no_error, &error); |
4985 | |
4986 | Fragment rethrow(error); |
4987 | rethrow += LoadLocal(exception_var); |
4988 | rethrow += LoadLocal(stack_trace_var); |
4989 | rethrow += RethrowException(position, kInvalidTryIndex); |
4990 | Drop(); |
4991 | |
4992 | continuation = Fragment(continuation.entry, no_error); |
4993 | } |
4994 | |
4995 | return continuation; |
4996 | } |
4997 | |
4998 | Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration() { |
4999 | intptr_t kernel_position_no_tag = ReaderOffset() + data_program_offset_; |
5000 | LocalVariable* variable = LookupVariable(kernel_position_no_tag); |
5001 | |
5002 | VariableDeclarationHelper helper(this); |
5003 | helper.ReadUntilExcluding(VariableDeclarationHelper::kType); |
5004 | T.BuildType(); // read type. |
5005 | bool has_initializer = (ReadTag() != kNothing); |
5006 | |
5007 | Fragment instructions; |
5008 | if (variable->is_late()) { |
5009 | // TODO(liama): Treat the field as non-late if the initializer is trivial. |
5010 | if (has_initializer) { |
5011 | SkipExpression(); |
5012 | } |
5013 | instructions += Constant(Object::sentinel()); |
5014 | } else if (!has_initializer) { |
5015 | instructions += NullConstant(); |
5016 | } else if (helper.IsConst()) { |
5017 | // Read const initializer form current position. |
5018 | const Instance& constant_value = |
5019 | Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression()); |
5020 | variable->SetConstValue(constant_value); |
5021 | instructions += Constant(constant_value); |
5022 | } else { |
5023 | // Initializer |
5024 | instructions += BuildExpression(); // read (actual) initializer. |
5025 | } |
5026 | |
5027 | // Use position of equal sign if it exists. If the equal sign does not exist |
5028 | // use the position of the identifier. |
5029 | TokenPosition debug_position = |
5030 | Utils::Maximum(helper.position_, helper.equals_position_); |
5031 | if (NeedsDebugStepCheck(stack(), debug_position)) { |
5032 | instructions = DebugStepCheck(debug_position) + instructions; |
5033 | } |
5034 | instructions += StoreLocal(helper.position_, variable); |
5035 | instructions += Drop(); |
5036 | return instructions; |
5037 | } |
5038 | |
5039 | Fragment StreamingFlowGraphBuilder::BuildFunctionDeclaration() { |
5040 | TokenPosition position = ReadPosition(); // read position. |
5041 | intptr_t variable_offset = ReaderOffset() + data_program_offset_; |
5042 | |
5043 | // read variable declaration. |
5044 | VariableDeclarationHelper helper(this); |
5045 | helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd); |
5046 | |
5047 | Fragment instructions = DebugStepCheck(position); |
5048 | instructions += BuildFunctionNode(position, helper.name_index_); |
5049 | instructions += StoreLocal(position, LookupVariable(variable_offset)); |
5050 | instructions += Drop(); |
5051 | return instructions; |
5052 | } |
5053 | |
5054 | Fragment StreamingFlowGraphBuilder::BuildFunctionNode( |
5055 | TokenPosition parent_position, |
5056 | StringIndex name_index) { |
5057 | intptr_t offset = ReaderOffset(); |
5058 | |
5059 | FunctionNodeHelper function_node_helper(this); |
5060 | function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters); |
5061 | TokenPosition position = function_node_helper.position_; |
5062 | |
5063 | bool declaration = name_index >= 0; |
5064 | |
5065 | if (declaration) { |
5066 | position = parent_position; |
5067 | } |
5068 | if (!position.IsReal()) { |
5069 | // Positions has to be unique in regards to the parent. |
5070 | // A non-real at this point is probably -1, we cannot blindly use that |
5071 | // as others might use it too. Create a new dummy non-real TokenPosition. |
5072 | position = TokenPosition(offset).ToSynthetic(); |
5073 | } |
5074 | |
5075 | // The VM has a per-isolate table of functions indexed by the enclosing |
5076 | // function and token position. |
5077 | Function& function = Function::ZoneHandle(Z); |
5078 | |
5079 | // NOTE: This is not TokenPosition in the general sense! |
5080 | function = I->LookupClosureFunction(parsed_function()->function(), position); |
5081 | if (function.IsNull()) { |
5082 | for (intptr_t i = 0; i < scopes()->function_scopes.length(); ++i) { |
5083 | if (scopes()->function_scopes[i].kernel_offset != offset) { |
5084 | continue; |
5085 | } |
5086 | |
5087 | const String* name; |
5088 | if (declaration) { |
5089 | name = &H.DartSymbolObfuscate(name_index); |
5090 | } else { |
5091 | name = &Symbols::AnonymousClosure(); |
5092 | } |
5093 | // NOTE: This is not TokenPosition in the general sense! |
5094 | if (!closure_owner_.IsNull()) { |
5095 | function = Function::NewClosureFunctionWithKind( |
5096 | FunctionLayout::kClosureFunction, *name, |
5097 | parsed_function()->function(), position, closure_owner_); |
5098 | } else { |
5099 | function = Function::NewClosureFunction( |
5100 | *name, parsed_function()->function(), position); |
5101 | } |
5102 | |
5103 | function.set_is_debuggable(function_node_helper.dart_async_marker_ == |
5104 | FunctionNodeHelper::kSync); |
5105 | switch (function_node_helper.dart_async_marker_) { |
5106 | case FunctionNodeHelper::kSyncStar: |
5107 | function.set_modifier(FunctionLayout::kSyncGen); |
5108 | break; |
5109 | case FunctionNodeHelper::kAsync: |
5110 | function.set_modifier(FunctionLayout::kAsync); |
5111 | function.set_is_inlinable(!FLAG_causal_async_stacks); |
5112 | break; |
5113 | case FunctionNodeHelper::kAsyncStar: |
5114 | function.set_modifier(FunctionLayout::kAsyncGen); |
5115 | function.set_is_inlinable(!FLAG_causal_async_stacks); |
5116 | break; |
5117 | default: |
5118 | // no special modifier |
5119 | break; |
5120 | } |
5121 | function.set_is_generated_body(function_node_helper.async_marker_ == |
5122 | FunctionNodeHelper::kSyncYielding); |
5123 | if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) { |
5124 | function.set_is_inlinable(!FLAG_causal_async_stacks && |
5125 | !FLAG_lazy_async_stacks); |
5126 | } |
5127 | |
5128 | function.set_end_token_pos(function_node_helper.end_position_); |
5129 | LocalScope* scope = scopes()->function_scopes[i].scope; |
5130 | const ContextScope& context_scope = ContextScope::Handle( |
5131 | Z, scope->PreserveOuterScope(flow_graph_builder_->context_depth_)); |
5132 | function.set_context_scope(context_scope); |
5133 | function.set_kernel_offset(offset); |
5134 | type_translator_.SetupFunctionParameters(Class::Handle(Z), function, |
5135 | false, // is_method |
5136 | true, // is_closure |
5137 | &function_node_helper); |
5138 | // type_translator_.SetupUnboxingInfoMetadata is not called here at the |
5139 | // moment because closures do not have unboxed parameters and return value |
5140 | function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); |
5141 | |
5142 | // Finalize function type. |
5143 | Type& signature_type = Type::Handle(Z, function.SignatureType()); |
5144 | signature_type ^= |
5145 | ClassFinalizer::FinalizeType(*active_class()->klass, signature_type); |
5146 | function.SetSignatureType(signature_type); |
5147 | |
5148 | I->AddClosureFunction(function); |
5149 | break; |
5150 | } |
5151 | } |
5152 | |
5153 | function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd); |
5154 | |
5155 | Fragment instructions = |
5156 | flow_graph_builder_->AllocateClosure(TokenPosition::kNoSource, function); |
5157 | LocalVariable* closure = MakeTemporary(); |
5158 | |
5159 | // The function signature can have uninstantiated class type parameters. |
5160 | if (!function.HasInstantiatedSignature(kCurrentClass)) { |
5161 | instructions += LoadLocal(closure); |
5162 | instructions += LoadInstantiatorTypeArguments(); |
5163 | instructions += flow_graph_builder_->StoreInstanceField( |
5164 | TokenPosition::kNoSource, Slot::Closure_instantiator_type_arguments(), |
5165 | StoreInstanceFieldInstr::Kind::kInitializing); |
5166 | } |
5167 | |
5168 | // TODO(30455): We only need to save these if the closure uses any captured |
5169 | // type parameters. |
5170 | instructions += LoadLocal(closure); |
5171 | instructions += LoadFunctionTypeArguments(); |
5172 | instructions += flow_graph_builder_->StoreInstanceField( |
5173 | TokenPosition::kNoSource, Slot::Closure_function_type_arguments(), |
5174 | StoreInstanceFieldInstr::Kind::kInitializing); |
5175 | |
5176 | if (function.IsGeneric()) { |
5177 | // Only generic functions need to have properly initialized |
5178 | // delayed_type_arguments. |
5179 | instructions += LoadLocal(closure); |
5180 | instructions += Constant(Object::empty_type_arguments()); |
5181 | instructions += flow_graph_builder_->StoreInstanceField( |
5182 | TokenPosition::kNoSource, Slot::Closure_delayed_type_arguments(), |
5183 | StoreInstanceFieldInstr::Kind::kInitializing); |
5184 | } |
5185 | |
5186 | // Store the function and the context in the closure. |
5187 | instructions += LoadLocal(closure); |
5188 | instructions += Constant(function); |
5189 | instructions += flow_graph_builder_->StoreInstanceField( |
5190 | TokenPosition::kNoSource, Slot::Closure_function(), |
5191 | StoreInstanceFieldInstr::Kind::kInitializing); |
5192 | |
5193 | instructions += LoadLocal(closure); |
5194 | instructions += LoadLocal(parsed_function()->current_context_var()); |
5195 | instructions += flow_graph_builder_->StoreInstanceField( |
5196 | TokenPosition::kNoSource, Slot::Closure_context(), |
5197 | StoreInstanceFieldInstr::Kind::kInitializing); |
5198 | |
5199 | return instructions; |
5200 | } |
5201 | |
5202 | Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() { |
5203 | const intptr_t argc = ReadUInt(); // read argument count. |
5204 | ASSERT(argc == 1); // pointer |
5205 | const intptr_t list_length = ReadListLength(); // read types list length. |
5206 | ASSERT(list_length == 2); // dart signature, then native signature |
5207 | const TypeArguments& type_arguments = |
5208 | T.BuildTypeArguments(list_length); // read types. |
5209 | Fragment code; |
5210 | const intptr_t positional_count = |
5211 | ReadListLength(); // read positional argument count |
5212 | ASSERT(positional_count == 1); |
5213 | code += BuildExpression(); // build first positional argument (pointer) |
5214 | const intptr_t named_args_len = |
5215 | ReadListLength(); // skip (empty) named arguments list |
5216 | ASSERT(named_args_len == 0); |
5217 | code += B->BuildFfiAsFunctionInternalCall(type_arguments); |
5218 | return code; |
5219 | } |
5220 | |
5221 | Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction() { |
5222 | // The call-site must look like this (guaranteed by the FE which inserts it): |
5223 | // |
5224 | // _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn) |
5225 | // |
5226 | // The FE also guarantees that all three arguments are constants. |
5227 | |
5228 | const intptr_t argc = ReadUInt(); // read argument count |
5229 | ASSERT(argc == 2); // target, exceptionalReturn |
5230 | |
5231 | const intptr_t list_length = ReadListLength(); // read types list length |
5232 | ASSERT(list_length == 1); // native signature |
5233 | const TypeArguments& type_arguments = |
5234 | T.BuildTypeArguments(list_length); // read types. |
5235 | ASSERT(type_arguments.Length() == 1 && type_arguments.IsInstantiated()); |
5236 | const Function& native_sig = Function::Handle( |
5237 | Z, Type::CheckedHandle(Z, type_arguments.TypeAt(0)).signature()); |
5238 | |
5239 | Fragment code; |
5240 | const intptr_t positional_count = |
5241 | ReadListLength(); // read positional argument count |
5242 | ASSERT(positional_count == 2); |
5243 | |
5244 | // Read target expression and extract the target function. |
5245 | code += BuildExpression(); // build first positional argument (target) |
5246 | Definition* target_def = B->Peek(); |
5247 | ASSERT(target_def->IsConstant()); |
5248 | const Closure& target_closure = |
5249 | Closure::Cast(target_def->AsConstant()->value()); |
5250 | ASSERT(!target_closure.IsNull()); |
5251 | Function& target = Function::Handle(Z, target_closure.function()); |
5252 | ASSERT(!target.IsNull() && target.IsImplicitClosureFunction()); |
5253 | target = target.parent_function(); |
5254 | code += Drop(); |
5255 | |
5256 | // Build second positional argument (exceptionalReturn). |
5257 | code += BuildExpression(); |
5258 | Definition* exceptional_return_def = B->Peek(); |
5259 | ASSERT(exceptional_return_def->IsConstant()); |
5260 | const Instance& exceptional_return = |
5261 | Instance::Cast(exceptional_return_def->AsConstant()->value()); |
5262 | code += Drop(); |
5263 | |
5264 | const intptr_t named_args_len = |
5265 | ReadListLength(); // skip (empty) named arguments list |
5266 | ASSERT(named_args_len == 0); |
5267 | |
5268 | const Function& result = |
5269 | Function::ZoneHandle(Z, compiler::ffi::NativeCallbackFunction( |
5270 | native_sig, target, exceptional_return)); |
5271 | code += Constant(result); |
5272 | return code; |
5273 | } |
5274 | |
5275 | } // namespace kernel |
5276 | } // namespace dart |
5277 | |