| // Copyright (c) 2025, 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:io'; | |
| import 'dart:typed_data'; | |
| import 'package:_fe_analyzer_shared/src/messages/codes.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/experimental_features.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/forwarding_listener.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/listener.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/parser_impl.dart'; | |
| import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart'; | |
| import 'package:_fe_analyzer_shared/src/scanner/scanner.dart'; | |
| void main(List<String> args) { | |
| if (args.length != 1) { | |
| throw "Expected 1 argument: <dart file>."; | |
| } | |
| File f = new File(args.single); | |
| if (!f.existsSync()) { | |
| throw "File $f doesn't exist."; | |
| } | |
| Uint8List contentBytes = f.readAsBytesSync(); | |
| ScannerResult scanResult = scan(contentBytes, includeComments: true); | |
| const int iterations = 1000; | |
| int countTokens = 0; | |
| { | |
| Token t = scanResult.tokens; | |
| while (!t.isEof) { | |
| countTokens++; | |
| t = t.next!; | |
| } | |
| } | |
| // TODO: Should we make 1000 copies of the tokens? | |
| // Or maybe scan 1000 times? | |
| /* | |
| List<Token> tokenStarts = []; | |
| tokenStarts.add(scanResult.tokens); | |
| while (tokenStarts.length < iterations) { | |
| tokenStarts.add( | |
| scan( | |
| contentBytes, | |
| configuration: new ScannerConfiguration(enableTripleShift: true), | |
| includeComments: true, | |
| ).tokens, | |
| ); | |
| } | |
| */ | |
| Listener listener = new NullListener(); | |
| if (f.path == "42") { | |
| // Let's not let it optimize too many things away or do weird optimizations | |
| // that it can't do in practise. | |
| listener = new FooListener(f.uri); | |
| } | |
| int numErrors = 0; | |
| Stopwatch stopwatch = new Stopwatch()..start(); | |
| int lengthProcessed = countTokens; | |
| for (int i = 0; i < iterations; i++) { | |
| Parser parser = new Parser( | |
| listener, | |
| experimentalFeatures: const DefaultExperimentalFeatures(), | |
| ); | |
| Token after = parser.parseUnit(scanResult.tokens); | |
| if (!after.isEof) { | |
| throw "parsed returned before eof?!?"; | |
| } | |
| if (listener is NullListener && listener.hasErrors) { | |
| numErrors++; | |
| } | |
| // Or maybe - maybe as an option - do another scan here? | |
| } | |
| stopwatch.stop(); | |
| print( | |
| "Parsed $lengthProcessed tokens $iterations times " | |
| "in ${stopwatch.elapsed}", | |
| ); | |
| print("Found errors $numErrors times"); | |
| double lengthPerMicrosecond = | |
| (lengthProcessed * iterations) / stopwatch.elapsedMicroseconds; | |
| print("That's $lengthPerMicrosecond tokens per microsecond"); | |
| print(""); | |
| } | |
| class FooListener extends StackListener { | |
| List<Message> problems = []; | |
| @override | |
| final Uri uri; | |
| FooListener(this.uri); | |
| @override | |
| void addProblem( | |
| Message message, | |
| int charOffset, | |
| int length, { | |
| bool wasHandled = false, | |
| List<LocatedMessage>? context, | |
| }) { | |
| if (charOffset == 42) { | |
| throw "$message $charOffset $length $wasHandled $context"; | |
| } | |
| problems.add(message); | |
| } | |
| @override | |
| Never internalProblem(Message message, int charOffset, Uri uri) { | |
| throw UnimplementedError(); | |
| } | |
| @override | |
| bool get isDartLibrary => false; | |
| @override | |
| void handleSendWithoutArguments( | |
| Token beginToken, | |
| Token endToken, | |
| Token nextToken, | |
| ) {} | |
| @override | |
| void handleInvocationWithoutTypeArguments(Token beginToken, Token endToken) {} | |
| } |