|  | // Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "vm/closure_functions_cache.h" | 
|  | #include "vm/compiler/backend/il_test_helper.h" | 
|  | #include "vm/compiler/compiler_pass.h" | 
|  | #include "vm/object.h" | 
|  | #include "vm/unit_test.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | using YieldPoints = ZoneGrowableArray<TokenPosition>; | 
|  |  | 
|  | int LowestFirst(const TokenPosition* a, const TokenPosition* b) { | 
|  | return a->Pos() - b->Pos(); | 
|  | } | 
|  |  | 
|  | static YieldPoints* GetYieldPointsFromGraph(FlowGraph* flow_graph) { | 
|  | auto array = new (flow_graph->zone()) YieldPoints(); | 
|  | const auto& blocks = flow_graph->reverse_postorder(); | 
|  | for (auto block : blocks) { | 
|  | ForwardInstructionIterator it(block); | 
|  | while (!it.Done()) { | 
|  | if (auto suspend_instr = it.Current()->AsSuspend()) { | 
|  | array->Add(suspend_instr->token_pos()); | 
|  | } | 
|  | it.Advance(); | 
|  | } | 
|  | } | 
|  | array->Sort(LowestFirst); | 
|  | return array; | 
|  | } | 
|  |  | 
|  | static YieldPoints* GetYieldPointsFromCode(const Code& code) { | 
|  | auto array = new YieldPoints(); | 
|  | const auto& pc_descriptor = PcDescriptors::Handle(code.pc_descriptors()); | 
|  | PcDescriptors::Iterator it(pc_descriptor, UntaggedPcDescriptors::kOther); | 
|  | while (it.MoveNext()) { | 
|  | if (it.YieldIndex() != UntaggedPcDescriptors::kInvalidYieldIndex) { | 
|  | array->Add(it.TokenPos()); | 
|  | } | 
|  | } | 
|  | array->Sort(LowestFirst); | 
|  | return array; | 
|  | } | 
|  |  | 
|  | void RunTestInMode(CompilerPass::PipelineMode mode) { | 
|  | const char* kScript = | 
|  | R"( | 
|  | import 'dart:async'; | 
|  |  | 
|  | @pragma("vm:entry-point", "call") | 
|  | Future foo() async { | 
|  | print('pos-0'); | 
|  | await 0; | 
|  | print('pos-1'); | 
|  | await 1; | 
|  | print('pos-2'); | 
|  | await 2; | 
|  | } | 
|  | )"; | 
|  |  | 
|  | SetupCoreLibrariesForUnitTest(); | 
|  |  | 
|  | const auto& root_library = Library::Handle(LoadTestScript(kScript)); | 
|  | // Ensure the outer function was compiled once, ensuring we have a closure | 
|  | // function for the inner closure. | 
|  | Invoke(root_library, "foo"); | 
|  |  | 
|  | const auto& function = Function::Handle(GetFunction(root_library, "foo")); | 
|  |  | 
|  | // Ensure we have 3 different return instructions with yield indices attached | 
|  | // to them. | 
|  | TestPipeline pipeline(function, mode); | 
|  | FlowGraph* flow_graph = pipeline.RunPasses({ | 
|  | CompilerPass::kComputeSSA, | 
|  | }); | 
|  |  | 
|  | auto validate_indices = [](const YieldPoints& yield_points) { | 
|  | EXPECT_EQ(3, yield_points.length()); | 
|  |  | 
|  | EXPECT_EQ(128, yield_points[0].Pos()); | 
|  | EXPECT_EQ(169, yield_points[1].Pos()); | 
|  | EXPECT_EQ(210, yield_points[2].Pos()); | 
|  | }; | 
|  |  | 
|  | validate_indices(*GetYieldPointsFromGraph(flow_graph)); | 
|  |  | 
|  | // Ensure we have 3 different yield indices attached to the code via pc | 
|  | // descriptors. | 
|  | const auto& error = Error::Handle( | 
|  | Compiler::EnsureUnoptimizedCode(Thread::Current(), function)); | 
|  | RELEASE_ASSERT(error.IsNull()); | 
|  | const auto& code = Code::Handle(function.CurrentCode()); | 
|  | validate_indices(*GetYieldPointsFromCode(code)); | 
|  | } | 
|  |  | 
|  | ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableJIT) { | 
|  | RunTestInMode(CompilerPass::kJIT); | 
|  | } | 
|  |  | 
|  | ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableAOT) { | 
|  | RunTestInMode(CompilerPass::kAOT); | 
|  | } | 
|  |  | 
|  | }  // namespace dart |