[dartdevc] Delete the legacy version of DDC

Change-Id: I2dc3999b0b7e93252402422d662fb5da4dcca3f9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/127840
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Mark Zhou <markzipan@google.com>
This commit is contained in:
Nicholas Shahan 2019-12-12 21:53:08 +00:00 committed by commit-bot@chromium.org
parent e51702a7a1
commit c561a9eacc
30 changed files with 29 additions and 11998 deletions

View file

@ -12,6 +12,20 @@
### Tools
#### Dart Dev Compiler (DDC)
* **Breaking Change**: Deleted the legacy (analyzer based) version of DDC. For
additional details see the [announcement].
* The `--kernel` option is now ignored and defaults to true. There is no
longer any way to invoke the legacy (analyzer based) version of DDC.
* Command line arguments that were only used for the legacy DDC have been
removed.
* The pre-compiled ddc_sdk.js artifacts generated by legacy DDC have
been deleted from `dart-sdk/lib/dev_compiler` in favor of the versions
located at `dart-sdk/lib/dev_compiler/kernel`.
[announcement]: https://github.com/dart-lang/sdk/issues/38994
#### Linter
The Linter was updated to `0.1.106`, which includes:

View file

@ -1,462 +0,0 @@
// Copyright (c) 2015, 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 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_ast_factory.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
export 'package:analyzer/dart/ast/standard_ast_factory.dart';
final ast = AstBuilder();
class AstBuilder {
KeywordToken get constKeyword => KeywordToken(Keyword.CONST, 0);
TypeName typeName(Identifier id, List<TypeAnnotation> args) {
TypeArgumentList argList;
if (args != null && args.isNotEmpty) argList = typeArgumentList(args);
return astFactory.typeName(id, argList);
}
FunctionTypeAlias functionTypeAlias(TypeName ret, SimpleIdentifier name,
List<TypeParameter> tParams, List<FormalParameter> params) {
TypeParameterList tps =
(tParams.isEmpty) ? null : typeParameterList(tParams);
FormalParameterList fps = formalParameterList(params);
Token semi = Token(TokenType.SEMICOLON, 0);
Token td = KeywordToken(Keyword.TYPEDEF, 0);
return astFactory.functionTypeAlias(
null, null, td, ret, name, tps, fps, semi);
}
Expression parenthesize(Expression exp) {
if (exp is Identifier ||
exp is ParenthesizedExpression ||
exp is FunctionExpressionInvocation ||
exp is MethodInvocation) return exp;
return parenthesizedExpression(exp);
}
PropertyAccess propertyAccess(Expression target, SimpleIdentifier name) {
var p = Token(TokenType.PERIOD, 0);
return astFactory.propertyAccess(target, p, name);
}
MethodInvocation methodInvoke(Expression target, SimpleIdentifier name,
TypeArgumentList typeArguments, NodeList<Expression> args) {
var p = Token(TokenType.PERIOD, 0);
return astFactory.methodInvocation(
target, p, name, typeArguments, argumentList(args));
}
TokenType getTokenType(String lexeme) {
switch (lexeme) {
case "&":
return TokenType.AMPERSAND;
case "&&":
return TokenType.AMPERSAND_AMPERSAND;
case "&=":
return TokenType.AMPERSAND_EQ;
case "@":
return TokenType.AT;
case "!":
return TokenType.BANG;
case "!=":
return TokenType.BANG_EQ;
case "|":
return TokenType.BAR;
case "||":
return TokenType.BAR_BAR;
case "|=":
return TokenType.BAR_EQ;
case ":":
return TokenType.COLON;
case ",":
return TokenType.COMMA;
case "^":
return TokenType.CARET;
case "^=":
return TokenType.CARET_EQ;
case "}":
return TokenType.CLOSE_CURLY_BRACKET;
case ")":
return TokenType.CLOSE_PAREN;
case "]":
return TokenType.CLOSE_SQUARE_BRACKET;
case "=":
return TokenType.EQ;
case "==":
return TokenType.EQ_EQ;
case "=>":
return TokenType.FUNCTION;
case ">":
return TokenType.GT;
case ">=":
return TokenType.GT_EQ;
case ">>":
return TokenType.GT_GT;
case ">>=":
return TokenType.GT_GT_EQ;
case ">>>":
return TokenType.GT_GT_GT;
case ">>>=":
return TokenType.GT_GT_GT_EQ;
case "#":
return TokenType.HASH;
case "[]":
return TokenType.INDEX;
case "[]=":
return TokenType.INDEX_EQ;
case "<":
return TokenType.LT;
case "<=":
return TokenType.LT_EQ;
case "<<":
return TokenType.LT_LT;
case "<<=":
return TokenType.LT_LT_EQ;
case "-":
return TokenType.MINUS;
case "-=":
return TokenType.MINUS_EQ;
case "--":
return TokenType.MINUS_MINUS;
case "{":
return TokenType.OPEN_CURLY_BRACKET;
case "(":
return TokenType.OPEN_PAREN;
case "[":
return TokenType.OPEN_SQUARE_BRACKET;
case "%":
return TokenType.PERCENT;
case "%=":
return TokenType.PERCENT_EQ;
case ".":
return TokenType.PERIOD;
case "..":
return TokenType.PERIOD_PERIOD;
case "+":
return TokenType.PLUS;
case "+=":
return TokenType.PLUS_EQ;
case "++":
return TokenType.PLUS_PLUS;
case "?":
return TokenType.QUESTION;
case ";":
return TokenType.SEMICOLON;
case "/":
return TokenType.SLASH;
case "/=":
return TokenType.SLASH_EQ;
case "*":
return TokenType.STAR;
case "*=":
return TokenType.STAR_EQ;
case "\${":
return TokenType.STRING_INTERPOLATION_EXPRESSION;
case "\$":
return TokenType.STRING_INTERPOLATION_IDENTIFIER;
case "~":
return TokenType.TILDE;
case "~/":
return TokenType.TILDE_SLASH;
case "~/=":
return TokenType.TILDE_SLASH_EQ;
case "`":
return TokenType.BACKPING;
case "\\":
return TokenType.BACKSLASH;
case "...":
return TokenType.PERIOD_PERIOD_PERIOD;
case "??":
return TokenType.QUESTION_QUESTION;
case "??=":
return TokenType.QUESTION_QUESTION_EQ;
default:
return null;
}
}
Token _binaryOperation(String oper) {
var type = getTokenType(oper);
assert(type != null);
return Token(type, 0);
}
BinaryExpression binaryExpression(Expression l, String oper, Expression r) {
Token token = _binaryOperation(oper);
return astFactory.binaryExpression(l, token, r);
}
ConditionalExpression conditionalExpression(
Expression cond, Expression tExp, Expression fExp) {
var q = Token(TokenType.QUESTION, 0);
var c = Token(TokenType.COLON, 0);
return astFactory.conditionalExpression(cond, q, tExp, c, fExp);
}
Expression application(Expression function, List<Expression> es) {
ArgumentList args = argumentList(es);
return functionExpressionInvocation(function, args);
}
Block block(List<Statement> statements) {
Token ld = BeginToken(TokenType.OPEN_CURLY_BRACKET, 0);
Token rd = Token(TokenType.CLOSE_CURLY_BRACKET, 0);
return astFactory.block(ld, statements, rd);
}
MethodDeclaration blockMethodDeclaration(TypeName rt, SimpleIdentifier m,
List<FormalParameter> params, List<Statement> statements,
{bool isStatic = false}) {
FormalParameterList fl = formalParameterList(params);
Block b = block(statements);
BlockFunctionBody body = blockFunctionBody(b);
return methodDeclaration(rt, m, fl, body, isStatic: isStatic);
}
FunctionDeclaration blockFunctionDeclaration(TypeName rt, SimpleIdentifier f,
List<FormalParameter> params, List<Statement> statements) {
FunctionExpression fexp = blockFunction(params, statements);
return functionDeclaration(rt, f, fexp);
}
FunctionExpression blockFunction(
List<FormalParameter> params, List<Statement> statements) {
FormalParameterList fl = formalParameterList(params);
Block b = block(statements);
BlockFunctionBody body = blockFunctionBody(b);
return functionExpression(fl, body);
}
FunctionExpression expressionFunction(
List<FormalParameter> params, Expression body,
[bool decl = false]) {
FormalParameterList fl = formalParameterList(params);
ExpressionFunctionBody b = expressionFunctionBody(body, decl);
return functionExpression(fl, b);
}
FunctionDeclarationStatement functionDeclarationStatement(
TypeName rType, SimpleIdentifier name, FunctionExpression fe) {
var fd = functionDeclaration(rType, name, fe);
return astFactory.functionDeclarationStatement(fd);
}
// let b = e1 in e2 == (\b.e2)(e1)
Expression letExpression(FormalParameter b, Expression e1, Expression e2) {
FunctionExpression l = expressionFunction(<FormalParameter>[b], e2);
return application(parenthesize(l), <Expression>[e1]);
}
FormalParameter requiredFormal(NormalFormalParameter fp) {
return requiredFormalParameter(fp);
}
FormalParameter optionalFormal(NormalFormalParameter fp) {
return optionalFormalParameter(fp);
}
FormalParameter namedFormal(NormalFormalParameter fp) {
return namedFormalParameter(fp);
}
NamedExpression namedParameter(String s, Expression e) {
return namedExpression(s, e);
}
NamedExpression namedExpression(String s, Expression e) {
Label l =
astFactory.label(identifierFromString(s), Token(TokenType.COLON, 0));
return astFactory.namedExpression(l, e);
}
/// Declares a single variable `var <name> = <init>` with the type and name
/// specified by the VariableElement. See also [variableStatement].
VariableDeclarationList declareVariable(SimpleIdentifier name,
[Expression init]) {
var eqToken = init != null ? Token(TokenType.EQ, 0) : null;
var varToken = KeywordToken(Keyword.VAR, 0);
return astFactory.variableDeclarationList(null, null, varToken, null,
[astFactory.variableDeclaration(name, eqToken, init)]);
}
VariableDeclarationStatement variableStatement(SimpleIdentifier name,
[Expression init]) {
return variableDeclarationStatement(declareVariable(name, init));
}
InstanceCreationExpression instanceCreation(
ConstructorName ctor, List<Expression> args) {
var newToken = KeywordToken(Keyword.NEW, 0);
return astFactory.instanceCreationExpression(
newToken, ctor, argumentList(args));
}
ConstructorName constructorName(TypeName type, [SimpleIdentifier name]) {
Token period = name != null ? Token(TokenType.PERIOD, 0) : null;
return astFactory.constructorName(type, period, name);
}
SimpleIdentifier identifierFromString(String name) {
StringToken token = SyntheticStringToken(TokenType.IDENTIFIER, name, 0);
return astFactory.simpleIdentifier(token);
}
PrefixedIdentifier prefixedIdentifier(
SimpleIdentifier pre, SimpleIdentifier id) {
Token period = Token(TokenType.PERIOD, 0);
return astFactory.prefixedIdentifier(pre, period, id);
}
TypeParameter typeParameter(SimpleIdentifier name, [TypeName bound]) {
Token keyword = (bound == null) ? null : KeywordToken(Keyword.EXTENDS, 0);
return astFactory.typeParameter(null, null, name, keyword, bound);
}
TypeParameterList typeParameterList(List<TypeParameter> params) {
Token lb = Token(TokenType.LT, 0);
Token rb = Token(TokenType.GT, 0);
return astFactory.typeParameterList(lb, params, rb);
}
TypeArgumentList typeArgumentList(List<TypeAnnotation> args) {
Token lb = Token(TokenType.LT, 0);
Token rb = Token(TokenType.GT, 0);
return astFactory.typeArgumentList(lb, args, rb);
}
ArgumentList argumentList(List<Expression> args) {
Token lp = BeginToken(TokenType.OPEN_PAREN, 0);
Token rp = Token(TokenType.CLOSE_PAREN, 0);
return astFactory.argumentList(lp, args, rp);
}
BooleanLiteral booleanLiteral(bool b) {
var k = KeywordToken(b ? Keyword.TRUE : Keyword.FALSE, 0);
return astFactory.booleanLiteral(k, b);
}
NullLiteral nullLiteral() {
var n = KeywordToken(Keyword.NULL, 0);
return astFactory.nullLiteral(n);
}
IntegerLiteral integerLiteral(int i) {
StringToken token = StringToken(TokenType.INT, '$i', 0);
return astFactory.integerLiteral(token, i);
}
SimpleStringLiteral simpleStringLiteral(String s) {
StringToken token = StringToken(TokenType.STRING, "\"" + s + "\"", 0);
return astFactory.simpleStringLiteral(token, s);
}
SimpleStringLiteral tripleQuotedStringLiteral(String s) {
StringToken token = StringToken(TokenType.STRING, '"""' + s + '"""', 0);
return astFactory.simpleStringLiteral(token, s);
}
AsExpression asExpression(Expression exp, TypeName type) {
Token token = KeywordToken(Keyword.AS, 0);
return astFactory.asExpression(exp, token, type);
}
IsExpression isExpression(Expression exp, TypeName type) {
Token token = KeywordToken(Keyword.IS, 0);
return astFactory.isExpression(exp, token, null, type);
}
ParenthesizedExpression parenthesizedExpression(Expression exp) {
Token lp = BeginToken(TokenType.OPEN_PAREN, exp.offset);
Token rp = Token(TokenType.CLOSE_PAREN, exp.end);
return astFactory.parenthesizedExpression(lp, exp, rp);
}
Expression functionExpressionInvocation(
Expression function, ArgumentList es) {
return astFactory.functionExpressionInvocation(function, null, es);
}
FormalParameterList formalParameterList(List<FormalParameter> params) {
Token lp = BeginToken(TokenType.OPEN_PAREN, 0);
Token rp = Token(TokenType.CLOSE_PAREN, 0);
bool hasOptional = params.any((p) => p.isPositional);
bool hasNamed = params.any((p) => p.isNamed);
assert(!(hasOptional && hasNamed));
Token ld;
Token rd;
if (hasOptional) {
ld = BeginToken(TokenType.OPEN_SQUARE_BRACKET, 0);
rd = Token(TokenType.CLOSE_SQUARE_BRACKET, 0);
}
if (hasNamed) {
ld = BeginToken(TokenType.OPEN_CURLY_BRACKET, 0);
rd = Token(TokenType.CLOSE_CURLY_BRACKET, 0);
}
return astFactory.formalParameterList(lp, params, ld, rd, rp);
}
BlockFunctionBody blockFunctionBody(Block b) {
return astFactory.blockFunctionBody(null, null, b);
}
ExpressionFunctionBody expressionFunctionBody(Expression body,
[bool decl = false]) {
Token semi = (decl) ? Token(TokenType.SEMICOLON, 0) : null;
return astFactory.expressionFunctionBody(null, null, body, semi);
}
ExpressionStatement expressionStatement(Expression expression) {
Token semi = Token(TokenType.SEMICOLON, 0);
return astFactory.expressionStatement(expression, semi);
}
FunctionDeclaration functionDeclaration(
TypeName rt, SimpleIdentifier f, FunctionExpression fexp) {
return astFactory.functionDeclaration(null, null, null, rt, null, f, fexp);
}
MethodDeclaration methodDeclaration(TypeName rt, SimpleIdentifier m,
FormalParameterList fl, FunctionBody body,
{bool isStatic = false}) {
Token st = isStatic ? KeywordToken(Keyword.STATIC, 0) : null;
return astFactory.methodDeclaration(
null, null, null, st, rt, null, null, m, null, fl, body);
}
FunctionExpression functionExpression(
FormalParameterList fl, FunctionBody body) {
return astFactory.functionExpression(null, fl, body);
}
Statement returnExpression([Expression e]) {
Token ret = KeywordToken(Keyword.RETURN, 0);
Token semi = Token(TokenType.SEMICOLON, 0);
return astFactory.returnStatement(ret, e, semi);
}
FormalParameter requiredFormalParameter(NormalFormalParameter fp) {
return fp;
}
FormalParameter optionalFormalParameter(NormalFormalParameter fp) {
return astFactory.defaultFormalParameter(
fp, ParameterKind.POSITIONAL, null, null);
}
FormalParameter namedFormalParameter(NormalFormalParameter fp) {
return astFactory.defaultFormalParameter(
fp, ParameterKind.NAMED, null, null);
}
VariableDeclarationStatement variableDeclarationStatement(
VariableDeclarationList varDecl) {
var semi = Token(TokenType.SEMICOLON, 0);
return astFactory.variableDeclarationStatement(varDecl, semi);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,255 +0,0 @@
// Copyright (c) 2016, 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 'package:analyzer/src/command_line/arguments.dart'
show defineAnalysisArguments, ignoreUnrecognizedFlagsFlag;
import 'package:analyzer/src/summary/package_bundle_reader.dart'
show ConflictingSummaryException;
import 'package:args/args.dart' show ArgParser, ArgResults;
import 'package:args/command_runner.dart' show UsageException;
import 'package:path/path.dart' as p;
import '../compiler/shared_command.dart' show CompilerResult;
import 'context.dart' show AnalyzerOptions;
import 'driver.dart';
import 'module_compiler.dart';
const _binaryName = 'dartdevc';
bool _verbose = false;
/// Runs a single compile for dartdevc.
///
/// This handles argument parsing, usage, error handling.
/// See bin/dartdevc.dart for the actual entry point, which includes Bazel
/// worker support.
CompilerResult compile(List<String> args,
{CompilerAnalysisDriver compilerState}) {
ArgResults argResults;
AnalyzerOptions analyzerOptions;
try {
var parser = ddcArgParser();
if (args.contains('--$ignoreUnrecognizedFlagsFlag')) {
args = filterUnknownArguments(args, parser);
}
argResults = parser.parse(args);
analyzerOptions = AnalyzerOptions.fromArguments(argResults);
} on FormatException catch (error) {
print('$error\n\n$_usageMessage');
return CompilerResult(64);
}
_verbose = argResults['verbose'] as bool;
if (argResults['help'] as bool || args.isEmpty) {
print(_usageMessage);
return CompilerResult(0);
}
if (argResults['version'] as bool) {
print('$_binaryName version ${_getVersion()}');
return CompilerResult(0);
}
try {
var driver = _compile(argResults, analyzerOptions);
return CompilerResult(0, analyzerState: driver);
} on UsageException catch (error) {
// Incorrect usage, input file not found, etc.
print('${error.message}\n\n$_usageMessage');
return CompilerResult(64);
} on ConflictingSummaryException catch (error) {
// Same input file appears in multiple provided summaries.
print(error);
return CompilerResult(65);
} on CompileErrorException catch (error) {
// Code has error(s) and failed to compile.
print(error);
return CompilerResult(1);
} catch (error, stackTrace) {
// Anything else is likely a compiler bug.
//
// --unsafe-force-compile is a bit of a grey area, but it's nice not to
// crash while compiling
// (of course, output code may crash, if it had errors).
//
print('''
We're sorry, you've found a bug in our compiler.
You can report this bug at:
https://github.com/dart-lang/sdk/issues/labels/web-dev-compiler
Please include the information below in your report, along with
any other information that may help us track it down. Thanks!
$_binaryName arguments: ${args.join(' ')}
dart --version: ${Platform.version}
```
$error
$stackTrace
```''');
return CompilerResult(70);
}
}
ArgParser ddcArgParser(
{bool hide = true, bool help = true, ArgParser argParser}) {
argParser ??= ArgParser(allowTrailingOptions: true);
if (help) {
argParser.addFlag('help',
abbr: 'h',
help: 'Display this message. Add -v to show hidden options.',
negatable: false);
}
argParser
..addFlag('verbose',
abbr: 'v', negatable: false, help: 'Verbose help output.', hide: hide)
..addFlag('version',
negatable: false, help: 'Print the $_binaryName version.', hide: hide)
..addFlag(ignoreUnrecognizedFlagsFlag,
help: 'Ignore unrecognized command line flags.',
defaultsTo: false,
hide: hide)
..addMultiOption('out', abbr: 'o', help: 'Output file (required).');
CompilerOptions.addArguments(argParser, hide: hide);
defineAnalysisArguments(argParser, hide: hide, ddc: true);
AnalyzerOptions.addArguments(argParser, hide: hide);
return argParser;
}
bool _changed(List<int> list1, List<int> list2) {
var length = list1.length;
if (length != list2.length) return true;
for (var i = 0; i < length; ++i) {
if (list1[i] != list2[i]) return true;
}
return false;
}
CompilerAnalysisDriver _compile(
ArgResults argResults, AnalyzerOptions analyzerOptions,
{CompilerAnalysisDriver compilerDriver}) {
var compilerOpts = CompilerOptions.fromArguments(argResults);
var summaryPaths = compilerOpts.summaryModules.keys.toList();
if (compilerDriver == null ||
!compilerDriver.isCompatibleWith(analyzerOptions, summaryPaths)) {
compilerDriver = CompilerAnalysisDriver(analyzerOptions,
summaryPaths: summaryPaths, experiments: compilerOpts.experiments);
}
var outPaths = argResults['out'] as List<String>;
var moduleFormats = compilerOpts.moduleFormats;
if (outPaths.isEmpty) {
throw UsageException(
'Please specify the output file location. For example:\n'
' -o PATH/TO/OUTPUT_FILE.js',
'');
} else if (outPaths.length != moduleFormats.length) {
throw UsageException(
'Number of output files (${outPaths.length}) must match '
'number of module formats (${moduleFormats.length}).',
'');
}
var module = compileWithAnalyzer(
compilerDriver,
argResults.rest,
analyzerOptions,
compilerOpts,
);
module.errors.forEach(print);
if (!module.isValid) {
throw compilerOpts.unsafeForceCompile
? ForceCompileErrorException()
: CompileErrorException();
}
// Write JS file, as well as source map and summary (if requested).
for (var i = 0; i < outPaths.length; i++) {
module.writeCodeSync(moduleFormats[i], outPaths[i]);
}
if (compilerOpts.summarizeApi) {
var summaryPaths = compilerOpts.summaryOutPath != null
? [compilerOpts.summaryOutPath]
: outPaths.map((path) =>
'${p.withoutExtension(path)}.${compilerOpts.summaryExtension}');
// place next to every compiled module
for (var summaryPath in summaryPaths) {
// Only overwrite if summary changed. This plays better with timestamp
// based build systems.
var file = File(summaryPath);
if (!file.existsSync() ||
_changed(file.readAsBytesSync(), module.summaryBytes)) {
if (!file.parent.existsSync()) file.parent.createSync(recursive: true);
file.writeAsBytesSync(module.summaryBytes);
}
}
}
return compilerDriver;
}
String get _usageMessage =>
'The Dart Development Compiler compiles Dart sources into a JavaScript '
'module.\n\n'
'Usage: $_binaryName [options...] <sources...>\n\n'
'${ddcArgParser(hide: !_verbose).usage}';
String _getVersion() {
try {
// This is relative to bin/snapshot, so ../..
String versionPath = Platform.script.resolve('../../version').toFilePath();
File versionFile = File(versionPath);
return versionFile.readAsStringSync().trim();
} catch (_) {
// This happens when the script is not running in the context of an SDK.
return "<unknown>";
}
}
/// Thrown when the input source code has errors.
class CompileErrorException implements Exception {
@override
toString() => '\nPlease fix all errors before compiling (warnings are okay).';
}
/// Thrown when force compilation failed (probably due to static errors).
class ForceCompileErrorException extends CompileErrorException {
@override
toString() =>
'\nForce-compilation not successful. Please check static errors.';
}
// TODO(jmesserly): fix this function in analyzer
List<String> filterUnknownArguments(List<String> args, ArgParser parser) {
Set<String> knownOptions = Set<String>();
Set<String> knownAbbreviations = Set<String>();
parser.options.forEach((String name, option) {
knownOptions.add(name);
String abbreviation = option.abbr;
if (abbreviation != null) {
knownAbbreviations.add(abbreviation);
}
});
List<String> filtered = <String>[];
for (int i = 0; i < args.length; i++) {
String argument = args[i];
if (argument.startsWith('--') && argument.length > 2) {
int equalsOffset = argument.lastIndexOf('=');
int end = equalsOffset < 0 ? argument.length : equalsOffset;
if (knownOptions.contains(argument.substring(2, end))) {
filtered.add(argument);
}
} else if (argument.startsWith('-') && argument.length > 1) {
// TODO(jmesserly): fix this line in analyzer
// It was discarding abbreviations such as -Da=b
// Abbreviations must be 1-character (this is enforced by ArgParser),
// so we don't need to use `optionName`
if (knownAbbreviations.contains(argument[1])) {
filtered.add(argument);
}
} else {
filtered.add(argument);
}
}
return filtered;
}

View file

@ -1,208 +0,0 @@
// Copyright (c) 2015, 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 'package:analyzer/file_system/file_system.dart'
show ResourceProvider, ResourceUriResolver;
import 'package:analyzer/file_system/physical_file_system.dart'
show PhysicalResourceProvider;
import 'package:analyzer/source/custom_resolver.dart';
import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/src/command_line/arguments.dart';
import 'package:analyzer/src/context/builder.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart' hide CustomUriResolver;
import 'package:analyzer/src/summary/package_bundle_reader.dart'
show InSummarySource, InSummaryUriResolver, SummaryDataStore;
import 'package:args/args.dart' show ArgParser, ArgResults;
import 'package:cli_util/cli_util.dart' show getSdkPath;
import 'package:path/path.dart' as p;
// ignore_for_file: deprecated_member_use
/// Options used to set up Source URI resolution in the analysis context.
class AnalyzerOptions {
final ContextBuilderOptions contextBuilderOptions;
/// Custom URI mappings, such as "dart:foo" -> "path/to/foo.dart"
final Map<String, String> customUrlMappings;
/// Path to the dart-sdk, or `null` if the path couldn't be determined.
final String dartSdkPath;
/// File resolvers if explicitly configured, otherwise null.
List<UriResolver> fileResolvers;
/// Stores the value of [resourceProvider].
ResourceProvider _resourceProvider;
/// The default analysis root.
String analysisRoot = p.current;
// May be null.
final DependencyTracker dependencyTracker;
AnalyzerOptions._(
{this.contextBuilderOptions,
String dartSdkPath,
this.customUrlMappings = const {},
this.dependencyTracker})
: dartSdkPath = dartSdkPath ?? getSdkPath() {
contextBuilderOptions.declaredVariables ??= const {};
}
factory AnalyzerOptions.basic(
{String dartSdkPath, String dartSdkSummaryPath}) {
return AnalyzerOptions._(
contextBuilderOptions: ContextBuilderOptions()
..defaultOptions = (AnalysisOptionsImpl()..previewDart2 = true)
..dartSdkSummaryPath = dartSdkSummaryPath,
dartSdkPath: dartSdkPath);
}
factory AnalyzerOptions.fromArguments(ArgResults args,
{String dartSdkSummaryPath}) {
var contextOpts =
createContextBuilderOptions(args, trackCacheDependencies: false);
(contextOpts.defaultOptions as AnalysisOptionsImpl).previewDart2 = true;
var dartSdkPath = args['dart-sdk'] as String ?? getSdkPath();
dartSdkSummaryPath ??= contextOpts.dartSdkSummaryPath ??
p.join(dartSdkPath, 'lib', '_internal', 'ddc_sdk.sum');
// For building the SDK, we explicitly set the path to none.
if (dartSdkSummaryPath == 'build') dartSdkSummaryPath = null;
contextOpts.dartSdkSummaryPath = dartSdkSummaryPath;
var summaryDepsOutput = args['summary-deps-output'] as String;
var dependencyTracker =
summaryDepsOutput != null ? DependencyTracker(summaryDepsOutput) : null;
return AnalyzerOptions._(
contextBuilderOptions: contextOpts,
dartSdkPath: dartSdkPath,
customUrlMappings:
_parseUrlMappings(args['url-mapping'] as List<String>),
dependencyTracker: dependencyTracker);
}
static void addArguments(ArgParser parser, {bool hide = true}) {
parser.addOption('url-mapping',
help: '--url-mapping=libraryUri,/path/to/library.dart uses\n'
'library.dart as the source for an import of of "libraryUri".',
allowMultiple: true,
splitCommas: false,
hide: hide);
}
/// Package root when resolving 'package:' urls the standard way.
String get packageRoot => contextBuilderOptions.defaultPackagesDirectoryPath;
/// Resource provider if explicitly set, otherwise this defaults to use
/// the file system.
ResourceProvider get resourceProvider =>
_resourceProvider ??= PhysicalResourceProvider.INSTANCE;
set resourceProvider(ResourceProvider value) {
_resourceProvider = value;
}
/// Path to the dart-sdk summary. If this is set, it will be used in favor
/// of the unsummarized one.
String get dartSdkSummaryPath => contextBuilderOptions.dartSdkSummaryPath;
/// Defined variables used by `bool.fromEnvironment` etc.
Map<String, String> get declaredVariables =>
contextBuilderOptions.declaredVariables;
ContextBuilder createContextBuilder() {
return ContextBuilder(
resourceProvider, DartSdkManager(dartSdkPath, true), ContentCache(),
options: contextBuilderOptions);
}
}
/// Creates a SourceFactory configured by the [options].
///
/// If supplied, [fileResolvers] will override the default `file:` and
/// `package:` URI resolvers.
SourceFactory createSourceFactory(AnalyzerOptions options,
{DartUriResolver sdkResolver, SummaryDataStore summaryData}) {
var resourceProvider = options.resourceProvider;
var resolvers = <UriResolver>[sdkResolver];
if (options.customUrlMappings.isNotEmpty) {
resolvers
.add(CustomUriResolver(resourceProvider, options.customUrlMappings));
}
if (summaryData != null) {
UriResolver summaryResolver =
InSummaryUriResolver(resourceProvider, summaryData);
if (options.dependencyTracker != null) {
// Wrap summaryResolver.
summaryResolver = TrackingInSummaryUriResolver(
summaryResolver, options.dependencyTracker);
}
resolvers.add(summaryResolver);
}
var fileResolvers = options.fileResolvers ?? createFileResolvers(options);
resolvers.addAll(fileResolvers);
return SourceFactory(resolvers);
}
List<UriResolver> createFileResolvers(AnalyzerOptions options) {
var resourceProvider = options.resourceProvider;
var builderOptions = ContextBuilderOptions();
if (options.packageRoot != null) {
builderOptions.defaultPackagesDirectoryPath = options.packageRoot;
}
var builder =
ContextBuilder(resourceProvider, null, null, options: builderOptions);
var packageResolver = PackageMapUriResolver(resourceProvider,
builder.convertPackagesToMap(builder.createPackageMap(p.current)));
return [ResourceUriResolver(resourceProvider), packageResolver];
}
Map<String, String> _parseUrlMappings(List<String> argument) {
var mappings = <String, String>{};
for (var mapping in argument) {
var splitMapping = mapping.split(',');
if (splitMapping.length >= 2) {
mappings[splitMapping[0]] = p.absolute(splitMapping[1]);
}
}
return mappings;
}
/// A set of path strings read during the build.
class DependencyTracker {
final _dependencies = Set<String>();
/// The path to the file to create once tracking is done.
final String outputPath;
DependencyTracker(this.outputPath);
Iterable<String> get dependencies => _dependencies;
void record(String path) => _dependencies.add(path);
}
/// Wrapper for [UriResolver] that tracks accesses to summaries.
class TrackingInSummaryUriResolver extends UriResolver {
final UriResolver _summaryResolver;
final DependencyTracker _dependencyTracker;
TrackingInSummaryUriResolver(this._summaryResolver, this._dependencyTracker);
@override
Source resolveAbsolute(Uri uri, [Uri actualUri]) {
var source = _summaryResolver.resolveAbsolute(uri, actualUri);
if (source != null && source is InSummarySource) {
_dependencyTracker.record(source.summaryPath);
}
return source;
}
}

View file

@ -1,241 +0,0 @@
// 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:typed_data';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart' show ResourceProvider;
import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/analysis/ddc.dart';
import 'package:analyzer/src/dart/analysis/driver.dart' show AnalysisDriver;
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/library_analyzer.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:meta/meta.dart';
import '../compiler/shared_command.dart' show sdkLibraryVariables;
import 'context.dart' show AnalyzerOptions, createSourceFactory;
import 'extension_types.dart' show ExtensionTypeSet;
/// The analysis driver for `dartdevc`.
///
/// [linkLibraries] can be used to link input sources and input summaries,
/// producing a [LinkedAnalysisDriver] that can analyze those sources.
///
/// This class can be reused to link different input files if they share the
/// same [analysisOptions] and [summaryData].
class CompilerAnalysisDriver {
/// The Analyzer options used for analyzing the input sources.
final AnalysisOptionsImpl analysisOptions;
/// The input summaries used for analyzing/compiling the input sources.
///
/// This should contain the summary of all imported/exported libraries and
/// transitive dependencies, including the Dart SDK.
final SummaryDataStore summaryData;
final ResourceProvider _resourceProvider;
final List<String> _summaryPaths;
@visibleForTesting
final DartSdk dartSdk;
/// SDK summary path, used by [isCompatibleWith] for batch/worker mode.
final String _dartSdkSummaryPath;
ExtensionTypeSet _extensionTypes;
factory CompilerAnalysisDriver(AnalyzerOptions options,
{SummaryDataStore summaryData,
List<String> summaryPaths = const [],
Map<String, bool> experiments = const {}}) {
var resourceProvider = options.resourceProvider;
var contextBuilder = options.createContextBuilder();
var analysisOptions = contextBuilder
.getAnalysisOptions(options.analysisRoot) as AnalysisOptionsImpl;
analysisOptions.enabledExperiments =
experiments.entries.where((e) => e.value).map((e) => e.key).toList();
var dartSdk = contextBuilder.findSdk(null, analysisOptions);
// Read the summaries.
summaryData ??=
SummaryDataStore(summaryPaths, resourceProvider: resourceProvider);
return CompilerAnalysisDriver._(dartSdk, summaryPaths, summaryData,
analysisOptions, resourceProvider, options.dartSdkSummaryPath);
}
CompilerAnalysisDriver._(this.dartSdk, this._summaryPaths, this.summaryData,
this.analysisOptions, this._resourceProvider, this._dartSdkSummaryPath) {
var bundle = dartSdk.getLinkedBundle();
if (bundle != null) summaryData.addBundle(null, bundle);
}
/// Information about native extension types.
///
/// This will be `null` until [linkLibraries] has been called (because we
/// could be compiling the Dart SDK, so it would not be available yet).
ExtensionTypeSet get extensionTypes => _extensionTypes;
/// Whether this driver can be reused for the given [dartSdkSummaryPath] and
/// [summaryPaths].
bool isCompatibleWith(AnalyzerOptions options, List<String> summaryPaths) {
return _dartSdkSummaryPath == options.dartSdkSummaryPath &&
_summaryPaths.toSet().containsAll(summaryPaths);
}
/// Parses [explicitSources] and any imports/exports/parts (that are not
/// included in [summaryData]), and links the results so
/// [LinkedAnalysisDriver.analyzeLibrary] can be called.
///
/// The analyzer [options] are used to configure URI resolution (Analyzer's
/// [SourceFactory]) and declared variables, if any (`-Dfoo=bar`).
LinkedAnalysisDriver linkLibraries(
List<Uri> explicitSources, AnalyzerOptions options) {
/// The URI resolution logic for this build unit.
var sourceFactory = createSourceFactory(options,
sdkResolver: DartUriResolver(dartSdk), summaryData: summaryData);
var declaredVariables = DeclaredVariables.fromMap(
Map.of(options.declaredVariables)..addAll(sdkLibraryVariables));
/// A fresh file system state for this list of [explicitSources].
var fsState = _createFileSystemState(declaredVariables, sourceFactory);
var resynthesizerBuilder = DevCompilerResynthesizerBuilder(
fsState: fsState,
analysisOptions: analysisOptions,
declaredVariables: declaredVariables,
sourceFactory: sourceFactory,
summaryData: summaryData,
explicitSources: explicitSources,
);
resynthesizerBuilder.build();
_extensionTypes ??= ExtensionTypeSet(
resynthesizerBuilder.context.typeProvider,
resynthesizerBuilder.elementFactory,
);
return LinkedAnalysisDriver(
analysisOptions,
resynthesizerBuilder.elementFactory,
sourceFactory,
resynthesizerBuilder.libraryUris,
declaredVariables,
resynthesizerBuilder.summaryBytes,
fsState,
_resourceProvider,
);
}
FileSystemState _createFileSystemState(
DeclaredVariables declaredVariables, SourceFactory sourceFactory) {
var unlinkedSalt =
Uint32List(1 + AnalysisOptionsImpl.unlinkedSignatureLength);
unlinkedSalt[0] = AnalysisDriver.DATA_VERSION;
unlinkedSalt.setAll(1, analysisOptions.unlinkedSignature);
var linkedSalt = Uint32List(1 + AnalysisOptions.signatureLength);
linkedSalt[0] = AnalysisDriver.DATA_VERSION;
linkedSalt.setAll(1, analysisOptions.signature);
return FileSystemState(
PerformanceLog(StringBuffer()),
MemoryByteStore(),
FileContentOverlay(),
_resourceProvider,
'contextName',
sourceFactory,
analysisOptions,
declaredVariables,
unlinkedSalt,
linkedSalt,
externalSummaries: summaryData);
}
}
/// The analysis driver used after linking all input summaries and explicit
/// sources, produced by [CompilerAnalysisDriver.linkLibraries].
class LinkedAnalysisDriver {
final AnalysisOptions analysisOptions;
final LinkedElementFactory elementFactory;
final SourceFactory sourceFactory;
final List<String> libraryUris;
final DeclaredVariables declaredVariables;
/// The summary bytes for this linked build unit.
final List<int> summaryBytes;
final FileSystemState _fsState;
final ResourceProvider _resourceProvider;
LinkedAnalysisDriver(
this.analysisOptions,
this.elementFactory,
this.sourceFactory,
this.libraryUris,
this.declaredVariables,
this.summaryBytes,
this._fsState,
this._resourceProvider);
TypeProvider get typeProvider {
return elementFactory.analysisContext.typeProvider;
}
/// Analyzes the library at [uri] and returns the results of analysis for all
/// file(s) in that library.
Map<FileState, UnitAnalysisResult> analyzeLibrary(String libraryUri) {
if (!_isLibraryUri(libraryUri)) {
throw ArgumentError('"$libraryUri" is not a library');
}
var analysisContext = elementFactory.analysisContext;
var libraryFile = _fsState.getFileForUri(Uri.parse(libraryUri));
var analyzer = LibraryAnalyzer(
analysisOptions as AnalysisOptionsImpl,
declaredVariables,
sourceFactory,
(uri) => _isLibraryUri('$uri'),
analysisContext,
elementFactory,
InheritanceManager3(analysisContext.typeSystem),
libraryFile,
_resourceProvider);
// TODO(jmesserly): ideally we'd use the existing public `analyze()` method,
// but it's async. We can't use `async` here because it would break our
// developer tools extension (see web/web_command.dart). We should be able
// to fix it, but it requires significant changes to code outside of this
// repository.
return analyzer.analyzeSync();
}
ClassElement getClass(String uri, String name) {
return getLibrary(uri).getType(name);
}
LibraryElement getLibrary(String uri) {
return elementFactory.libraryOfUri(uri);
}
/// True if [uri] refers to a Dart library (i.e. a Dart source file exists
/// with this uri, and it is not a part file).
bool _isLibraryUri(String uri) {
return elementFactory.isLibraryUri(uri);
}
}

View file

@ -1,428 +0,0 @@
// Copyright (c) 2016, 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:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart'
show DartType, InterfaceType, FunctionType;
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/constant.dart'
show DartObject, DartObjectImpl;
import 'package:analyzer/src/generated/constant.dart';
import 'package:analyzer/src/generated/type_system.dart' show TypeSystemImpl;
import 'type_utilities.dart';
class Tuple2<T0, T1> {
final T0 e0;
final T1 e1;
Tuple2(this.e0, this.e1);
}
/// Instantiates [t] with dynamic bounds.
///
/// Note: this should only be used when the resulting type is later replaced,
/// such as a "deferred supertype" (used to break circularity between the
/// supertype's type arguments and the class definition). Otherwise
/// [instantiateElementTypeToBounds] should be used instead.
InterfaceType fillDynamicTypeArgsForClass(InterfaceType t) {
if (t.typeArguments.isNotEmpty) {
var dyn =
List.filled(t.element.typeParameters.length, DynamicTypeImpl.instance);
return t.element.instantiate(
typeArguments: dyn,
nullabilitySuffix: NullabilitySuffix.star,
);
}
return t;
}
/// Instantiates the [element] type to bounds.
///
/// Conceptually this is similar to [Dart2TypeSystem.instantiateToBounds] on
/// `element.type`, but unfortunately typedef elements do not return a
/// meaningful type, so we need to work around that.
DartType instantiateElementTypeToBounds(
TypeSystemImpl rules, TypeDefiningElement element) {
if (element is TypeParameterizedElement) {
if (element is ClassElement) {
var typeArguments = rules.instantiateTypeFormalsToBounds2(element);
return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: NullabilitySuffix.star,
);
} else if (element is GenericTypeAliasElement) {
var typeArguments = rules.instantiateTypeFormalsToBounds2(element);
return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: NullabilitySuffix.star,
);
} else {
throw StateError('${element.runtimeType}');
}
}
return getLegacyElementType(element);
}
/// Given an [element] and a [test] function, returns the first matching
/// constant valued metadata annotation on the element.
///
/// If the element is a synthetic getter/setter (Analyzer creates these for
/// fields), then this will use the corresponding real element, which will have
/// the metadata annotations.
///
/// For example if we had the [ClassDeclaration] node for `FontElement`:
///
/// @js.JS('HTMLFontElement')
/// @deprecated
/// class FontElement { ... }
///
/// We could match `@deprecated` with a test function like:
///
/// (v) => v.type.name == 'Deprecated' && v.type.element.library.isDartCore
///
DartObject findAnnotation(Element element, bool test(DartObjectImpl value)) {
if (element == null) return null;
var accessor = element;
if (accessor is PropertyAccessorElement && accessor.isSynthetic) {
// Look for metadata on the real element, not the synthetic one.
element = accessor.variable;
}
for (var metadata in element.metadata) {
var value = metadata.computeConstantValue();
if (value is DartObjectImpl && test(value)) return value;
}
return null;
}
/// Searches all supertype, in order of most derived members, to see if any
/// [match] a condition. If so, returns the first match, otherwise returns null.
InterfaceType findSupertype(InterfaceType type, bool match(InterfaceType t)) {
for (var m in type.mixins.reversed) {
if (match(m)) return m;
}
var s = type.superclass;
if (s == null) return null;
if (match(s)) return type;
return findSupertype(s, match);
}
//TODO(leafp): Is this really necessary? In theory I think
// the static type should always be filled in for resolved
// ASTs. This may be a vestigial workaround.
DartType getStaticType(Expression e) =>
e.staticType ?? DynamicTypeImpl.instance;
// TODO(leafp) Factor this out or use an existing library
/// Similar to [SimpleIdentifier] inGetterContext, inSetterContext, and
/// inDeclarationContext, this method returns true if [node] is used in an
/// invocation context such as a MethodInvocation.
bool inInvocationContext(Expression node) {
if (node == null) return false;
var parent = node.parent;
while (parent is ParenthesizedExpression) {
node = parent as Expression;
parent = node.parent;
}
return parent is InvocationExpression && identical(node, parent.function) ||
parent is MethodInvocation &&
parent.methodName.name == 'call' &&
identical(node, parent.target);
}
bool isInlineJS(Element e) {
if (e != null && e.name == 'JS' && e is FunctionElement) {
var uri = e.librarySource.uri;
return uri.scheme == 'dart' && uri.path == '_foreign_helper';
}
return false;
}
bool isLibraryPrefix(Expression node) =>
node is SimpleIdentifier && node.staticElement is PrefixElement;
ExecutableElement getFunctionBodyElement(FunctionBody body) {
var f = body.parent;
if (f is FunctionExpression) {
return f.declaredElement;
} else if (f is MethodDeclaration) {
return f.declaredElement;
} else {
return (f as ConstructorDeclaration).declaredElement;
}
}
/// Returns the value of the `name` field from the [match]ing annotation on
/// [element], or `null` if we didn't find a valid match or it was not a string.
///
/// For example, consider this code:
///
/// class MyAnnotation {
/// final String name;
/// // ...
/// const MyAnnotation(this.name/*, ... other params ... */);
/// }
///
/// @MyAnnotation('FooBar')
/// main() { ... }
///
/// If we match the annotation for the `@MyAnnotation('FooBar')` this will
/// return the string 'FooBar'.
String getAnnotationName(Element element, bool match(DartObjectImpl value)) =>
findAnnotation(element, match)?.getField('name')?.toStringValue();
List<ClassElement> getSuperclasses(ClassElement cls) {
var result = <ClassElement>[];
var visited = HashSet<ClassElement>();
while (cls != null && visited.add(cls)) {
for (var mixinType in cls.mixins.reversed) {
var mixin = mixinType.element;
if (mixin != null) result.add(mixin);
}
var supertype = cls.supertype;
// Object or mixin declaration.
if (supertype == null) break;
cls = supertype.element;
result.add(cls);
}
return result;
}
List<ClassElement> getImmediateSuperclasses(ClassElement c) {
var result = <ClassElement>[];
for (var m in c.mixins.reversed) {
result.add(m.element);
}
var s = c.supertype;
if (s != null) result.add(s.element);
return result;
}
/// Returns true if the library [l] is dart:_runtime.
// TODO(jmesserly): unlike other methods in this file, this one wouldn't be
// suitable for upstream to Analyzer, as it's DDC specific.
bool isSdkInternalRuntime(LibraryElement l) {
var uri = l.source.uri;
return uri.scheme == 'dart' && uri.path == '_runtime';
}
/// Return `true` if the given [classElement] has a noSuchMethod() method
/// distinct from the one declared in class Object, as per the Dart Language
/// Specification (section 10.4).
// TODO(jmesserly): this was taken from error_verifier.dart
bool hasNoSuchMethod(ClassElement classElement) {
// TODO(jmesserly): this is slow in Analyzer. It's a linear scan through all
// methods, up through the class hierarchy.
var method = classElement.lookUpMethod(
FunctionElement.NO_SUCH_METHOD_METHOD_NAME, classElement.library);
var definingClass = method?.enclosingElement;
return definingClass is ClassElement && !definingClass.isDartCoreObject;
}
/// Returns true if this class is of the form:
/// `class C = Object with M [implements I1, I2 ...];`
///
/// A mixin alias class is a mixin application, that can also be itself used as
/// a mixin.
bool isMixinAliasClass(ClassElement c) {
return c.isMixinApplication && c.supertype.isObject && c.mixins.length == 1;
}
bool isCallableClass(ClassElement c) {
// See if we have a "call" with a statically known function type:
//
// - if it's a method, then it does because all methods do,
// - if it's a getter, check the return type.
//
// Other cases like a getter returning dynamic/Object/Function will be
// handled at runtime by the dynamic call mechanism. So we only
// concern ourselves with statically known function types.
//
// We can ignore `noSuchMethod` because:
// * `dynamic d; d();` without a declared `call` method is handled by dcall.
// * for `class C implements Callable { noSuchMethod(i) { ... } }` we find
// the `call` method on the `Callable` interface.
var callMethod =
getLegacyRawClassType(c).lookUpInheritedGetterOrMethod('call');
return callMethod is PropertyAccessorElement
? callMethod.returnType is FunctionType
: callMethod != null;
}
/// Returns true if [x] and [y] are equal, in other words, `x <: y` and `y <: x`
/// and they have equivalent display form when printed.
//
// TODO(jmesserly): this exists to work around broken FunctionTypeImpl.== in
// Analyzer. It has two bugs:
// - typeArguments are considered, even though this has no semantic effect.
// For example: `int -> int` that resulted from `(<T>(T) -> T)<int>` will not
// equal another `int -> int`, even though they are the same type.
// - named arguments are incorrectly treated as ordered, see
// https://github.com/dart-lang/sdk/issues/26126.
bool typesAreEqual(DartType x, DartType y) {
if (identical(x, y)) return true;
if (x is FunctionType) {
if (y is FunctionType) {
if (x.typeFormals.length != y.typeFormals.length) {
return false;
}
// `<T>T -> T` should be equal to `<U>U -> U`
// To test this, we instantiate both types with the same (unique) type
// variables, and see if the result is equal.
if (x.typeFormals.isNotEmpty) {
var fresh = FunctionTypeImpl.relateTypeFormals(
x, y, (t, s, _, __) => typesAreEqual(t, s));
if (fresh == null) {
return false;
}
return typesAreEqual(x.instantiate(fresh), y.instantiate(fresh));
}
return typesAreEqual(x.returnType, y.returnType) &&
_argumentsAreEqual(x.normalParameterTypes, y.normalParameterTypes) &&
_argumentsAreEqual(
x.optionalParameterTypes, y.optionalParameterTypes) &&
_namedArgumentsAreEqual(x.namedParameterTypes, y.namedParameterTypes);
} else {
return false;
}
}
if (x is InterfaceType) {
return y is InterfaceType &&
x.element == y.element &&
_argumentsAreEqual(x.typeArguments, y.typeArguments);
}
return x == y;
}
bool _argumentsAreEqual(List<DartType> first, List<DartType> second) {
if (first.length != second.length) return false;
for (int i = 0; i < first.length; i++) {
if (!typesAreEqual(first[i], second[i])) return false;
}
return true;
}
bool _namedArgumentsAreEqual(
Map<String, DartType> xArgs, Map<String, DartType> yArgs) {
if (yArgs.length != xArgs.length) return false;
for (var name in xArgs.keys) {
var x = xArgs[name];
var y = yArgs[name];
if (y == null || !typesAreEqual(x, y)) return false;
}
return true;
}
/// Returns a valid hashCode for [t] for use with [typesAreEqual].
int typeHashCode(DartType t) {
// TODO(jmesserly): this is from Analyzer; it's not a great hash function.
// We should at least fix how this combines hashes.
if (t is FunctionType) {
if (t.typeFormals.isNotEmpty) {
// Instantiate the generic function type, so we hash equivalent
// generic function types to the same value. For example, `<X>(X) -> X`
// and `<Y>(Y) -> Y` shouold have the same hash.
//
// TODO(jmesserly): it would be better to instantiate these with unique
// types for each position, so we can distinguish cases like these:
//
// <A, B>(A) -> B
// <C, D>(D) -> C // reversed
// <E, F>(E) -> void // uses `void`
//
// Currently all of those will have the same hash code.
//
// The choice of `void` is rather arbitrary. Of the types we can easily
// obtain from Analyzer, it should collide a bit less than something like
// `dynamic`.
int code = t.typeFormals.length;
code = (code << 1) +
typeHashCode(t.instantiate(
List.filled(t.typeFormals.length, VoidTypeImpl.instance)));
return code;
}
int code = typeHashCode(t.returnType);
for (var p in t.normalParameterTypes) {
code = (code << 1) + typeHashCode(p);
}
for (var p in t.optionalParameterTypes) {
code = (code << 1) + typeHashCode(p);
}
for (var p in t.namedParameterTypes.values) {
code ^= typeHashCode(p); // xor because named parameters are unordered.
}
return code;
}
return t.hashCode;
}
Uri uriForCompilationUnit(CompilationUnitElement unit) {
if (unit.source.isInSystemLibrary) {
return unit.source.uri;
}
// TODO(jmesserly): this needs serious cleanup.
// There does appear to be something strange going on with Analyzer
// URIs if we try and use them directly on Windows.
// See also compiler.dart placeSourceMap, which could use cleanup too.
var sourcePath = unit.source.fullName;
return sourcePath.startsWith('package:')
? Uri.parse(sourcePath)
// TODO(jmesserly): shouldn't this be path.toUri?
: Uri.file(sourcePath);
}
/// Returns true iff this factory constructor just throws [UnsupportedError]/
///
/// `dart:html` has many of these.
bool isUnsupportedFactoryConstructor(ConstructorDeclaration node) {
var ctorBody = node.body;
var element = node.declaredElement;
if (element.isPrivate &&
element.librarySource.isInSystemLibrary &&
ctorBody is BlockFunctionBody) {
var statements = ctorBody.block.statements;
if (statements.length == 1) {
var statement = statements[0];
if (statement is ExpressionStatement) {
var expr = statement.expression;
if (expr is ThrowExpression &&
expr.expression is InstanceCreationExpression) {
if (expr.expression.staticType.name == 'UnsupportedError') {
// HTML adds a lot of private constructors that are unreachable.
// Skip these.
return true;
}
}
}
}
}
return false;
}
bool isBuiltinAnnotation(
DartObjectImpl value, String libraryName, String annotationName) {
var e = value?.type?.element;
if (e?.name != annotationName) return false;
var uri = e.source.uri;
var path = uri.pathSegments[0];
return uri.scheme == 'dart' && path == libraryName;
}
/// Returns the integer value for [node] as a [BigInt].
///
/// `node.value` should not be used directly as it depends on platform integers
/// and may be `null` for some valid integer literals (in either an `int` or a
/// `double` context)
BigInt getLiteralBigIntValue(IntegerLiteral node) {
// TODO(jmesserly): workaround for #34360: Analyzer tree does not store
// the BigInt or double value, so we need to re-parse it from the token.
return BigInt.parse(node.literal.lexeme);
}

