From 59bcc4ea1e72afe7b9c92b3414ff8af4ad7cdf8a Mon Sep 17 00:00:00 2001 From: Zichang Guo Date: Thu, 12 Sep 2019 16:37:16 +0000 Subject: [PATCH] [vm/kernel] record yield positions at streaming flow graph builder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collect yield positions inside StreamFlowGraphBuilder. BuildGraph() when yield_positions() are required. All yield positions will be stored into script as a hashmap. Key of hashmap is the starting token position of function, value is an array containing all yield positions. Bug: https://github.com/dart-lang/sdk/issues/37635 Change-Id: I6d301d1cb0f74c432f855c9dbc20ac2c13acb07f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116743 Reviewed-by: Alexander Markov Reviewed-by: Régis Crelier Commit-Queue: Zichang Guo --- .../yield_positions_with_finally_test.dart | 212 ++++++++++++++++++ .../frontend/kernel_binary_flowgraph.cc | 4 + .../frontend/kernel_binary_flowgraph.h | 12 +- runtime/vm/compiler/frontend/kernel_to_il.cc | 6 +- runtime/vm/compiler/frontend/kernel_to_il.h | 5 +- .../frontend/kernel_translation_helper.cc | 3 +- .../frontend/kernel_translation_helper.h | 6 - runtime/vm/compiler/jit/compiler.cc | 39 ++++ runtime/vm/compiler/jit/compiler.h | 2 + runtime/vm/debugger.cc | 47 ++-- runtime/vm/kernel.cc | 42 +--- runtime/vm/object.cc | 26 ++- runtime/vm/object.h | 2 + 13 files changed, 321 insertions(+), 85 deletions(-) create mode 100644 runtime/observatory/tests/service/yield_positions_with_finally_test.dart diff --git a/runtime/observatory/tests/service/yield_positions_with_finally_test.dart b/runtime/observatory/tests/service/yield_positions_with_finally_test.dart new file mode 100644 index 00000000000..af9643bb185 --- /dev/null +++ b/runtime/observatory/tests/service/yield_positions_with_finally_test.dart @@ -0,0 +1,212 @@ +// 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. +import 'dart:developer'; +import 'package:observatory/service_io.dart'; +import 'package:unittest/unittest.dart'; +import 'service_test_common.dart'; +import 'test_helper.dart'; + +const int LINE = 106; +const int LINE_A = 23; +const int LINE_B = 36; +const int LINE_C = 47; +const int LINE_D = 61; +const int LINE_E = 78; +const int LINE_F = 93; + +// break statement +Stream testBreak() async* { + for (int t = 0; t < 10; t++) { + try { + if (t == 1) break; + await throwException(); // LINE_A + } catch (e) {} finally { + yield t; + } + } +} + +// return statement +Stream testReturn() async* { + for (int t = 0; t < 10; t++) { + try { + yield t; + if (t == 1) return; + await throwException(); // LINE_B + } catch (e) {} finally { + yield t; + } + } +} + +// Multiple functions +Stream testMultipleFunctions() async* { + try { + yield 0; + await throwException(); // LINE_C + } catch (e) {} finally { + yield 1; + } +} + +// continue statement +Stream testContinueSwitch() async* { + int currentState = 0; + switch (currentState) { + case 0: + { + try { + if (currentState == 1) continue label; + await throwException(); // LINE_D + } catch (e) {} finally { + yield 0; + } + yield 1; + break; + } + label: + case 1: + break; + } +} + +Stream testNestFinally() async* { + int i = 0; + try { + if (i == 1) return; + await throwException(); //LINE_E + } catch (e) {} finally { + try { + yield i; + } finally { + yield 1; + } + yield 1; + } +} + +Stream testAsyncClosureInFinally() async* { + int i = 0; + try { + if (i == 1) return; + await throwException(); //LINE_F + } catch (e) {} finally { + inner() async { + await Future.delayed(Duration(milliseconds: 10)); + } + + await inner; + yield 1; + } +} + +Future throwException() async { + await Future.delayed(Duration(milliseconds: 10)); + throw new Exception(""); // LINE +} + +code() async { + await for (var x in testBreak()) {} + await for (var x in testReturn()) {} + await for (var x in testMultipleFunctions()) {} + await for (var x in testContinueSwitch()) {} + await for (var x in testNestFinally()) {} + await for (var x in testAsyncClosureInFinally()) {} +} + +var tests = [ + hasPausedAtStart, + setBreakpointAtLine(LINE), + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE), + (Isolate isolate) async { + // test break statement + ServiceMap stack = await isolate.getStack(); + expect(stack['awaiterFrames'], isNotNull); + expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2)); + + // Check second top frame contains correct line number + Script script = stack['awaiterFrames'][1].location.script; + expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos), + equals(LINE_A)); + }, + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE), + (Isolate isolate) async { + // test return statement + ServiceMap stack = await isolate.getStack(); + expect(stack['awaiterFrames'], isNotNull); + expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2)); + + // Check second top frame contains correct line number + Script script = stack['awaiterFrames'][1].location.script; + expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos), + equals(LINE_B)); + }, + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE), + (Isolate isolate) async { + // test break statement + ServiceMap stack = await isolate.getStack(); + expect(stack['awaiterFrames'], isNotNull); + expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2)); + + // Check second top frame contains correct line number + Script script = stack['awaiterFrames'][1].location.script; + expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos), + equals(LINE_C)); + }, + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE), + (Isolate isolate) async { + // test break statement + ServiceMap stack = await isolate.getStack(); + expect(stack['awaiterFrames'], isNotNull); + expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2)); + + // Check second top frame contains correct line number + Script script = stack['awaiterFrames'][1].location.script; + expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos), + equals(LINE_D)); + }, + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE), + (Isolate isolate) async { + // test nested finally statement + ServiceMap stack = await isolate.getStack(); + expect(stack['awaiterFrames'], isNotNull); + expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2)); + + // Check second top frame contains correct line number + Script script = stack['awaiterFrames'][1].location.script; + expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos), + equals(LINE_E)); + }, + resumeIsolate, + hasStoppedAtBreakpoint, + stoppedAtLine(LINE), + (Isolate isolate) async { + // test async closure within finally block + ServiceMap stack = await isolate.getStack(); + expect(stack['awaiterFrames'], isNotNull); + expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2)); + + // Check second top frame contains correct line number + Script script = stack['awaiterFrames'][1].location.script; + expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos), + equals(LINE_F)); + }, + resumeIsolate, + hasStoppedAtExit +]; + +main(args) { + runIsolateTestsSynchronous(args, tests, + testeeConcurrent: code, pause_on_start: true, pause_on_exit: true); +} diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc index 3b1f642e9a2..f2297d500d3 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -4764,6 +4764,10 @@ Fragment StreamingFlowGraphBuilder::BuildYieldStatement() { ASSERT(flags == kNativeYieldFlags); // Must have been desugared. + // Collect yield position + if (record_yield_positions_ != nullptr) { + record_yield_positions_->Add(Smi::Handle(Z, Smi::New(position.value()))); + } // Setup yield/continue point: // // ... diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h index e8b36bd634d..0b4f7d5505d 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h @@ -21,9 +21,11 @@ namespace kernel { class StreamingFlowGraphBuilder : public KernelReaderHelper { public: - StreamingFlowGraphBuilder(FlowGraphBuilder* flow_graph_builder, - const ExternalTypedData& data, - intptr_t data_program_offset) + StreamingFlowGraphBuilder( + FlowGraphBuilder* flow_graph_builder, + const ExternalTypedData& data, + intptr_t data_program_offset, + GrowableObjectArray* record_yield_positions = nullptr) : KernelReaderHelper( flow_graph_builder->zone_, &flow_graph_builder->translation_helper_, @@ -44,7 +46,8 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper { inferred_type_metadata_helper_(this), procedure_attributes_metadata_helper_(this), call_site_attributes_metadata_helper_(this, &type_translator_), - closure_owner_(Object::Handle(flow_graph_builder->zone_)) {} + closure_owner_(Object::Handle(flow_graph_builder->zone_)), + record_yield_positions_(record_yield_positions) {} virtual ~StreamingFlowGraphBuilder() {} @@ -363,6 +366,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper { ProcedureAttributesMetadataHelper procedure_attributes_metadata_helper_; CallSiteAttributesMetadataHelper call_site_attributes_metadata_helper_; Object& closure_owner_; + GrowableObjectArray* record_yield_positions_; friend class KernelLoader; diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc index 2169ebdab10..939830e8420 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.cc +++ b/runtime/vm/compiler/frontend/kernel_to_il.cc @@ -40,7 +40,8 @@ FlowGraphBuilder::FlowGraphBuilder( bool optimizing, intptr_t osr_id, intptr_t first_block_id, - bool inlining_unchecked_entry) + bool inlining_unchecked_entry, + GrowableObjectArray* record_yield_positions) : BaseFlowGraphBuilder(parsed_function, first_block_id - 1, osr_id, @@ -68,6 +69,7 @@ FlowGraphBuilder::FlowGraphBuilder( catch_block_(NULL) { const Script& script = Script::Handle(Z, parsed_function->function().script()); + record_yield_positions_ = record_yield_positions; H.InitFromScript(script); } @@ -643,7 +645,7 @@ FlowGraph* FlowGraphBuilder::BuildGraph() { // TODO(alexmarkov): refactor this - StreamingFlowGraphBuilder should not be // used for bytecode functions. StreamingFlowGraphBuilder streaming_flow_graph_builder( - this, kernel_data, kernel_data_program_offset); + this, kernel_data, kernel_data_program_offset, record_yield_positions_); return streaming_flow_graph_builder.BuildGraph(); } diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h index 0b9d2fe14ed..37750007547 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.h +++ b/runtime/vm/compiler/frontend/kernel_to_il.h @@ -56,7 +56,8 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder { bool optimizing, intptr_t osr_id, intptr_t first_block_id = 1, - bool inlining_unchecked_entry = false); + bool inlining_unchecked_entry = false, + GrowableObjectArray* record_yield_position = nullptr); virtual ~FlowGraphBuilder(); FlowGraph* BuildGraph(); @@ -377,6 +378,8 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder { ActiveClass active_class_; + GrowableObjectArray* record_yield_positions_; + friend class BreakableBlock; friend class CatchBlock; friend class ConstantEvaluator; diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc index 4469fce475c..7dac928825d 100644 --- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc +++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc @@ -2475,8 +2475,7 @@ void KernelReaderHelper::SkipStatement() { SkipStatement(); // read finalizer. return; case kYieldStatement: { - TokenPosition position = ReadPosition(); // read position. - RecordYieldPosition(position); + ReadPosition(); // read position. ReadByte(); // read flags. SkipExpression(); // read expression. return; diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h index 8f9371b114c..6187caaf407 100644 --- a/runtime/vm/compiler/frontend/kernel_translation_helper.h +++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h @@ -1036,12 +1036,6 @@ class KernelReaderHelper { USE(id); } - virtual void RecordYieldPosition(TokenPosition position) { - // Do nothing by default. - // This is overridden in KernelTokenPositionCollector. - USE(position); - } - virtual void RecordTokenPosition(TokenPosition position) { // Do nothing by default. // This is overridden in KernelTokenPositionCollector. diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc index 5bf7d0a6750..171f8cff945 100644 --- a/runtime/vm/compiler/jit/compiler.cc +++ b/runtime/vm/compiler/jit/compiler.cc @@ -1012,6 +1012,41 @@ void Compiler::ComputeLocalVarDescriptors(const Code& code) { } } +void Compiler::ComputeYieldPositions(const Function& function) { + Thread* thread = Thread::Current(); + Zone* zone = thread->zone(); + const Script& script = Script::Handle(zone, function.script()); + Array& yield_position_map = Array::Handle(zone, script.yield_positions()); + if (yield_position_map.IsNull()) { + yield_position_map = HashTables::New>(4); + } + UnorderedHashMap function_map(yield_position_map.raw()); + Smi& key = Smi::Handle(zone, Smi::New(function.token_pos().value())); + + if (function_map.ContainsKey(key)) { + ASSERT(function_map.Release().raw() == yield_position_map.raw()); + return; + } + + CompilerState state(thread); + LongJumpScope jump; + auto& array = GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + if (setjmp(*jump.Set()) == 0) { + ParsedFunction* parsed_function = + new ParsedFunction(thread, Function::ZoneHandle(zone, function.raw())); + ZoneGrowableArray* ic_data_array = + new ZoneGrowableArray(); + kernel::FlowGraphBuilder builder(parsed_function, ic_data_array, nullptr, + /* not inlining */ nullptr, false, + Compiler::kNoOSRDeoptId, 1, false, &array); + builder.BuildGraph(); + } + function_map.UpdateOrInsert(key, array); + // Release and store back to script + yield_position_map = function_map.Release().raw(); + script.set_yield_positions(yield_position_map); +} + RawError* Compiler::CompileAllFunctions(const Class& cls) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -1412,6 +1447,10 @@ void Compiler::ComputeLocalVarDescriptors(const Code& code) { UNREACHABLE(); } +void Compiler::ComputeYieldPositions(const Function& function) { + UNREACHABLE(); +} + RawError* Compiler::CompileAllFunctions(const Class& cls) { FATAL1("Attempt to compile class %s", cls.ToCString()); return Error::null(); diff --git a/runtime/vm/compiler/jit/compiler.h b/runtime/vm/compiler/jit/compiler.h index 123e16acff8..94e22b7969a 100644 --- a/runtime/vm/compiler/jit/compiler.h +++ b/runtime/vm/compiler/jit/compiler.h @@ -108,6 +108,8 @@ class Compiler : public AllStatic { // Generates local var descriptors and sets it in 'code'. Do not call if the // local var descriptor already exists. static void ComputeLocalVarDescriptors(const Code& code); + // Collect yield positions for function and store into its script object + static void ComputeYieldPositions(const Function& function); // Eagerly compiles all functions in a class. // diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index 38650fdf6f4..4628b84755e 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -1060,41 +1060,18 @@ void ActivationFrame::ExtractTokenPositionFromAsyncClosure() { ASSERT(!IsInterpreted()); ASSERT(script.kind() == RawScript::kKernelTag); - const Array& await_to_token_map = - Array::Handle(zone, script.yield_positions()); - if (await_to_token_map.IsNull()) { - // No mapping. - return; - } const intptr_t await_jump_var = GetAwaitJumpVariable(); if (await_jump_var < 0) { return; } intptr_t await_to_token_map_index = await_jump_var - 1; - // yield_positions returns all yield positions for the script (in sorted - // order). - // We thus need to offset the function start to get the actual index. - if (!function_.token_pos().IsReal()) { - return; - } - const intptr_t function_start = function_.token_pos().value(); - for (intptr_t i = 0; - i < await_to_token_map.Length() && - Smi::Value(reinterpret_cast(await_to_token_map.At(i))) < - function_start; - i++) { - await_to_token_map_index++; - } - - if (await_to_token_map_index >= await_to_token_map.Length()) { - return; - } - + const auto& array = + GrowableObjectArray::Handle(zone, script.GetYieldPositions(function_)); + // await_jump_var is non zero means that array should not be empty + // index also fall into the correct range + ASSERT(array.Length() > 0 && await_to_token_map_index < array.Length()); const Object& token_pos = - Object::Handle(await_to_token_map.At(await_to_token_map_index)); - if (token_pos.IsNull()) { - return; - } + Object::Handle(zone, array.At(await_to_token_map_index)); ASSERT(token_pos.IsSmi()); token_pos_ = TokenPosition(Smi::Cast(token_pos).Value()); token_pos_initialized_ = true; @@ -4406,12 +4383,16 @@ bool Debugger::IsAtAsyncJump(ActivationFrame* top_frame) { ASSERT(!top_frame->IsInterpreted()); const Script& script = Script::Handle(zone, top_frame->SourceScript()); ASSERT(script.kind() == RawScript::kKernelTag); - // Are we at a yield point (previous await)? - const Array& yields = Array::Handle(script.yield_positions()); + const auto& yield_positions = GrowableObjectArray::Handle( + zone, script.GetYieldPositions(top_frame->function())); + // No yield statements + if (yield_positions.IsNull() || (yield_positions.Length() == 0)) { + return false; + } intptr_t looking_for = top_frame->TokenPos().value(); Smi& value = Smi::Handle(zone); - for (int i = 0; i < yields.Length(); i++) { - value ^= yields.At(i); + for (int i = 0; i < yield_positions.Length(); i++) { + value ^= yield_positions.At(i); if (value.Value() == looking_for) { return true; } diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc index 9e6af122d6c..0760b1f182e 100644 --- a/runtime/vm/kernel.cc +++ b/runtime/vm/kernel.cc @@ -8,6 +8,7 @@ #include "vm/compiler/frontend/bytecode_reader.h" #include "vm/compiler/frontend/constant_evaluator.h" #include "vm/compiler/frontend/kernel_translation_helper.h" +#include "vm/compiler/jit/compiler.h" #include "vm/longjump.h" #include "vm/object_store.h" #include "vm/parser.h" // For Parser::kParameter* constants. @@ -134,8 +135,7 @@ class KernelTokenPositionCollector : public KernelReaderHelper { intptr_t data_program_offset, intptr_t initial_script_index, intptr_t record_for_script_id, - GrowableArray* record_token_positions_into, - GrowableArray* record_yield_positions_into) + GrowableArray* record_token_positions_into) : KernelReaderHelper(zone, translation_helper, script, @@ -143,13 +143,11 @@ class KernelTokenPositionCollector : public KernelReaderHelper { data_program_offset), current_script_id_(initial_script_index), record_for_script_id_(record_for_script_id), - record_token_positions_into_(record_token_positions_into), - record_yield_positions_into_(record_yield_positions_into) {} + record_token_positions_into_(record_token_positions_into) {} void CollectTokenPositions(intptr_t kernel_offset); void RecordTokenPosition(TokenPosition position) override; - void RecordYieldPosition(TokenPosition position) override; void set_current_script_id(intptr_t id) override { current_script_id_ = id; } @@ -157,7 +155,6 @@ class KernelTokenPositionCollector : public KernelReaderHelper { intptr_t current_script_id_; intptr_t record_for_script_id_; GrowableArray* record_token_positions_into_; - GrowableArray* record_yield_positions_into_; DISALLOW_COPY_AND_ASSIGN(KernelTokenPositionCollector); }; @@ -195,13 +192,6 @@ void KernelTokenPositionCollector::RecordTokenPosition(TokenPosition position) { } } -void KernelTokenPositionCollector::RecordYieldPosition(TokenPosition position) { - if (record_for_script_id_ == current_script_id_ && - record_yield_positions_into_ != NULL && position.IsReal()) { - record_yield_positions_into_->Add(position.value()); - } -} - static int LowestFirst(const intptr_t* a, const intptr_t* b) { return *a - *b; } @@ -246,8 +236,7 @@ static void CollectKernelDataTokenPositions( intptr_t data_kernel_offset, Zone* zone, TranslationHelper* helper, - GrowableArray* token_positions, - GrowableArray* yield_positions) { + GrowableArray* token_positions) { if (kernel_data.IsNull()) { return; } @@ -255,7 +244,7 @@ static void CollectKernelDataTokenPositions( KernelTokenPositionCollector token_position_collector( zone, helper, script, kernel_data, data_kernel_offset, entry_script.kernel_script_index(), script.kernel_script_index(), - token_positions, yield_positions); + token_positions); token_position_collector.CollectTokenPositions(kernel_offset); } @@ -333,7 +322,6 @@ void CollectTokenPositionsFor(const Script& interesting_script) { helper.InitFromScript(interesting_script); GrowableArray token_positions(10); - GrowableArray yield_positions(1); Isolate* isolate = thread->isolate(); const GrowableObjectArray& libs = @@ -396,7 +384,7 @@ void CollectTokenPositionsFor(const Script& interesting_script) { data, interesting_script, entry_script, temp_field.kernel_offset(), temp_field.KernelDataProgramOffset(), zone, &helper, - &token_positions, &yield_positions); + &token_positions); } } temp_array = klass.functions(); @@ -415,7 +403,7 @@ void CollectTokenPositionsFor(const Script& interesting_script) { data, interesting_script, entry_script, temp_function.kernel_offset(), temp_function.KernelDataProgramOffset(), zone, &helper, - &token_positions, &yield_positions); + &token_positions); } } } else { @@ -432,10 +420,9 @@ void CollectTokenPositionsFor(const Script& interesting_script) { if (entry_script.raw() != interesting_script.raw()) { continue; } - CollectKernelDataTokenPositions(data, interesting_script, - entry_script, class_offset, - library_kernel_offset, zone, &helper, - &token_positions, &yield_positions); + CollectKernelDataTokenPositions( + data, interesting_script, entry_script, class_offset, + library_kernel_offset, zone, &helper, &token_positions); } } else if (entry.IsFunction()) { temp_function ^= entry.raw(); @@ -452,7 +439,7 @@ void CollectTokenPositionsFor(const Script& interesting_script) { data, interesting_script, entry_script, temp_function.kernel_offset(), temp_function.KernelDataProgramOffset(), zone, &helper, - &token_positions, &yield_positions); + &token_positions); } } else if (entry.IsField()) { const Field& field = Field::Cast(entry); @@ -476,8 +463,7 @@ void CollectTokenPositionsFor(const Script& interesting_script) { data = field.KernelData(); CollectKernelDataTokenPositions( data, interesting_script, entry_script, field.kernel_offset(), - field.KernelDataProgramOffset(), zone, &helper, &token_positions, - &yield_positions); + field.KernelDataProgramOffset(), zone, &helper, &token_positions); } } } @@ -487,10 +473,6 @@ void CollectTokenPositionsFor(const Script& interesting_script) { Array& array_object = Array::Handle(zone); array_object = AsSortedDuplicateFreeArray(&token_positions); script.set_debug_positions(array_object); - array_object = AsSortedDuplicateFreeArray(&yield_positions); - // Note that yield positions in members declared in bytecode are not collected - // here, but on demand in the debugger. - script.set_yield_positions(array_object); } class MetadataEvaluator : public KernelReaderHelper { diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index a9055e91a3e..c1c4a8fbdf3 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -9503,16 +9503,28 @@ void Script::set_yield_positions(const Array& value) const { } RawArray* Script::yield_positions() const { -#if !defined(DART_PRECOMPILED_RUNTIME) - Array& yields = Array::Handle(raw_ptr()->yield_positions_); - if (yields.IsNull() && kind() == RawScript::kKernelTag) { - // This is created lazily. Now we need it. - kernel::CollectTokenPositionsFor(*this); - } -#endif // !defined(DART_PRECOMPILED_RUNTIME) return raw_ptr()->yield_positions_; } +RawGrowableObjectArray* Script::GetYieldPositions( + const Function& function) const { + if (!function.IsAsyncClosure() && !function.IsAsyncGenClosure()) + return GrowableObjectArray::null(); + ASSERT(!function.is_declared_in_bytecode()); + Compiler::ComputeYieldPositions(function); + UnorderedHashMap function_map(raw_ptr()->yield_positions_); + const auto& key = Smi::Handle(Smi::New(function.token_pos().value())); + intptr_t entry = function_map.FindKey(key); + GrowableObjectArray& array = GrowableObjectArray::Handle(); + if (entry < 0) { + array ^= GrowableObjectArray::null(); + } else { + array ^= function_map.GetPayload(entry, 0); + } + function_map.Release(); + return array.raw(); +} + RawTypedData* Script::line_starts() const { return raw_ptr()->line_starts_; } diff --git a/runtime/vm/object.h b/runtime/vm/object.h index bd2b8105154..ec1ba409356 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -3879,6 +3879,8 @@ class Script : public Object { RawArray* yield_positions() const; + RawGrowableObjectArray* GetYieldPositions(const Function& function) const; + RawLibrary* FindLibrary() const; RawString* GetLine(intptr_t line_number, Heap::Space space = Heap::kNew) const;