73a059d406
Also add functionality for the tool to do offsets. Finally run in on the pkg/vm_service/test/ folder. Change-Id: Ifa4ca60ed0707e6211baf4b9df140802d5cecf6f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404881 Commit-Queue: Jens Johansen <jensj@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
189 lines
4.7 KiB
Dart
189 lines
4.7 KiB
Dart
// Copyright (c) 2023, 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.
|
|
// VMOptions=--verbose_debug
|
|
|
|
// ignore_for_file: prefer_function_declarations_over_variables
|
|
|
|
import 'dart:developer';
|
|
|
|
import 'package:test/test.dart';
|
|
import 'package:vm_service/vm_service.dart';
|
|
|
|
import 'common/service_test_common.dart';
|
|
import 'common/test_helper.dart';
|
|
|
|
// AUTOGENERATED START
|
|
//
|
|
// Update these constants by running:
|
|
//
|
|
// dart pkg/vm_service/test/update_line_numbers.dart pkg/vm_service/test/local_variable_declaration_test.dart
|
|
//
|
|
const LINE_A = 28;
|
|
const LINE_B = 37;
|
|
const LINE_C = 42;
|
|
// AUTOGENERATED END
|
|
|
|
void testParameters(int jjjj, int oooo, [int? hhhh, int? nnnn]) {
|
|
debugger(); // LINE_A
|
|
}
|
|
|
|
void testMain() {
|
|
// ignore: unused_local_variable
|
|
int? xxx, yyyy, zzzzz;
|
|
for (int i = 0; i < 1; i++) {
|
|
// ignore: unused_local_variable
|
|
final foo = () {};
|
|
debugger(); // LINE_B
|
|
}
|
|
final bar = () {
|
|
print(xxx);
|
|
print(yyyy);
|
|
debugger(); // LINE_C
|
|
};
|
|
|
|
bar();
|
|
testParameters(0, 0);
|
|
}
|
|
|
|
String? getLine(Script script, int line) {
|
|
final index = line - script.lineOffset! - 1;
|
|
final lines = script.source!.split('\n');
|
|
if (lines.length < index) {
|
|
return null;
|
|
}
|
|
return lines[index];
|
|
}
|
|
|
|
String? getToken(Script script, int tokenPos) {
|
|
final line = script.getLineNumberFromTokenPos(tokenPos);
|
|
int? col = script.getColumnNumberFromTokenPos(tokenPos);
|
|
if ((line == null) || (col == null)) {
|
|
return null;
|
|
}
|
|
// Line and column numbers start at 1 in the VM.
|
|
--col;
|
|
final sourceLine = getLine(script, line);
|
|
if (sourceLine == null) {
|
|
return null;
|
|
}
|
|
final length = guessTokenLength(script, line, col);
|
|
if (length == null) {
|
|
return sourceLine.substring(col);
|
|
}
|
|
return sourceLine.substring(col, col + length);
|
|
}
|
|
|
|
bool _isOperatorChar(int c) {
|
|
switch (c) {
|
|
case 25: // %
|
|
case 26: // &
|
|
case 42: // *
|
|
case 43: // +
|
|
case 45: // -:
|
|
case 47: // /
|
|
case 60: // <
|
|
case 61: // =
|
|
case 62: // >
|
|
case 94: // ^
|
|
case 124: // |
|
|
case 126: // ~
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool _isInitialIdentifierChar(int c) {
|
|
if (c >= 65 && c <= 90) return true; // Upper
|
|
if (c >= 97 && c <= 122) return true; // Lower
|
|
if (c == 95) return true; // Underscore
|
|
if (c == 36) return true; // Dollar
|
|
return false;
|
|
}
|
|
|
|
bool _isIdentifierChar(int c) {
|
|
if (_isInitialIdentifierChar(c)) return true;
|
|
return c >= 48 && c <= 57; // Digit
|
|
}
|
|
|
|
int? guessTokenLength(Script script, int line, int column) {
|
|
final String source = getLine(script, line)!;
|
|
|
|
int pos = column;
|
|
if (pos >= source.length) {
|
|
return null;
|
|
}
|
|
|
|
final c = source.codeUnitAt(pos);
|
|
if (c == 123) return 1; // { - Map literal
|
|
|
|
if (c == 91) return 1; // [ - List literal, index, index assignment
|
|
|
|
if (c == 40) return 1; // ( - Closure call
|
|
|
|
if (_isOperatorChar(c)) {
|
|
while (++pos < source.length && _isOperatorChar(source.codeUnitAt(pos))) {}
|
|
return pos - column;
|
|
}
|
|
|
|
if (_isInitialIdentifierChar(c)) {
|
|
while (
|
|
++pos < source.length && _isIdentifierChar(source.codeUnitAt(pos))) {}
|
|
return pos - column;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
Future<void> verifyVariables(VmService service, IsolateRef isolateRef) async {
|
|
final isolateId = isolateRef.id!;
|
|
final stack = await service.getStack(isolateId);
|
|
final frames = stack.frames!;
|
|
expect(frames.length, greaterThanOrEqualTo(1));
|
|
// Grab the top frame.
|
|
final frame = frames.first;
|
|
// Grab the script.
|
|
final script = await service.getObject(
|
|
isolateId,
|
|
frame.location!.script!.id!,
|
|
) as Script;
|
|
|
|
// Ensure that the token at each declaration position is the name of the
|
|
// variable.
|
|
final variables = frame.vars!;
|
|
for (final variable in variables) {
|
|
final declarationTokenPos = variable.declarationTokenPos!;
|
|
final name = variable.name!;
|
|
final token = getToken(script, declarationTokenPos);
|
|
// When running from an appjit snapshot, sources aren't available so the returned token will
|
|
// be null.
|
|
if (token != null) {
|
|
expect(name, token);
|
|
}
|
|
}
|
|
}
|
|
|
|
final tests = <IsolateTest>[
|
|
hasStoppedAtBreakpoint,
|
|
stoppedAtLine(LINE_B),
|
|
verifyVariables,
|
|
resumeIsolate,
|
|
hasStoppedAtBreakpoint,
|
|
stoppedAtLine(LINE_C),
|
|
// We have stopped in the anonymous closure assigned to bar. Verify that
|
|
// variables captured in the context have valid declaration positions.
|
|
verifyVariables,
|
|
resumeIsolate,
|
|
hasStoppedAtBreakpoint,
|
|
stoppedAtLine(LINE_A),
|
|
verifyVariables,
|
|
];
|
|
|
|
void main([args = const <String>[]]) => runIsolateTests(
|
|
args,
|
|
tests,
|
|
'local_variable_declaration_test.dart',
|
|
testeeConcurrent: testMain,
|
|
);
|