| // 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=--resolve-dwarf-paths --save-debugging-info=$TEST_COMPILATION_DIR/debug.so |
| |
| import "dart:async"; |
| import "dart:io"; |
| |
| import 'package:expect/expect.dart'; |
| import 'package:native_stack_traces/native_stack_traces.dart'; |
| import 'package:path/path.dart' as path; |
| |
| final dwarfPath = |
| path.join(Platform.environment['TEST_COMPILATION_DIR']!, 'debug.so'); |
| final usesObfuscation = |
| const String.fromEnvironment("test_runner.configuration") |
| .contains('obfuscate'); |
| final usesDwarf = |
| const String.fromEnvironment("test_runner.configuration").contains('dwarf'); |
| |
| Future<void> main(List<String> args) async { |
| if (Platform.isAndroid) return; |
| |
| final List<String> stack = await run(6); |
| final o = usesObfuscation ? '!' : ''; |
| |
| compareFrames([ |
| '${o}bottom', |
| 'KeepClass.keepMethod', |
| '${o}NormalClass.normalMethod', |
| 'keepStatic', |
| '${o}normalStatic', |
| '${o}run', |
| ], stack); |
| } |
| |
| void compareFrames(List<String> patterns, List<String> stack) { |
| if (patterns.length != stack.length) { |
| throw 'Expected ${patterns.length} frames'; |
| } |
| print('Comparing this pattern: \n ${patterns.join('\n ')}'); |
| print('Against these frames: \n ${stack.join('\n ')}'); |
| for (int i = 0; i < patterns.length; ++i) { |
| final pattern = patterns[i]; |
| final frame = stack[i]; |
| if (pattern.startsWith('!')) { |
| Expect.notEquals(pattern.substring(1), frame); |
| } else { |
| Expect.equals(pattern, frame); |
| } |
| } |
| print(''); |
| } |
| |
| @pragma('vm:never-inline') |
| void bottom() { |
| throw 'bad'; |
| } |
| |
| @pragma('vm:keep-name') |
| class KeepClass { |
| @pragma('vm:never-inline') |
| @pragma('vm:keep-name') |
| void keepMethod() { |
| bottom(); |
| } |
| } |
| |
| class NormalClass { |
| @pragma('vm:never-inline') |
| void normalMethod() { |
| keep.keepMethod(); |
| } |
| } |
| |
| final normal = NormalClass(); |
| final keep = KeepClass(); |
| |
| @pragma('vm:never-inline') |
| @pragma('vm:keep-name') |
| void keepStatic() { |
| normal.normalMethod(); |
| } |
| |
| @pragma('vm:never-inline') |
| void normalStatic() { |
| keepStatic(); |
| } |
| |
| Future<List<String>> run(int n) async { |
| try { |
| normalStatic(); |
| } catch (e, s) { |
| List<String> lines = s.toString().split('\n'); |
| if (usesDwarf) { |
| final dwarf = Dwarf.fromFile(dwarfPath)!; |
| lines = await Stream<String>.fromIterable(lines) |
| .transform(DwarfStackTraceDecoder(dwarf)) |
| .toList(); |
| } |
| final start = lines.indexWhere((line) => line.startsWith('#0')); |
| lines = lines.skip(start).take(n).toList(); |
| return lines.map((String line) { |
| line = line.substring(line.indexOf(' ')).trim(); |
| line = line.substring(0, line.indexOf(' ')).trim(); |
| return line; |
| }).toList(); |
| } |
| throw 'failed'; |
| } |