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