blob: 984c0010a38e72523c554b9c640cb18771479f0b [file] [log] [blame]
// 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 <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,
);