blob: e707a31aea6011df75f69d1d3abf6ceee288dae0 [file] [log] [blame]
// Copyright (c) 2018, 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:convert' show jsonDecode;
import 'package:front_end/src/base/processed_options.dart'
show ProcessedOptions;
import 'package:front_end/src/fasta/builder/declaration.dart';
import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
import 'package:front_end/src/fasta/messages.dart'
show LocatedMessage, templateUnspecified;
import 'package:front_end/src/fasta/parser.dart' show Parser;
import 'package:front_end/src/fasta/scanner.dart' show Token;
import 'package:front_end/src/fasta/severity.dart' show Severity;
import 'package:front_end/src/fasta/source/type_promotion_look_ahead_listener.dart'
show
TypePromotionLookAheadListener,
TypePromotionState,
UnspecifiedDeclaration;
import 'package:kernel/ast.dart' show Source;
import 'package:testing/testing.dart'
show Chain, ChainContext, ExpectationSet, Future, Result, Step, runMe;
import '../utils/kernel_chain.dart' show MatchContext;
import '../utils/scanner_chain.dart' show Read, Scan, ScannedFile;
const String EXPECTATIONS = '''
[
{
"name": "ExpectationFileMismatch",
"group": "Fail"
},
{
"name": "ExpectationFileMissing",
"group": "Fail"
}
]
''';
Future<ChainContext> createContext(
Chain suite, Map<String, String> environment) async {
CompilerContext context =
await CompilerContext.runWithOptions<CompilerContext>(
new ProcessedOptions(),
(CompilerContext context) =>
new Future<CompilerContext>.value(context),
errorOnMissingInput: false);
context.disableColors();
return new TypePromotionLookAheadContext(
context, environment["updateExpectations"] == "true");
}
class TypePromotionLookAheadContext extends ChainContext with MatchContext {
final CompilerContext context;
final List<Step> steps = const <Step>[
const Read(),
const Scan(),
const TypePromotionLookAheadStep(),
const CheckTypePromotionResult(),
];
final bool updateExpectations;
final ExpectationSet expectationSet =
new ExpectationSet.fromJsonList(jsonDecode(EXPECTATIONS));
TypePromotionLookAheadContext(this.context, this.updateExpectations);
}
class TypePromotionLookAheadStep extends Step<ScannedFile, TypePromotionResult,
TypePromotionLookAheadContext> {
const TypePromotionLookAheadStep();
String get name => "Type Promotion Look Ahead";
Future<Result<TypePromotionResult>> run(
ScannedFile file, TypePromotionLookAheadContext context) async {
return context.context
.runInContext<Result<TypePromotionResult>>((CompilerContext c) async {
Uri uri = file.file.uri;
c.uriToSource[uri] =
new Source(file.result.lineStarts, file.file.bytes, uri, uri);
StringBuffer buffer = new StringBuffer();
Parser parser = new Parser(new TestListener(uri, buffer));
try {
parser.parseUnit(file.result.tokens);
} finally {
c.uriToSource.remove(uri);
}
return pass(new TypePromotionResult(uri, "$buffer"));
});
}
}
class TestState extends TypePromotionState {
final StringBuffer buffer;
TestState(Uri uri, this.buffer) : super(uri);
void note(String message, Token token) {
buffer.writeln(CompilerContext.current.format(
debugMessage(message, uri, token.charOffset, token.lexeme.length),
Severity.context));
}
@override
void checkEmpty(Token token) {
if (stack.isNotEmpty) {
throw CompilerContext.current.format(
debugMessage("Stack not empty", uri, token?.charOffset ?? -1,
token?.length ?? 1),
Severity.internalProblem);
}
}
@override
void declareIdentifier(Token token) {
super.declareIdentifier(token);
trace("Declared ${token.lexeme}", token);
}
@override
Builder nullValue(String name, Token token) {
return new DebugDeclaration(name, uri, token?.charOffset ?? -1);
}
@override
void registerWrite(UnspecifiedDeclaration declaration, Token token) {
note("Write to ${declaration.name}@${declaration.charOffset}", token);
}
@override
void registerPromotionCandidate(
UnspecifiedDeclaration declaration, Token token) {
note("Possible promotion of ${declaration.name}@${declaration.charOffset}",
token);
}
@override
void report(LocatedMessage message, Severity severity,
{List<LocatedMessage> context}) {
CompilerContext.current.report(message, severity, context: context);
}
@override
void trace(String message, Token token) {
report(
debugMessage(message, uri, token?.charOffset ?? -1, token?.length ?? 1),
Severity.warning);
for (Object o in stack) {
String s = " $o";
int index = s.indexOf("\n");
if (index != -1) {
s = s.substring(0, index) + "...";
}
print(s);
}
print('------------------\n');
}
}
LocatedMessage debugMessage(String text, Uri uri, int offset, int length) {
return templateUnspecified
.withArguments(text)
.withLocation(uri, offset, length);
}
class TestListener extends TypePromotionLookAheadListener {
TestListener(Uri uri, StringBuffer buffer)
: super(new TestState(uri, buffer));
@override
void debugEvent(String name, Token token) {
state.trace(name, token);
}
}
class DebugDeclaration extends BuilderImpl {
final String name;
@override
final Uri fileUri;
@override
int charOffset;
DebugDeclaration(this.name, this.fileUri, this.charOffset);
Builder get parent => null;
String get fullNameForErrors => name;
String toString() => "<<$name@$charOffset>>";
}
class TypePromotionResult {
final Uri uri;
final String trace;
const TypePromotionResult(this.uri, this.trace);
}
class CheckTypePromotionResult
extends Step<TypePromotionResult, Null, TypePromotionLookAheadContext> {
const CheckTypePromotionResult();
String get name => "Check Type Promotion Result";
Future<Result<Null>> run(
TypePromotionResult result, TypePromotionLookAheadContext context) {
return context.match<Null>(
".type_promotion.expect", result.trace, result.uri, null);
}
}
main([List<String> arguments = const []]) =>
runMe(arguments, createContext, configurationPath: "../../testing.json");