View file

@ -1,128 +0,0 @@
// Copyright (c) 2016, 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:collection';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/error_processor.dart' show ErrorProcessor;
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptions;
import 'package:path/path.dart' as p;
class ErrorCollector {
final bool _replCompile;
final AnalysisOptions _options;
SplayTreeMap<AnalysisError, String> _errors;
ErrorCollector(this._options, this._replCompile) {
_errors = SplayTreeMap<AnalysisError, String>(_compareErrors);
}
bool get hasFatalErrors => _errors.keys.any(_isFatalError);
Iterable<String> get formattedErrors => _errors.values;
void add(LineInfo lineInfo, AnalysisError error) {
if (_shouldIgnoreError(error)) return;
// Skip hints, some like TODOs are not useful.
if (_errorSeverity(error).ordinal <= ErrorSeverity.INFO.ordinal) return;
_errors[error] = _formatError(lineInfo, error);
}
void addAll(LineInfo lineInfo, Iterable<AnalysisError> errors) {
for (var e in errors) {
add(lineInfo, e);
}
}
ErrorSeverity _errorSeverity(AnalysisError error) {
var errorCode = error.errorCode;
if (errorCode == StrongModeCode.TOP_LEVEL_FUNCTION_LITERAL_BLOCK ||
errorCode == StrongModeCode.TOP_LEVEL_INSTANCE_GETTER ||
errorCode == StrongModeCode.TOP_LEVEL_INSTANCE_METHOD) {
// These are normally hints, but they should be errors when running DDC, so
// that users won't be surprised by behavioral differences between DDC and
// dart2js.
return ErrorSeverity.ERROR;
}
// TODO(jmesserly): remove support for customizing error levels via
// analysis_options from DDC. (it won't work with --kernel).
return ErrorProcessor.getProcessor(_options, error)?.severity ??
errorCode.errorSeverity;
}
String _formatError(LineInfo lineInfo, AnalysisError error) {
var location = lineInfo.getLocation(error.offset);
// [warning] 'foo' is not a... (/Users/.../tmp/foo.dart, line 1, col 2)
return (StringBuffer()
..write('[${_errorSeverity(error).displayName}] ')
..write(error.message)
..write(' (${p.prettyUri(error.source.uri)}')
..write(
', line ${location.lineNumber}, col ${location.columnNumber})'))
.toString();
}
bool _shouldIgnoreError(AnalysisError error) {
var uri = error.source.uri;
if (uri.scheme != 'dart') return false;
var sdkLib = uri.pathSegments[0];
if (sdkLib == 'html' || sdkLib == 'svg' || sdkLib == '_interceptors') {
var c = error.errorCode;
return c == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1 ||
c == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_2 ||
c == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS;
}
return false;
}
int _compareErrors(AnalysisError error1, AnalysisError error2) {
// severity
int compare = _errorSeverity(error2).compareTo(_errorSeverity(error1));
if (compare != 0) return compare;
// path
compare = Comparable.compare(error1.source.fullName.toLowerCase(),
error2.source.fullName.toLowerCase());
if (compare != 0) return compare;
// offset
compare = error1.offset - error2.offset;
if (compare != 0) return compare;
// compare message, in worst case.
return error1.message.compareTo(error2.message);
}
bool _isFatalError(AnalysisError e) {
if (_errorSeverity(e) != ErrorSeverity.ERROR) return false;
// These errors are not fatal in the REPL compile mode as we
// allow access to private members across library boundaries
// and those accesses will show up as undefined members unless
// additional analyzer changes are made to support them.
// TODO(jacobr): consider checking that the identifier name
// referenced by the error is private.
return !_replCompile ||
(e.errorCode != StaticTypeWarningCode.UNDEFINED_GETTER &&
e.errorCode != StaticTypeWarningCode.UNDEFINED_SETTER &&
e.errorCode != StaticTypeWarningCode.UNDEFINED_METHOD);
}
}
const invalidImportDartMirrors = StrongModeCode(
ErrorType.COMPILE_TIME_ERROR,
'IMPORT_DART_MIRRORS',
'Cannot import "dart:mirrors" in web applications (https://goo.gl/R1anEs).');
const invalidJSInteger = StrongModeCode(
ErrorType.COMPILE_TIME_ERROR,
'INVALID_JS_INTEGER',
"The integer literal '{0}' can't be represented exactly in JavaScript. "
"The nearest value that can be represented exactly is '{1}'.");

