// 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 #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 Pair = std::pair; using YieldPoints = ZoneGrowableArray; int LowestFirst(const Pair* a, const Pair* b) { return a->first - b->first; } 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 return_instr = it.Current()->AsReturn()) { if (return_instr->yield_index() != UntaggedPcDescriptors::kInvalidYieldIndex) { ASSERT(return_instr->yield_index() > 0); array->Add( Pair(return_instr->yield_index(), return_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(Pair(it.YieldIndex(), it.TokenPos())); } } array->Sort(LowestFirst); return array; } void RunTestInMode(CompilerPass::PipelineMode mode) { const char* kScript = R"( import 'dart:async'; 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& outer_function = Function::Handle(GetFunction(root_library, "foo")); // Grab the inner, lazily created, closure from the object store. const auto& function = Function::Handle( ClosureFunctionsCache::GetUniqueInnerClosure(outer_function)); RELEASE_ASSERT(function.IsFunction()); // 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(1, yield_points[0].first); EXPECT_EQ(88, yield_points[0].second.Pos()); EXPECT_EQ(2, yield_points[1].first); EXPECT_EQ(129, yield_points[1].second.Pos()); EXPECT_EQ(3, yield_points[2].first); EXPECT_EQ(170, yield_points[2].second.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