blob: 66b2d52fa5349518031a257186f167a46acb8c33 [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:async';
import 'dart:io' show File;
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/fasta/ast_body_builder.dart';
import 'package:analyzer/src/generated/parser.dart' as analyzer;
import 'package:analyzer/src/generated/resolver.dart';
import "package:front_end/src/api_prototype/front_end.dart";
import "package:front_end/src/api_prototype/memory_file_system.dart";
import "package:front_end/src/base/processed_options.dart";
import "package:front_end/src/compute_platform_binaries_location.dart";
import 'package:front_end/src/fasta/compiler_context.dart';
import 'package:front_end/src/fasta/constant_context.dart';
import 'package:front_end/src/fasta/dill/dill_target.dart';
import "package:front_end/src/fasta/fasta_codes.dart";
import 'package:front_end/src/fasta/kernel/forest.dart' hide Identifier;
import 'package:front_end/src/fasta/kernel/kernel_builder.dart';
import "package:front_end/src/fasta/kernel/kernel_target.dart";
import 'package:front_end/src/fasta/modifier.dart' as Modifier;
import 'package:front_end/src/fasta/parser/async_modifier.dart';
import 'package:front_end/src/fasta/parser/parser.dart';
import 'package:front_end/src/fasta/scanner.dart';
import 'package:front_end/src/fasta/ticker.dart';
import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
import 'package:front_end/src/fasta/uri_translator_impl.dart';
import 'package:kernel/class_hierarchy.dart' as kernel;
import 'package:kernel/core_types.dart' as kernel;
import 'package:kernel/kernel.dart' as kernel;
import 'package:test/test.dart';
import '../../generated/parser_test.dart';
import '../../generated/test_support.dart';
Element _buildElement(kernel.Class coreType) {
ClassElementImpl element =
new ClassElementImpl(coreType.name, coreType.fileOffset);
element.typeParameters = coreType.typeParameters.map((parameter) {
TypeParameterElementImpl element =
new TypeParameterElementImpl(parameter.name, parameter.fileOffset);
element.type = new TypeParameterTypeImpl(element);
return element;
}).toList();
return element;
}
class CompilerTestContext extends CompilerContext {
KernelTarget kernelTarget;
TypeProvider _typeProvider;
CompilerTestContext(ProcessedOptions options) : super(options);
Uri get entryPoint => options.inputs.single;
static Future<T> runWithTestOptions<T>(
Future<T> action(CompilerTestContext c)) async {
// TODO(danrubel): Consider HybridFileSystem.
final MemoryFileSystem fs =
new MemoryFileSystem(Uri.parse("org-dartlang-test:///"));
/// The custom URI used to locate the dill file in the MemoryFileSystem.
final Uri sdkSummary = fs.currentDirectory.resolve("vm_platform.dill");
/// The in memory test code URI
final Uri entryPoint = fs.currentDirectory.resolve("main.dart");
// Read the dill file containing kernel platform summaries into memory.
List<int> sdkSummaryBytes = await new File.fromUri(
computePlatformBinariesLocation().resolve("vm_platform.dill"))
.readAsBytes();
fs.entityForUri(sdkSummary).writeAsBytesSync(sdkSummaryBytes);
final CompilerOptions optionBuilder = new CompilerOptions()
..strongMode = false // TODO(danrubel): enable strong mode.
..reportMessages = true
..verbose = false
..fileSystem = fs
..sdkSummary = sdkSummary
..onProblem = (FormattedMessage problem, Severity severity,
List<FormattedMessage> context) {
// TODO(danrubel): Capture problems and check against expectations.
// print(problem.formatted);
};
final ProcessedOptions options =
new ProcessedOptions(optionBuilder, [entryPoint]);
UriTranslatorImpl uriTranslator = await options.getUriTranslator();
return await new CompilerTestContext(options)
.runInContext<T>((CompilerContext _c) async {
CompilerTestContext c = _c;
DillTarget dillTarget = new DillTarget(
new Ticker(isVerbose: false), uriTranslator, options.target);
c.kernelTarget = new KernelTarget(fs, true, dillTarget, uriTranslator);
// Load the dill file containing platform code.
dillTarget.loader.read(Uri.parse('dart:core'), -1, fileUri: sdkSummary);
kernel.Component sdkComponent =
kernel.loadComponentFromBytes(sdkSummaryBytes);
dillTarget.loader
.appendLibraries(sdkComponent, byteCount: sdkSummaryBytes.length);
await dillTarget.buildOutlines();
await c.kernelTarget.buildOutlines();
c.kernelTarget.computeCoreTypes();
assert(c.kernelTarget.loader.coreTypes != null);
// Initialize the typeProvider if types should be resolved.
Map<String, Element> map = <String, Element>{};
var coreTypes = c.kernelTarget.loader.coreTypes;
for (var coreType in [
coreTypes.boolClass,
coreTypes.doubleClass,
coreTypes.functionClass,
coreTypes.futureClass,
coreTypes.futureOrClass,
coreTypes.intClass,
coreTypes.iterableClass,
coreTypes.iteratorClass,
coreTypes.listClass,
coreTypes.mapClass,
coreTypes.nullClass,
coreTypes.numClass,
coreTypes.objectClass,
coreTypes.stackTraceClass,
coreTypes.streamClass,
coreTypes.stringClass,
coreTypes.symbolClass,
coreTypes.typeClass
]) {
map[coreType.name] = _buildElement(coreType);
}
Namespace namespace = new Namespace(map);
c._typeProvider =
new TypeProviderImpl.forNamespaces(namespace, namespace);
T result;
Completer<T> completer = new Completer<T>();
// Since we're using `package:test_reflective_loader`, we can't rely on
// normal async behavior, as `defineReflectiveSuite` doesn't return a
// future. However, since it's built on top of `package:test`, we can
// obtain a future that completes when all the tests are done using
// `tearDownAll`. This allows this function to complete no earlier than
// when the tests are done. This is important, as we don't want to call
// `CompilerContext.clear` before then.
tearDownAll(() => completer.complete(result));
result = await action(c);
return completer.future;
});
}
static CompilerTestContext get current => CompilerContext.current;
}
/// Implementation of [AbstractParserTestCase] specialized for testing building
/// Analyzer AST using the fasta [Forest] API.
class FastaBodyBuilderTestCase extends Object
with ParserTestHelpers
implements AbstractParserTestCase {
final bool resolveTypes;
String content;
/// The expected offset of the next token to be parsed after the parser has
/// finished parsing, or `null` (the default) if EOF is expected.
int expectedEndOffset;
@override
void set enableGenericMethodComments(_) {
// Ignored.
}
FastaBodyBuilderTestCase(this.resolveTypes);
analyzer.Parser get parser => new ParserProxy(this);
TypeProvider get typeProvider => CompilerTestContext.current._typeProvider;
bool get usingFastaParser => true;
@override
void assertNoErrors() {
// TODO(brianwilkerson) Implement this.
}
void createParser(String content, {int expectedEndOffset}) {
this.content = content;
this.expectedEndOffset = expectedEndOffset;
}
@override
void expectNotNullIfNoErrors(Object result) {
// TODO(brianwilkerson) Implement this.
}
@override
ExpectedError expectedError(ErrorCode code, int offset, int length) {
return new ExpectedError(code, offset, length);
}
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
Expression parseAdditiveExpression(String code) {
return parseExpression(code);
}
@override
Expression parseAssignableExpression(String code, bool primaryAllowed) {
return parseExpression(code);
}
@override
Expression parseAssignableSelector(String code, bool optional,
{bool allowConditional: true}) {
return parseExpression(code);
}
@override
AwaitExpression parseAwaitExpression(String code) {
return parseExpression(code, inAsync: true);
}
@override
Expression parseBitwiseAndExpression(String code) {
return parseExpression(code);
}
@override
Expression parseBitwiseOrExpression(String code) {
return parseExpression(code);
}
@override
Expression parseBitwiseXorExpression(String code) {
return parseExpression(code);
}
@override
Expression parseCascadeSection(String code) {
return parseExpression(code);
}
@override
CompilationUnit parseCompilationUnit(String source,
{List<ErrorCode> codes, List<ExpectedError> errors}) {
return _parse(source, (parser, token) => parser.parseUnit(token.next));
}
@override
ConditionalExpression parseConditionalExpression(String code) {
return parseExpression(code);
}
@override
Expression parseConstExpression(String code) {
return parseExpression(code);
}
@override
ConstructorInitializer parseConstructorInitializer(String code) {
throw new UnimplementedError();
}
@override
CompilationUnit parseDirectives(String source,
[List<ErrorCode> errorCodes = const <ErrorCode>[]]) {
return parseCompilationUnit(content, codes: errorCodes);
}
@override
BinaryExpression parseEqualityExpression(String code) {
return parseExpression(code);
}
@override
Expression parseExpression(String source,
{List<ErrorCode> codes,
List<ExpectedError> errors,
int expectedEndOffset,
bool inAsync: false,
bool inCatchBlock: false}) {
// TODO(brianwilkerson) Check error codes.
Object result = _parse(
source, (parser, token) => parser.parseExpression(token),
inAsync: inAsync, inCatchBlock: inCatchBlock);
if (result is Generator) {
result = (result as Generator).buildForEffect();
}
if (result is! Expression) {
throw new StateError('Expected Expression, found ${result.runtimeType}');
}
return result;
}
@override
List<Expression> parseExpressionList(String code) {
throw new UnimplementedError();
}
@override
Expression parseExpressionWithoutCascade(String code) {
return parseExpression(code);
}
@override
FormalParameter parseFormalParameter(String code, ParameterKind kind,
{List<ErrorCode> errorCodes: const <ErrorCode>[]}) {
throw new UnimplementedError();
}
@override
FormalParameterList parseFormalParameterList(String code,
{bool inFunctionType: false,
List<ErrorCode> errorCodes: const <ErrorCode>[],
List<ExpectedError> errors}) {
throw new UnimplementedError();
}
@override
CompilationUnitMember parseFullCompilationUnitMember() {
CompilationUnit unit = parseCompilationUnit(content);
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
return unit.declarations[0];
}
@override
Directive parseFullDirective() {
CompilationUnit unit = parseCompilationUnit(content);
expect(unit.directives, hasLength(1));
expect(unit.declarations, hasLength(0));
return unit.directives[0];
}
@override
FunctionExpression parseFunctionExpression(String code) {
return parseExpression(code);
}
@override
InstanceCreationExpression parseInstanceCreationExpression(
String code, Token newToken) {
return parseExpression(code);
}
@override
ListLiteral parseListLiteral(
Token token, String typeArgumentsCode, String code) {
StringBuffer buffer = new StringBuffer();
if (token != null) {
buffer.write(token.lexeme);
buffer.write(' ');
}
if (typeArgumentsCode != null) {
buffer.write(typeArgumentsCode);
}
buffer.write(code);
return parseExpression(buffer.toString());
}
@override
TypedLiteral parseListOrMapLiteral(Token modifier, String code) {
return parseExpression(code);
}
@override
Expression parseLogicalAndExpression(String code) {
return parseExpression(code);
}
@override
Expression parseLogicalOrExpression(String code) {
return parseExpression(code);
}
@override
MapLiteral parseMapLiteral(
Token token, String typeArgumentsCode, String code) {
StringBuffer buffer = new StringBuffer();
if (token != null) {
buffer.write(token.lexeme);
buffer.write(' ');
}
if (typeArgumentsCode != null) {
buffer.write(typeArgumentsCode);
}
buffer.write(code);
return parseExpression(buffer.toString());
}
@override
MapLiteralEntry parseMapLiteralEntry(String code) {
Expression expression = parseExpression('{$code}');
expect(expression, new isInstanceOf<MapLiteral>());
MapLiteral literal = expression;
expect(literal.entries, hasLength(1));
return literal.entries[0];
}
@override
Expression parseMultiplicativeExpression(String code) {
return parseExpression(code);
}
@override
InstanceCreationExpression parseNewExpression(String code) {
return parseExpression(code);
}
@override
NormalFormalParameter parseNormalFormalParameter(String code,
{bool inFunctionType: false,
List<ErrorCode> errorCodes: const <ErrorCode>[]}) {
throw new UnimplementedError();
}
@override
Expression parsePostfixExpression(String code) {
return parseExpression(code);
}
@override
Identifier parsePrefixedIdentifier(String code) {
return parseExpression(code);
}
@override
Expression parsePrimaryExpression(String code,
{int expectedEndOffset, List<ExpectedError> errors}) {
return parseExpression(code,
expectedEndOffset: expectedEndOffset, errors: errors);
}
@override
Expression parseRelationalExpression(String code) {
return parseExpression(code);
}
@override
RethrowExpression parseRethrowExpression(String code) {
Statement statement = parseStatement(code, inCatchBlock: true);
expect(statement, new isInstanceOf<ExpressionStatement>());
Expression expression = (statement as ExpressionStatement).expression;
expect(expression, new isInstanceOf<RethrowExpression>());
return expression;
}
@override
BinaryExpression parseShiftExpression(String code) {
return parseExpression(code);
}
@override
SimpleIdentifier parseSimpleIdentifier(String code) {
return parseExpression(code);
}
@override
Statement parseStatement(String source,
{bool enableLazyAssignmentOperators,
int expectedEndOffset,
bool inCatchBlock: true}) {
// TODO(brianwilkerson) Check error codes.
return _parse(source, (parser, token) => parser.parseStatement(token),
inCatchBlock: inCatchBlock);
}
@override
Expression parseStringLiteral(String code) {
return parseExpression(code);
}
@override
SymbolLiteral parseSymbolLiteral(String code) {
return parseExpression(code);
}
@override
Expression parseThrowExpression(String code) {
return parseExpression(code);
}
@override
Expression parseThrowExpressionWithoutCascade(String code) {
return parseExpression(code);
}
@override
PrefixExpression parseUnaryExpression(String code) {
return parseExpression(code);
}
@override
VariableDeclarationList parseVariableDeclarationList(String source) {
CompilationUnit unit = parseCompilationUnit('''
f() {
$source;
}
''');
FunctionDeclaration function = unit.declarations[0];
BlockFunctionBody body = function.functionExpression.body;
VariableDeclarationStatement statement = body.block.statements[0];
return statement.variables;
}
T _parse<T>(
String source, void parseFunction(Parser parser, Token previousToken),
{bool inAsync: false, bool inCatchBlock: false}) {
ScannerResult scan = scanString(source);
CompilerTestContext c = CompilerTestContext.current;
KernelLibraryBuilder library = new KernelLibraryBuilder(
c.entryPoint,
c.entryPoint,
c.kernelTarget.loader,
null /* actualOrigin */,
null /* enclosingLibrary */,
);
List<KernelTypeVariableBuilder> typeVariableBuilders =
<KernelTypeVariableBuilder>[];
List<KernelFormalParameterBuilder> formalParameterBuilders =
<KernelFormalParameterBuilder>[];
KernelProcedureBuilder procedureBuilder = new KernelProcedureBuilder(
null /* metadata */,
Modifier.staticMask /* or Modifier.varMask */,
c.kernelTarget.dynamicType,
"analyzerTest",
typeVariableBuilders,
formalParameterBuilders,
kernel.ProcedureKind.Method,
library,
-1 /* charOffset */,
-1 /* charOpenParenOffset */,
-1 /* charEndOffset */);
TypeInferrerDisabled typeInferrer =
new TypeInferrerDisabled(new TypeSchemaEnvironment(
c.kernelTarget.loader.coreTypes,
c.kernelTarget.loader.hierarchy,
// TODO(danrubel): Enable strong mode.
false /* strong mode */,
));
AstBodyBuilder builder = new AstBodyBuilder(
library,
procedureBuilder,
new UnlinkedScope(),
null,
c.kernelTarget.loader.hierarchy,
c.kernelTarget.loader.coreTypes,
null /* classBuilder */,
false /* isInstanceMember */,
null /* uri */,
typeInferrer,
typeProvider,
)..constantContext = ConstantContext.none; // .inferred ?
Parser parser = new Parser(builder);
if (inAsync) {
parser.asyncState = AsyncModifier.Async;
}
if (inCatchBlock) {
builder.inCatchBlock = inCatchBlock;
}
parseFunction(parser, parser.syntheticPreviousToken(scan.tokens));
// TODO(brianwilkerson) Check `expectedEndOffset` if it is not `null`.
return builder.pop();
}
}
/// A parser that can be used by [FastaBodyBuilderTestCase] to support the tests
/// that still call methods on the parser directly.
class ParserProxy implements analyzer.Parser {
final FastaBodyBuilderTestCase testCase;
ParserProxy(this.testCase);
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
Annotation parseAnnotation() {
CompilationUnit unit =
testCase.parseCompilationUnit('${testCase.content} var v;');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(
unit.declarations[0], new isInstanceOf<TopLevelVariableDeclaration>());
TopLevelVariableDeclaration declaration = unit.declarations[0];
expect(declaration.metadata, hasLength(1));
return declaration.metadata[0];
}
@override
ArgumentList parseArgumentList() {
Expression expression = testCase.parseExpression('f${testCase.content}');
expect(expression, new isInstanceOf<MethodInvocation>());
MethodInvocation invocation = expression;
return invocation.argumentList;
}
@override
ClassMember parseClassMember(String className) {
CompilationUnit unit = testCase
.parseCompilationUnit('class $className { ${testCase.content} }');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(unit.declarations[0], new isInstanceOf<ClassDeclaration>());
ClassDeclaration classDeclaration = unit.declarations[0];
expect(classDeclaration.members, hasLength(1));
return classDeclaration.members[0];
}
@override
List<Combinator> parseCombinators() {
CompilationUnit unit = testCase
.parseCompilationUnit('import "file.dart" ${testCase.content};');
expect(unit.directives, hasLength(1));
expect(unit.declarations, hasLength(0));
expect(unit.directives[0], new isInstanceOf<LibraryDirective>());
ImportDirective directive = unit.directives[0];
return directive.combinators;
}
@override
CommentReference parseCommentReference(
String referenceSource, int sourceOffset) {
// TODO(brianwilkerson) Implement this.
throw new UnimplementedError();
}
@override
CompilationUnit parseCompilationUnit2() {
return testCase.parseCompilationUnit(testCase.content);
}
@override
Configuration parseConfiguration() {
CompilationUnit unit = testCase
.parseCompilationUnit('import "file.dart" ${testCase.content};');
expect(unit.directives, hasLength(1));
expect(unit.declarations, hasLength(0));
expect(unit.directives[0], new isInstanceOf<LibraryDirective>());
ImportDirective directive = unit.directives[0];
expect(directive.configurations, hasLength(1));
return directive.configurations[0];
}
@override
DottedName parseDottedName() {
CompilationUnit unit = testCase.parseCompilationUnit(
'import "file.dart" if (${testCase.content}) "file2.dart";');
expect(unit.directives, hasLength(1));
expect(unit.declarations, hasLength(0));
expect(unit.directives[0], new isInstanceOf<LibraryDirective>());
ImportDirective directive = unit.directives[0];
expect(directive.configurations, hasLength(1));
return directive.configurations[0].name;
}
@override
ExtendsClause parseExtendsClause() {
CompilationUnit unit =
testCase.parseCompilationUnit('class C ${testCase.content} {}');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(unit.declarations[0], new isInstanceOf<ClassDeclaration>());
ClassDeclaration classDeclaration = unit.declarations[0];
return classDeclaration.extendsClause;
}
@override
analyzer.FinalConstVarOrType parseFinalConstVarOrType(bool optional,
{bool inFunctionType: false}) {
// TODO(brianwilkerson) Implement this or re-write the tests.
throw new UnimplementedError();
}
@override
FormalParameterList parseFormalParameterList({bool inFunctionType: false}) {
CompilationUnit unit =
testCase.parseCompilationUnit('f${testCase.content} {}');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(unit.declarations[0], new isInstanceOf<FunctionDeclaration>());
FunctionDeclaration function = unit.declarations[0];
return function.functionExpression.parameters;
}
@override
FunctionBody parseFunctionBody(bool mayBeEmpty,
analyzer.ParserErrorCode emptyErrorCode, bool inExpression) {
CompilationUnit unit =
testCase.parseCompilationUnit('f() ${testCase.content}');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(unit.declarations[0], new isInstanceOf<FunctionDeclaration>());
FunctionDeclaration declaration = unit.declarations[0];
return declaration.functionExpression.body;
}
@override
ImplementsClause parseImplementsClause() {
CompilationUnit unit =
testCase.parseCompilationUnit('class C ${testCase.content} {}');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(unit.declarations[0], new isInstanceOf<ClassDeclaration>());
ClassDeclaration classDeclaration = unit.declarations[0];
return classDeclaration.implementsClause;
}
@override
analyzer.Modifiers parseModifiers() {
// TODO(brianwilkerson) Implement this or re-write the tests (this might
// need context to create the right kind of declaration for the modifiers).
throw new UnimplementedError();
}
@override
Expression parseMultiplicativeExpression() {
return testCase.parseExpression(testCase.content);
}
@override
Expression parsePrimaryExpression() {
return testCase.parseExpression(testCase.content);
}
@override
SimpleIdentifier parseSimpleIdentifier(
{bool allowKeyword: false, bool isDeclaration: false}) {
return testCase.parseExpression(testCase.content);
}
@override
Statement parseStatement2() {
return testCase.parseStatement(testCase.content);
}
@override
TypeAnnotation parseTypeAnnotation(bool inExpression) {
if (inExpression) {
// TODO(brianwilkerson) As far as I can see, this path is not used.
throw new UnimplementedError();
}
CompilationUnit unit =
testCase.parseCompilationUnit('${testCase.content} x;');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(
unit.declarations[0], new isInstanceOf<TopLevelVariableDeclaration>());
TopLevelVariableDeclaration variable = unit.declarations[0];
return variable.variables.type;
}
@override
TypeArgumentList parseTypeArgumentList() {
CompilationUnit unit =
testCase.parseCompilationUnit('C${testCase.content} c;');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(
unit.declarations[0], new isInstanceOf<TopLevelVariableDeclaration>());
TopLevelVariableDeclaration variable = unit.declarations[0];
return (variable.variables.type as TypeName).typeArguments;
}
@override
TypeName parseTypeName(bool inExpression) {
return parseTypeAnnotation(inExpression) as TypeName;
}
@override
TypeParameter parseTypeParameter() {
CompilationUnit unit =
testCase.parseCompilationUnit('class C<${testCase.content}> {}');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(unit.declarations[0], new isInstanceOf<ClassDeclaration>());
ClassDeclaration classDeclaration = unit.declarations[0];
return classDeclaration.typeParameters.typeParameters[0];
}
@override
Expression parseUnaryExpression() {
return testCase.parseExpression(testCase.content);
}
@override
WithClause parseWithClause() {
CompilationUnit unit =
testCase.parseCompilationUnit('class C ${testCase.content} {}');
expect(unit.directives, hasLength(0));
expect(unit.declarations, hasLength(1));
expect(unit.declarations[0], new isInstanceOf<ClassDeclaration>());
ClassDeclaration classDeclaration = unit.declarations[0];
return classDeclaration.withClause;
}
}