View file

@ -1,177 +0,0 @@
// Copyright (c) 2016, 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:collection';
import 'package:analyzer/dart/element/element.dart'
show ClassElement, CompilationUnitElement, Element, LibraryElement;
import 'package:analyzer/dart/element/type.dart' show DartType, InterfaceType;
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'element_helpers.dart' show getAnnotationName, isBuiltinAnnotation;
import 'type_utilities.dart';
/// Contains information about native JS types (those types provided by the
/// implementation) that are also provided by the Dart SDK.
///
/// For types provided by JavaScript, it is important that we don't add methods
/// directly to those types. Instead, we must call through a special set of
/// JS Symbol names, that are used for the "Dart extensions". For example:
///
/// // Dart
/// Iterable iter = myList;
/// print(iter.first);
///
/// // JS
/// let iter = myLib.myList;
/// core.print(iter[dartx.first]);
///
/// This will provide the [Iterable.first] property, without needing to add
/// `first` to the `Array.prototype`.
class ExtensionTypeSet {
final LinkedElementFactory _elementFactory;
// Abstract types that may be implemented by both native and non-native
// classes.
final _extensibleTypes = HashSet<ClassElement>();
// Concrete native types.
final _nativeTypes = HashSet<ClassElement>();
final _pendingLibraries = HashSet<String>();
ExtensionTypeSet(TypeProvider types, this._elementFactory) {
// TODO(vsm): Eventually, we want to make this extensible - i.e., find
// annotations in user code as well. It would need to be summarized in
// the element model - not searched this way on every compile. To make this
// a little more efficient now, we do this in two phases.
// First, core types:
// TODO(vsm): If we're analyzing against the main SDK, those
// types are not explicitly annotated.
_extensibleTypes.add(types.objectType.element);
_addExtensionType(types.intType, true);
_addExtensionType(types.doubleType, true);
_addExtensionType(types.boolType, true);
_addExtensionType(types.stringType, true);
_addExtensionTypes('dart:_interceptors');
_addExtensionTypes('dart:_native_typed_data');
// These are used natively by dart:html but also not annotated.
_addExtensionTypesForLibrary('dart:core', ['Comparable', 'Map']);
_addExtensionTypesForLibrary('dart:collection', ['ListMixin', 'MapMixin']);
_addExtensionTypesForLibrary('dart:math', ['Rectangle']);
// Second, html types - these are only searched if we use dart:html, etc.:
_addPendingExtensionTypes('dart:html');
_addPendingExtensionTypes('dart:indexed_db');
_addPendingExtensionTypes('dart:svg');
_addPendingExtensionTypes('dart:web_audio');
_addPendingExtensionTypes('dart:web_gl');
_addPendingExtensionTypes('dart:web_sql');
}
/// Gets the JS peer for this Dart type if any, otherwise null.
///
/// For example for dart:_interceptors `JSArray` this will return "Array",
/// referring to the JavaScript built-in `Array` type.
List<String> getNativePeers(ClassElement classElem) {
if (classElem.isDartCoreObject) return ['Object'];
var names = getAnnotationName(
classElem,
(a) =>
isBuiltinAnnotation(a, '_js_helper', 'JsPeerInterface') ||
isBuiltinAnnotation(a, '_js_helper', 'Native'));
if (names == null) return [];
// Omit the special name "!nonleaf" and any future hacks starting with "!"
return names.split(',').where((peer) => !peer.startsWith("!")).toList();
}
bool hasNativeSubtype(DartType type) =>
isNativeInterface(type.element) || isNativeClass(type.element);
bool isNativeClass(Element element) => _setContains(_nativeTypes, element);
bool isNativeInterface(Element element) =>
_setContains(_extensibleTypes, element);
void _addExtensionType(InterfaceType t, [bool mustBeNative = false]) {
if (t.isObject) return;
var element = t.element;
if (_extensibleTypes.contains(element) || _nativeTypes.contains(element)) {
return;
}
bool isNative = mustBeNative || _isNative(element);
if (isNative) {
_nativeTypes.add(element);
} else {
_extensibleTypes.add(element);
}
element.interfaces.forEach(_addExtensionType);
element.mixins.forEach(_addExtensionType);
var supertype = element.supertype;
if (supertype != null) _addExtensionType(element.supertype);
}
void _addExtensionTypes(String libraryUri) {
var library = _getLibraryByUri(libraryUri);
_visitCompilationUnit(library.definingCompilationUnit);
library.parts.forEach(_visitCompilationUnit);
}
void _addExtensionTypesForLibrary(String libraryUri, List<String> typeNames) {
var library = _getLibraryByUri(libraryUri);
for (var typeName in typeNames) {
_addExtensionType(getLegacyRawClassType(library.getType(typeName)));
}
}
void _addPendingExtensionTypes(String libraryUri) {
_pendingLibraries.add(libraryUri);
}
LibraryElement _getLibraryByUri(String uriStr) {
return _elementFactory.libraryOfUri(uriStr);
}
bool _isNative(ClassElement element) {
for (var metadata in element.metadata) {
var e = metadata.element?.enclosingElement;
if (e.name == 'Native' || e.name == 'JsPeerInterface') {
if (e.source.isInSystemLibrary) return true;
}
}
return false;
}
bool _processPending(Element element) {
if (_pendingLibraries.isEmpty) return false;
if (element is ClassElement) {
var uri = element.library.source.uri.toString();
if (_pendingLibraries.contains(uri)) {
// Load all pending libraries
_pendingLibraries.forEach(_addExtensionTypes);
_pendingLibraries.clear();
return true;
}
}
return false;
}
bool _setContains(HashSet<ClassElement> set, Element element) {
return set.contains(element) ||
_processPending(element) && set.contains(element);
}
void _visitClass(ClassElement element) {
if (_isNative(element)) {
_addExtensionType(getLegacyRawClassType(element), true);
}
}
void _visitCompilationUnit(CompilationUnitElement unit) {
unit.types.forEach(_visitClass);
}
}

View file

