[vm/kernel] record yield positions at streaming flow graph builder
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 <alexmarkov@google.com> Reviewed-by: Régis Crelier <regis@google.com> Commit-Queue: Zichang Guo <zichangguo@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
c74e68e501
commit
59bcc4ea1e
@@ -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<int> 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<int> 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<int> testMultipleFunctions() async* {
|
||||
try {
|
||||
yield 0;
|
||||
await throwException(); // LINE_C
|
||||
} catch (e) {} finally {
|
||||
yield 1;
|
||||
}
|
||||
}
|
||||
|
||||
// continue statement
|
||||
Stream<int> 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<int> 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<int> 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<void> 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 = <IsolateTest>[
|
||||
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);
|
||||
}
|
||||
@@ -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:
|
||||
//
|
||||
// ...
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<UnorderedHashMap<SmiTraits>>(4);
|
||||
}
|
||||
UnorderedHashMap<SmiTraits> 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<const ICData*>* ic_data_array =
|
||||
new ZoneGrowableArray<const ICData*>();
|
||||
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();
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
+14
-33
@@ -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<RawSmi*>(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;
|
||||
}
|
||||
|
||||
+12
-30
@@ -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<intptr_t>* record_token_positions_into,
|
||||
GrowableArray<intptr_t>* record_yield_positions_into)
|
||||
GrowableArray<intptr_t>* 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<intptr_t>* record_token_positions_into_;
|
||||
GrowableArray<intptr_t>* 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<intptr_t>* token_positions,
|
||||
GrowableArray<intptr_t>* yield_positions) {
|
||||
GrowableArray<intptr_t>* 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<intptr_t> token_positions(10);
|
||||
GrowableArray<intptr_t> 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 {
|
||||
|
||||
+19
-7
@@ -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<SmiTraits> 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_;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user