@ -1,79 +0,0 @@
// Copyright (c) 2015, 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 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/generated/constant.dart';
import 'package:analyzer/dart/element/element.dart';
import 'element_helpers.dart';
bool _isJsLibType(String expectedName, Element e) =>
e?.name == expectedName && _isJsLib(e.library);
/// Returns true if [e] represents any library from `package:js` or is the
/// internal `dart:_js_helper` library.
bool _isJsLib(LibraryElement e) {
if (e == null) return false;
var uri = e.source.uri;
if (uri.scheme == 'package' && uri.path.startsWith('js/')) return true;
if (uri.scheme == 'dart') {
// TODO(jmesserly): this needs cleanup: many of the annotations don't exist
// in these libraries.
return uri.path == '_js_helper' || uri.path == '_foreign_helper';
}
return false;
}
/// Whether [value] is a `@rest` annotation (to be used on function parameters
/// to have them compiled as `...` rest params in ES6 outputs).
bool isJsRestAnnotation(DartObjectImpl value) =>
_isJsLibType('_Rest', value.type.element);
/// Whether [i] is a `spread` invocation (to be used on function arguments
/// to have them compiled as `...` spread args in ES6 outputs).
bool isJsSpreadInvocation(MethodInvocation i) =>
_isJsLibType('spread', i.methodName?.staticElement);
// TODO(jmesserly): Move JsPeerInterface to package:js (see issue #135).
// TODO(jacobr): The 'JS' annotation is the new, publically accessible one.
// The 'JsName' annotation is the old one using internally by dart2js and
// html libraries. These two concepts will probably merge eventually.
bool isJSAnnotation(DartObjectImpl value) =>
_isJsLibType('JS', value.type.element) || isJSName(value);
/// Returns [true] if [e] is the `JS` annotation from `package:js`.
bool isPublicJSAnnotation(DartObjectImpl value) =>
_isJsLibType('JS', value.type.element);
bool isJSAnonymousAnnotation(DartObjectImpl value) =>
_isJsLibType('_Anonymous', value.type.element);
/// Whether [value] is a `@JSExportName` (internal annotation used in SDK
/// instead of `@JS` from `package:js`).
bool isJSExportNameAnnotation(DartObjectImpl value) =>
isBuiltinAnnotation(value, '_foreign_helper', 'JSExportName');
/// Whether [value] is a `@JSName` (internal annotation used in dart:html for
/// renaming members).
bool isJSName(DartObjectImpl value) =>
isBuiltinAnnotation(value, '_js_helper', 'JSName');
bool isNotNullAnnotation(DartObjectImpl value) =>
isBuiltinAnnotation(value, '_js_helper', '_NotNull');
bool isNullCheckAnnotation(DartObjectImpl value) =>
isBuiltinAnnotation(value, '_js_helper', '_NullCheck');
bool isUndefinedAnnotation(DartObjectImpl value) =>
isBuiltinAnnotation(value, '_js_helper', '_Undefined');
/// Returns the name value of the `JSExportName` annotation (when compiling
/// the SDK), or `null` if there's none. This is used to control the name
/// under which functions are compiled and exported.
String getJSExportName(Element e) {
if (!e.source.isInSystemLibrary) return null;
e = e is PropertyAccessorElement && e.isSynthetic ? e.variable : e;
return getAnnotationName(e, isJSExportNameAnnotation);
}

View file

@ -1,64 +0,0 @@
// Copyright (c) 2017, 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 'package:analyzer/dart/element/element.dart' show ClassElement;
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/type_system.dart' show TypeSystemImpl;
import '../compiler/js_typerep.dart';
import 'driver.dart';
import 'type_utilities.dart';
class JSTypeRep extends SharedJSTypeRep<DartType> {
final TypeSystemImpl rules;
final TypeProvider types;
final ClassElement _jsBool;
final ClassElement _jsNumber;
final ClassElement _jsString;
JSTypeRep(this.rules, LinkedAnalysisDriver driver)
: types = driver.typeProvider,
_jsBool = driver.getClass('dart:_interceptors', 'JSBool'),
_jsString = driver.getClass('dart:_interceptors', 'JSString'),
_jsNumber = driver.getClass('dart:_interceptors', 'JSNumber');
@override
JSType typeFor(DartType type) {
while (type is TypeParameterType) {
type = (type as TypeParameterType).element.bound;
}
if (type == null) return JSType.jsUnknown;
if (type.isDartCoreNull) return JSType.jsNull;
// Note that this should be changed if Dart gets non-nullable types
if (type.isBottom) return JSType.jsNull;
if (rules.isSubtypeOf(type, types.numType)) return JSType.jsNumber;
if (rules.isSubtypeOf(type, types.boolType)) return JSType.jsBoolean;
if (rules.isSubtypeOf(type, types.stringType)) return JSType.jsString;
if (type.isDartAsyncFutureOr) {
var argument = (type as InterfaceType).typeArguments[0];
var argumentRep = typeFor(argument);
if (argumentRep is JSObject || argumentRep is JSNull) {
return JSType.jsObject;
}
return JSType.jsUnknown;
}
if (type.isDynamic || type.isObject || type.isVoid) return JSType.jsUnknown;
return JSType.jsObject;
}
/// Given a Dart type return the known implementation type, if any.
/// Given `bool`, `String`, or `num`/`int`/`double`,
/// returns the corresponding type in `dart:_interceptors`:
/// `JSBool`, `JSString`, and `JSNumber` respectively, otherwise null.
InterfaceType getImplementationType(DartType t) {
var rep = typeFor(t);
// Number, String, and Bool are final
if (rep == JSType.jsNumber) return getLegacyRawClassType(_jsNumber);
if (rep == JSType.jsBoolean) return getLegacyRawClassType(_jsBool);
if (rep == JSType.jsString) return getLegacyRawClassType(_jsString);
return null;
}
}

View file

@ -1,348 +0,0 @@
// Copyright (c) 2016, 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 json;
import 'dart:io' show File;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart'
show LibraryElement, UriReferencedElement;
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
import 'package:args/args.dart' show ArgParser, ArgResults;
import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart';
import '../compiler/js_names.dart' as js_ast;
import '../compiler/module_builder.dart'
show transformModuleFormat, ModuleFormat;
import '../compiler/shared_command.dart';
import '../compiler/shared_compiler.dart';
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
import 'code_generator.dart' show CodeGenerator;
import 'context.dart';
import 'driver.dart';
import 'error_helpers.dart';
/// Compiles a set of Dart files into a single JavaScript module.
///
/// For a single build unit, this will produce a [JSModuleFile].
///
/// A build unit is a collection of Dart sources that is sufficient to be
/// compiled together. This can be as small as a single Dart library file, but
/// if the library has parts, or if the library has cyclic dependencies on other
/// libraries, those must be included as well. A common build unit is the lib
/// directory of a Dart package.
///
/// This class exists to cache global state associated with a single in-memory
/// [AnalysisContext], such as information about extension types in the Dart
/// SDK. It can be used once to produce a single module, or reused to save
/// warm-up time. (Currently there is no warm up, but there may be in the
/// future.)
///
/// The SDK source code is assumed to be immutable for the life of this class.
///
/// For all other files, it is up to the analysis context to decide whether or
/// not any caching is performed. By default an analysis context will assume
/// sources are immutable for the life of the context, and cache information
/// about them.
JSModuleFile compileWithAnalyzer(
CompilerAnalysisDriver compilerDriver,
List<String> sourcePaths,
AnalyzerOptions analyzerOptions,
CompilerOptions options) {
var trees = <CompilationUnit>[];
var explicitSources = <Uri>[];
var compilingSdk = false;
for (var sourcePath in sourcePaths) {
var sourceUri = sourcePathToUri(sourcePath);
if (sourceUri.scheme == "dart") {
compilingSdk = true;
}
explicitSources.add(sourceUri);
}
var driver = compilerDriver.linkLibraries(explicitSources, analyzerOptions);
var errors = ErrorCollector(driver.analysisOptions, options.replCompile);
for (var libraryUri in driver.libraryUris) {
var analysisResults = driver.analyzeLibrary(libraryUri);
CompilationUnit definingUnit;
for (var result in analysisResults.values) {
if (result.file.uriStr == libraryUri) definingUnit = result.unit;
errors.addAll(result.unit.lineInfo, result.errors);
trees.add(result.unit);
}
var library = driver.getLibrary(libraryUri);
// TODO(jmesserly): remove "dart:mirrors" from DDC's SDK, and then remove
// this special case error message.
if (!compilingSdk && !options.emitMetadata) {
var node = _getDartMirrorsImport(library);
if (node != null) {
errors.add(
definingUnit.lineInfo,
AnalysisError(library.source, node.uriOffset, node.uriEnd,
invalidImportDartMirrors));
}
}
}
js_ast.Program jsProgram;
if (options.unsafeForceCompile || !errors.hasFatalErrors) {
var codeGenerator = CodeGenerator(
driver,
driver.typeProvider,
compilerDriver.summaryData,
options,
compilerDriver.extensionTypes,
errors);
try {
jsProgram = codeGenerator.compile(trees);
} catch (e) {
// If force compilation failed, suppress the exception and report the
// static errors instead. Otherwise, rethrow an internal compiler error.
if (!errors.hasFatalErrors) rethrow;
}
if (!options.unsafeForceCompile && errors.hasFatalErrors) {
jsProgram = null;
}
}
if (analyzerOptions.dependencyTracker != null) {
var file = File(analyzerOptions.dependencyTracker.outputPath);
file.writeAsStringSync(
(analyzerOptions.dependencyTracker.dependencies.toList()..sort())
.join('\n'));
}
var jsModule = JSModuleFile(
errors.formattedErrors.toList(), options, jsProgram, driver.summaryBytes);
return jsModule;
}
UriReferencedElement _getDartMirrorsImport(LibraryElement library) {
return library.imports.firstWhere(_isDartMirrorsImort, orElse: () => null) ??
library.exports.firstWhere(_isDartMirrorsImort, orElse: () => null);
}
bool _isDartMirrorsImort(UriReferencedElement import) {
return import.uri == 'dart:mirrors';
}
class CompilerOptions extends SharedCompilerOptions {
/// If [sourceMap] is emitted, this will emit a `sourceMappingUrl` comment
/// into the output JavaScript module.
final bool sourceMapComment;
/// The file extension for summaries.
final String summaryExtension;
/// Whether to force compilation of code with static errors.
final bool unsafeForceCompile;
/// If specified, the path to write the summary file.
/// Used when building the SDK.
final String summaryOutPath;
/// *deprecated* If specified, this is used to initialize the import paths for
/// [summaryModules].
final String moduleRoot;
/// *deprecated* If specified, `dartdevc` will synthesize library names that
/// are relative to this path for all libraries in the JS module.
String libraryRoot;
CompilerOptions(
{bool sourceMap = true,
this.sourceMapComment = true,
bool summarizeApi = true,
this.summaryExtension = 'sum',
this.unsafeForceCompile = false,
bool replCompile = false,
bool emitMetadata = false,
bool enableAsserts = true,
Map<String, String> bazelMapping = const {},
this.summaryOutPath,
Map<String, String> summaryModules = const {},
this.moduleRoot,
this.libraryRoot})
: super(
sourceMap: sourceMap,
summarizeApi: summarizeApi,
emitMetadata: emitMetadata,
enableAsserts: enableAsserts,
replCompile: replCompile,
bazelMapping: bazelMapping,
summaryModules: summaryModules);
CompilerOptions.fromArguments(ArgResults args)
: sourceMapComment = args['source-map-comment'] as bool,
summaryExtension = args['summary-extension'] as String,
unsafeForceCompile = args['unsafe-force-compile'] as bool,
summaryOutPath = args['summary-out'] as String,
moduleRoot = args['module-root'] as String,
libraryRoot = _getLibraryRoot(args),
super.fromArguments(args, args['module-root'] as String,
args['summary-extension'] as String);
static void addArguments(ArgParser parser, {bool hide = true}) {
SharedCompilerOptions.addArguments(parser, hide: hide);
parser
..addOption('summary-extension',
help: 'file extension for Dart summary files',
defaultsTo: 'sum',
hide: hide)
..addFlag('source-map-comment',
help: 'adds a sourceMappingURL comment to the end of the JS,\n'
'disable if using X-SourceMap header',
defaultsTo: true,
hide: hide)
..addFlag('unsafe-force-compile',
help: 'Compile code even if it has errors. ಠ_ಠ\n'
'This has undefined behavior!',
hide: hide)
..addOption('summary-out',
help: 'location to write the summary file', hide: hide)
..addOption('summary-deps-output',
help: 'Path to a file to dump summary dependency info to.',
hide: hide)
..addOption('module-root',
help: '(deprecated) used to determine the default module name and\n'
'summary import name if those are not provided.',
hide: hide);
}
static String _getLibraryRoot(ArgResults args) {
var root = args['library-root'] as String;
return root != null ? p.absolute(root) : p.current;
}
}
/// The output of Dart->JS compilation.
///
/// This contains the file contents of the JS module, as well as a list of
/// Dart libraries that are contained in this module.
class JSModuleFile {
/// The list of messages (errors and warnings)
final List<String> errors;
/// The AST that will be used to generate the [code] and [sourceMap] for this
/// module.
final js_ast.Program moduleTree;
/// The compiler options used to generate this module.
final CompilerOptions options;
/// The binary contents of the API summary file, including APIs from each of
/// the libraries in this module.
final List<int> summaryBytes;
JSModuleFile(this.errors, this.options, this.moduleTree, this.summaryBytes);
JSModuleFile.invalid(this.errors, this.options)
: moduleTree = null,
summaryBytes = null;
/// The name of this module.
String get name => options.moduleName;
/// True if this library was successfully compiled.
bool get isValid => moduleTree != null;
/// Gets the source code and source map for this JS module, given the
/// locations where the JS file and map file will be served from.
///
/// Relative URLs will be used to point from the .js file to the .map file
//
// TODO(jmesserly): this should match our old logic, but I'm not sure we are
// correctly handling the pointer from the .js file to the .map file.
JSModuleCode getCode(ModuleFormat format, String jsUrl, String mapUrl) {
var opts = js_ast.JavaScriptPrintingOptions(
allowKeywordsInProperties: true, allowSingleLineIfStatements: true);
js_ast.SimpleJavaScriptPrintingContext printer;
SourceMapBuilder sourceMap;
if (options.sourceMap) {
var sourceMapContext = SourceMapPrintingContext();
sourceMap = sourceMapContext.sourceMap;
printer = sourceMapContext;
} else {
printer = js_ast.SimpleJavaScriptPrintingContext();
}
var tree = transformModuleFormat(format, moduleTree);
tree.accept(
js_ast.Printer(opts, printer, localNamer: js_ast.TemporaryNamer(tree)));
Map builtMap;
if (options.sourceMap && sourceMap != null) {
builtMap = placeSourceMap(
sourceMap.build(jsUrl), mapUrl, options.bazelMapping, null);
if (options.sourceMapComment) {
var jsDir = p.dirname(p.fromUri(jsUrl));
var relative = p.relative(p.fromUri(mapUrl), from: jsDir);
var relativeMapUrl = p.toUri(relative).toString();
assert(p.dirname(jsUrl) == p.dirname(mapUrl));
printer.emit('\n//# sourceMappingURL=');
printer.emit(relativeMapUrl);
printer.emit('\n');
}
}
var text = printer.getText();
var rawSourceMap = options.inlineSourceMap
? js.escapedString(json.encode(builtMap), "'").value
: 'null';
text = text.replaceFirst(SharedCompiler.sourceMapLocationID, rawSourceMap);
return JSModuleCode(text, builtMap);
}
/// Similar to [getCode] but immediately writes the resulting files.
///
/// If [mapPath] is not supplied but [options.sourceMap] is set, mapPath
/// will default to [jsPath].map.
void writeCodeSync(ModuleFormat format, String jsPath) {
String mapPath = jsPath + '.map';
var code = getCode(
format, p.toUri(jsPath).toString(), p.toUri(mapPath).toString());
var file = File(jsPath);
if (!file.parent.existsSync()) file.parent.createSync(recursive: true);
file.writeAsStringSync(code.code);
// TODO(jacobr): it is a bit strange we are writing the source map to a file
// even when options.inlineSourceMap is true. To be consistent perhaps we
// should also write a copy of the source file without a sourcemap even when
// inlineSourceMap is true.
if (code.sourceMap != null) {
file = File(mapPath);
if (!file.parent.existsSync()) file.parent.createSync(recursive: true);
file.writeAsStringSync(json.encode(code.sourceMap));
}
}
}
/// The output of compiling a JavaScript module in a particular format.
class JSModuleCode {
/// The JavaScript code for this module.
///
/// If a [sourceMap] is available, this will include the `sourceMappingURL`
/// comment at end of the file.
final String code;
/// The JSON of the source map, if generated, otherwise `null`.
///
/// The source paths will initially be absolute paths. They can be adjusted
/// using [placeSourceMap].
final Map sourceMap;
JSModuleCode(this.code, this.sourceMap);
}

View file

@ -1,447 +0,0 @@
// Copyright (c) 2015, 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:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart' show TokenType;
import 'package:analyzer/dart/ast/visitor.dart' show RecursiveAstVisitor;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'element_helpers.dart' show getStaticType, isInlineJS, findAnnotation;
import 'js_interop.dart' show isNotNullAnnotation, isNullCheckAnnotation;
import 'js_typerep.dart';
import 'property_model.dart';
import 'type_utilities.dart';
/// An inference engine for nullable types.
///
/// This can answer questions about whether expressions are nullable
/// (see [isNullable]). Given a set of compilation units in a library, it will
/// determine if locals can be null using flow-insensitive analysis.
///
/// The analysis for null expressions is conservative and incomplete, but it can
/// optimize some patterns.
// TODO(vsm): Revisit whether we really need this when we get
// better non-nullability in the type system.
abstract class NullableTypeInference {
LibraryElement get coreLibrary;
VirtualFieldModel get virtualFields;
JSTypeRep get jsTypeRep;
bool isObjectMember(String name);
/// Known non-null local variables.
HashSet<LocalVariableElement> _notNullLocals;
void inferNullableTypes(AstNode node) {
var visitor = _NullableLocalInference(this);
node.accept(visitor);
_notNullLocals = visitor.computeNotNullLocals();
}
/// Adds a new variable, typically a compiler generated temporary, and record
/// whether its type is nullable.
void addTemporaryVariable(LocalVariableElement local,
{bool nullable = true}) {
if (!nullable) _notNullLocals.add(local);
}
/// Returns true if [expr] can be null.
bool isNullable(Expression expr) => _isNullable(expr);
bool _isNonNullMethodInvocation(MethodInvocation expr) {
// TODO(vsm): This logic overlaps with the resolver.
// Where is the best place to put this?
var e = expr.methodName.staticElement;
if (e == null) return false;
if (isInlineJS(e)) {
// Fix types for JS builtin calls.
//
// This code was taken from analyzer. It's not super sophisticated:
// only looks for the type name in dart:core, so we just copy it here.
//
// TODO(jmesserly): we'll likely need something that can handle a wider
// variety of types, especially when we get to JS interop.
var args = expr.argumentList.arguments;
var first = args.isNotEmpty ? args.first : null;
if (first is SimpleStringLiteral) {
var types = first.stringValue;
if (types != '' &&
types != 'var' &&
!types.split('|').contains('Null')) {
return true;
}
}
}
if (e.name == 'identical' && identical(e.library, coreLibrary)) {
return true;
}
// If this is a method call, check to see whether it is to a final
// type for which we have a known implementation type (i.e. int, bool,
// double, and String), and if so use the element for the implementation
// type instead.
if (e is MethodElement) {
Element container = e.enclosingElement;
if (container is ClassElement) {
DartType targetType = getLegacyRawClassType(container);
InterfaceType implType = jsTypeRep.getImplementationType(targetType);
if (implType != null) {
MethodElement method = implType.lookUpMethod(e.name, coreLibrary);
if (method != null) e = method;
}
}
}
// If the method or function is annotated as returning a non-null value
// then the result of the call is non-null.
return (e is MethodElement || e is FunctionElement) && _assertedNotNull(e);
}
bool _isNonNullProperty(Element element, String name) {
if (element is! PropertyInducingElement &&
element is! PropertyAccessorElement) {
return false;
}
// If this is a reference to an element of a type for which
// we have a known implementation type (i.e. int, double,
// bool, String), then use the element for the implementation
// type.
Element container = element.enclosingElement;
if (container is ClassElement) {
var targetType = getLegacyRawClassType(container);
var implType = jsTypeRep.getImplementationType(targetType);
if (implType != null) {
var getter = implType.lookUpGetter(name, coreLibrary);
if (getter != null) element = getter;
}
}
// If the getter is a synthetic element, then any annotations will
// be on the variable, so use those instead.
if (element is PropertyAccessorElement && element.isSynthetic) {
return _assertedNotNull(element.variable);
}
// Return true if the element is annotated as returning a non-null value.
return _assertedNotNull(element);
}
/// Returns true if [expr] can be null, optionally using [localIsNullable]
/// for locals.
///
/// If [localIsNullable] is not supplied, this will use the known list of
/// [_notNullLocals].
bool _isNullable(Expression expr,
[bool localIsNullable(LocalVariableElement e)]) {
// TODO(jmesserly): we do recursive calls in a few places. This could
// leads to O(depth) cost for calling this function. We could store the
// resulting value if that becomes an issue, so we maintain the invariant
// that each node is visited once.
Element element;
String name;
if (expr is PropertyAccess &&
expr.operator?.type != TokenType.QUESTION_PERIOD) {
element = expr.propertyName.staticElement;
name = expr.propertyName.name;
} else if (expr is PrefixedIdentifier) {
element = expr.staticElement;
name = expr.identifier.name;
} else if (expr is Identifier) {
element = expr.staticElement;
name = expr.name;
}
if (element != null) {
if (_isNonNullProperty(element, name)) return false;
// Type literals are not null.
if (element is ClassElement || element is FunctionTypeAliasElement) {
return false;
}
if (element is LocalVariableElement) {
if (localIsNullable != null) {
return localIsNullable(element);
}
return !_notNullLocals.contains(element);
}
if (element is ParameterElement && _assertedNotNull(element)) {
return false;
}
if (element is FunctionElement || element is MethodElement) {
// A function or method. This can't be null.
return false;
}
if (element is PropertyAccessorElement && element.isGetter) {
PropertyInducingElement variable = element.variable;
if (variable is FieldElement && virtualFields.isVirtual(variable)) {
return true;
}
var value = variable.computeConstantValue();
return value == null || value.isNull || !value.hasKnownValue;
}
// Other types of identifiers are nullable (parameters, fields).
return true;
}
if (expr is Literal) return expr is NullLiteral;
if (expr is IsExpression) return false;
if (expr is FunctionExpression) return false;
if (expr is ThisExpression) return false;
if (expr is SuperExpression) return false;
if (expr is CascadeExpression) {
// Cascades normally can't return `null`, because if the target is null,
// they will throw noSuchMethod.
// The only properties/methods on `null` are those on Object itself.
for (var section in expr.cascadeSections) {
Element e;
if (section is PropertyAccess) {
e = section.propertyName.staticElement;
} else if (section is MethodInvocation) {
e = section.methodName.staticElement;
} else if (section is IndexExpression) {
// Object does not have operator []=.
return false;
}
// We encountered a non-Object method/property.
if (e != null && !isObjectMember(e.name)) {
return false;
}
}
return _isNullable(expr.target, localIsNullable);
}
if (expr is ConditionalExpression) {
return _isNullable(expr.thenExpression, localIsNullable) ||
_isNullable(expr.elseExpression, localIsNullable);
}
if (expr is ParenthesizedExpression) {
return _isNullable(expr.expression, localIsNullable);
}
if (expr is InstanceCreationExpression) {
var e = expr.staticElement;
if (e == null) return true;
// Follow redirects.
while (e.redirectedConstructor != null) {
e = e.redirectedConstructor;
}
// Generative constructors are not nullable.
if (!e.isFactory) return false;
// Factory constructors are nullable. However it is a bad pattern and
// our own SDK will never do this.
// TODO(jmesserly): we could enforce this for user-defined constructors.
if (e.library.source.isInSystemLibrary) return false;
return true;
}
if (expr is MethodInvocation &&
(expr.operator?.lexeme != '?.' ||
!_isNullable(expr.target, localIsNullable)) &&
_isNonNullMethodInvocation(expr)) {
return false;
}
Expression operand, rightOperand;
String op;
if (expr is AssignmentExpression) {
op = expr.operator.lexeme;
assert(op.endsWith('='));
if (op == '=') {
return _isNullable(expr.rightHandSide, localIsNullable);
}
// op assignment like +=, remove trailing '='
op = op.substring(0, op.length - 1);
operand = expr.leftHandSide;
rightOperand = expr.rightHandSide;
} else if (expr is BinaryExpression) {
operand = expr.leftOperand;
rightOperand = expr.rightOperand;
op = expr.operator.lexeme;
} else if (expr is PrefixExpression) {
operand = expr.operand;
op = expr.operator.lexeme;
} else if (expr is PostfixExpression) {
operand = expr.operand;
op = expr.operator.lexeme;
}
switch (op) {
case '==':
case '!=':
case '&&':
case '||':
case '!':
return false;
case '??':
return _isNullable(operand, localIsNullable) &&
_isNullable(rightOperand, localIsNullable);
}
if (operand != null && jsTypeRep.isPrimitive(getStaticType(operand))) {
return false;
}
// TODO(ochafik,jmesserly): handle other cases such as: refs to top-level
// finals that have been assigned non-nullable values.
// Failed to recognize a non-nullable case: assume it can be null.
return true;
}
}
/// A visitor that determines which local variables are non-nullable.
///
/// This will consider all assignments to local variables using
/// flow-insensitive inference. That information is used to determine which
/// variables are nullable in the given scope.
// TODO(ochafik): Introduce flow analysis (a variable may be nullable in
// some places and not in others).
class _NullableLocalInference extends RecursiveAstVisitor {
final NullableTypeInference _nullInference;
/// Known local variables.
final _locals = HashSet<LocalVariableElement>.identity();
/// Variables that are known to be nullable.
final _nullableLocals = HashSet<LocalVariableElement>.identity();
/// Given a variable, tracks all other variables that it is assigned to.
final _assignments =
HashMap<LocalVariableElement, Set<LocalVariableElement>>.identity();
_NullableLocalInference(this._nullInference);
/// After visiting nodes, this can be called to compute the set of not-null
/// locals.
///
/// This method must only be called once. After it is called, the visitor
/// should be discarded.
HashSet<LocalVariableElement> computeNotNullLocals() {
// Given a set of variables that are nullable, remove them from our list of
// local variables. The end result of this process is a list of variables
// known to be not null.
visitNullableLocal(LocalVariableElement e) {
_locals.remove(e);
// Visit all other locals that this one is assigned to, and record that
// they are nullable too.
_assignments.remove(e)?.forEach(visitNullableLocal);
}
_nullableLocals.forEach(visitNullableLocal);
// Any remaining locals are non-null.
return _locals;
}
@override
visitVariableDeclaration(VariableDeclaration node) {
var element = node.declaredElement;
var initializer = node.initializer;
if (element is LocalVariableElement) {
_locals.add(element);
if (initializer != null) {
_visitAssignment(node.name, initializer);
} else if (!_assertedNotNull(element)) {
_nullableLocals.add(element);
}
}
super.visitVariableDeclaration(node);
}
@override
visitForStatement(ForStatement node) {
var forLoopParts = node.forLoopParts;
if (forLoopParts is ForEachParts) {
if (forLoopParts is ForEachPartsWithIdentifier &&
forLoopParts.identifier != null) {
var element = forLoopParts.identifier.staticElement;
if (element is LocalVariableElement && !_assertedNotNull(element)) {
_nullableLocals.add(element);
}
} else if (forLoopParts is ForEachPartsWithDeclaration) {
var declaration = forLoopParts.loopVariable;
var element = declaration.declaredElement;
_locals.add(element);
if (!_assertedNotNull(element)) {
_nullableLocals.add(element);
}
} else {
throw StateError('Unrecognized for loop parts');
}
}
super.visitForStatement(node);
}
@override
visitCatchClause(CatchClause node) {
var e = node.exceptionParameter?.staticElement;
if (e is LocalVariableElement) {
_locals.add(e);
// TODO(jmesserly): we allow throwing of `null`, for better or worse.
_nullableLocals.add(e);
}
e = node.stackTraceParameter?.staticElement;
if (e is LocalVariableElement) _locals.add(e);
super.visitCatchClause(node);
}
@override
visitAssignmentExpression(AssignmentExpression node) {
if (node.operator.lexeme == '=') {
_visitAssignment(node.leftHandSide, node.rightHandSide);
} else {
_visitAssignment(node.leftHandSide, node);
}
super.visitAssignmentExpression(node);
}
@override
visitPostfixExpression(PostfixExpression node) {
var op = node.operator.type;
if (op.isIncrementOperator) {
_visitAssignment(node.operand, node);
}
super.visitPostfixExpression(node);
}
@override
visitPrefixExpression(PrefixExpression node) {
var op = node.operator.type;
if (op.isIncrementOperator) {
_visitAssignment(node.operand, node);
}
super.visitPrefixExpression(node);
}
void _visitAssignment(Expression left, Expression right) {
if (left is SimpleIdentifier) {
var element = left.staticElement;
if (element is LocalVariableElement && !_assertedNotNull(element)) {
bool visitLocal(LocalVariableElement otherLocal) {
// Record the assignment.
_assignments
.putIfAbsent(otherLocal, () => HashSet.identity())
.add(element);
// Optimistically assume this local is not null.
// We will validate this assumption later.
return false;
}
if (_nullInference._isNullable(right, visitLocal)) {
_nullableLocals.add(element);
}
}
}
}
}
bool _assertedNotNull(Element e) =>
findAnnotation(e, isNotNullAnnotation) != null ||
findAnnotation(e, isNullCheckAnnotation) != null;

View file

@ -1,432 +0,0 @@
// Copyright (c) 2016, 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:collection' show HashMap, HashSet, Queue;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart' show InterfaceType;
import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl;
import '../compiler/js_names.dart' as js_ast;
import 'element_helpers.dart';
import 'extension_types.dart';
import 'type_utilities.dart';
/// Dart allows all fields to be overridden.
///
/// To prevent a performance/code size penalty for allowing this, we analyze
/// private classes within each library that is being compiled to determine
/// if those fields should be virtual or not. In effect, we devirtualize fields
/// when possible by analyzing the class hierarchy and using knowledge of
/// which members are private and thus, could not be overridden outside of the
/// current library.
class VirtualFieldModel {
final _modelForLibrary = HashMap<LibraryElement, _LibraryVirtualFieldModel>();
_LibraryVirtualFieldModel _getModel(LibraryElement library) =>
_modelForLibrary.putIfAbsent(
library, () => _LibraryVirtualFieldModel.build(library));
/// Returns true if a field is virtual.
bool isVirtual(FieldElement field) =>
_getModel(field.library).isVirtual(field);
}
/// This is a building block of [VirtualFieldModel], used to track information
/// about a single library that has been analyzed.
class _LibraryVirtualFieldModel {
/// Fields that are private (or public fields of a private class) and
/// overridden in this library.
///
/// This means we must generate them as virtual fields using a property pair
/// in JavaScript.
final _overriddenPrivateFields = HashSet<FieldElement>();
/// Private classes that can be extended outside of this library.
///
/// Normally private classes cannot be accessed outside this library, however,
/// this can happen if they are extended by a public class, for example:
///
/// class _A { int x = 42; }
/// class _B { int x = 42; }
///
/// // _A is now effectively public for the purpose of overrides.
/// class C extends _A {}
///
/// The class _A must treat is "x" as virtual, however _B does not.
final _extensiblePrivateClasses = HashSet<ClassElement>();
_LibraryVirtualFieldModel.build(LibraryElement library) {
var allClasses = Set<ClassElement>();
for (var libraryPart in library.units) {
allClasses.addAll(libraryPart.types);
allClasses.addAll(libraryPart.mixins);
}
// The set of public types is our initial extensible type set.
// From there, visit all immediate private types in this library, and so on
// from those private types, marking them as extensible.
var classesToVisit =
Queue<ClassElement>.from(allClasses.where((t) => t.isPublic));
while (classesToVisit.isNotEmpty) {
var extensibleClass = classesToVisit.removeFirst();
// For each supertype of a public type in this library,
// if we encounter a private class, we mark it as being extended, and
// add it to our work set if this is the first time we've visited it.
for (var superclass in getImmediateSuperclasses(extensibleClass)) {
if (!superclass.isPublic && superclass.library == library) {
if (_extensiblePrivateClasses.add(superclass)) {
classesToVisit.add(superclass);
}
}
}
}
// ClassElement can only look up inherited members with an O(N) scan through
// the class, so we build up a mapping of all fields in the library ahead of
// time.
Map<String, FieldElement> getInstanceFieldMap(ClassElement c) {
var instanceFields = c.fields.where((f) => !f.isStatic);
return HashMap.fromIterables(
instanceFields.map((f) => f.name), instanceFields);
}
var allFields =
HashMap.fromIterables(allClasses, allClasses.map(getInstanceFieldMap));
for (var class_ in allClasses) {
Set<ClassElement> superclasses;
// Visit accessors in the current class, and see if they override an
// otherwise private field.
for (var accessor in class_.accessors) {
// For getter/setter pairs only process them once.
if (accessor.correspondingGetter != null) continue;
// Ignore abstract or static accessors.
if (accessor.isAbstract || accessor.isStatic) continue;
// Ignore public accessors in extensible classes.
if (accessor.isPublic &&
(class_.isPublic || _extensiblePrivateClasses.contains(class_))) {
continue;
}
if (superclasses == null) {
superclasses = Set();
void collectSuperclasses(ClassElement cls) {
if (!superclasses.add(cls)) return;
var s = cls.supertype?.element;
if (s != null) collectSuperclasses(s);
cls.mixins.forEach((m) => collectSuperclasses(m.element));
}
collectSuperclasses(class_);
superclasses.remove(class_);
superclasses.removeWhere((c) => c.library != library);
}
// Look in all super classes to see if we're overriding a field in our
// library, if so mark that field as overridden.
var name = accessor.variable.name;
_overriddenPrivateFields.addAll(superclasses
.map((c) => allFields[c][name])
.where((f) => f != null));
}
}
}
/// Returns true if a field inside this library is virtual.
bool isVirtual(FieldElement field) {
if (field.isStatic) return false;
var type = field.enclosingElement;
var uri = type.source.uri;
if (uri.scheme == 'dart' && uri.path.startsWith('_')) {
// There should be no extensible fields in private SDK libraries.
return false;
}
if (field.isPublic) {
// Public fields in public classes (or extensible private classes)
// are always virtual.
// They could be overridden by someone using our library.
if (type.isPublic) return true;
if (_extensiblePrivateClasses.contains(type)) return true;
}
// Otherwise, the field is effectively private and we only need to make it
// virtual if it's overridden.
return _overriddenPrivateFields.contains(field);
}
}
/// Tracks how fields, getters and setters are represented when emitting JS.
///
/// Dart classes have implicit features that must be made explicit:
///
/// - virtual fields induce a getter and setter pair.
/// - getters and setters are independent.
/// - getters and setters can be overridden.
///
class ClassPropertyModel {
final ExtensionTypeSet extensionTypes;
/// Fields that are virtual, that is, they must be generated as a property
/// pair in JavaScript.
///
/// The value property stores the symbol used for the field's storage slot.
final virtualFields = <FieldElement, js_ast.TemporaryId>{};
/// The set of inherited getters, used because JS getters/setters are paired,
/// so if we're generating a setter we may need to emit a getter that calls
/// super.
final inheritedGetters = HashSet<String>();
/// The set of inherited setters, used because JS getters/setters are paired,
/// so if we're generating a getter we may need to emit a setter that calls
/// super.
final inheritedSetters = HashSet<String>();
final mockMembers = <String, ExecutableElement>{};
final extensionMethods = Set<String>();
final extensionAccessors = Set<String>();
/// Parameters that are covariant due to covariant generics.
final Set<Element> covariantParameters;
ClassPropertyModel.build(
this.extensionTypes,
VirtualFieldModel fieldModel,
ClassElement classElem,
this.covariantParameters,
Set<ExecutableElement> covariantPrivateMembers) {
// Visit superclasses to collect information about their fields/accessors.
// This is expensive so we try to collect everything in one pass.
for (var base in getSuperclasses(classElem)) {
for (var accessor in base.accessors) {
// For getter/setter pairs only process them once.
if (accessor.correspondingGetter != null) continue;
var field = accessor.variable;
// Ignore private names from other libraries.
if (field.isPrivate && accessor.library != classElem.library) {
continue;
}
if (field.getter?.isAbstract == false) inheritedGetters.add(field.name);
if (field.setter?.isAbstract == false) inheritedSetters.add(field.name);
}
}
_collectMockMembers(getLegacyRawClassType(classElem));
_collectExtensionMembers(classElem);
var virtualAccessorNames = HashSet<String>()
..addAll(inheritedGetters)
..addAll(inheritedSetters)
..addAll(extensionAccessors)
..addAll(mockMembers.values
.map((m) => m is PropertyAccessorElement ? m.variable.name : m.name));
// Visit accessors in the current class, and see if they need to be
// generated differently based on the inherited fields/accessors.
for (var accessor in classElem.accessors) {
// For getter/setter pairs only process them once.
if (accessor.correspondingGetter != null) continue;
// Also ignore abstract fields.
if (accessor.isAbstract || accessor.isStatic) continue;
var field = accessor.variable;
var name = field.name;
// Is it a field?
if (!field.isSynthetic && field is FieldElementImpl) {
var setter = field.setter;
if (virtualAccessorNames.contains(name) ||
fieldModel.isVirtual(field) ||
setter != null &&
covariantParameters != null &&
covariantParameters.contains(setter.parameters[0]) &&
covariantPrivateMembers.contains(setter)) {
virtualFields[field] = js_ast.TemporaryId(name);
}
}
}
}
void _collectMockMembers(InterfaceType type) {
// TODO(jmesserly): every type with nSM will generate new stubs for all
// abstract members. For example:
//
// class C { m(); noSuchMethod(...) { ... } }
// class D extends C { m(); noSuchMethod(...) { ... } }
//
// We'll generate D.m even though it is not necessary.
//
// Doing better is a bit tricky, as our current codegen strategy for the
// mock methods encodes information about the number of arguments (and type
// arguments) that D expects.
var element = type.element;
if (element.isMixin || !hasNoSuchMethod(element)) return;
// Collect all unimplemented members.
//
// Initially, we track abstract and concrete members separately, then
// remove concrete from the abstract set. This is done because abstract
// members are allowed to "override" concrete ones in Dart.
// (In that case, it will still be treated as a concrete member and can be
// called at runtime.)
var concreteMembers = HashSet<String>();
void visit(InterfaceType type, bool isAbstract) {
if (type == null) return;
visit(type.superclass, isAbstract);
for (var m in type.mixins) {
visit(m, isAbstract);
}
for (var i in type.interfaces) {
visit(i, true);
}
for (var m in [type.methods, type.accessors].expand((m) => m)) {
if (m.isStatic) continue;
if (isAbstract || m.isAbstract) {
mockMembers[m.name] = m;
} else {
concreteMembers.add(m.name);
}
}
}
visit(type, false);
concreteMembers.forEach(mockMembers.remove);
}
void _collectExtensionMembers(ClassElement element) {
if (extensionTypes.isNativeClass(element)) return;
// Find all generic interfaces that could be used to call into members of
// this class. This will help us identify which parameters need checks
// for soundness.
var allNatives = HashSet<String>();
_collectNativeMembers(getLegacyRawClassType(element), allNatives);
if (allNatives.isEmpty) return;
// For members on this class, check them against all generic interfaces.
var seenConcreteMembers = HashSet<String>();
_findExtensionMembers(
getLegacyRawClassType(element), seenConcreteMembers, allNatives);
// Add mock members. These are compiler-generated concrete members that
// forward to `noSuchMethod`.
for (var m in mockMembers.values) {
var name = m is PropertyAccessorElement ? m.variable.name : m.name;
if (seenConcreteMembers.add(name) && allNatives.contains(name)) {
var extMembers = m is PropertyAccessorElement
? extensionAccessors
: extensionMethods;
extMembers.add(name);
}
}
// For members of the superclass, we may need to add checks because this
// class adds a new unsafe interface. Collect those checks.
var visited = HashSet<ClassElement>()..add(element);
var existingMembers = HashSet<String>();
void visitImmediateSuper(InterfaceType type) {
// For members of mixins/supertypes, check them against new interfaces,
// and also record any existing checks they already had.
var oldCovariant = HashSet<String>();
_collectNativeMembers(type, oldCovariant);
var newCovariant = allNatives.difference(oldCovariant);
if (newCovariant.isEmpty) return;
existingMembers.addAll(oldCovariant);
void visitSuper(InterfaceType type) {
var element = type.element;
if (visited.add(element)) {
_findExtensionMembers(type, seenConcreteMembers, newCovariant);
element.mixins.reversed.forEach(visitSuper);
var s = element.supertype;
if (s != null) visitSuper(s);
}
}
visitSuper(type);
}
element.mixins.reversed.forEach(visitImmediateSuper);
var s = element.supertype;
if (s != null) visitImmediateSuper(s);
}
/// Searches all concrete instance members declared on this type, skipping
/// already [seenConcreteMembers], and adds them to [extensionMembers] if
/// needed.
///
/// By tracking the set of seen members, we can visit superclasses and mixins
/// and ultimately collect every most-derived member exposed by a given type.
void _findExtensionMembers(InterfaceType type,
HashSet<String> seenConcreteMembers, Set<String> allNatives) {
// We only visit each most derived concrete member.
// To avoid visiting an overridden superclass member, we skip members
// we've seen, and visit starting from the class, then mixins in
// reverse order, then superclasses.
for (var m in type.methods) {
var name = m.name;
if (!m.isStatic &&
!m.isAbstract &&
seenConcreteMembers.add(name) &&
allNatives.contains(name)) {
extensionMethods.add(name);
}
}
for (var m in type.accessors) {
var name = m.variable.name;
if (!m.isStatic &&
!m.isAbstract &&
seenConcreteMembers.add(name) &&
allNatives.contains(name)) {
extensionAccessors.add(name);
}
}
if (type.element.isEnum) {
extensionMethods.add('toString');
}
}
/// Collects all supertypes that may themselves contain native subtypes,
/// excluding [Object], for example `List` is implemented by several native
/// types.
void _collectNativeMembers(InterfaceType type, Set<String> members) {
var element = type.element;
if (extensionTypes.hasNativeSubtype(type)) {
for (var m in type.methods) {
if (m.isPublic && !m.isStatic) members.add(m.name);
}
for (var m in type.accessors) {
if (m.isPublic && !m.isStatic) members.add(m.variable.name);
}
}
for (var m in element.mixins.reversed) {
_collectNativeMembers(m, members);
}
for (var i in element.interfaces) {
_collectNativeMembers(i, members);
}
var supertype = element.supertype;
if (supertype != null) {
_collectNativeMembers(element.supertype, members);
}
if (element.isEnum) {
// TODO(jmesserly): analyzer does not create the synthetic element
// for the enum's `toString()` method, so we'll use the one on Object.
members.add('toString');
}
}
}

View file

@ -1,216 +0,0 @@
// Copyright (c) 2015, 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 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_ast_factory.dart';
import 'package:analyzer/dart/ast/visitor.dart' show GeneralizingAstVisitor;
import 'package:analyzer/dart/element/type.dart' show DartType;
import 'package:analyzer/src/dart/ast/ast.dart'
show
FunctionBodyImpl,
FunctionExpressionInvocationImpl,
MethodInvocationImpl;
import 'package:analyzer/src/dart/ast/utilities.dart'
show AstCloner, NodeReplacer;
import 'package:analyzer/src/generated/parser.dart' show ResolutionCopier;
import 'package:analyzer/src/task/strong/ast_properties.dart' as ast_properties;
import 'ast_builder.dart';
import 'element_helpers.dart' show isInlineJS;
// This class implements a pass which modifies (in place) the ast replacing
// abstract coercion nodes with their dart implementations.
class CoercionReifier extends GeneralizingAstVisitor<void> {
final cloner = _TreeCloner();
CoercionReifier._();
/// Transforms the given compilation units, and returns a new AST with
/// explicit coercion nodes in appropriate places.
static List<CompilationUnit> reify(List<CompilationUnit> units) {
var cr = CoercionReifier._();
return units.map(cr.visitCompilationUnit).toList(growable: false);
}
/// True if the `as` [node] is a required runtime check for soundness.
// TODO(sra): Find a better way to recognize reified coercion, since we
// can't set the isSynthetic attribute.
static bool isImplicit(AsExpression node) => node.asOperator.offset == 0;
/// Creates an implicit cast for expression [e] to [toType].
static Expression castExpression(Expression e, DartType toType) {
// We use an empty name in the AST, because the JS code generator only cares
// about the target type. It does not look at the AST name.
var typeName = astFactory.typeName(ast.identifierFromString(''), null);
typeName.type = toType;
var cast = ast.asExpression(e, typeName);
cast.staticType = toType;
return cast;
}
@override
CompilationUnit visitCompilationUnit(CompilationUnit node) {
if (ast_properties.hasImplicitCasts(node)) {
// Clone compilation unit, so we don't modify the originals.
node = _clone(node);
super.visitCompilationUnit(node);
}
return node;
}
@override
visitExpression(Expression node) {
node.visitChildren(this);
var castType = ast_properties.getImplicitCast(node);
if (castType != null) {
_replaceNode(node.parent, node, castExpression(node, castType));
}
}
@override
visitSpreadElement(SpreadElement node) {
// Skip visiting the expression so we can handle all casts during code
// generation.
node.expression.visitChildren(this);
}
@override
visitMethodInvocation(MethodInvocation node) {
if (isInlineJS(node.methodName.staticElement)) {
// Don't cast our inline-JS code in SDK.
ast_properties.setImplicitCast(node, null);
}
visitExpression(node);
}
@override
visitParenthesizedExpression(ParenthesizedExpression node) {
super.visitParenthesizedExpression(node);
node.staticType = node.expression.staticType;
}
@override
void visitForStatement(ForStatement node) {
var forLoopParts = node.forLoopParts;
if (forLoopParts is ForEachParts) {
// Visit other children.
forLoopParts.iterable.accept(this);
node.body.accept(this);
} else {
super.visitForStatement(node);
}
}
@override
void visitForElement(ForElement node) {
var forLoopParts = node.forLoopParts;
if (forLoopParts is ForEachParts) {
// Visit other children.
forLoopParts.iterable.accept(this);
node.body.accept(this);
} else {
super.visitForElement(node);
}
}
void _replaceNode(AstNode parent, AstNode oldNode, AstNode newNode) {
if (!identical(oldNode, newNode)) {
var replaced = parent.accept(NodeReplacer(oldNode, newNode));
// It looks like NodeReplacer will always return true.
// It does throw IllegalArgumentException though, if child is not found.
assert(replaced);
}
}
T _clone<T extends AstNode>(T node) {
var copy = node.accept(cloner) as T;
ResolutionCopier.copyResolutionData(node, copy);
return copy;
}
}
class _TreeCloner extends AstCloner {
void _cloneProperties(AstNode clone, AstNode node) {
if (clone is Expression && node is Expression) {
ast_properties.setImplicitCast(
clone, ast_properties.getImplicitCast(node));
ast_properties.setImplicitOperationCast(
clone, ast_properties.getImplicitOperationCast(node));
ast_properties.setImplicitSpreadCast(
clone, ast_properties.getImplicitSpreadCast(node));
ast_properties.setImplicitSpreadKeyCast(
clone, ast_properties.getImplicitSpreadKeyCast(node));
ast_properties.setImplicitSpreadValueCast(
clone, ast_properties.getImplicitSpreadValueCast(node));
ast_properties.setIsDynamicInvoke(
clone, ast_properties.isDynamicInvoke(node));
}
if (clone is Declaration && node is Declaration) {
ast_properties.setClassCovariantParameters(
clone, ast_properties.getClassCovariantParameters(node));
ast_properties.setSuperclassCovariantParameters(
clone, ast_properties.getSuperclassCovariantParameters(node));
}
}
@override
E cloneNode<E extends AstNode>(E node) {
var clone = super.cloneNode(node);
_cloneProperties(clone, node);
return clone;
}
@override
List<E> cloneNodeList<E extends AstNode>(List<E> list) {
var clone = super.cloneNodeList(list);
for (int i = 0, len = list.length; i < len; i++) {
_cloneProperties(clone[i], list[i]);
}
return clone;
}
// TODO(jmesserly): ResolutionCopier is not copying this yet.
@override
BlockFunctionBody visitBlockFunctionBody(BlockFunctionBody node) {
var clone = super.visitBlockFunctionBody(node);
(clone as FunctionBodyImpl).localVariableInfo =
(node as FunctionBodyImpl).localVariableInfo;
return clone;
}
@override
ExpressionFunctionBody visitExpressionFunctionBody(
ExpressionFunctionBody node) {
var clone = super.visitExpressionFunctionBody(node);
(clone as FunctionBodyImpl).localVariableInfo =
(node as FunctionBodyImpl).localVariableInfo;
return clone;
}
@override
FunctionExpressionInvocation visitFunctionExpressionInvocation(
FunctionExpressionInvocation node) {
var clone = super.visitFunctionExpressionInvocation(node);
(clone as FunctionExpressionInvocationImpl).typeArgumentTypes =
node.typeArgumentTypes;
return clone;
}
@override
MethodInvocation visitMethodInvocation(MethodInvocation node) {
var clone = super.visitMethodInvocation(node);
(clone as MethodInvocationImpl).typeArgumentTypes = node.typeArgumentTypes;
return clone;
}
// TODO(jmesserly): workaround for
// https://github.com/dart-lang/sdk/issues/26368
@override
TypeName visitTypeName(TypeName node) {
var clone = super.visitTypeName(node);
clone.type = node.type;
return clone;
}
}

View file

@ -1,170 +0,0 @@
// Copyright (c) 2015, 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 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/generated/constant.dart';
import 'package:analyzer/error/listener.dart'
show AnalysisErrorListener, ErrorReporter;
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
import 'package:analyzer/src/generated/source.dart' show Source;
import 'package:analyzer/src/dart/ast/ast.dart';
/// True is the expression can be evaluated multiple times without causing
/// code execution. This is true for final fields. This can be true for local
/// variables, if:
///
/// * they are not assigned within the [context] scope.
/// * they are not assigned in a function closure anywhere.
///
/// This method is used to avoid creating temporaries in cases where we know
/// we can safely re-evaluate [node] multiple times in [context]. This lets
/// us generate prettier code.
///
/// This method is conservative: it should never return `true` unless it is
/// certain the [node] is stateless, because generated code may rely on the
/// correctness of a `true` value. However it may return `false` for things
/// that are in fact, stateless.
bool isStateless(FunctionBody function, Expression node, [AstNode context]) {
// `this` and `super` cannot be reassigned.
if (node is ThisExpression || node is SuperExpression) return true;
if (node is Identifier) {
var e = node.staticElement;
if (e is PropertyAccessorElement) {
e = (e as PropertyAccessorElement).variable;
}
if (e is VariableElement && !e.isSynthetic) {
if (e.isFinal) return true;
if (e is LocalVariableElement || e is ParameterElement) {
// make sure the local isn't mutated in the context.
return !isPotentiallyMutated(function, e, context);
}
}
}
return false;
}
/// Returns true if the local variable is potentially mutated within [context].
/// This accounts for closures that may have been created outside of [context].
bool isPotentiallyMutated(FunctionBody function, VariableElement e,
[AstNode context]) {
if (function is FunctionBodyImpl && function.localVariableInfo == null) {
// TODO(jmesserly): this is a caching bug in Analyzer. They don't restore
// this info in some cases.
return true;
}
if (function.isPotentiallyMutatedInClosure(e)) return true;
if (function.isPotentiallyMutatedInScope(e)) {
// Need to visit the context looking for assignment to this local.
if (context != null) {
var visitor = _AssignmentFinder(e);
context.accept(visitor);
return visitor._potentiallyMutated;
}
return true;
}
return false;
}
/// Adapted from VariableResolverVisitor. Finds an assignment to a given
/// local variable.
class _AssignmentFinder extends RecursiveAstVisitor {
final VariableElement _variable;
bool _potentiallyMutated = false;
_AssignmentFinder(this._variable);
@override
visitSimpleIdentifier(SimpleIdentifier node) {
// Ignore if qualified.
AstNode parent = node.parent;
if (parent is PrefixedIdentifier && identical(parent.identifier, node)) {
return;
}
if (parent is PropertyAccess && identical(parent.propertyName, node)) {
return;
}
if (parent is MethodInvocation && identical(parent.methodName, node)) {
return;
}
if (parent is ConstructorName) return;
if (parent is Label) return;
if (node.inSetterContext() && node.staticElement == _variable) {
_potentiallyMutated = true;
}
}
}
class ConstFieldVisitor {
final ConstantVisitor constantVisitor;
ConstFieldVisitor(
TypeProvider typeProvider, DeclaredVariables declaredVariables,
{Source dummySource})
: constantVisitor = ConstantVisitor(
ConstantEvaluationEngine(typeProvider, declaredVariables),
ErrorReporter(AnalysisErrorListener.NULL_LISTENER, dummySource));
// TODO(jmesserly): this is used to determine if the field initialization is
// side effect free. We should make the check more general, as things like
// list/map literals/regexp are also side effect free and fairly common
// to use as field initializers.
bool isFieldInitConstant(VariableDeclaration field) =>
field.initializer == null || computeConstant(field) != null;
DartObject computeConstant(VariableDeclaration field) {
// If the constant is already computed by ConstantEvaluator, just return it.
VariableElement element = field.declaredElement;
var result = element.computeConstantValue();
if (result != null) return result;
// ConstantEvaluator will not compute constants for non-const fields,
// so run ConstantVisitor for those to figure out if the initializer is
// actually a constant (and therefore side effect free to evaluate).
assert(!field.isConst);
var initializer = field.initializer;
if (initializer == null) return null;
return initializer.accept(constantVisitor);
}
}
class LabelContinueFinder extends SimpleAstVisitor {
var found = false;
visit(Statement s) {
if (!found && s != null) s.accept(this);
}
@override
visitBlock(Block node) => node.statements.forEach(visit);
@override
visitWhileStatement(WhileStatement node) => visit(node.body);
@override
visitDoStatement(DoStatement node) => visit(node.body);
@override
visitForStatement(ForStatement node) => visit(node.body);
@override
visitLabeledStatement(LabeledStatement node) => visit(node.statement);
@override
visitContinueStatement(ContinueStatement node) => found = node.label != null;
@override
visitSwitchStatement(SwitchStatement node) {
node.members.forEach((m) => m.statements.forEach(visit));
}
@override
visitIfStatement(IfStatement node) {
visit(node.thenStatement);
visit(node.elseStatement);
}
@override
visitTryStatement(TryStatement node) {
node.body.accept(this);
node.finallyBlock.accept(this);
}
}

View file

@ -1,279 +0,0 @@
// Copyright (c) 2015, 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:collection';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart' show TypeParameterMember;
import '../analyzer/element_helpers.dart';
import '../compiler/js_names.dart' as js_ast;
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
/// Return the [InterfaceType] that itself has the legacy nullability, and for
/// every type parameter a [TypeParameterType] instance with the legacy
/// nullability is used as the corresponding type argument.
InterfaceType getLegacyRawClassType(ClassElement element) {
var typeParameters = element.typeParameters;
var typeArguments = typeParameters.map(getLegacyTypeParameterType).toList();
return element.instantiate(
typeArguments: typeArguments,
nullabilitySuffix: NullabilitySuffix.star,
);
}
/// Return the [TypeParameterType] with the legacy nullability for the given
/// type parameter [element].
TypeParameterType getLegacyTypeParameterType(TypeParameterElement element) {
return element.instantiate(nullabilitySuffix: NullabilitySuffix.star);
}
/// Return the raw type (i.e. the type where type parameters are replaced with
/// the corresponding [TypeParameterType]) for the given [element]. The type
/// returned, and every [TypeParameterType] instance will have the legacy
/// nullability suffix.
DartType getLegacyElementType(TypeDefiningElement element) {
if (element is ClassElement) {
return getLegacyRawClassType(element);
} else if (element is DynamicElementImpl) {
return element.type;
} else if (element is TypeParameterElement) {
return getLegacyTypeParameterType(element);
} else {
throw StateError('Unsupported element: (${element.runtimeType}) $element');
}
}
Set<TypeParameterElement> freeTypeParameters(DartType t) {
var result = Set<TypeParameterElement>();
void find(DartType t) {
if (t is TypeParameterType) {
result.add(t.element);
} else if (t is FunctionType) {
if (t.name != '' && t.name != null) {
// For a typedef type like `Foo<T>`, we only need to check if the
// type argument `T` has or is a free type parameter.
//
// For example, if `Foo` is a typedef type and `S` is a type parameter,
// then the type `Foo<List<S>>` has `S` as its free type parameter,
// regardless of what function is declared by the typedef.
//
// Also we need to find free type parameters whether or not they're
// actually used by the typedef's function type (for example,
// `typedef Foo<T> = int Function()` does not use `T`). So we must visit
// the type arguments, instead of the substituted parameter and return
// types.
t.typeArguments.forEach(find);
} else {
find(t.returnType);
t.parameters.forEach((p) => find(p.type));
t.typeFormals.forEach((p) => find(p.bound));
t.typeFormals.forEach(result.remove);
}
} else if (t is InterfaceType) {
t.typeArguments.forEach(find);
}
}
find(t);
return result;
}
/// _CacheTable tracks cache variables for variables that
/// are emitted in place with a hoisted variable for a cache.
class _CacheTable {
/// Mapping from types to their canonical names.
// Use a LinkedHashMap to maintain key insertion order so the generated code
// is stable under slight perturbation. (If this is not good enough we could
// sort by name to canonicalize order.)
final _names = LinkedHashMap<DartType, js_ast.TemporaryId>(
equals: typesAreEqual, hashCode: typeHashCode);
Iterable<DartType> get keys => _names.keys.toList();
js_ast.Statement _dischargeType(DartType type) {
var name = _names.remove(type);
if (name != null) {
return js.statement('let #;', [name]);
}
return null;
}
/// Emit a list of statements declaring the cache variables for
/// types tracked by this table. If [typeFilter] is given,
/// only emit the types listed in the filter.
List<js_ast.Statement> discharge([Iterable<DartType> typeFilter]) {
var decls = <js_ast.Statement>[];
var types = typeFilter ?? keys;
for (var t in types) {
var stmt = _dischargeType(t);
if (stmt != null) decls.add(stmt);
}
return decls;
}
bool isNamed(DartType type) => _names.containsKey(type);
String _safeTypeName(String name) {
if (name == "<bottom>") return "bottom";
return name;
}
String _typeString(DartType type, {bool flat = false}) {
if (type is ParameterizedType && type.name != null) {
var clazz = type.name;
var params = type.typeArguments;
if (params == null) return clazz;
if (params.every((p) => p.isDynamic)) return clazz;
var paramStrings = params.map(_typeString);
var paramString = paramStrings.join("\$");
return "${clazz}Of${paramString}";
}
if (type is FunctionType) {
if (flat) return "Fn";
var rType = _typeString(type.returnType, flat: true);
var paramStrings = type.normalParameterTypes
.take(3)
.map((p) => _typeString(p, flat: true));
var paramString = paramStrings.join("And");
var count = type.normalParameterTypes.length;
if (count > 3 ||
type.namedParameterTypes.isNotEmpty ||
type.optionalParameterTypes.isNotEmpty) {
paramString = "${paramString}__";
} else if (count == 0) {
paramString = "Void";
}
return "${paramString}To${rType}";
}
if (type is TypeParameterType) return type.name;
return _safeTypeName(type.name ?? "type");
}
/// Heuristically choose a good name for the cache and generator
/// variables.
js_ast.TemporaryId chooseTypeName(DartType type) {
return js_ast.TemporaryId(_typeString(type));
}
}
/// _GeneratorTable tracks types which have been
/// named and hoisted.
class _GeneratorTable extends _CacheTable {
final _defs = LinkedHashMap<DartType, js_ast.Expression>(
equals: typesAreEqual, hashCode: typeHashCode);
final js_ast.Identifier _runtimeModule;
_GeneratorTable(this._runtimeModule);
@override
js_ast.Statement _dischargeType(DartType t) {
var name = _names.remove(t);
if (name != null) {
js_ast.Expression init = _defs.remove(t);
assert(init != null);
return js.statement('let # = () => ((# = #.constFn(#))());',
[name, name, _runtimeModule, init]);
}
return null;
}
/// If [type] does not already have a generator name chosen for it,
/// assign it one, using [typeRep] as the initializer for it.
/// Emit the generator name.
js_ast.TemporaryId _nameType(DartType type, js_ast.Expression typeRep) {
var temp = _names[type];
if (temp == null) {
_names[type] = temp = chooseTypeName(type);
_defs[type] = typeRep;
}
return temp;
}
}
class TypeTable {
/// Generator variable names for hoisted types.
final _GeneratorTable _generators;
/// Mapping from type parameters to the types which must have their
/// cache/generator variables discharged at the binding site for the
/// type variable since the type definition depends on the type
/// parameter.
final _scopeDependencies = <TypeParameterElement, List<DartType>>{};
TypeTable(js_ast.Identifier runtime) : _generators = _GeneratorTable(runtime);
/// Emit a list of statements declaring the cache variables and generator
/// definitions tracked by the table. If [formals] is present, only
/// emit the definitions which depend on the formals.
List<js_ast.Statement> discharge([List<TypeParameterElement> formals]) {
var filter = formals?.expand((p) => _scopeDependencies[p] ?? <DartType>[]);
var stmts = [_generators].expand((c) => c.discharge(filter)).toList();
formals?.forEach(_scopeDependencies.remove);
return stmts;
}
/// Record the dependencies of the type on its free variables
bool recordScopeDependencies(DartType type) {
var freeVariables = freeTypeParameters(type);
// TODO(leafp): This is a hack to avoid trying to hoist out of
// generic functions and generic function types. This often degrades
// readability to little or no benefit. It would be good to do this
// when we know that we can hoist it to an outer scope, but for
// now we just disable it.
if (freeVariables.any((i) =>
i.enclosingElement is FunctionTypedElement ||
// Strict function types don't have element, so their type parameters
// don't have any enclosing element. Analyzer started returning
// strict function types for generic methods.
i.enclosingElement == null)) {
return true;
}
for (var free in freeVariables) {
// If `free` is a promoted type parameter, get the original one so we can
// find it in our map.
var key = free is TypeParameterMember ? free.baseElement : free;
_scopeDependencies.putIfAbsent(key, () => []).add(type);
}
return false;
}
/// Given a type [type], and a JS expression [typeRep] which implements it,
/// add the type and its representation to the table, returning an
/// expression which implements the type (but which caches the value).
js_ast.Expression nameType(
ParameterizedType type, js_ast.Expression typeRep) {
if (!_generators.isNamed(type) && recordScopeDependencies(type)) {
return typeRep;
}
var name = _generators._nameType(type, typeRep);
return js.call('#()', [name]);
}
/// Like [nameType] but for function types.
///
/// The boolean parameter [definite] distinguishes between definite function
/// types and other types (since the same DartType may have different
/// representations as definite and indefinite function types).
///
/// The boolean parameter [lazy] indicates that the resulting expression
/// should be a function that is invoked to compute the type, rather than the
/// type itself. This allows better integration with `lazyFn`, avoiding an
/// extra level of indirection.
js_ast.Expression nameFunctionType(
FunctionType type, js_ast.Expression typeRep,
{bool lazy = false}) {
if (!_generators.isNamed(type) && recordScopeDependencies(type)) {
return lazy ? js_ast.ArrowFun([], typeRep) : typeRep;
}
var name = _generators._nameType(type, typeRep);
return lazy ? name : js.call('#()', [name]);
}
}

View file

@ -9,10 +9,12 @@ import 'package:front_end/src/api_unstable/ddc.dart'
show InitializedCompilerState, parseExperimentalArguments;
import 'package:path/path.dart' as p;
import 'module_builder.dart';
import '../analyzer/driver.dart' show CompilerAnalysisDriver;
import '../kernel/command.dart' as kernel_compiler;
/// Shared code between Analyzer and Kernel CLI interfaces.
// TODO(nshahan) Merge all of this file the locations where they are used in
// the kernel (only) version of DDC.
/// Previously was shared code between Analyzer and Kernel CLI interfaces.
///
/// This file should only implement functionality that does not depend on
/// Analyzer/Kernel imports.
@ -45,7 +47,7 @@ Map<String, String> sdkLibraryVariables = {
'dart.library.web_sql': 'true',
};
/// Shared compiler options between `dartdevc` kernel and analyzer backends.
/// Compiler options for the `dartdevc` backend.
class SharedCompilerOptions {
/// Whether to emit the source mapping file.
///
@ -424,28 +426,15 @@ class CompilerResult {
/// Optionally provides the front_end state from the previous compilation,
/// which can be passed to [compile] to potentially speed up the next
/// compilation.
///
/// This field is unused when using the Analyzer-backend for DDC.
final InitializedCompilerState kernelState;
/// Optionally provides the analyzer state from the previous compilation,
/// which can be passed to [compile] to potentially speeed up the next
/// compilation.
///
/// This field is unused when using the Kernel-backend for DDC.
// TODO(38777) Cleanup when we delete all the DDC code.
final CompilerAnalysisDriver analyzerState;
/// The process exit code of the compiler.
final int exitCode;
CompilerResult(this.exitCode, {this.kernelState, this.analyzerState}) {
assert(kernelState == null || analyzerState == null,
'kernel and analyzer state should not both be supplied');
}
CompilerResult(this.exitCode, {this.kernelState});
/// Gets the kernel or analyzer compiler state, if any.
Object get compilerState => kernelState ?? analyzerState;
/// Gets the kernel compiler state, if any.
Object get compilerState => kernelState;
/// Whether the program compiled without any fatal errors (equivalent to
/// [exitCode] == 0).
@ -464,9 +453,6 @@ class CompilerResult {
///
/// [isBatch]/[isWorker] mode are preprocessed because they can combine
/// argument lists from the initial invocation and from batch/worker jobs.
///
/// [isKernel] is also preprocessed because the Kernel backend supports
/// different options compared to the Analyzer backend.
class ParsedArguments {
/// The user's arguments to the compiler for this compialtion.
final List<String> rest;
@ -485,8 +471,7 @@ class ParsedArguments {
/// Whether to use the Kernel-based back end for dartdevc.
///
/// This is similar to the Analyzer-based back end, but uses Kernel trees
/// instead of Analyzer trees for representing the Dart code.
/// This is always true now and will be removed in a future version.
final bool isKernel;
/// Whether to re-use the last compiler result when in a worker.
@ -539,8 +524,6 @@ class ParsedArguments {
isWorker = true;
} else if (arg == '--batch') {
isBatch = true;
} else if (arg == '--kernel' || arg == '-k') {
isKernel = true;
} else if (arg == '--reuse-compiler-result') {
reuseResult = true;
} else if (arg == '--use-incremental-compiler') {
@ -579,7 +562,7 @@ class ParsedArguments {
return ParsedArguments._(rest.toList()..addAll(newArgs.rest),
isWorker: isWorker,
isBatch: isBatch,
isKernel: isKernel || newArgs.isKernel,
isKernel: isKernel,
reuseResult: reuseResult || newArgs.reuseResult,
useIncrementalCompiler:
useIncrementalCompiler || newArgs.useIncrementalCompiler);

View file

@ -108,9 +108,12 @@ Future<CompilerResult> _compile(List<String> args,
..addOption('libraries-file',
help: 'The path to the libraries.json file for the sdk.')
..addOption('used-inputs-file',
help: 'If set, the file to record inputs used.', hide: true);
help: 'If set, the file to record inputs used.', hide: true)
..addFlag('kernel',
abbr: 'k',
help: 'Deprecated and ignored. To be removed in a future release.',
hide: true);
SharedCompilerOptions.addArguments(argParser);
var declaredVariables = parseAndRemoveDeclaredVariables(args);
ArgResults argResults;
try {

View file

@ -1,328 +0,0 @@
// Copyright (c) 2019, 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.
/// Test the modular compilation pipeline of ddc.
///
/// This is a shell that runs multiple tests, one per folder under `data/`.
import 'dart:io';
import 'package:modular_test/src/io_pipeline.dart';
import 'package:modular_test/src/pipeline.dart';
import 'package:modular_test/src/suite.dart';
import 'package:modular_test/src/runner.dart';
// TODO(vsm): Hack until we have either:
// (1) an NNBD version of the below
// (2) the ability to compile the primary version with NNBD
List<String> _nnbdOptOut = ['sdk'];
String _test_package = 'ddc_modular_test';
Uri sdkRoot = Platform.script.resolve("../../../");
Options _options;
String _dartdevcScript;
String _buildSdkScript;
String _patchSdkScript;
String _librariesJson;
String _librariesJsonNnbd;
main(List<String> args) async {
_options = Options.parse(args);
await _resolveScripts();
await runSuite(
sdkRoot.resolve('tests/modular/'),
'tests/modular',
_options,
IOPipeline([
DDCStep(),
RunD8(),
], cacheSharedModules: true));
// DDC only test suite.
await runSuite(
sdkRoot.resolve('tests/compiler/dartdevc/modular/'),
'tests/compiler/dartdevc/modular',
_options,
IOPipeline([
DDCStep(),
RunD8(),
], cacheSharedModules: true));
}
const sumId = DataId("sum");
const jsId = DataId("js");
const txtId = DataId("txt");
class DDCStep implements IOModularStep {
@override
List<DataId> get resultData => const [sumId, jsId];
@override
bool get needsSources => true;
@override
List<DataId> get dependencyDataNeeded => const [sumId];
@override
List<DataId> get moduleDataNeeded => const [];
@override
bool get onlyOnMain => false;
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
if (_options.verbose) print("\nstep: ddc on $module");
Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
await _createPackagesFile(module, root, transitiveDependencies);
ProcessResult result;
bool nnbd = flags.contains('non-nullable');
bool allowErrors = nnbd && _nnbdOptOut.contains(module.name);
if (module.isSdk) {
assert(transitiveDependencies.isEmpty);
// Apply patches.
result = await _runProcess(
Platform.resolvedExecutable,
[
_patchSdkScript,
'--target',
'dartdevc',
'--libraries',
if (nnbd) _librariesJsonNnbd else _librariesJson,
'--out',
'patched_sdk/',
if (nnbd) '--nnbd'
],
root.toFilePath());
_checkExitCode(result, this, module);
// Build the SDK.
result = await _runProcess(
Platform.resolvedExecutable,
[
_buildSdkScript,
'--dart-sdk',
'patched_sdk',
'--dart-sdk-summary=build',
'--summary-out',
'${toUri(module, sumId)}',
'--modules=es6',
if (allowErrors) '--unsafe-force-compile',
for (String flag in flags) '--enable-experiment=$flag',
'-o',
'${toUri(module, jsId)}',
],
root.toFilePath());
_checkExitCode(result, this, module);
} else {
Module sdkModule = module.dependencies.firstWhere((m) => m.isSdk);
List<String> sources = module.sources
.map((relativeUri) => _sourceToImportUri(module, relativeUri))
.toList();
Map<String, String> _urlMappings = {};
if (!module.isPackage) {
for (var source in module.sources) {
var importUri = _sourceToImportUri(module, source);
_urlMappings[importUri] = '$source';
}
}
for (var dependency in transitiveDependencies) {
if (!dependency.isPackage && !dependency.isSdk) {
for (var source in dependency.sources) {
var importUri = _sourceToImportUri(dependency, source);
_urlMappings[importUri] = '$source';
}
}
}
var extraArgs = [
'--dart-sdk-summary',
'${toUri(sdkModule, sumId)}',
'--packages',
'.packages',
];
Uri output = toUri(module, jsId);
List<String> args = [
'--packages=${sdkRoot.toFilePath()}.packages',
_dartdevcScript,
'--modules=es6',
'--summarize',
'--no-source-map',
...sources,
if (_urlMappings.isNotEmpty)
'--url-mapping=${_urlMappings.entries.map((entry) => '${entry.key},${entry.value}').join(',')}',
...extraArgs,
for (String flag in flags) '--enable-experiment=$flag',
...(transitiveDependencies
.where((m) => !m.isSdk)
.expand((m) => ['-s', '${toUri(m, sumId)}'])),
'-o',
'$output',
];
result = await _runProcess(
Platform.resolvedExecutable, args, root.toFilePath());
_checkExitCode(result, this, module);
}
}
@override
void notifyCached(Module module) {
if (_options.verbose) print("\ncached step: ddc on $module");
}
}
class RunD8 implements IOModularStep {
@override
List<DataId> get resultData => const [txtId];
@override
bool get needsSources => false;
@override
List<DataId> get dependencyDataNeeded => const [jsId];
@override
List<DataId> get moduleDataNeeded => const [jsId];
@override
bool get onlyOnMain => true;
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
if (_options.verbose) print("\nstep: d8 on $module");
// Rename sdk.js to dart_sdk.js (the alternative, but more hermetic solution
// would be to rename the import on all other .js files, but seems
// overkill/unnecessary.
if (await File.fromUri(root.resolve('dart_sdk.js')).exists()) {
throw 'error: dart_sdk.js already exists.';
}
await File.fromUri(root.resolve('sdk.js'))
.copy(root.resolve('dart_sdk.js').toFilePath());
var runjs = '''
import { dart, _isolate_helper } from 'dart_sdk.js';
import { main } from 'main.js';
_isolate_helper.startRootIsolate(() => {}, []);
main.main();
''';
var wrapper =
root.resolveUri(toUri(module, jsId)).toFilePath() + ".wrapper.js";
await File(wrapper).writeAsString(runjs);
List<String> d8Args = ['--module', wrapper];
var result = await _runProcess(
sdkRoot.resolve(_d8executable).toFilePath(), d8Args, root.toFilePath());
_checkExitCode(result, this, module);
await File.fromUri(root.resolveUri(toUri(module, txtId)))
.writeAsString(result.stdout as String);
}
@override
void notifyCached(Module module) {
if (_options.verbose) print("\ncached step: d8 on $module");
}
}
void _checkExitCode(ProcessResult result, IOModularStep step, Module module) {
if (result.exitCode != 0 || _options.verbose) {
stdout.write(result.stdout);
stderr.write(result.stderr);
}
if (result.exitCode != 0) {
throw "${step.runtimeType} failed on $module:\n\n"
"stdout:\n${result.stdout}\n\n"
"stderr:\n${result.stderr}";
}
}
Future<ProcessResult> _runProcess(
String command, List<String> arguments, String workingDirectory) {
if (_options.verbose) {
print('command:\n$command ${arguments.join(' ')} from $workingDirectory');
}
return Process.run(command, arguments, workingDirectory: workingDirectory);
}
String get _d8executable {
if (Platform.isWindows) {
return 'third_party/d8/windows/d8.exe';
} else if (Platform.isLinux) {
return 'third_party/d8/linux/d8';
} else if (Platform.isMacOS) {
return 'third_party/d8/macos/d8';
}
throw UnsupportedError('Unsupported platform.');
}
Future<void> _createPackagesFile(
Module module, Uri root, Set<Module> transitiveDependencies) async {
// We create a .packages file which defines the location of this module if
// it is a package. The CFE requires that if a `package:` URI of a
// dependency is used in an import, then we need that package entry in the
// .packages file. However, after it checks that the definition exists, the
// CFE will not actually use the resolved URI if a library for the import
// URI is already found in one of the provided .dill files of the
// dependencies. For that reason, and to ensure that a step only has access
// to the files provided in a module, we generate a .packages with invalid
// folders for other packages.
// TODO(sigmund): follow up with the CFE to see if we can remove the need
// for the .packages entry altogether if they won't need to read the
// sources.
var packagesContents = StringBuffer();
if (module.isPackage) {
packagesContents.write('${module.name}:${module.packageBase}\n');
}
for (Module dependency in transitiveDependencies) {
if (dependency.isPackage) {
packagesContents.write('${dependency.name}:unused\n');
}
}
await File.fromUri(root.resolve('.packages'))
.writeAsString('$packagesContents');
}
String _sourceToImportUri(Module module, Uri relativeUri) {
if (module.isPackage) {
var basePath = module.packageBase.path;
var packageRelativePath = basePath == "./"
? relativeUri.path
: relativeUri.path.substring(basePath.length);
return 'package:${module.name}/$packageRelativePath';
} else {
return 'package:${_test_package}/$relativeUri';
}
}
Future<void> _resolveScripts() async {
Future<String> resolve(String sdkSourcePath,
[String relativeSnapshotPath]) async {
String result = sdkRoot.resolve(sdkSourcePath).toFilePath();
if (_options.useSdk && relativeSnapshotPath != null) {
String snapshot = Uri.file(Platform.resolvedExecutable)
.resolve(relativeSnapshotPath)
.toFilePath();
if (await File(snapshot).exists()) {
return snapshot;
}
}
return result;
}
_dartdevcScript = await resolve(
'pkg/dev_compiler/bin/dartdevc.dart', 'snapshots/dartdevc.dart.snapshot');
_buildSdkScript = await resolve('pkg/dev_compiler/tool/build_sdk.dart');
_patchSdkScript = await resolve('pkg/dev_compiler/tool/patch_sdk.dart');
_librariesJson = await resolve('sdk/lib/libraries.json');
_librariesJsonNnbd = await resolve('sdk_nnbd/lib/libraries.json');
}

View file

@ -1,4 +0,0 @@
analyzer:
strong-mode: true
errors:
undefined_class: ignore

View file

@ -1,4 +0,0 @@
analyzer:
strong-mode: true
errors:
duplicate_definition: ignore

View file

@ -1,92 +0,0 @@
// Copyright (c) 2016, 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 'package:analyzer/src/command_line/arguments.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/summary/summary_sdk.dart';
import 'package:dev_compiler/src/analyzer/context.dart';
import 'package:dev_compiler/src/analyzer/command.dart';
import 'package:dev_compiler/src/analyzer/driver.dart';
import 'package:dev_compiler/src/analyzer/module_compiler.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import '../testing.dart' show repoDirectory, testDirectory;
/// The `test/options` directory.
final optionsDir = p.join(testDirectory, 'options');
/// Summary file for testing.
final sdkSummaryFile = p.join(repoDirectory, 'gen', 'sdk', 'ddc_sdk.sum');
final sdkSummaryArgs = ['--$sdkSummaryPathOption', sdkSummaryFile];
void main() {
test('basic', () {
var options = AnalyzerOptions.basic()..analysisRoot = optionsDir;
var driver = CompilerAnalysisDriver(options);
var processors = driver.analysisOptions.errorProcessors;
expect(processors, hasLength(1));
expect(processors[0].code, CompileTimeErrorCode.UNDEFINED_CLASS.name);
});
test('basic sdk summary', () {
expect(File(sdkSummaryFile).existsSync(), isTrue);
var options = AnalyzerOptions.basic(dartSdkSummaryPath: sdkSummaryFile)
..analysisRoot = optionsDir;
var driver = CompilerAnalysisDriver(options);
var sdk = driver.dartSdk;
expect(sdk, const TypeMatcher<SummaryBasedDartSdk>());
var processors = driver.analysisOptions.errorProcessors;
expect(processors, hasLength(1));
expect(processors[0].code, CompileTimeErrorCode.UNDEFINED_CLASS.name);
});
test('fromArgs', () {
var args = <String>[];
//TODO(danrubel) remove sdkSummaryArgs once all SDKs have summary file
args.addAll(sdkSummaryArgs);
var argResults = ddcArgParser().parse(args);
var options = AnalyzerOptions.fromArguments(argResults)
..analysisRoot = optionsDir;
var driver = CompilerAnalysisDriver(options);
var processors = driver.analysisOptions.errorProcessors;
expect(processors, hasLength(1));
expect(processors[0].code, CompileTimeErrorCode.UNDEFINED_CLASS.name);
});
test('fromArgs options file 2', () {
var optionsFile2 = p.join(optionsDir, 'analysis_options_2.yaml');
expect(File(optionsFile2).existsSync(), isTrue);
var args = <String>['--$analysisOptionsFileOption', optionsFile2];
//TODO(danrubel) remove sdkSummaryArgs once all SDKs have summary file
args.addAll(sdkSummaryArgs);
var argResults = ddcArgParser().parse(args);
var options = AnalyzerOptions.fromArguments(argResults)
..analysisRoot = optionsDir;
var driver = CompilerAnalysisDriver(options);
var processors = driver.analysisOptions.errorProcessors;
expect(processors, hasLength(1));
expect(processors[0].code, CompileTimeErrorCode.DUPLICATE_DEFINITION.name);
});
test('custom module name for summary', () {
var args = <String>[
'-snormal',
'-scustom/path=module',
'-sanother',
'--summary=custom/path2=module2'
];
var argResults = ddcArgParser().parse(args);
var options = CompilerOptions.fromArguments(argResults);
expect(options.summaryModules.keys.toList(),
orderedEquals(['normal', 'custom/path', 'another', 'custom/path2']));
expect(options.summaryModules['custom/path'], equals('module'));
expect(options.summaryModules['custom/path2'], equals('module2'));
expect(options.summaryModules.containsKey('normal'), isFalse);
});
}

View file

@ -1,3 +0,0 @@
# Copyright (c) 2017, 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.md file.

View file

@ -1,92 +0,0 @@
// Copyright (c) 2017, 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 'package:dev_compiler/src/analyzer/command.dart';
import 'package:testing/testing.dart';
import 'common.dart';
import 'ddc_common.dart';
Future<ChainContext> createContext(
Chain suite, Map<String, String> environment) async {
return SourceMapContext(environment);
}
class SourceMapContext extends ChainContextWithCleanupHelper {
final Map<String, String> environment;
SourceMapContext(this.environment);
List<Step> _steps;
@override
List<Step> get steps => _steps ??= <Step>[
const Setup(),
Compile(DevCompilerRunner(debugging: environment.containsKey("debug"))),
const StepWithD8(),
CheckSteps(environment.containsKey("debug")),
];
@override
bool debugging() => environment.containsKey("debug");
}
class DevCompilerRunner implements CompilerRunner {
final bool debugging;
final bool absoluteRoot;
const DevCompilerRunner({this.debugging = false, this.absoluteRoot = true});
@override
Future<Null> run(Uri inputFile, Uri outputFile, Uri outWrapperFile) async {
Uri outDir = outputFile.resolve(".");
String outputFilename = outputFile.pathSegments.last;
File sdkJsFile = findInOutDir("gen/utils/dartdevc/js/es6/dart_sdk.js");
var jsSdkPath = sdkJsFile.uri;
File ddcSdkSummary = findInOutDir("gen/utils/dartdevc/ddc_sdk.sum");
var ddc = getDdcDir().uri.resolve("bin/dartdevc.dart");
List<String> args = <String>[
"--modules=es6",
"--dart-sdk-summary=${ddcSdkSummary.path}",
"--library-root",
absoluteRoot ? "/" : outDir.toFilePath(),
"-o",
outputFile.toFilePath(),
inputFile.toFilePath()
];
var result = compile(args);
var exitCode = result?.exitCode;
if (exitCode != 0) {
throw "Exit code: $exitCode from ddc when running something like "
"$dartExecutable ${ddc.toFilePath()} "
"${args.reduce((value, element) => '$value "$element"')}";
}
var jsContent = File.fromUri(outputFile).readAsStringSync();
File.fromUri(outputFile).writeAsStringSync(jsContent.replaceFirst(
"from 'dart_sdk.js'", "from '${uriPathForwardSlashed(jsSdkPath)}'"));
if (debugging) {
createHtmlWrapper(
sdkJsFile, outputFile, jsContent, outputFilename, outDir);
}
var inputFileName =
absoluteRoot ? inputFile.path : inputFile.pathSegments.last;
inputFileName = inputFileName.split(":").last;
var inputFileNameNoExt = pathToJSIdentifier(
inputFileName.substring(0, inputFileName.lastIndexOf(".")));
File.fromUri(outWrapperFile).writeAsStringSync(
getWrapperContent(jsSdkPath, inputFileNameNoExt, outputFilename));
}
}
void main(List<String> arguments) =>
runMe(arguments, createContext, configurationPath: "testing.json");

View file

@ -1,8 +0,0 @@
# Copyright (c) 2017, 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.md file.
null_interceptor_field: Crash
throw_in_constructor_from_async: Crash
throw_in_instance_method: Crash
throw_in_static_method: Crash # Test works, but DDC's hover support means the expected column in the test is wrong

View file

@ -1,25 +0,0 @@
import 'package:testing/testing.dart';
import 'common.dart';
import 'ddc_common.dart';
import 'sourcemaps_ddc_suite.dart' as ddc;
Future<ChainContext> createContext(
Chain suite, Map<String, String> environment) async {
return StackTraceContext();
}
class StackTraceContext extends ChainContextWithCleanupHelper {
@override
final List<Step> steps = <Step>[
const Setup(),
const SetCwdToSdkRoot(),
const TestStackTrace(
ddc.DevCompilerRunner(debugging: false, absoluteRoot: false),
"ddc",
["ddc", "ddk"]),
];
}
void main(List<String> arguments) =>
runMe(arguments, createContext, configurationPath: "testing.json");

View file

@ -1,164 +0,0 @@
#!/usr/bin/env dart
import 'dart:async';
import 'dart:io';
import 'package:args/args.dart';
import 'package:path/path.dart' as p;
import 'package:dev_compiler/src/compiler/shared_command.dart';
final String scriptDirectory = p.dirname(p.fromUri(Platform.script));
final String repoDirectory = p.normalize(p.join(scriptDirectory, "../../../"));
/// Path to the SDK analyzer summary file, "ddc_sdk.sum".
String analyzerSummary;
/// Path to the SDK kernel summary file, "ddc_sdk.dill".
String kernelSummary;
/// The directory that output is written to.
///
/// The DDC kernel SDK should be directly in this directory. The resulting
/// packages will be placed in a "pkg" subdirectory of this.
String outputDirectory;
/// List of language experiments to enable when building.
List<String> experiments;
/// Whether to force the analyzer backend to generate code even if there are
/// errors.
bool unsafeForceCompile;
/// Compiles the packages that the DDC tests use to JS into the given output
/// directory.
///
/// If "--travis" is passed, builds the all of the modules tested on Travis.
/// Otherwise, only builds the modules needed by the tests.
///
/// If "--analyzer-sdk" is provided, uses that summary file and compiles the
/// packages to JS and analyzer summaries against that SDK summary. Otherwise,
/// it skips generating analyzer summaries and compiling to JS.
///
/// If "--kernel-sdk" is provided, uses that summary file and generates kernel
/// summaries for the test packages against that SDK summary. Otherwise, skips
/// generating kernel summaries.
Future main(List<String> arguments) async {
var argParser = ArgParser();
argParser.addOption("analyzer-sdk",
help: "Path to SDK analyzer summary '.sum' file");
argParser.addOption("kernel-sdk",
help: "Path to SDK Kernel summary '.dill' file");
argParser.addOption("output",
abbr: "o", help: "Directory to write output to.");
argParser.addFlag("travis",
help: "Build the additional packages tested on Travis.");
argParser.addMultiOption("enable-experiment",
help: "Enable experimental language features.");
argParser.addFlag("unsafe-force-compile",
help: "Generate output even if compile errors are reported.");
ArgResults argResults;
try {
argResults = argParser.parse(arguments);
} on ArgParserException catch (ex) {
_usageError(argParser, ex.message);
}
if (argResults.rest.isNotEmpty) {
_usageError(
argParser, 'Unexpected arguments "${argResults.rest.join(' ')}".');
}
var isTravis = argResults["travis"] as bool;
analyzerSummary = argResults["analyzer-sdk"] as String;
kernelSummary = argResults["kernel-sdk"] as String;
outputDirectory = argResults["output"] as String;
experiments = argResults["enable-experiment"] as List<String>;
unsafeForceCompile = argResults["unsafe-force-compile"] as bool;
// Build leaf packages. These have no other package dependencies.
// Under pkg.
await compileModule('async_helper');
await compileModule('expect', libs: ['minitest']);
await compileModule('js', libs: ['js_util']);
await compileModule('meta');
// Under third_party/pkg.
await compileModule('collection');
await compileModule('path');
if (isTravis) {
await compileModule('args', libs: ['command_runner']);
await compileModule('charcode');
await compileModule('fixnum');
await compileModule('logging');
await compileModule('markdown');
await compileModule('mime');
await compileModule('plugin', libs: ['manager']);
await compileModule('typed_data');
await compileModule('usage');
await compileModule('utf');
}
// Composite packages with dependencies.
await compileModule('stack_trace', deps: ['path']);
await compileModule('matcher', deps: ['stack_trace']);
if (isTravis) {
await compileModule('async', deps: ['collection']);
}
if (!isTravis) {
await compileModule('unittest', deps: [
'path',
'stack_trace'
], libs: [
'html_config',
'html_individual_config',
'html_enhanced_config'
]);
}
}
void _usageError(ArgParser parser, [String message]) {
if (message != null) {
stderr.writeln(message);
stderr.writeln();
}
stderr.writeln("Usage: dart build_pkgs.dart ...");
stderr.writeln();
stderr.writeln(parser.usage);
exit(1);
}
/// Compiles a [module] with a single matching ".dart" library and additional
/// [libs] and [deps] on other modules.
Future compileModule(String module,
{List<String> libs = const [], List<String> deps = const []}) async {
makeArgs({bool kernel = false}) {
var pkgDirectory = p.join(outputDirectory, kernel ? 'pkg_kernel' : 'pkg');
Directory(pkgDirectory).createSync(recursive: true);
return [
if (kernel) '-k',
if (experiments.isNotEmpty)
'--enable-experiment=${experiments.join(",")}',
if (unsafeForceCompile && !kernel) '--unsafe-force-compile',
'--dart-sdk-summary=${kernel ? kernelSummary : analyzerSummary}',
'-o${pkgDirectory}/$module.js',
'package:$module/$module.dart',
for (var lib in libs) 'package:$module/$lib.dart',
for (var dep in deps) '-s${pkgDirectory}/$dep.${kernel ? "dill" : "sum"}',
];
}
if (analyzerSummary != null) {
var result = await compile(ParsedArguments.from(makeArgs()));
if (!result.success) exit(result.exitCode);
}
if (kernelSummary != null) {
var result = await compile(ParsedArguments.from(makeArgs(kernel: true)));
if (!result.success) exit(result.exitCode);
}
}

View file

@ -1,53 +0,0 @@
#!/usr/bin/env dart
// Copyright (c) 2016, 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.
/// A straightforward script that builds the SDK.
///
/// This would be easy enough to do in a shell script, as we go through the
/// command line interface. But being able to build from a Dart library means
/// we can call this during code coverage to get more realistic numbers.
import 'dart:io';
import 'package:dev_compiler/src/analyzer/command.dart';
void main(List<String> arguments) async {
var args = ['--no-source-map', '--no-emit-metadata'];
args.addAll(arguments);
args.addAll([
'dart:_runtime',
'dart:_debugger',
'dart:_foreign_helper',
'dart:_interceptors',
'dart:_internal',
'dart:_isolate_helper',
'dart:_js_helper',
'dart:_js_mirrors',
'dart:_js_primitives',
'dart:_metadata',
'dart:_native_typed_data',
'dart:async',
'dart:collection',
'dart:convert',
'dart:core',
'dart:developer',
'dart:io',
'dart:isolate',
'dart:js',
'dart:js_util',
'dart:math',
'dart:mirrors',
'dart:typed_data',
'dart:indexed_db',
'dart:html',
'dart:html_common',
'dart:svg',
'dart:web_audio',
'dart:web_gl',
'dart:web_sql'
]);
exit((await compile(args)).exitCode);
}

View file

@ -1,510 +0,0 @@
#!/usr/bin/env dart
// Copyright (c) 2015, 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.
// TODO(sigmund): delete. This file is now replaced by `patch_sdk.dart`. We
// are keeping this copy around because there is one remaining use of it in the
// build system of our internal apps. That use is deprecated and will be
// deleted, at that point this file will be deleted too.
/// Command line tool to merge the SDK libraries and our patch files.
/// This is currently designed as an offline tool, but we could automate it.
import 'dart:io';
import 'dart:math' as math;
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:path/path.dart' as p;
void main(List<String> argv) {
var self = p.relative(p.fromUri(Platform.script));
if (argv.length < 3) {
var toolDir = p.relative(p.dirname(p.fromUri(Platform.script)));
var dartDir =
p.dirname(p.dirname(p.dirname(p.dirname(p.fromUri(Platform.script)))));
var repoExample = p.join(toolDir, '..', '..', '..');
var patchExample =
p.join(dartDir, 'sdk', 'lib', '_internal', 'js_dev_runtime');
var outExample = p.relative(p.normalize(p.join('gen', 'patched_sdk')));
print('Usage: $self DART_REPO_DIR PATCH_DIR OUTPUT_DIR');
print('For example:');
print('\$ $self $repoExample $patchExample $outExample');
exit(1);
}
var sdk = 'sdk';
var useNnbd = false;
if (argv.length > 3) {
sdk = argv[3];
// TODO(38701): While the core libraries have been forked for NNBD, use the
// SDK directory name to determine whether to enable the NNBD experiment
// when parsing the lib sources. Once the libraries have been unforked, we
// should unconditionally enable the experiment flag since then the
// canonical SDK libs will use NNBD syntax.
useNnbd = sdk.contains("nnbd");
}
var selfModifyTime = File(self).lastModifiedSync().millisecondsSinceEpoch;
var repoDir = argv[0];
var patchDir = argv[1];
var sdkLibIn = p.join(repoDir, sdk, 'lib');
var patchIn = p.join(patchDir, 'patch');
var privateIn = p.join(patchDir, 'private');
var sdkOut = p.join(argv[2], 'lib');
var INTERNAL_PATH = '_internal/js_runtime/lib/';
// Copy libraries.dart and version
var librariesDart = p.join(patchDir, 'libraries.dart');
var libContents = File(librariesDart).readAsStringSync();
// TODO(jmesserly): can we remove this?
_writeSync(p.join(sdkOut, '_internal', 'libraries.dart'), libContents);
_writeSync(
p.join(
sdkOut, '_internal', 'sdk_library_metadata', 'lib', 'libraries.dart'),
libContents);
_writeSync(p.join(sdkOut, '..', 'version'),
File(p.join(repoDir, 'tools', 'VERSION')).readAsStringSync());
// Parse libraries.dart
var sdkLibraries = _getSdkLibraries(libContents, useNnbd: useNnbd);
// Enumerate core libraries and apply patches
for (SdkLibrary library in sdkLibraries) {
// TODO(jmesserly): analyzer does not handle the default case of
// "both platforms" correctly, and treats it as being supported on neither.
// So instead we skip explicitly marked as VM libs.
if (library.isVmLibrary) continue;
var libraryOut = p.join(sdkLibIn, library.path);
var libraryOverride = p.join(patchDir, 'lib', library.path);
String libraryIn;
if (library.path.contains(INTERNAL_PATH)) {
libraryIn = p.join(privateIn, library.path.replaceAll(INTERNAL_PATH, ''));
} else if (File(libraryOverride).existsSync()) {
libraryIn = libraryOverride;
} else {
libraryIn = libraryOut;
}
var libraryFile = File(libraryIn);
if (libraryFile.existsSync()) {
var outPaths = <String>[libraryOut];
var libraryContents = libraryFile.readAsStringSync();
int inputModifyTime = math.max(selfModifyTime,
libraryFile.lastModifiedSync().millisecondsSinceEpoch);
var partFiles = <File>[];
for (var part
in _parseString(libraryContents, useNnbd: useNnbd).unit.directives) {
if (part is PartDirective) {
var partPath = part.uri.stringValue;
outPaths.add(p.join(p.dirname(libraryOut), partPath));
var partFile = File(p.join(p.dirname(libraryIn), partPath));
partFiles.add(partFile);
inputModifyTime = math.max(inputModifyTime,
partFile.lastModifiedSync().millisecondsSinceEpoch);
}
}
// See if we can find a patch file.
var patchPath = p.join(
patchIn, p.basenameWithoutExtension(libraryIn) + '_patch.dart');
var patchFile = File(patchPath);
bool patchExists = patchFile.existsSync();
if (patchExists) {
inputModifyTime = math.max(inputModifyTime,
patchFile.lastModifiedSync().millisecondsSinceEpoch);
}
// Compute output paths
outPaths = outPaths
.map((path) => p.join(sdkOut, p.relative(path, from: sdkLibIn)))
.toList();
// Compare output modify time with input modify time.
bool needsUpdate = false;
for (var outPath in outPaths) {
var outFile = File(outPath);
if (!outFile.existsSync() ||
outFile.lastModifiedSync().millisecondsSinceEpoch <
inputModifyTime) {
needsUpdate = true;
break;
}
}
if (needsUpdate) {
var contents = <String>[libraryContents];
contents.addAll(partFiles.map((f) => f.readAsStringSync()));
if (patchExists) {
var patchContents = patchFile.readAsStringSync();
contents = _patchLibrary(contents, patchContents, useNnbd: useNnbd);
}
if (contents != null) {
for (var i = 0; i < outPaths.length; i++) {
_writeSync(outPaths[i], contents[i]);
}
} else {
exitCode = 2;
}
}
}
}
}
/// Writes a file, creating the directory if needed.
void _writeSync(String filePath, String contents) {
var outDir = Directory(p.dirname(filePath));
if (!outDir.existsSync()) outDir.createSync(recursive: true);
File(filePath).writeAsStringSync(contents);
}
/// Merges dart:* library code with code from *_patch.dart file.
///
/// Takes a list of the library's parts contents, with the main library contents
/// first in the list, and the contents of the patch file.
///
/// The result will have `@patch` implementations merged into the correct place
/// (e.g. the class or top-level function declaration) and all other
/// declarations introduced by the patch will be placed into the main library
/// file.
///
/// This is purely a syntactic transformation. Unlike dart2js patch files, there
/// is no semantic meaning given to the *_patch files, and they do not magically
/// get their own library scope, etc.
///
/// Editorializing: the dart2js approach requires a Dart front end such as
/// package:analyzer to semantically model a feature beyond what is specified
/// in the Dart language. Since this feature is only for the convenience of
/// writing the dart:* libraries, and not a tool given to Dart developers, it
/// seems like a non-ideal situation. Instead we keep the preprocessing simple.
List<String> _patchLibrary(List<String> partsContents, String patchContents,
{bool useNnbd = false}) {
var results = <StringEditBuffer>[];
// Parse the patch first. We'll need to extract bits of this as we go through
// the other files.
var patchFinder = PatchFinder.parseAndVisit(patchContents, useNnbd: useNnbd);
// Merge `external` declarations with the corresponding `@patch` code.
bool failed = false;
for (var partContent in partsContents) {
var partEdits = StringEditBuffer(partContent);
var partUnit = _parseString(partContent, useNnbd: useNnbd).unit;
var patcher = PatchApplier(partEdits, patchFinder);
partUnit.accept(patcher);
if (!failed) failed = patcher.patchWasMissing;
results.add(partEdits);
}
if (failed) return null;
return List<String>.from(results.map((e) => e.toString()));
}
/// Merge `@patch` declarations into `external` declarations.
class PatchApplier extends GeneralizingAstVisitor {
final StringEditBuffer edits;
final PatchFinder patch;
bool _isLibrary = true; // until proven otherwise.
bool patchWasMissing = false;
PatchApplier(this.edits, this.patch);
@override
visitCompilationUnit(CompilationUnit node) {
super.visitCompilationUnit(node);
if (_isLibrary) _mergeUnpatched(node);
}
void _merge(AstNode node, int pos) {
var code = patch.contents.substring(node.offset, node.end);
edits.insert(pos, '\n' + code);
}
/// Merges directives and declarations that are not `@patch` into the library.
void _mergeUnpatched(CompilationUnit unit) {
// Merge imports from the patch
// TODO(jmesserly): remove duplicate imports
// To patch a library, we must have a library directive
var libDir = unit.directives.first as LibraryDirective;
int importPos = unit.directives
.lastWhere((d) => d is ImportDirective, orElse: () => libDir)
.end;
for (var d in patch.unit.directives.whereType<ImportDirective>()) {
_merge(d, importPos);
}
int partPos = unit.directives.last.end;
for (var d in patch.unit.directives.whereType<PartDirective>()) {
_merge(d, partPos);
}
// Merge declarations from the patch
int declPos = edits.original.length;
for (var d in patch.mergeDeclarations) {
_merge(d, declPos);
}
}
@override
visitPartOfDirective(PartOfDirective node) {
_isLibrary = false;
}
@override
visitFunctionDeclaration(FunctionDeclaration node) {
_maybePatch(node);
}
/// Merge patches and extensions into the class
@override
visitClassDeclaration(ClassDeclaration node) {
node.members.forEach(_maybePatch);
var mergeMembers = patch.mergeMembers[_qualifiedName(node)];
if (mergeMembers == null) return;
// Merge members from the patch
var pos = node.members.last.end;
for (var member in mergeMembers) {
var code = patch.contents.substring(member.offset, member.end);
edits.insert(pos, '\n\n ' + code);
}
}
void _maybePatch(Declaration node) {
if (node is FieldDeclaration) return;
var externalKeyword = (node as dynamic).externalKeyword as Token;
if (externalKeyword == null) return;
var name = _qualifiedName(node);
var patchNode = patch.patches[name];
if (patchNode == null) {
print('warning: patch not found for $name: $node');
patchWasMissing = true;
return;
}
Annotation patchMeta = patchNode.metadata.lastWhere(_isPatchAnnotation);
int start = patchMeta.endToken.next.offset;
var code = patch.contents.substring(start, patchNode.end);
// Const factory constructors can't be legally parsed from the patch file,
// so we need to omit the "const" there, but still preserve it.
if (node is ConstructorDeclaration &&
node.constKeyword != null &&
patchNode is ConstructorDeclaration &&
patchNode.constKeyword == null) {
code = 'const $code';
}
// For some node like static fields, the node's offset doesn't include
// the external keyword. Also starting from the keyword lets us preserve
// documentation comments.
edits.replace(externalKeyword.offset, node.end, code);
}
}
class PatchFinder extends GeneralizingAstVisitor {
final String contents;
final CompilationUnit unit;
final patches = <String, Declaration>{};
final mergeMembers = <String, List<ClassMember>>{};
final mergeDeclarations = <CompilationUnitMember>[];
PatchFinder.parseAndVisit(String contents, {bool useNnbd})
: contents = contents,
unit = _parseString(contents, useNnbd: useNnbd).unit {
visitCompilationUnit(unit);
}
@override
visitCompilationUnitMember(CompilationUnitMember node) {
mergeDeclarations.add(node);
}
@override
visitClassDeclaration(ClassDeclaration node) {
if (_isPatch(node)) {
var members = <ClassMember>[];
for (var member in node.members) {
if (_isPatch(member)) {
patches[_qualifiedName(member)] = member;
} else {
members.add(member);
}
}
if (members.isNotEmpty) {
mergeMembers[_qualifiedName(node)] = members;
}
} else {
mergeDeclarations.add(node);
}
}
@override
visitFunctionDeclaration(FunctionDeclaration node) {
if (_isPatch(node)) {
patches[_qualifiedName(node)] = node;
} else {
mergeDeclarations.add(node);
}
}
@override
visitFunctionBody(node) {} // skip method bodies
}
String _qualifiedName(Declaration node) {
var result = "";
var parent = node.parent;
if (parent is ClassDeclaration) {
result = "${parent.name.name}.";
}
var name = (node as dynamic).name as SimpleIdentifier;
if (name != null) result += name.name;
// Make sure setters and getters don't collide.
if (node is FunctionDeclaration && node.isSetter ||
node is MethodDeclaration && node.isSetter) {
result += "=";
}
return result;
}
bool _isPatch(AnnotatedNode node) => node.metadata.any(_isPatchAnnotation);
bool _isPatchAnnotation(Annotation m) =>
m.name.name == 'patch' && m.constructorName == null && m.arguments == null;
/// Editable string buffer.
///
/// Applies a series of edits (insertions, removals, replacements) using
/// original location information, and composes them into the edited string.
///
/// For example, starting with a parsed AST with original source locations,
/// this type allows edits to be made without regards to other edits.
class StringEditBuffer {
final String original;
final _edits = <_StringEdit>[];
/// Creates a new transaction.
StringEditBuffer(this.original);
bool get hasEdits => _edits.isNotEmpty;
/// Edit the original text, replacing text on the range [begin] and
/// exclusive [end] with the [replacement] string.
void replace(int begin, int end, String replacement) {
_edits.add(_StringEdit(begin, end, replacement));
}
/// Insert [string] at [offset].
/// Equivalent to `replace(offset, offset, string)`.
void insert(int offset, String string) => replace(offset, offset, string);
/// Remove text from the range [begin] to exclusive [end].
/// Equivalent to `replace(begin, end, '')`.
void remove(int begin, int end) => replace(begin, end, '');
/// Applies all pending [edit]s and returns a new string.
///
/// This method is non-destructive: it does not discard existing edits or
/// change the [original] string. Further edits can be added and this method
/// can be called again.
///
/// Throws [UnsupportedError] if the edits were overlapping. If no edits were
/// made, the original string will be returned.
@override
String toString() {
var sb = StringBuffer();
if (_edits.isEmpty) return original;
// Sort edits by start location.
_edits.sort();
int consumed = 0;
for (var edit in _edits) {
if (consumed > edit.begin) {
sb = StringBuffer();
sb.write('overlapping edits. Insert at offset ');
sb.write(edit.begin);
sb.write(' but have consumed ');
sb.write(consumed);
sb.write(' input characters. List of edits:');
for (var e in _edits) {
sb.write('\n ');
sb.write(e);
}
throw UnsupportedError(sb.toString());
}
// Add characters from the original string between this edit and the last
// one, if any.
var betweenEdits = original.substring(consumed, edit.begin);
sb.write(betweenEdits);
sb.write(edit.replace);
consumed = edit.end;
}
// Add any text from the end of the original string that was not replaced.
sb.write(original.substring(consumed));
return sb.toString();
}
}
class _StringEdit implements Comparable<_StringEdit> {
final int begin;
final int end;
final String replace;
_StringEdit(this.begin, this.end, this.replace);
int get length => end - begin;
@override
String toString() => '(Edit @ $begin,$end: "$replace")';
@override
int compareTo(_StringEdit other) {
int diff = begin - other.begin;
if (diff != 0) return diff;
return end - other.end;
}
}
List<SdkLibrary> _getSdkLibraries(String contents, {bool useNnbd}) {
// TODO(jmesserly): fix SdkLibrariesReader_LibraryBuilder in Analyzer.
// It doesn't understand optional new/const in Dart 2. For now, we keep
// redundant `const` in tool/input_sdk/libraries.dart as a workaround.
var libraryBuilder = SdkLibrariesReader_LibraryBuilder();
_parseString(contents, useNnbd: useNnbd).unit.accept(libraryBuilder);
return libraryBuilder.librariesMap.sdkLibraries;
}
ParseStringResult _parseString(String source, {bool useNnbd}) {
var features = FeatureSet.fromEnableFlags([if (useNnbd) "non-nullable"]);
return parseString(content: source, featureSet: features);
}