Version 0.7.3.0
svn merge -r 27229:27471 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@27480 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/.gitignore b/.gitignore
index 1632934..3b24772 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,14 @@
# Vim temporary swap files.
*.swp
+# Kate temporary files.
+*~
+*.kate-swp
+
+# Merge files.
+*.orig
+*.rej
+
# Generated files.
tools/out
tools/xcodebuild
diff --git a/pkg/analyzer_experimental/bin/formatter.dart b/pkg/analyzer_experimental/bin/formatter.dart
index d80a0e2..52b8fd7 100755
--- a/pkg/analyzer_experimental/bin/formatter.dart
+++ b/pkg/analyzer_experimental/bin/formatter.dart
@@ -5,6 +5,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
+import 'dart:utf';
import 'package:args/args.dart';
import 'package:path/path.dart' as path;
@@ -17,15 +18,16 @@
final argParser = _initArgParser();
bool overwriteFileContents;
+const followLinks = false;
-void main() {
+main() {
var options = argParser.parse(new Options().arguments);
if (options['help']) {
_printUsage();
return;
}
overwriteFileContents = options['write'];
-
+
if (options.rest.isEmpty) {
_formatStdin(options);
} else {
@@ -51,10 +53,10 @@
}
}
-_formatDirectory(dir) =>
- dir.listSync().forEach((resource) => _formatResource(resource));
+_formatDirectory(dir) => dir.listSync(followLinks: followLinks)
+ .forEach((resource) => _formatResource(resource));
-void _formatFile(file) {
+_formatFile(file) {
if (_isDartFile(file)) {
try {
var buffer = new StringBuffer();
@@ -66,7 +68,7 @@
print(formatted);
}
} catch (e) {
- _log('Error formatting "${file.path}": $e');
+ _log('Unable to format "${file.path}": $e');
}
}
}
@@ -74,11 +76,11 @@
_isDartFile(file) => dartFileRegExp.hasMatch(path.basename(file.path));
_formatStdin(options) {
- _log('not supported yet!');
-// stdin.transform(new StringDecoder())
-// .listen((String data) => print(data),
-// onError: (error) => print('Error reading from stdin'),
-// onDone: () => print('Finished reading data'));
+ var input = new StringBuffer();
+ stdin.transform(new Utf8DecoderTransformer())
+ .listen((data) => input.write(data),
+ onError: (error) => _log('Error reading from stdin'),
+ onDone: () => print(_formatCU(input.toString())));
}
/// Initialize the arg parser instance.
diff --git a/pkg/analyzer_experimental/lib/src/generated/java_core.dart b/pkg/analyzer_experimental/lib/src/generated/java_core.dart
index d13bb3d..769fdf1 100644
--- a/pkg/analyzer_experimental/lib/src/generated/java_core.dart
+++ b/pkg/analyzer_experimental/lib/src/generated/java_core.dart
@@ -31,15 +31,12 @@
if (oTypeName == tTypeName) {
return true;
}
- if (oTypeName.startsWith("HashMap") && tTypeName == "Map") {
- return true;
- }
- if (oTypeName.startsWith("LinkedHashMap") && tTypeName == "Map") {
- return true;
- }
if (oTypeName.startsWith("List") && tTypeName == "List") {
return true;
}
+ if (tTypeName == "Map" && o is Map) {
+ return true;
+ }
// Dart Analysis Engine specific
if (oTypeName == "${tTypeName}Impl") {
return true;
diff --git a/pkg/analyzer_experimental/lib/src/services/formatter_impl.dart b/pkg/analyzer_experimental/lib/src/services/formatter_impl.dart
index 71855f5..180e9de 100644
--- a/pkg/analyzer_experimental/lib/src/services/formatter_impl.dart
+++ b/pkg/analyzer_experimental/lib/src/services/formatter_impl.dart
@@ -12,9 +12,6 @@
import 'package:analyzer_experimental/src/generated/source.dart';
import 'package:analyzer_experimental/src/services/writer.dart';
-/// OS line separator. --- TODO(pquitslund): may not be necessary
-const NEW_LINE = '\n' ; //Platform.pathSeparator;
-
/// Formatter options.
class FormatterOptions {
@@ -43,13 +40,19 @@
final String message;
/// Creates a new FormatterException with an optional error [message].
- const FormatterException([this.message = '']);
+ const FormatterException([this.message = 'FormatterException']);
- FormatterException.forError(List<AnalysisError> errors) :
- // TODO(pquitslund): add descriptive message based on errors
- message = 'an analysis error occured during format';
+ FormatterException.forError(List<AnalysisError> errors, [LineInfo line]) :
+ message = _createMessage(errors);
- String toString() => 'FormatterException: $message';
+ static String _createMessage(errors) {
+ //TODO(pquitslund): consider a verbosity flag to add/suppress details
+ var errorCode = errors[0].errorCode;
+ var phase = errorCode is ParserErrorCode ? 'parsing' : 'scanning';
+ return 'An error occured while ${phase} (${errorCode.name}).';
+ }
+
+ String toString() => '$message';
}
/// Specifies the kind of code snippet to format.
@@ -84,6 +87,7 @@
final FormatterOptions options;
final errors = <AnalysisError>[];
+ final whitespace = new RegExp(r'[\s]+');
LineInfo lineInfo;
@@ -92,18 +96,25 @@
String format(CodeKind kind, String source, {int offset, int end,
int indentationLevel: 0}) {
- var start = tokenize(source);
+ var startToken = tokenize(source);
checkForErrors();
- var node = parse(kind, start);
+ var node = parse(kind, startToken);
checkForErrors();
var formatter = new SourceVisitor(options, lineInfo);
node.accept(formatter);
- return formatter.writer.toString();
+ var formattedSource = formatter.writer.toString();
+
+ checkTokenStreams(startToken, tokenize(formattedSource));
+
+ return formattedSource;
}
+ checkTokenStreams(Token t1, Token t2) =>
+ new TokenStreamComparator(lineInfo, t1, t2).verifyEquals();
+
ASTNode parse(CodeKind kind, Token start) {
var parser = new Parser(null, this);
@@ -118,13 +129,13 @@
throw new FormatterException('Unsupported format kind: $kind');
}
- void checkForErrors() {
+ checkForErrors() {
if (errors.length > 0) {
throw new FormatterException.forError(errors);
}
}
- void onError(AnalysisError error) {
+ onError(AnalysisError error) {
errors.add(error);
}
@@ -138,6 +149,123 @@
}
+// Compares two token streams. Used for sanity checking formatted results.
+class TokenStreamComparator {
+
+ final LineInfo lineInfo;
+ Token token1, token2;
+
+ TokenStreamComparator(this.lineInfo, this.token1, this.token2);
+
+ /// Verify that these two token streams are equal.
+ verifyEquals() {
+ while (!isEOF(token1)) {
+ checkPrecedingComments();
+ if (!checkTokens()) {
+ throwNotEqualException(token1, token2);
+ }
+ advance();
+
+ }
+ if (!isEOF(token2)) {
+ throw new FormatterException(
+ 'Expected "EOF" but got "${token2}".');
+ }
+ }
+
+ checkPrecedingComments() {
+ var comment1 = token1.precedingComments;
+ var comment2 = token2.precedingComments;
+ while (comment1 != null) {
+ if (comment2 == null) {
+ throw new FormatterException(
+ 'Expected comment, "${comment1}", at ${describeLocation(token1)}, '
+ 'but got none.');
+ }
+ if (!equivalentComments(comment1, comment2)) {
+ throwNotEqualException(comment1, comment2);
+ }
+ comment1 = comment1.next;
+ comment2 = comment2.next;
+ }
+ if (comment2 != null) {
+ throw new FormatterException(
+ 'Unexpected comment, "${comment2}", at ${describeLocation(token2)}.');
+ }
+ }
+
+ bool equivalentComments(Token comment1, Token comment2) =>
+ comment1.lexeme.trim() == comment2.lexeme.trim();
+
+ throwNotEqualException(t1, t2) {
+ throw new FormatterException(
+ 'Expected "${t1}" but got "${t2}", at ${describeLocation(t1)}.');
+ }
+
+ String describeLocation(Token token) => lineInfo == null ? '<unknown>' :
+ 'Line: ${lineInfo.getLocation(token.offset).lineNumber}, '
+ 'Column: ${lineInfo.getLocation(token.offset).columnNumber}';
+
+ advance() {
+ token1 = token1.next;
+ token2 = token2.next;
+ }
+
+ bool checkTokens() {
+ if (token1 == null || token2 == null) {
+ return false;
+ }
+ if (token1 == token2 || token1.lexeme == token2.lexeme) {
+ return true;
+ }
+
+ // '[' ']' => '[]'
+ if (isOPEN_SQ_BRACKET(token1) && isCLOSE_SQUARE_BRACKET(token1.next)) {
+ if (isINDEX(token2)) {
+ token1 = token1.next;
+ return true;
+ }
+ }
+ // '>' '>' => '>>'
+ if (isGT(token1) && isGT(token1.next)) {
+ if (isGT_GT(token2)) {
+ token1 = token1.next;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
+
+// Cached parser for testing token types.
+final tokenTester = new Parser(null,null);
+
+/// Test if this token is an EOF token.
+bool isEOF(Token token) => tokenIs(token, TokenType.EOF);
+
+/// Test for token type.
+bool tokenIs(Token token, TokenType type) =>
+ token != null && tokenTester.matches4(token, type);
+
+/// Test if this token is a GT token.
+bool isGT(Token token) => tokenIs(token, TokenType.GT);
+
+/// Test if this token is a GT_GT token.
+bool isGT_GT(Token token) => tokenIs(token, TokenType.GT_GT);
+
+/// Test if this token is an INDEX token.
+bool isINDEX(Token token) => tokenIs(token, TokenType.INDEX);
+
+/// Test if this token is a OPEN_SQUARE_BRACKET token.
+bool isOPEN_SQ_BRACKET(Token token) =>
+ tokenIs(token, TokenType.OPEN_SQUARE_BRACKET);
+
+/// Test if this token is a CLOSE_SQUARE_BRACKET token.
+bool isCLOSE_SQUARE_BRACKET(Token token) =>
+ tokenIs(token, TokenType.CLOSE_SQUARE_BRACKET);
+
/// An AST visitor that drives formatting heuristics.
class SourceVisitor implements ASTVisitor {
@@ -155,7 +283,7 @@
/// Used for matching EOL comments
final twoSlashes = new RegExp(r'//[^/]');
-
+
/// Initialize a newly created visitor to write source code representing
/// the visited nodes to the given [writer].
SourceVisitor(FormatterOptions options, this.lineInfo) :
@@ -327,6 +455,9 @@
// Handle trailing whitespace
token(node.endToken /* EOF */);
+
+ // Be a good citizen, end with a NL
+ ensureTrailingNewline();
}
visitConditionalExpression(ConditionalExpression node) {
@@ -671,6 +802,7 @@
visit(node.typeArguments);
token(node.leftBracket);
visitNodes(node.elements, separatedBy: commaSeperator);
+ optionalTrailingComma(node.rightBracket);
token(node.rightBracket);
}
@@ -679,9 +811,10 @@
visitNode(node.typeArguments, followedBy: space);
token(node.leftBracket);
visitNodes(node.entries, separatedBy: commaSeperator);
+ optionalTrailingComma(node.rightBracket);
token(node.rightBracket);
}
-
+
visitMapLiteralEntry(MapLiteralEntry node) {
visit(node.key);
token(node.separator);
@@ -1025,13 +1158,19 @@
token(modifier, followedBy: space);
}
-
/// Indicate that at least one newline should be emitted and possibly more
/// if the source has them.
newlines() {
needsNewline = true;
}
+ /// Optionally emit a trailing comma.
+ optionalTrailingComma(Token rightBracket) {
+ if (rightBracket.previous.lexeme == ',') {
+ comma();
+ }
+ }
+
token(Token token, {precededBy(), followedBy(), int minNewlines: 0}) {
if (token != null) {
if (needsNewline) {
@@ -1041,7 +1180,7 @@
if (emitted > 0) {
needsNewline = false;
}
- if (precededBy !=null) {
+ if (precededBy != null) {
precededBy();
}
append(token.lexeme);
@@ -1074,7 +1213,7 @@
/// Append the given [string] to the source writer if it's non-null.
append(String string) {
- if (string != null) {
+ if (string != null && !string.isEmpty) {
writer.print(string);
}
}
@@ -1089,30 +1228,30 @@
writer.unindent();
}
-
- /// Emit any detected comments and newlines or a minimum as specified
+
+ /// Emit any detected comments and newlines or a minimum as specified
/// by [min].
int emitPrecedingCommentsAndNewlines(Token token, {min: 0}) {
-
+
var comment = token.precedingComments;
var currentToken = comment != null ? comment : token;
-
+
//Handle EOLs before newlines
if (isAtEOL(comment)) {
emitComment(comment, previousToken);
comment = comment.next;
currentToken = comment != null ? comment : token;
}
-
+
var lines = max(min, countNewlinesBetween(previousToken, currentToken));
writer.newlines(lines);
-
+
var previousToken = currentToken.previous;
-
+
while (comment != null) {
emitComment(comment, previousToken);
-
+
var nextToken = comment.next != null ? comment.next : token;
var newlines = calculateNewlinesBetweenComments(comment, nextToken);
if (newlines > 0) {
@@ -1121,7 +1260,7 @@
} else if (!isEOF(token)) {
space();
}
-
+
previousToken = comment;
comment = comment.next;
}
@@ -1130,9 +1269,17 @@
return lines;
}
+
+ ensureTrailingNewline() {
+ if (writer.lastToken is! NewlineToken) {
+ writer.newline();
+ }
+ }
+
+
/// Test if this [comment] is at the end of a line.
- bool isAtEOL(Token comment) =>
- comment != null && comment.toString().trim().startsWith(twoSlashes) &&
+ bool isAtEOL(Token comment) =>
+ comment != null && comment.toString().trim().startsWith(twoSlashes) &&
sameLine(comment, previousToken);
/// Emit this [comment], inserting leading whitespace if appropriate.
@@ -1144,17 +1291,14 @@
space();
}
}
-
+
append(comment.toString().trim());
}
- /// Test if this token is an EOF token.
- bool isEOF(Token token) => token.type == TokenType.EOF;
-
/// Count spaces between these tokens. Tokens on different lines return 0.
- int countSpacesBetween(Token last, Token current) => isEOF(last) ||
- countNewlinesBetween(last, current) > 0 ? 0 : current.offset - last.end;
-
+ int countSpacesBetween(Token last, Token current) => isEOF(last) ||
+ countNewlinesBetween(last, current) > 0 ? 0 : current.offset - last.end;
+
/// Count the blanks between these two nodes.
int countBlankLinesBetween(ASTNode lastNode, ASTNode currentNode) =>
countNewlinesBetween(lastNode.endToken, currentNode.beginToken);
@@ -1175,37 +1319,37 @@
return linesBetween(last.end - 1, current.offset);
}
-
+
/// Calculate the newlines that should separate these comments.
int calculateNewlinesBetweenComments(Token last, Token current) {
// Insist on a newline after doc comments or single line comments
// (NOTE that EOL comments have already been processed).
if (isOldSingleLineDocComment(last) || isSingleLineComment(last)) {
- return max(1, countNewlinesBetween(last, current));
+ return max(1, countNewlinesBetween(last, current));
} else {
return countNewlinesBetween(last, current);
}
}
-
+
/// Single line multi-line comments (e.g., '/** like this */').
bool isOldSingleLineDocComment(Token comment) =>
comment.lexeme.startsWith(r'/**') && singleLine(comment);
-
+
/// Test if this [token] spans just one line.
bool singleLine(Token token) => linesBetween(token.offset, token.end) < 1;
/// Test if token [first] is on the same line as [second].
bool sameLine(Token first, Token second) =>
countNewlinesBetween(first, second) == 0;
-
+
/// Test if this is a multi-line [comment] (e.g., '/* ...' or '/** ...')
- bool isMultiLineComment(Token comment) =>
+ bool isMultiLineComment(Token comment) =>
comment.type == TokenType.MULTI_LINE_COMMENT;
-
+
/// Test if this is a single-line [comment] (e.g., '// ...')
- bool isSingleLineComment(Token comment) =>
+ bool isSingleLineComment(Token comment) =>
comment.type == TokenType.SINGLE_LINE_COMMENT;
-
+
/// Test if this [comment] is a block comment (e.g., '/* like this */')..
bool isBlock(Token comment) =>
isMultiLineComment(comment) && singleLine(comment);
diff --git a/pkg/analyzer_experimental/lib/src/services/writer.dart b/pkg/analyzer_experimental/lib/src/services/writer.dart
index d73a1fa..d7f8fd3 100644
--- a/pkg/analyzer_experimental/lib/src/services/writer.dart
+++ b/pkg/analyzer_experimental/lib/src/services/writer.dart
@@ -41,9 +41,7 @@
String toString() {
var buffer = new StringBuffer();
- for (var tok in tokens) {
- buffer.write(tok.toString());
- }
+ tokens.forEach((tok) => buffer.write(tok.toString()));
return buffer.toString();
}
@@ -84,28 +82,29 @@
final String lineSeparator;
int indentCount = 0;
-
- SourceWriter({this.indentCount: 0, this.lineSeparator: '\n'}) {
+
+ LineToken _lastToken;
+
+ SourceWriter({this.indentCount: 0, this.lineSeparator: NEW_LINE}) {
currentLine = new Line(indent: indentCount);
}
+ LineToken get lastToken => _lastToken;
+
+ _addToken(LineToken token) {
+ _lastToken = token;
+ currentLine.addToken(token);
+ }
+
void indent() {
++indentCount;
}
- void unindent() {
- --indentCount;
- }
-
- void print(x) {
- currentLine.addToken(new LineToken(x));
- }
-
void newline() {
if (currentLine.isWhitespace()) {
currentLine.tokens.clear();
}
- currentLine.addToken(new NewlineToken(this.lineSeparator));
+ _addToken(new NewlineToken(this.lineSeparator));
buffer.write(currentLine.toString());
currentLine = new Line(indent: indentCount);
}
@@ -116,6 +115,15 @@
}
}
+ void print(x) {
+ _addToken(new LineToken(x));
+ }
+
+ void println(String s) {
+ print(s);
+ newline();
+ }
+
void space() {
spaces(1);
}
@@ -123,12 +131,11 @@
void spaces(n) {
currentLine.addSpaces(n);
}
-
- void println(String s) {
- print(s);
- newline();
+
+ void unindent() {
+ --indentCount;
}
-
+
String toString() {
var source = new StringBuffer(buffer.toString());
if (!currentLine.isWhitespace()) {
@@ -139,7 +146,7 @@
}
-
+const NEW_LINE = '\n';
const SPACE = ' ';
const SPACES = const [
'',
diff --git a/pkg/analyzer_experimental/test/services/formatter_test.dart b/pkg/analyzer_experimental/test/services/formatter_test.dart
index afd2fc7..37147f7 100644
--- a/pkg/analyzer_experimental/test/services/formatter_test.dart
+++ b/pkg/analyzer_experimental/test/services/formatter_test.dart
@@ -46,7 +46,7 @@
'class A {\n'
' }',
'class A {\n'
- '}'
+ '}\n'
);
});
@@ -64,7 +64,7 @@
'class A { int meaningOfLife() => 42; }',
'class A {\n'
' int meaningOfLife() => 42;\n'
- '}'
+ '}\n'
);
});
@@ -162,7 +162,7 @@
'library a; class B { }',
'library a;\n'
'class B {\n'
- '}'
+ '}\n'
);
});
@@ -196,7 +196,7 @@
'import "foo";\n\n'
'//Killer class\n'
'class A {\n'
- '}'
+ '}\n'
);
});
@@ -410,7 +410,7 @@
'}\n'
);
});
-
+
test('CU - mixed comments', () {
expectCUFormatsTo(
'library foo;\n'
@@ -428,17 +428,17 @@
'\n'
'// Comment 2\n'
'\n'
- '/* Comment 3 */'
+ '/* Comment 3 */\n'
);
});
-
+
test('CU - comments (EOF)', () {
expectCUFormatsTo(
'library foo; //zamm',
'library foo; //zamm\n' //<-- note extra NEWLINE
);
- });
-
+ });
+
test('CU - comments (0)', () {
expectCUFormatsTo(
'library foo; //zamm\n'
@@ -458,11 +458,11 @@
'/* foo */ /* bar */\n'
);
});
-
+
test('CU - comments (2)', () {
expectCUFormatsTo(
'/** foo */ /** bar */\n',
- '/** foo */\n'
+ '/** foo */\n'
'/** bar */\n'
);
});
@@ -479,10 +479,10 @@
'class X { //X!\n'
'}',
'class X { //X!\n'
- '}'
+ '}\n'
);
});
-
+
test('CU - comments (5)', () {
expectCUFormatsTo(
'//comment one\n\n'
@@ -490,22 +490,22 @@
'//comment one\n\n'
'//comment two\n\n'
);
- });
-
+ });
+
test('CU - comments (6)', () {
expectCUFormatsTo(
'var x; //x\n',
'var x; //x\n'
);
- });
+ });
test('CU - comments (6)', () {
expectCUFormatsTo(
'var /* int */ x; //x\n',
'var /* int */ x; //x\n'
);
- });
-
+ });
+
test('CU - comments (7)', () {
expectCUFormatsTo(
'library foo;\n'
@@ -534,7 +534,13 @@
'int x;\n'
);
});
-
+
+ test('CU - EOF nl', () {
+ expectCUFormatsTo(
+ 'var x = 1;',
+ 'var x = 1;\n'
+ );
+ });
test('CU - constructor', () {
expectCUFormatsTo(
@@ -671,12 +677,30 @@
'var numbers = <int>[1, 2, (3 + 4)];'
);
});
+
+ test('stmt (lists)', () {
+ expectStmtFormatsTo(
+ 'var l = [1,2,3,4];',
+ 'var l = [1, 2, 3, 4];'
+ );
+ //Dangling ','
+ expectStmtFormatsTo(
+ 'var l = [1,];',
+ 'var l = [1,];'
+ );
+ });
test('stmt (maps)', () {
expectStmtFormatsTo(
'var map = const {"foo": "bar", "fuz": null};',
'var map = const {"foo": "bar", "fuz": null};'
);
+
+ //Dangling ','
+ expectStmtFormatsTo(
+ 'var map = {"foo": "bar",};',
+ 'var map = {"foo": "bar",};'
+ );
});
test('stmt (try/catch)', () {
@@ -750,6 +774,51 @@
});
+ /// Token streams
+ group('token streams', () {
+
+ test('string tokens', () {
+ expectTokenizedEqual('class A{}', 'class A{ }');
+ expectTokenizedEqual('class A{}', 'class A{\n }\n');
+ expectTokenizedEqual('class A {}', 'class A{ }');
+ expectTokenizedEqual(' class A {}', 'class A{ }');
+ });
+
+ test('string tokens - w/ comments', () {
+ expectTokenizedEqual('//foo\nint bar;', '//foo\nint bar;');
+ expectTokenizedNotEqual('int bar;', '//foo\nint bar;');
+ expectTokenizedNotEqual('//foo\nint bar;', 'int bar;');
+ });
+
+ test('INDEX', () {
+ /// '[' ']' => '[]'
+ var t1 = openSqBracket()..setNext(closeSqBracket()..setNext(eof()));
+ var t2 = index()..setNext(eof());
+ expectStreamsEqual(t1, t2);
+ });
+
+ test('GT_GT', () {
+ /// '>' '>' => '>>'
+ var t1 = gt()..setNext(gt()..setNext(eof()));
+ var t2 = gt_gt()..setNext(eof());
+ expectStreamsEqual(t1, t2);
+ });
+
+ test('t1 < t2', () {
+ var t1 = string('foo')..setNext(eof());
+ var t2 = string('foo')..setNext(string('bar')..setNext(eof()));
+ expectStreamsNotEqual(t1, t2);
+ });
+
+ test('t1 > t2', () {
+ var t1 = string('foo')..setNext(string('bar')..setNext(eof()));
+ var t2 = string('foo')..setNext(eof());
+ expectStreamsNotEqual(t1, t2);
+ });
+
+ });
+
+
/// Line tests
group('line', () {
@@ -853,8 +922,21 @@
}
-Token classKeyword(int offset) =>
- new KeywordToken(Keyword.CLASS, offset);
+Token closeSqBracket() => new Token(TokenType.CLOSE_SQUARE_BRACKET, 0);
+
+Token eof() => new Token(TokenType.EOF, 0);
+
+Token gt() => new Token(TokenType.GT, 0);
+
+Token gt_gt() => new Token(TokenType.GT_GT, 0);
+
+Token index() => new Token(TokenType.INDEX, 0);
+
+Token openSqBracket() => new BeginToken(TokenType.OPEN_SQUARE_BRACKET, 0);
+
+Token string(String lexeme) => new StringToken(TokenType.STRING, lexeme, 0);
+
+Token classKeyword(int offset) => new KeywordToken(Keyword.CLASS, offset);
Token identifier(String value, int offset) =>
new StringToken(TokenType.IDENTIFIER, value, offset);
@@ -878,6 +960,23 @@
String formatStatement(src, {options: const FormatterOptions()}) =>
new CodeFormatter(options).format(CodeKind.STATEMENT, src);
+Token tokenize(String str) => new StringScanner(null, str, null).tokenize();
+
+
+expectTokenizedEqual(String s1, String s2) =>
+ expectStreamsEqual(tokenize(s1), tokenize(s2));
+
+expectTokenizedNotEqual(String s1, String s2) =>
+ expect(()=> expectStreamsEqual(tokenize(s1), tokenize(s2)),
+ throwsA(new isInstanceOf<FormatterException>()));
+
+expectStreamsEqual(Token t1, Token t2) =>
+ new TokenStreamComparator(null, t1, t2).verifyEquals();
+
+expectStreamsNotEqual(Token t1, Token t2) =>
+ expect(() => new TokenStreamComparator(null, t1, t2).verifyEquals(),
+ throwsA(new isInstanceOf<FormatterException>()));
+
expectCUFormatsTo(src, expected) => expect(formatCU(src), equals(expected));
expectStmtFormatsTo(src, expected) => expect(formatStatement(src),
diff --git a/pkg/args/lib/args.dart b/pkg/args/lib/args.dart
index 0950654..0b877cd 100644
--- a/pkg/args/lib/args.dart
+++ b/pkg/args/lib/args.dart
@@ -3,141 +3,144 @@
// BSD-style license that can be found in the LICENSE file.
/**
- * This library lets you define parsers for parsing raw command-line arguments
- * into a set of options and values using [GNU][] and [POSIX][] style options.
+ * Parser support for transforming raw command-line arguments into a set
+ * of options and values.
*
- * ## Installing ##
+ * This library supports [GNU][] and [POSIX][] style options, and it works
+ * in both server-side and client-side apps.
*
- * Use [pub][] to install this package. Add the following to your `pubspec.yaml`
- * file.
- *
- * dependencies:
- * args: any
- *
- * Then run `pub install`.
- *
- * For more information, see the
+ * For information on installing this library, see the
* [args package on pub.dartlang.org](http://pub.dartlang.org/packages/args).
+ * Here's an example of importing this library:
*
- * ## Defining options ##
+ * import 'package:args/args.dart';
*
- * To use this library, you create an [ArgParser] object which will contain
- * the set of options you support:
+ * ## Defining options
+ *
+ * To use this library, first create an [ArgParser]:
*
* var parser = new ArgParser();
*
- * Then you define a set of options on that parser using [addOption()] and
- * [addFlag()]. The minimal way to create an option is:
+ * Then define a set of options on that parser using [addOption()] and
+ * [addFlag()]. Here's the minimal way to create an option named "name":
*
* parser.addOption('name');
*
- * This creates an option named "name". Options must be given a value on the
- * command line. If you have a simple on/off flag, you can instead use:
+ * When an option can only be set or unset (as opposed to taking a string
+ * value), use a flag:
*
* parser.addFlag('name');
*
- * Flag options will, by default, accept a 'no-' prefix to negate the option.
- * This can be disabled like so:
+ * Flag options, by default, accept a 'no-' prefix to negate the option.
+ * You can disable the 'no-' prefix using the `negatable` parameter:
*
* parser.addFlag('name', negatable: false);
*
- * (From here on out "option" will refer to both "regular" options and flags.
- * In cases where the distinction matters, we'll use "non-flag option".)
+ * **Terminology note:**
+ * From here on out, the term _option_ refers to both regular options and
+ * flags. In cases where the distinction matters, this documentation uses
+ * the term _non-flag option._
*
- * Options may have an optional single-character abbreviation:
+ * Options can have an optional single-character abbreviation, specified
+ * with the `abbr` parameter:
*
* parser.addOption('mode', abbr: 'm');
* parser.addFlag('verbose', abbr: 'v');
*
- * They may also specify a default value. The default value will be used if the
- * option isn't provided:
+ * Options can also have a default value, specified with the `defaultsTo`
+ * parameter. The default value is used when arguments don't specify the
+ * option.
*
* parser.addOption('mode', defaultsTo: 'debug');
* parser.addFlag('verbose', defaultsTo: false);
*
- * The default value for non-flag options can be any [String]. For flags, it
- * must be a [bool].
+ * The default value for non-flag options can be any [String]. For flags,
+ * it must be a [bool].
*
- * To validate non-flag options, you may provide an allowed set of values. When
- * you do, it will throw a [FormatException] when you parse the arguments if
- * the value for an option is not in the allowed set:
+ * To validate a non-flag option, you can use the `allowed` parameter to
+ * provide an allowed set of values. When you do, the parser throws a
+ * [FormatException] if the value for an option is not in the allowed set.
+ * Here's an example of specifying allowed values:
*
* parser.addOption('mode', allowed: ['debug', 'release']);
*
- * You can provide a callback when you define an option. When you later parse
- * a set of arguments, the callback for that option will be invoked with the
- * value provided for it:
+ * You can use the `callback` parameter to associate a function with an
+ * option. Later, when parsing occurs, the callback function is invoked
+ * with the value of the option:
*
* parser.addOption('mode', callback: (mode) => print('Got mode $mode));
* parser.addFlag('verbose', callback: (verbose) {
* if (verbose) print('Verbose');
* });
*
- * The callback for each option will *always* be called when you parse a set of
- * arguments. If the option isn't provided in the args, the callback will be
- * passed the default value, or `null` if there is none set.
+ * The callbacks for all options are called whenever a set of arguments
+ * is parsed. If an option isn't provided in the args, its callback is
+ * passed the default value, or `null` if no default value is set.
*
- * ## Parsing arguments ##
+ * ## Parsing arguments
*
- * Once you have an [ArgParser] set up with some options and flags, you use it
- * by calling [ArgParser.parse()] with a set of arguments:
+ * Once you have an [ArgParser] set up with some options and flags, you
+ * use it by calling [ArgParser.parse()] with a set of arguments:
*
* var results = parser.parse(['some', 'command', 'line', 'args']);
*
- * These will usually come from `new Options().arguments`, but you can pass in
- * any list of strings. It returns an instance of [ArgResults]. This is a
- * map-like object that will return the value of any parsed option.
+ * These arguments usually come from dart:io's Options class
+ * (`new Options().arguments`), but you can pass in any list of strings.
+ * The parse() method returns an instance of [ArgResults], a map-like
+ * object that contains the values of the parsed options.
*
* var parser = new ArgParser();
* parser.addOption('mode');
* parser.addFlag('verbose', defaultsTo: true);
- * var results = parser.parse('['--mode', 'debug', 'something', 'else']);
+ * var results = parser.parse(['--mode', 'debug', 'something', 'else']);
*
* print(results['mode']); // debug
* print(results['verbose']); // true
*
- * The [parse()] method will stop as soon as it reaches `--` or anything that
- * it doesn't recognize as an option, flag, or option value. If there are still
- * arguments left, they will be provided to you in
- * [ArgResults.rest].
+ * By default, the parse() method stops as soon as it reaches `--` by itself
+ * or anything that the parser doesn't recognize as an option, flag, or
+ * option value. If arguments still remain, they go into [ArgResults.rest].
*
* print(results.rest); // ['something', 'else']
*
- * ## Specifying options ##
+ * To continue to parse options found after non-option arguments, call
+ * parse() with `allowTrailingOptions: true`.
*
- * To actually pass in options and flags on the command line, use GNU or POSIX
- * style. If you define an option like:
+ * ## Specifying options
+ *
+ * To actually pass in options and flags on the command line, use GNU or
+ * POSIX style. Consider this option:
*
* parser.addOption('name', abbr: 'n');
*
- * Then a value for it can be specified on the command line using any of:
+ * You can specify its value on the command line using any of the following:
*
* --name=somevalue
* --name somevalue
* -nsomevalue
* -n somevalue
*
- * Given this flag:
+ * Consider this flag:
*
* parser.addFlag('name', abbr: 'n');
*
- * You can set it on using one of:
+ * You can set it to true using one of the following:
*
* --name
* -n
*
- * Or set it off using:
+ * You can set it to false using the following:
*
* --no-name
*
- * Multiple flag abbreviation can also be collapsed into a single argument. If
- * you define:
+ * Multiple flag abbreviations can be collapsed into a single argument. Say
+ * you define these flags:
*
* parser.addFlag('verbose', abbr: 'v');
* parser.addFlag('french', abbr: 'f');
* parser.addFlag('iambic-pentameter', abbr: 'i');
*
- * Then all three flags could be set using:
+ * You can set all three flags at once:
*
* -vfi
*
@@ -149,9 +152,9 @@
* var results = parser.parse(['--mode', 'on', '--mode', 'off']);
* print(results['mode']); // prints 'off'
*
- * If you need multiple values, set the [allowMultiple] flag. In that
- * case the option can occur multiple times and when parsing arguments a
- * List of values will be returned:
+ * If you need multiple values, set the `allowMultiple` parameter. In that
+ * case the option can occur multiple times, and the parse() method returns
+ * a list of values:
*
* var parser = new ArgParser();
* parser.addOption('mode', allowMultiple: true);
@@ -160,68 +163,73 @@
*
* ## Defining commands ##
*
- * In addition to *options*, you can also define *commands*. A command is a
- * named argument that has its own set of options. For example, when you run:
+ * In addition to *options*, you can also define *commands*. A command is
+ * a named argument that has its own set of options. For example, consider
+ * this shell command:
*
* $ git commit -a
*
- * The executable is `git`, the command is `commit`, and the `-a` option is an
- * option passed to the command. You can add a command like so:
+ * The executable is `git`, the command is `commit`, and the `-a` option is
+ * an option passed to the command. You can add a command using the
+ * [addCommand] method:
*
* var parser = new ArgParser();
* var command = parser.addCommand('commit');
*
- * It returns another [ArgParser] which you can then use to define options
- * specific to that command. If you already have an [ArgParser] for the
- * command's options, you can pass it to [addCommand]:
+ * The addCommand() method returns another [ArgParser], which you can then
+ * use to define options specific to that command. If you already have an
+ * [ArgParser] for the command's options, you can pass it to addCommand:
*
* var parser = new ArgParser();
* var command = new ArgParser();
* parser.addCommand('commit', command);
*
- * The [ArgParser] for a command can then define whatever options or flags:
+ * The [ArgParser] for a command can then define options or flags:
*
* command.addFlag('all', abbr: 'a');
*
* You can add multiple commands to the same parser so that a user can select
- * one from a range of possible commands. When an argument list is parsed,
+ * one from a range of possible commands. When parsing an argument list,
* you can then determine which command was entered and what options were
* provided for it.
*
* var results = parser.parse(['commit', '-a']);
- * print(results.command.name); // "commit"
- * print(results.command['a']); // true
+ * print(results.command.name); // "commit"
+ * print(results.command['all']); // true
*
* Options for a command must appear after the command in the argument list.
* For example, given the above parser, "git -a commit" is *not* valid. The
- * parser will try to find the right-most command that accepts an option. For
+ * parser tries to find the right-most command that accepts an option. For
* example:
*
* var parser = new ArgParser();
* parser.addFlag('all', abbr: 'a');
- * var command = new ArgParser().addCommand('commit');
- * parser.addFlag('all', abbr: 'a');
+ * var command = parser.addCommand('commit');
+ * command.addFlag('all', abbr: 'a');
+ *
* var results = parser.parse(['commit', '-a']);
- * print(results.command['a']); // true
+ * print(results.command['all']); // true
*
- * Here, both the top-level parser and the "commit" command can accept a "-a"
- * (which is probably a bad command line interface, admittedly). In that case,
- * when "-a" appears after "commit", it will be applied to that command. If it
- * appears to the left of "commit", it will be given to the top-level parser.
+ * Here, both the top-level parser and the "commit" command can accept a
+ * "-a" (which is probably a bad command line interface, admittedly). In
+ * that case, when "-a" appears after "commit", it is applied to that
+ * command. If it appears to the left of "commit", it is given to the
+ * top-level parser.
*
- * ## Displaying usage ##
+ * ## Displaying usage
*
- * This library can also be used to automatically generate nice usage help
- * text like you get when you run a program with `--help`. To use this, you
- * will also want to provide some help text when you create your options. To
- * define help text for the entire option, do:
+ * You can automatically generate nice help text, suitable for use as the
+ * output of `--help`. To display good usage information, you should
+ * provide some help text when you create your options.
+ *
+ * To define help text for an entire option, use the `help` parameter:
*
* parser.addOption('mode', help: 'The compiler configuration',
* allowed: ['debug', 'release']);
* parser.addFlag('verbose', help: 'Show additional diagnostic info');
*
* For non-flag options, you can also provide detailed help for each expected
- * value using a map:
+ * value by using the `allowedHelp` parameter:
*
* parser.addOption('arch', help: 'The architecture to compile for',
* allowedHelp: {
@@ -229,11 +237,11 @@
* 'arm': 'ARM Holding 32-bit chip'
* });
*
- * If you define a set of options like the above, then calling this:
+ * To display the help, use the ArgParser getUsage() method:
*
* print(parser.getUsage());
- *
- * Will display something like:
+ *
+ * The resulting string looks something like this:
*
* --mode The compiler configuration
* [debug, release]
@@ -244,14 +252,12 @@
* [arm] ARM Holding 32-bit chip
* [ia32] Intel x86
*
- * To assist the formatting of the usage help, single line help text will
- * be followed by a single new line. Options with multi-line help text
- * will be followed by two new lines. This provides spatial diversity between
- * options.
+ * To assist the formatting of the usage help, single-line help text is
+ * followed by a single new line. Options with multi-line help text are
+ * followed by two new lines. This provides spatial diversity between options.
*
* [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
* [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces
- * [pub]: http://pub.dartlang.org
*/
library args;
diff --git a/pkg/async_helper/lib/async_helper.dart b/pkg/async_helper/lib/async_helper.dart
index 84df557..808e198 100644
--- a/pkg/async_helper/lib/async_helper.dart
+++ b/pkg/async_helper/lib/async_helper.dart
@@ -8,13 +8,18 @@
/// finished] even if the asynchronous operations fail).
/// Tests which can't use the unittest framework should use the helper functions
/// in this library.
-/// This library provides two methods
+/// This library provides four methods
/// - asyncStart(): Needs to be called before an asynchronous operation is
/// scheduled.
/// - asyncEnd(): Needs to be called as soon as the asynchronous operation
/// ended.
+/// - asyncSuccess(_): Variant of asyncEnd useful together with Future.then.
+/// - asyncTest(f()): Helper method that wraps a computation that returns a
+/// Future with matching calls to asyncStart() and
+/// asyncSuccess(_).
/// After the last asyncStart() called was matched with a corresponding
-/// asyncEnd() call, the testing driver will be notified that the tests is done.
+/// asyncEnd() or asyncSuccess(_) call, the testing driver will be notified that
+/// the tests is done.
library async_helper;
@@ -32,6 +37,7 @@
return new Exception('Fatal: $msg. This is most likely a bug in your test.');
}
+/// Call this method before an asynchronous test is created.
void asyncStart() {
if (_initialized && _asyncLevel == 0) {
throw _buildException('asyncStart() was called even though we are done '
@@ -45,6 +51,7 @@
_asyncLevel++;
}
+/// Call this after an asynchronous test has ended successfully.
void asyncEnd() {
if (_asyncLevel <= 0) {
if (!_initialized) {
@@ -62,7 +69,26 @@
}
}
+/**
+ * Call this after an asynchronous test has ended successfully. This is a helper
+ * for calling [asyncEnd].
+ *
+ * This method intentionally has a signature that matches [:Future.then:] as a
+ * convenience for calling [asyncEnd] when a [:Future:] completes without error,
+ * like this:
+ *
+ * asyncStart();
+ * Future result = test();
+ * result.then(asyncSuccess);
+ */
+void asyncSuccess(_) => asyncEnd();
+
+/**
+ * Helper method for performing asynchronous tests involving [:Future:].
+ *
+ * [f] must return a [:Future:] for the test computation.
+ */
void asyncTest(f()) {
asyncStart();
- f().whenComplete(() => asyncEnd());
+ f().then(asyncSuccess);
}
\ No newline at end of file
diff --git a/pkg/barback/lib/src/utils.dart b/pkg/barback/lib/src/utils.dart
index 37816bc..95f498e 100644
--- a/pkg/barback/lib/src/utils.dart
+++ b/pkg/barback/lib/src/utils.dart
@@ -83,7 +83,7 @@
///
/// This returns a map whose keys are the return values of [fn] and whose values
/// are lists of each element in [iter] for which [fn] returned that key.
-Map groupBy(Iterable iter, fn(element)) {
+Map<Object, List> groupBy(Iterable iter, fn(element)) {
var map = {};
for (var element in iter) {
var list = map.putIfAbsent(fn(element), () => []);
diff --git a/pkg/csslib/lib/parser.dart b/pkg/csslib/lib/parser.dart
index a1cd715..9f7b3ff 100644
--- a/pkg/csslib/lib/parser.dart
+++ b/pkg/csslib/lib/parser.dart
@@ -13,6 +13,7 @@
import 'src/options.dart';
part 'src/analyzer.dart';
+part 'src/polyfill.dart';
part 'src/property.dart';
part 'src/token.dart';
part 'src/tokenizer_base.dart';
@@ -44,7 +45,8 @@
// TODO(terry): Remove nested name parameter.
/** Parse and analyze the CSS file. */
-StyleSheet compile(var input, {List errors, List options, bool nested: true}) {
+StyleSheet compile(var input,
+ {List errors, List options, bool nested: true, bool polyfill: false}) {
var source = _inputAsString(input);
_createMessages(errors: errors, options: options);
@@ -55,6 +57,11 @@
analyze([tree], errors: errors, options: options);
+ if (polyfill) {
+ var processCss = new PolyFill(messages, true);
+ processCss.process(tree);
+ }
+
return tree;
}
diff --git a/pkg/csslib/lib/src/polyfill.dart b/pkg/csslib/lib/src/polyfill.dart
new file mode 100644
index 0000000..ab56771
--- /dev/null
+++ b/pkg/csslib/lib/src/polyfill.dart
@@ -0,0 +1,246 @@
+// Copyright (c) 2012, 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.
+
+part of csslib.parser;
+
+/**
+ * CSS polyfill emits CSS to be understood by older parsers that which do not
+ * understand (var, calc, etc.).
+ */
+class PolyFill {
+ final Messages _messages;
+ final bool _warningsAsErrors;
+
+ Set<StyleSheet> allStyleSheets = new Set<StyleSheet>();
+
+ /**
+ * [_pseudoElements] list of known pseudo attributes found in HTML, any
+ * CSS pseudo-elements 'name::custom-element' is mapped to the manged name
+ * associated with the pseudo-element key.
+ */
+ PolyFill(this._messages, this._warningsAsErrors);
+
+ /**
+ * Run the analyzer on every file that is a style sheet or any component that
+ * has a style tag.
+ */
+ void process(StyleSheet stylesheet) {
+ // TODO(terry): Process all imported stylesheets.
+
+ var styleSheets = processVars([stylesheet]);
+ allStyleSheets.addAll(styleSheets);
+
+ normalize();
+ }
+
+ void normalize() {
+ // Remove all var definitions for all style sheets analyzed.
+ for (var tree in allStyleSheets)
+ new _RemoveVarDefinitions().visitTree(tree);
+ }
+
+ List<StyleSheet> processVars(List<StyleSheet> styleSheets) {
+ // TODO(terry): Process all dependencies.
+ // Build list of all var definitions.
+ Map varDefs = new Map();
+ for (var tree in styleSheets) {
+ var allDefs = (new _VarDefinitions()..visitTree(tree)).found;
+ allDefs.forEach((key, value) {
+ varDefs[key] = value;
+ });
+ }
+
+ // Resolve all definitions to a non-VarUsage (terminal expression).
+ varDefs.forEach((key, value) {
+ for (var expr in (value.expression as Expressions).expressions) {
+ var def = _findTerminalVarDefinition(varDefs, value);
+ varDefs[key] = def;
+ }
+ });
+
+ // Resolve all var usages.
+ for (var tree in styleSheets) {
+ new _ResolveVarUsages(varDefs).visitTree(tree);
+ }
+
+ return styleSheets;
+ }
+}
+
+/**
+ * Find var- definitions in a style sheet.
+ * [found] list of known definitions.
+ */
+class _VarDefinitions extends Visitor {
+ final Map<String, VarDefinition> found = new Map();
+
+ void visitTree(StyleSheet tree) {
+ visitStyleSheet(tree);
+ }
+
+ visitVarDefinition(VarDefinition node) {
+ // Replace with latest variable definition.
+ found[node.definedName] = node;
+ super.visitVarDefinition(node);
+ }
+
+ void visitVarDefinitionDirective(VarDefinitionDirective node) {
+ visitVarDefinition(node.def);
+ }
+}
+
+/**
+ * Resolve any CSS expression which contains a var() usage to the ultimate real
+ * CSS expression value e.g.,
+ *
+ * var-one: var(two);
+ * var-two: #ff00ff;
+ *
+ * .test {
+ * color: var(one);
+ * }
+ *
+ * then .test's color would be #ff00ff
+ */
+class _ResolveVarUsages extends Visitor {
+ final Map<String, VarDefinition> varDefs;
+ bool inVarDefinition = false;
+ bool inUsage = false;
+ Expressions currentExpressions;
+
+ _ResolveVarUsages(this.varDefs);
+
+ void visitTree(StyleSheet tree) {
+ visitStyleSheet(tree);
+ }
+
+ void visitVarDefinition(VarDefinition varDef) {
+ inVarDefinition = true;
+ super.visitVarDefinition(varDef);
+ inVarDefinition = false;
+ }
+
+ void visitExpressions(Expressions node) {
+ currentExpressions = node;
+ super.visitExpressions(node);
+ currentExpressions = null;
+ }
+
+ void visitVarUsage(VarUsage node) {
+ // Don't process other var() inside of a varUsage. That implies that the
+ // default is a var() too. Also, don't process any var() inside of a
+ // varDefinition (they're just place holders until we've resolved all real
+ // usages.
+ if (!inUsage && !inVarDefinition && currentExpressions != null) {
+ var expressions = currentExpressions.expressions;
+ var index = expressions.indexOf(node);
+ assert(index >= 0);
+ var def = varDefs[node.name];
+ if (def != null) {
+ // Found a VarDefinition use it.
+ _resolveVarUsage(currentExpressions.expressions, index, def);
+ } else if (node.defaultValues.any((e) => e is VarUsage)) {
+ // Don't have a VarDefinition need to use default values resolve all
+ // default values.
+ var terminalDefaults = [];
+ for (var defaultValue in node.defaultValues) {
+ terminalDefaults.addAll(resolveUsageTerminal(defaultValue));
+ }
+ expressions.replaceRange(index, index + 1, terminalDefaults);
+ } else {
+ // No VarDefinition but default value is a terminal expression; use it.
+ expressions.replaceRange(index, index + 1, node.defaultValues);
+ }
+ }
+
+ inUsage = true;
+ super.visitVarUsage(node);
+ inUsage = false;
+ }
+
+ List<Expression> resolveUsageTerminal(VarUsage usage) {
+ var result = [];
+
+ var varDef = varDefs[usage.name];
+ var expressions;
+ if (varDef == null) {
+ // VarDefinition not found try the defaultValues.
+ expressions = usage.defaultValues;
+ } else {
+ // Use the VarDefinition found.
+ expressions = (varDef.expression as Expressions).expressions;
+ }
+
+ for (var expr in expressions) {
+ if (expr is VarUsage) {
+ // Get terminal value.
+ result.addAll(resolveUsageTerminal(expr));
+ }
+ }
+
+ // We're at a terminal just return the VarDefinition expression.
+ if (result.isEmpty && varDef != null) {
+ result = (varDef.expression as Expressions).expressions;
+ }
+
+ return result;
+ }
+
+ _resolveVarUsage(List<Expressions> expressions, int index,
+ VarDefinition def) {
+ var defExpressions = (def.expression as Expressions).expressions;
+ expressions.replaceRange(index, index + 1, defExpressions);
+ }
+}
+
+/** Remove all var definitions. */
+class _RemoveVarDefinitions extends Visitor {
+ void visitTree(StyleSheet tree) {
+ visitStyleSheet(tree);
+ }
+
+ void visitStyleSheet(StyleSheet ss) {
+ ss.topLevels.removeWhere((e) => e is VarDefinitionDirective);
+ super.visitStyleSheet(ss);
+ }
+
+ void visitDeclarationGroup(DeclarationGroup node) {
+ node.declarations.removeWhere((e) => e is VarDefinition);
+ super.visitDeclarationGroup(node);
+ }
+}
+
+/** Find terminal definition (non VarUsage implies real CSS value). */
+VarDefinition _findTerminalVarDefinition(Map<String, VarDefinition> varDefs,
+ VarDefinition varDef) {
+ var expressions = varDef.expression as Expressions;
+ for (var expr in expressions.expressions) {
+ if (expr is VarUsage) {
+ var usageName = (expr as VarUsage).name;
+ var foundDef = varDefs[usageName];
+
+ // If foundDef is unknown check if defaultValues; if it exist then resolve
+ // to terminal value.
+ if (foundDef == null) {
+ // We're either a VarUsage or terminal definition if in varDefs;
+ // either way replace VarUsage with it's default value because the
+ // VarDefinition isn't found.
+ var defaultValues = (expr as VarUsage).defaultValues;
+ var replaceExprs = expressions.expressions;
+ assert(replaceExprs.length == 1);
+ replaceExprs.replaceRange(0, 1, defaultValues);
+ return varDef;
+ }
+ if (foundDef is VarDefinition) {
+ return _findTerminalVarDefinition(varDefs, foundDef);
+ }
+ } else {
+ // Return real CSS property.
+ return varDef;
+ }
+ }
+
+ // Didn't point to a var definition that existed.
+ return varDef;
+}
diff --git a/pkg/csslib/test/testing.dart b/pkg/csslib/test/testing.dart
index e7b14bd..f2e1273 100644
--- a/pkg/csslib/test/testing.dart
+++ b/pkg/csslib/test/testing.dart
@@ -27,9 +27,11 @@
* CSS will allow any property/value pairs regardless of validity; all of our
* tests (by default) will ensure that the CSS is really valid.
*/
-StyleSheet compileCss(String cssInput, {List errors, List opts}) =>
+StyleSheet compileCss(String cssInput,
+ {List errors, List opts, bool polyfill: false}) =>
compile(cssInput, errors: errors, options: opts == null ?
- ['--no-colors', '--checked', '--warnings_as_errors', 'memory'] : opts);
+ ['--no-colors', '--checked', '--warnings_as_errors', 'memory'] : opts,
+ polyfill: polyfill);
/** CSS emitter walks the style sheet tree and emits readable CSS. */
var _emitCss = new CssPrinter();
diff --git a/pkg/csslib/test/var_test.dart b/pkg/csslib/test/var_test.dart
index afdc570..11e94be 100644
--- a/pkg/csslib/test/var_test.dart
+++ b/pkg/csslib/test/var_test.dart
@@ -623,6 +623,31 @@
expect(prettyPrint(stylesheet), generated2);
}
+void polyfill() {
+ var errors = [];
+ var input = r'''
+@color-background: red;
+@color-foreground: blue;
+.test {
+ background-color: @color-background;
+ color: @color-foreground;
+}''';
+
+ var generated = r'''.test {
+ background-color: #f00;
+ color: #00f;
+}''';
+
+ var stylesheet = compileCss(input, errors: errors,
+ opts: ['--no-colors', 'memory'], polyfill: true);
+
+ expect(stylesheet != null, true);
+ expect(errors.isEmpty, true, reason: errors.toString());
+ expect(prettyPrint(stylesheet), generated);
+}
+
+
+
main() {
test('Simple var', simpleVar);
test('Expressions var', expressionsVar);
@@ -631,4 +656,5 @@
test('Var syntax', testVar);
test('Cycles var', cyclesVar);
test('Less syntax', testLess);
+ test('Polyfill', polyfill);
}
diff --git a/pkg/custom_element/lib/custom-elements.debug.js b/pkg/custom_element/lib/custom-elements.debug.js
index 400730b..be0a360 100644
--- a/pkg/custom_element/lib/custom-elements.debug.js
+++ b/pkg/custom_element/lib/custom-elements.debug.js
@@ -1045,23 +1045,23 @@
*
* When a registered element is created, a `readyCallback` method is called
* in the scope of the element. The `readyCallback` method can be specified on
- * either `inOptions.prototype` or `inOptions.lifecycle` with the latter taking
+ * either `options.prototype` or `options.lifecycle` with the latter taking
* precedence.
*
* @method register
- * @param {String} inName The tag name to register. Must include a dash ('-'),
+ * @param {String} name The tag name to register. Must include a dash ('-'),
* for example 'x-component'.
- * @param {Object} inOptions
- * @param {String} [inOptions.extends]
+ * @param {Object} options
+ * @param {String} [options.extends]
* (_off spec_) Tag name of an element to extend (or blank for a new
* element). This parameter is not part of the specification, but instead
* is a hint for the polyfill because the extendee is difficult to infer.
* Remember that the input prototype must chain to the extended element's
* prototype (or HTMLElement.prototype) regardless of the value of
* `extends`.
- * @param {Object} inOptions.prototype The prototype to use for the new
+ * @param {Object} options.prototype The prototype to use for the new
* element. The prototype must inherit from HTMLElement.
- * @param {Object} [inOptions.lifecycle]
+ * @param {Object} [options.lifecycle]
* Callbacks that fire at important phases in the life of the custom
* element.
*
@@ -1078,18 +1078,23 @@
* });
* @return {Function} Constructor for the newly registered type.
*/
- function register(inName, inOptions) {
- //console.warn('document.register("' + inName + '", ', inOptions, ')');
+ function register(name, options) {
+ //console.warn('document.register("' + name + '", ', options, ')');
// construct a defintion out of options
- // TODO(sjmiles): probably should clone inOptions instead of mutating it
- var definition = inOptions || {};
- if (!inName) {
+ // TODO(sjmiles): probably should clone options instead of mutating it
+ var definition = options || {};
+ if (!name) {
// TODO(sjmiles): replace with more appropriate error (EricB can probably
// offer guidance)
- throw new Error('Name argument must not be empty');
+ throw new Error('document.register: first argument `name` must not be empty');
+ }
+ if (name.indexOf('-') < 0) {
+ // TODO(sjmiles): replace with more appropriate error (EricB can probably
+ // offer guidance)
+ throw new Error('document.register: first argument `name` must contain a dash (\'-\'). Argument was \'' + String(name) + '\'.');
}
// record name
- definition.name = inName;
+ definition.name = name;
// must have a prototype, default to an extension of HTMLElement
// TODO(sjmiles): probably should throw if no prototype, check spec
if (!definition.prototype) {
@@ -1112,7 +1117,7 @@
// overrides to implement attributeChanged callback
overrideAttributeApi(definition.prototype);
// 7.1.5: Register the DEFINITION with DOCUMENT
- registerDefinition(inName, definition);
+ registerDefinition(name, definition);
// 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
// 7.1.8. Return the output of the previous step.
definition.ctor = generateConstructor(definition);
@@ -1127,40 +1132,40 @@
return definition.ctor;
}
- function ancestry(inExtends) {
- var extendee = registry[inExtends];
+ function ancestry(extnds) {
+ var extendee = registry[extnds];
if (extendee) {
return ancestry(extendee.extends).concat([extendee]);
}
return [];
}
- function resolveTagName(inDefinition) {
+ function resolveTagName(definition) {
// if we are explicitly extending something, that thing is our
// baseTag, unless it represents a custom component
- var baseTag = inDefinition.extends;
+ var baseTag = definition.extends;
// if our ancestry includes custom components, we only have a
// baseTag if one of them does
- for (var i=0, a; (a=inDefinition.ancestry[i]); i++) {
+ for (var i=0, a; (a=definition.ancestry[i]); i++) {
baseTag = a.is && a.tag;
}
// our tag is our baseTag, if it exists, and otherwise just our name
- inDefinition.tag = baseTag || inDefinition.name;
+ definition.tag = baseTag || definition.name;
if (baseTag) {
// if there is a base tag, use secondary 'is' specifier
- inDefinition.is = inDefinition.name;
+ definition.is = definition.name;
}
}
- function resolvePrototypeChain(inDefinition) {
+ function resolvePrototypeChain(definition) {
// if we don't support __proto__ we need to locate the native level
// prototype for precise mixing in
if (!Object.__proto__) {
// default prototype
var native = HTMLElement.prototype;
// work out prototype when using type-extension
- if (inDefinition.is) {
- var inst = document.createElement(inDefinition.tag);
+ if (definition.is) {
+ var inst = document.createElement(definition.tag);
native = Object.getPrototypeOf(inst);
}
// ensure __proto__ reference is installed at each point on the prototype
@@ -1168,7 +1173,7 @@
// NOTE: On platforms without __proto__, a mixin strategy is used instead
// of prototype swizzling. In this case, this generated __proto__ provides
// limited support for prototype traversal.
- var proto = inDefinition.prototype, ancestor;
+ var proto = definition.prototype, ancestor;
while (proto && (proto !== native)) {
var ancestor = Object.getPrototypeOf(proto);
proto.__proto__ = ancestor;
@@ -1176,49 +1181,49 @@
}
}
// cache this in case of mixin
- inDefinition.native = native;
+ definition.native = native;
}
// SECTION 4
- function instantiate(inDefinition) {
+ function instantiate(definition) {
// 4.a.1. Create a new object that implements PROTOTYPE
// 4.a.2. Let ELEMENT by this new object
//
// the custom element instantiation algorithm must also ensure that the
// output is a valid DOM element with the proper wrapper in place.
//
- return upgrade(domCreateElement(inDefinition.tag), inDefinition);
+ return upgrade(domCreateElement(definition.tag), definition);
}
- function upgrade(inElement, inDefinition) {
+ function upgrade(element, definition) {
// some definitions specify an 'is' attribute
- if (inDefinition.is) {
- inElement.setAttribute('is', inDefinition.is);
+ if (definition.is) {
+ element.setAttribute('is', definition.is);
}
- // make 'element' implement inDefinition.prototype
- implement(inElement, inDefinition);
+ // make 'element' implement definition.prototype
+ implement(element, definition);
// flag as upgraded
- inElement.__upgraded__ = true;
- // there should never be a shadow root on inElement at this point
+ element.__upgraded__ = true;
+ // there should never be a shadow root on element at this point
// we require child nodes be upgraded before `created`
- scope.upgradeSubtree(inElement);
+ scope.upgradeSubtree(element);
// lifecycle management
- created(inElement);
+ created(element);
// OUTPUT
- return inElement;
+ return element;
}
- function implement(inElement, inDefinition) {
+ function implement(element, definition) {
// prototype swizzling is best
if (Object.__proto__) {
- inElement.__proto__ = inDefinition.prototype;
+ element.__proto__ = definition.prototype;
} else {
// where above we can re-acquire inPrototype via
// getPrototypeOf(Element), we cannot do so when
// we use mixin, so we install a magic reference
- customMixin(inElement, inDefinition.prototype, inDefinition.native);
- inElement.__proto__ = inDefinition.prototype;
+ customMixin(element, definition.prototype, definition.native);
+ element.__proto__ = definition.prototype;
}
}
@@ -1246,10 +1251,10 @@
}
}
- function created(inElement) {
+ function created(element) {
// invoke createdCallback
- if (inElement.createdCallback) {
- inElement.createdCallback();
+ if (element.createdCallback) {
+ element.createdCallback();
}
}
@@ -1282,34 +1287,55 @@
var registry = {};
- function registerDefinition(inName, inDefinition) {
- if (registry[inName]) {
- throw new Error('Cannot register a tag more than once');
+ function registerDefinition(name, definition) {
+ if (registry[name]) {
+ throw new Error('a type with that name is already registered.');
}
- registry[inName] = inDefinition;
+ registry[name] = definition;
}
- function generateConstructor(inDefinition) {
+ function generateConstructor(definition) {
return function() {
- return instantiate(inDefinition);
+ return instantiate(definition);
};
}
function createElement(tag, typeExtension) {
- // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could
- // error check it, or perhaps there should only ever be one argument
var definition = registry[typeExtension || tag];
if (definition) {
- return new definition.ctor();
+ if (tag == definition.tag && typeExtension == definition.is) {
+ return new definition.ctor();
+ }
+ // Handle empty string for type extension.
+ if (!typeExtension && !definition.is) {
+ return new definition.ctor();
+ }
}
- return domCreateElement(tag);
+
+ if (typeExtension) {
+ var element = createElement(tag);
+ element.setAttribute('is', typeExtension);
+ return element;
+ }
+ var element = domCreateElement(tag);
+ // Custom tags should be HTMLElements even if not upgraded.
+ if (tag.indexOf('-') >= 0) {
+ implement(element, HTMLElement);
+ }
+ return element;
}
- function upgradeElement(inElement) {
- if (!inElement.__upgraded__ && (inElement.nodeType === Node.ELEMENT_NODE)) {
- var type = inElement.getAttribute('is') || inElement.localName;
- var definition = registry[type];
- return definition && upgrade(inElement, definition);
+ function upgradeElement(element) {
+ if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) {
+ var is = element.getAttribute('is');
+ var definition = registry[is || element.localName];
+ if (definition) {
+ if (is && definition.tag == element.localName) {
+ return upgrade(element, definition);
+ } else if (!is && !definition.extends) {
+ return upgrade(element, definition);
+ }
+ }
}
}
@@ -1345,7 +1371,7 @@
* if it matches no registered custom tag name.
*
* @method ugprade
- * @param {Element} inElement The element to upgrade.
+ * @param {Element} element The element to upgrade.
* @return {Element} The upgraded element.
*/
scope.upgrade = upgradeElement;
diff --git a/pkg/custom_element/lib/custom-elements.min.js b/pkg/custom_element/lib/custom-elements.min.js
index f0c3e81..7f7c6e3 100644
--- a/pkg/custom_element/lib/custom-elements.min.js
+++ b/pkg/custom_element/lib/custom-elements.min.js
@@ -25,4 +25,4 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-window.CustomElements={flags:{}};var SideTable;if("undefined"!=typeof WeakMap&&navigator.userAgent.indexOf("Firefox/")<0?SideTable=WeakMap:function(){var a=Object.defineProperty,b=Object.hasOwnProperty,c=(new Date).getTime()%1e9;SideTable=function(){this.name="__st"+(1e9*Math.random()>>>0)+(c++ +"__")},SideTable.prototype={set:function(b,c){a(b,this.name,{value:c,writable:!0})},get:function(a){return b.call(a,this.name)?a[this.name]:void 0},"delete":function(a){this.set(a,void 0)}}}(),function(a){function b(a){u.push(a),t||(t=!0,q(d))}function c(a){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(a)||a}function d(){t=!1;var a=u;u=[],a.sort(function(a,b){return a.uid_-b.uid_});var b=!1;a.forEach(function(a){var c=a.takeRecords();e(a),c.length&&(a.callback_(c,a),b=!0)}),b&&d()}function e(a){a.nodes_.forEach(function(b){var c=p.get(b);c&&c.forEach(function(b){b.observer===a&&b.removeTransientObservers()})})}function f(a,b){for(var c=a;c;c=c.parentNode){var d=p.get(c);if(d)for(var e=0;e<d.length;e++){var f=d[e],g=f.options;if(c===a||g.subtree){var h=b(g);h&&f.enqueue(h)}}}}function g(a){this.callback_=a,this.nodes_=[],this.records_=[],this.uid_=++v}function h(a,b){this.type=a,this.target=b,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function i(a){var b=new h(a.type,a.target);return b.addedNodes=a.addedNodes.slice(),b.removedNodes=a.removedNodes.slice(),b.previousSibling=a.previousSibling,b.nextSibling=a.nextSibling,b.attributeName=a.attributeName,b.attributeNamespace=a.attributeNamespace,b.oldValue=a.oldValue,b}function j(a,b){return w=new h(a,b)}function k(a){return x?x:(x=i(w),x.oldValue=a,x)}function l(){w=x=void 0}function m(a){return a===x||a===w}function n(a,b){return a===b?a:x&&m(a)?x:null}function o(a,b,c){this.observer=a,this.target=b,this.options=c,this.transientObservedNodes=[]}var p=new SideTable,q=window.msSetImmediate;if(!q){var r=[],s=String(Math.random());window.addEventListener("message",function(a){if(a.data===s){var b=r;r=[],b.forEach(function(a){a()})}}),q=function(a){r.push(a),window.postMessage(s,"*")}}var t=!1,u=[],v=0;g.prototype={observe:function(a,b){if(a=c(a),!b.childList&&!b.attributes&&!b.characterData||b.attributeOldValue&&!b.attributes||b.attributeFilter&&b.attributeFilter.length&&!b.attributes||b.characterDataOldValue&&!b.characterData)throw new SyntaxError;var d=p.get(a);d||p.set(a,d=[]);for(var e,f=0;f<d.length;f++)if(d[f].observer===this){e=d[f],e.removeListeners(),e.options=b;break}e||(e=new o(this,a,b),d.push(e),this.nodes_.push(a)),e.addListeners()},disconnect:function(){this.nodes_.forEach(function(a){for(var b=p.get(a),c=0;c<b.length;c++){var d=b[c];if(d.observer===this){d.removeListeners(),b.splice(c,1);break}}},this),this.records_=[]},takeRecords:function(){var a=this.records_;return this.records_=[],a}};var w,x;o.prototype={enqueue:function(a){var c=this.observer.records_,d=c.length;if(c.length>0){var e=c[d-1],f=n(e,a);if(f)return c[d-1]=f,void 0}else b(this.observer);c[d]=a},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(a){var b=this.options;b.attributes&&a.addEventListener("DOMAttrModified",this,!0),b.characterData&&a.addEventListener("DOMCharacterDataModified",this,!0),b.childList&&a.addEventListener("DOMNodeInserted",this,!0),(b.childList||b.subtree)&&a.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(a){var b=this.options;b.attributes&&a.removeEventListener("DOMAttrModified",this,!0),b.characterData&&a.removeEventListener("DOMCharacterDataModified",this,!0),b.childList&&a.removeEventListener("DOMNodeInserted",this,!0),(b.childList||b.subtree)&&a.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(a){if(a!==this.target){this.addListeners_(a),this.transientObservedNodes.push(a);var b=p.get(a);b||p.set(a,b=[]),b.push(this)}},removeTransientObservers:function(){var a=this.transientObservedNodes;this.transientObservedNodes=[],a.forEach(function(a){this.removeListeners_(a);for(var b=p.get(a),c=0;c<b.length;c++)if(b[c]===this){b.splice(c,1);break}},this)},handleEvent:function(a){switch(a.stopImmediatePropagation(),a.type){case"DOMAttrModified":var b=a.attrName,c=a.relatedNode.namespaceURI,d=a.target,e=new j("attributes",d);e.attributeName=b,e.attributeNamespace=c;var g=a.attrChange===MutationEvent.ADDITION?null:a.prevValue;f(d,function(a){return!a.attributes||a.attributeFilter&&a.attributeFilter.length&&-1===a.attributeFilter.indexOf(b)&&-1===a.attributeFilter.indexOf(c)?void 0:a.attributeOldValue?k(g):e});break;case"DOMCharacterDataModified":var d=a.target,e=j("characterData",d),g=a.prevValue;f(d,function(a){return a.characterData?a.characterDataOldValue?k(g):e:void 0});break;case"DOMNodeRemoved":this.addTransientObserver(a.target);case"DOMNodeInserted":var h,i,d=a.relatedNode,m=a.target;"DOMNodeInserted"===a.type?(h=[m],i=[]):(h=[],i=[m]);var n=m.previousSibling,o=m.nextSibling,e=j("childList",d);e.addedNodes=h,e.removedNodes=i,e.previousSibling=n,e.nextSibling=o,f(d,function(a){return a.childList?e:void 0})}l()}},a.JsMutationObserver=g}(this),!window.MutationObserver&&(window.MutationObserver=window.WebKitMutationObserver||window.JsMutationObserver,!MutationObserver))throw new Error("no mutation observer support");!function(a){function b(a,c,d){var e=a.firstElementChild;if(!e)for(e=a.firstChild;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.nextSibling;for(;e;)c(e,d)!==!0&&b(e,c,d),e=e.nextElementSibling;return null}function c(a,b){for(var c=a.webkitShadowRoot;c;)d(c,b),c=c.olderShadowRoot}function d(a,d){b(a,function(a){return d(a)?!0:(c(a,d),void 0)}),c(a,d)}function e(a){return h(a)?(i(a),!0):(j(a),void 0)}function f(a){d(a,function(a){return e(a)?!0:void 0})}function g(a){return e(a)||f(a)}function h(b){if(!b.__upgraded__&&b.nodeType===Node.ELEMENT_NODE){var c=b.getAttribute("is")||b.localName,d=a.registry[c];if(d)return logFlags.dom&&console.group("upgrade:",b.localName),a.upgrade(b),logFlags.dom&&console.groupEnd(),!0}}function i(a){j(a),m(a)&&d(a,function(a){j(a)})}function j(a){(a.enteredDocumentCallback||a.__upgraded__&&logFlags.dom)&&(logFlags.dom&&console.group("inserted:",a.localName),m(a)&&(a.__inserted=(a.__inserted||0)+1,a.__inserted<1&&(a.__inserted=1),a.__inserted>1?logFlags.dom&&console.warn("inserted:",a.localName,"insert/remove count:",a.__inserted):a.enteredDocumentCallback&&(logFlags.dom&&console.log("inserted:",a.localName),a.enteredDocumentCallback())),logFlags.dom&&console.groupEnd())}function k(a){l(a),d(a,function(a){l(a)})}function l(a){(a.leftDocumentCallback||a.__upgraded__&&logFlags.dom)&&(logFlags.dom&&console.log("removed:",a.localName),m(a)||(a.__inserted=(a.__inserted||0)-1,a.__inserted>0&&(a.__inserted=0),a.__inserted<0?logFlags.dom&&console.warn("removed:",a.localName,"insert/remove count:",a.__inserted):a.leftDocumentCallback&&a.leftDocumentCallback()))}function m(a){for(var b=a;b;){if(b==a.ownerDocument)return!0;b=b.parentNode||b.host}}function n(a){if(a.webkitShadowRoot&&!a.webkitShadowRoot.__watched){logFlags.dom&&console.log("watching shadow-root for: ",a.localName);for(var b=a.webkitShadowRoot;b;)o(b),b=b.olderShadowRoot}}function o(a){a.__watched||(t(a),a.__watched=!0)}function p(a){n(a),d(a,function(){n(a)})}function q(a){switch(a.localName){case"style":case"script":case"template":case void 0:return!0}}function r(a){if(logFlags.dom){var b=a[0];if(b&&"childList"===b.type&&b.addedNodes&&b.addedNodes){for(var c=b.addedNodes[0];c&&c!==document&&!c.host;)c=c.parentNode;var d=c&&(c.URL||c._URL||c.host&&c.host.localName)||"";d=d.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",a.length,d||"")}a.forEach(function(a){"childList"===a.type&&(x(a.addedNodes,function(a){q(a)||g(a)}),x(a.removedNodes,function(a){q(a)||k(a)}))}),logFlags.dom&&console.groupEnd()}function s(){r(w.takeRecords())}function t(a){w.observe(a,{childList:!0,subtree:!0})}function u(a){t(a)}function v(a){logFlags.dom&&console.group("upgradeDocument: ",(a.URL||a._URL||"").split("/").pop()),g(a),logFlags.dom&&console.groupEnd()}var w=new MutationObserver(r),x=Array.prototype.forEach.call.bind(Array.prototype.forEach);a.watchShadow=n,a.watchAllShadows=p,a.upgradeAll=g,a.upgradeSubtree=f,a.observeDocument=u,a.upgradeDocument=v,a.takeRecords=s}(window.CustomElements),function(a){function b(b,f){var g=f||{};if(!b)throw new Error("Name argument must not be empty");if(g.name=b,!g.prototype)throw new Error("Options missing required prototype property");return g.lifecycle=g.lifecycle||{},g.ancestry=c(g.extends),d(g),e(g),k(g.prototype),m(b,g),g.ctor=n(g),g.ctor.prototype=g.prototype,g.prototype.constructor=g.ctor,a.ready&&a.upgradeAll(document),g.ctor}function c(a){var b=v[a];return b?c(b.extends).concat([b]):[]}function d(a){for(var b,c=a.extends,d=0;b=a.ancestry[d];d++)c=b.is&&b.tag;a.tag=c||a.name,c&&(a.is=a.name)}function e(a){if(!Object.__proto__){var b=HTMLElement.prototype;if(a.is){var c=document.createElement(a.tag);b=Object.getPrototypeOf(c)}for(var d,e=a.prototype;e&&e!==b;){var d=Object.getPrototypeOf(e);e.__proto__=d,e=d}}a.native=b}function f(a){return g(w(a.tag),a)}function g(b,c){return c.is&&b.setAttribute("is",c.is),h(b,c),b.__upgraded__=!0,a.upgradeSubtree(b),j(b),b}function h(a,b){Object.__proto__?a.__proto__=b.prototype:(i(a,b.prototype,b.native),a.__proto__=b.prototype)}function i(a,b,c){for(var d={},e=b;e!==c&&e!==HTMLUnknownElement.prototype;){for(var f,g=Object.getOwnPropertyNames(e),h=0;f=g[h];h++)d[f]||(Object.defineProperty(a,f,Object.getOwnPropertyDescriptor(e,f)),d[f]=1);e=Object.getPrototypeOf(e)}}function j(a){a.createdCallback&&a.createdCallback()}function k(a){var b=a.setAttribute;a.setAttribute=function(a,c){l.call(this,a,c,b)};var c=a.removeAttribute;a.removeAttribute=function(a,b){l.call(this,a,b,c)}}function l(a,b,c){var d=this.getAttribute(a);c.apply(this,arguments),this.attributeChangedCallback&&this.getAttribute(a)!==d&&this.attributeChangedCallback(a,d)}function m(a,b){if(v[a])throw new Error("Cannot register a tag more than once");v[a]=b}function n(a){return function(){return f(a)}}function o(a,b){var c=v[b||a];return c?new c.ctor:w(a)}function p(a){if(!a.__upgraded__&&a.nodeType===Node.ELEMENT_NODE){var b=a.getAttribute("is")||a.localName,c=v[b];return c&&g(a,c)}}function q(b){var c=x.call(this,b);return a.upgradeAll(c),c}a||(a=window.CustomElements={flags:{}});var r=a.flags,s=Boolean(document.webkitRegister||document.register),t=!r.register&&s;if(t){document.register=document.register||document.webkitRegister;var u=function(){};a.registry={},a.upgradeElement=u,a.watchShadow=u,a.watchAllShadows=u,a.upgrade=u,a.upgradeAll=u,a.upgradeSubtree=u,a.observeDocument=u,a.upgradeDocument=u,a.takeRecords=u}else{var v={},w=document.createElement.bind(document),x=Node.prototype.cloneNode;document.register=b,document.createElement=o,Node.prototype.cloneNode=q,a.registry=v,a.upgrade=p}a.hasNative=s,a.useNative=t}(window.CustomElements),function(){function a(a){return"link"===a.localName&&a.getAttribute("rel")===b}var b=window.HTMLImports?HTMLImports.IMPORT_LINK_TYPE:"none",c={selectors:["link[rel="+b+"]"],map:{link:"parseLink"},parse:function(a){if(!a.__parsed){a.__parsed=!0;var b=a.querySelectorAll(c.selectors);d(b,function(a){c[c.map[a.localName]](a)}),CustomElements.upgradeDocument(a),CustomElements.observeDocument(a)}},parseLink:function(b){a(b)&&this.parseImport(b)},parseImport:function(a){a.content&&c.parse(a.content)}},d=Array.prototype.forEach.call.bind(Array.prototype.forEach);CustomElements.parser=c}(),function(){function a(){setTimeout(function(){CustomElements.parser.parse(document),CustomElements.upgradeDocument(document),CustomElements.ready=!0,CustomElements.readyTime=Date.now(),window.HTMLImports&&(CustomElements.elapsed=CustomElements.readyTime-HTMLImports.readyTime),document.body.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))},0)}if("function"!=typeof window.CustomEvent&&(window.CustomEvent=function(a){var b=document.createEvent("HTMLEvents");return b.initEvent(a,!0,!0),b}),"complete"===document.readyState)a();else{var b=window.HTMLImports?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(b,a)}}();
+window.CustomElements={flags:{}};var SideTable;if("undefined"!=typeof WeakMap&&navigator.userAgent.indexOf("Firefox/")<0?SideTable=WeakMap:function(){var a=Object.defineProperty,b=Object.hasOwnProperty,c=(new Date).getTime()%1e9;SideTable=function(){this.name="__st"+(1e9*Math.random()>>>0)+(c++ +"__")},SideTable.prototype={set:function(b,c){a(b,this.name,{value:c,writable:!0})},get:function(a){return b.call(a,this.name)?a[this.name]:void 0},"delete":function(a){this.set(a,void 0)}}}(),function(a){function b(a){u.push(a),t||(t=!0,q(d))}function c(a){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(a)||a}function d(){t=!1;var a=u;u=[],a.sort(function(a,b){return a.uid_-b.uid_});var b=!1;a.forEach(function(a){var c=a.takeRecords();e(a),c.length&&(a.callback_(c,a),b=!0)}),b&&d()}function e(a){a.nodes_.forEach(function(b){var c=p.get(b);c&&c.forEach(function(b){b.observer===a&&b.removeTransientObservers()})})}function f(a,b){for(var c=a;c;c=c.parentNode){var d=p.get(c);if(d)for(var e=0;e<d.length;e++){var f=d[e],g=f.options;if(c===a||g.subtree){var h=b(g);h&&f.enqueue(h)}}}}function g(a){this.callback_=a,this.nodes_=[],this.records_=[],this.uid_=++v}function h(a,b){this.type=a,this.target=b,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function i(a){var b=new h(a.type,a.target);return b.addedNodes=a.addedNodes.slice(),b.removedNodes=a.removedNodes.slice(),b.previousSibling=a.previousSibling,b.nextSibling=a.nextSibling,b.attributeName=a.attributeName,b.attributeNamespace=a.attributeNamespace,b.oldValue=a.oldValue,b}function j(a,b){return w=new h(a,b)}function k(a){return x?x:(x=i(w),x.oldValue=a,x)}function l(){w=x=void 0}function m(a){return a===x||a===w}function n(a,b){return a===b?a:x&&m(a)?x:null}function o(a,b,c){this.observer=a,this.target=b,this.options=c,this.transientObservedNodes=[]}var p=new SideTable,q=window.msSetImmediate;if(!q){var r=[],s=String(Math.random());window.addEventListener("message",function(a){if(a.data===s){var b=r;r=[],b.forEach(function(a){a()})}}),q=function(a){r.push(a),window.postMessage(s,"*")}}var t=!1,u=[],v=0;g.prototype={observe:function(a,b){if(a=c(a),!b.childList&&!b.attributes&&!b.characterData||b.attributeOldValue&&!b.attributes||b.attributeFilter&&b.attributeFilter.length&&!b.attributes||b.characterDataOldValue&&!b.characterData)throw new SyntaxError;var d=p.get(a);d||p.set(a,d=[]);for(var e,f=0;f<d.length;f++)if(d[f].observer===this){e=d[f],e.removeListeners(),e.options=b;break}e||(e=new o(this,a,b),d.push(e),this.nodes_.push(a)),e.addListeners()},disconnect:function(){this.nodes_.forEach(function(a){for(var b=p.get(a),c=0;c<b.length;c++){var d=b[c];if(d.observer===this){d.removeListeners(),b.splice(c,1);break}}},this),this.records_=[]},takeRecords:function(){var a=this.records_;return this.records_=[],a}};var w,x;o.prototype={enqueue:function(a){var c=this.observer.records_,d=c.length;if(c.length>0){var e=c[d-1],f=n(e,a);if(f)return c[d-1]=f,void 0}else b(this.observer);c[d]=a},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(a){var b=this.options;b.attributes&&a.addEventListener("DOMAttrModified",this,!0),b.characterData&&a.addEventListener("DOMCharacterDataModified",this,!0),b.childList&&a.addEventListener("DOMNodeInserted",this,!0),(b.childList||b.subtree)&&a.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(a){var b=this.options;b.attributes&&a.removeEventListener("DOMAttrModified",this,!0),b.characterData&&a.removeEventListener("DOMCharacterDataModified",this,!0),b.childList&&a.removeEventListener("DOMNodeInserted",this,!0),(b.childList||b.subtree)&&a.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(a){if(a!==this.target){this.addListeners_(a),this.transientObservedNodes.push(a);var b=p.get(a);b||p.set(a,b=[]),b.push(this)}},removeTransientObservers:function(){var a=this.transientObservedNodes;this.transientObservedNodes=[],a.forEach(function(a){this.removeListeners_(a);for(var b=p.get(a),c=0;c<b.length;c++)if(b[c]===this){b.splice(c,1);break}},this)},handleEvent:function(a){switch(a.stopImmediatePropagation(),a.type){case"DOMAttrModified":var b=a.attrName,c=a.relatedNode.namespaceURI,d=a.target,e=new j("attributes",d);e.attributeName=b,e.attributeNamespace=c;var g=a.attrChange===MutationEvent.ADDITION?null:a.prevValue;f(d,function(a){return!a.attributes||a.attributeFilter&&a.attributeFilter.length&&-1===a.attributeFilter.indexOf(b)&&-1===a.attributeFilter.indexOf(c)?void 0:a.attributeOldValue?k(g):e});break;case"DOMCharacterDataModified":var d=a.target,e=j("characterData",d),g=a.prevValue;f(d,function(a){return a.characterData?a.characterDataOldValue?k(g):e:void 0});break;case"DOMNodeRemoved":this.addTransientObserver(a.target);case"DOMNodeInserted":var h,i,d=a.relatedNode,m=a.target;"DOMNodeInserted"===a.type?(h=[m],i=[]):(h=[],i=[m]);var n=m.previousSibling,o=m.nextSibling,e=j("childList",d);e.addedNodes=h,e.removedNodes=i,e.previousSibling=n,e.nextSibling=o,f(d,function(a){return a.childList?e:void 0})}l()}},a.JsMutationObserver=g}(this),!window.MutationObserver&&(window.MutationObserver=window.WebKitMutationObserver||window.JsMutationObserver,!MutationObserver))throw new Error("no mutation observer support");!function(a){function b(a,c,d){var e=a.firstElementChild;if(!e)for(e=a.firstChild;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.nextSibling;for(;e;)c(e,d)!==!0&&b(e,c,d),e=e.nextElementSibling;return null}function c(a,b){for(var c=a.webkitShadowRoot;c;)d(c,b),c=c.olderShadowRoot}function d(a,d){b(a,function(a){return d(a)?!0:(c(a,d),void 0)}),c(a,d)}function e(a){return h(a)?(i(a),!0):(j(a),void 0)}function f(a){d(a,function(a){return e(a)?!0:void 0})}function g(a){return e(a)||f(a)}function h(b){if(!b.__upgraded__&&b.nodeType===Node.ELEMENT_NODE){var c=b.getAttribute("is")||b.localName,d=a.registry[c];if(d)return logFlags.dom&&console.group("upgrade:",b.localName),a.upgrade(b),logFlags.dom&&console.groupEnd(),!0}}function i(a){j(a),m(a)&&d(a,function(a){j(a)})}function j(a){(a.enteredDocumentCallback||a.__upgraded__&&logFlags.dom)&&(logFlags.dom&&console.group("inserted:",a.localName),m(a)&&(a.__inserted=(a.__inserted||0)+1,a.__inserted<1&&(a.__inserted=1),a.__inserted>1?logFlags.dom&&console.warn("inserted:",a.localName,"insert/remove count:",a.__inserted):a.enteredDocumentCallback&&(logFlags.dom&&console.log("inserted:",a.localName),a.enteredDocumentCallback())),logFlags.dom&&console.groupEnd())}function k(a){l(a),d(a,function(a){l(a)})}function l(a){(a.leftDocumentCallback||a.__upgraded__&&logFlags.dom)&&(logFlags.dom&&console.log("removed:",a.localName),m(a)||(a.__inserted=(a.__inserted||0)-1,a.__inserted>0&&(a.__inserted=0),a.__inserted<0?logFlags.dom&&console.warn("removed:",a.localName,"insert/remove count:",a.__inserted):a.leftDocumentCallback&&a.leftDocumentCallback()))}function m(a){for(var b=a;b;){if(b==a.ownerDocument)return!0;b=b.parentNode||b.host}}function n(a){if(a.webkitShadowRoot&&!a.webkitShadowRoot.__watched){logFlags.dom&&console.log("watching shadow-root for: ",a.localName);for(var b=a.webkitShadowRoot;b;)o(b),b=b.olderShadowRoot}}function o(a){a.__watched||(t(a),a.__watched=!0)}function p(a){n(a),d(a,function(){n(a)})}function q(a){switch(a.localName){case"style":case"script":case"template":case void 0:return!0}}function r(a){if(logFlags.dom){var b=a[0];if(b&&"childList"===b.type&&b.addedNodes&&b.addedNodes){for(var c=b.addedNodes[0];c&&c!==document&&!c.host;)c=c.parentNode;var d=c&&(c.URL||c._URL||c.host&&c.host.localName)||"";d=d.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",a.length,d||"")}a.forEach(function(a){"childList"===a.type&&(x(a.addedNodes,function(a){q(a)||g(a)}),x(a.removedNodes,function(a){q(a)||k(a)}))}),logFlags.dom&&console.groupEnd()}function s(){r(w.takeRecords())}function t(a){w.observe(a,{childList:!0,subtree:!0})}function u(a){t(a)}function v(a){logFlags.dom&&console.group("upgradeDocument: ",(a.URL||a._URL||"").split("/").pop()),g(a),logFlags.dom&&console.groupEnd()}var w=new MutationObserver(r),x=Array.prototype.forEach.call.bind(Array.prototype.forEach);a.watchShadow=n,a.watchAllShadows=p,a.upgradeAll=g,a.upgradeSubtree=f,a.observeDocument=u,a.upgradeDocument=v,a.takeRecords=s}(window.CustomElements),function(a){function b(b,f){var g=f||{};if(!b)throw new Error("document.register: first argument `name` must not be empty");if(b.indexOf("-")<0)throw new Error("document.register: first argument `name` must contain a dash ('-'). Argument was '"+String(b)+"'.");if(g.name=b,!g.prototype)throw new Error("Options missing required prototype property");return g.lifecycle=g.lifecycle||{},g.ancestry=c(g.extends),d(g),e(g),k(g.prototype),m(b,g),g.ctor=n(g),g.ctor.prototype=g.prototype,g.prototype.constructor=g.ctor,a.ready&&a.upgradeAll(document),g.ctor}function c(a){var b=v[a];return b?c(b.extends).concat([b]):[]}function d(a){for(var b,c=a.extends,d=0;b=a.ancestry[d];d++)c=b.is&&b.tag;a.tag=c||a.name,c&&(a.is=a.name)}function e(a){if(!Object.__proto__){var b=HTMLElement.prototype;if(a.is){var c=document.createElement(a.tag);b=Object.getPrototypeOf(c)}for(var d,e=a.prototype;e&&e!==b;){var d=Object.getPrototypeOf(e);e.__proto__=d,e=d}}a.native=b}function f(a){return g(w(a.tag),a)}function g(b,c){return c.is&&b.setAttribute("is",c.is),h(b,c),b.__upgraded__=!0,a.upgradeSubtree(b),j(b),b}function h(a,b){Object.__proto__?a.__proto__=b.prototype:(i(a,b.prototype,b.native),a.__proto__=b.prototype)}function i(a,b,c){for(var d={},e=b;e!==c&&e!==HTMLUnknownElement.prototype;){for(var f,g=Object.getOwnPropertyNames(e),h=0;f=g[h];h++)d[f]||(Object.defineProperty(a,f,Object.getOwnPropertyDescriptor(e,f)),d[f]=1);e=Object.getPrototypeOf(e)}}function j(a){a.createdCallback&&a.createdCallback()}function k(a){var b=a.setAttribute;a.setAttribute=function(a,c){l.call(this,a,c,b)};var c=a.removeAttribute;a.removeAttribute=function(a,b){l.call(this,a,b,c)}}function l(a,b,c){var d=this.getAttribute(a);c.apply(this,arguments),this.attributeChangedCallback&&this.getAttribute(a)!==d&&this.attributeChangedCallback(a,d)}function m(a,b){if(v[a])throw new Error("a type with that name is already registered.");v[a]=b}function n(a){return function(){return f(a)}}function o(a,b){var c=v[b||a];if(c){if(a==c.tag&&b==c.is)return new c.ctor;if(!b&&!c.is)return new c.ctor}if(b){var d=o(a);return d.setAttribute("is",b),d}var d=w(a);return a.indexOf("-")>=0&&h(d,HTMLElement),d}function p(a){if(!a.__upgraded__&&a.nodeType===Node.ELEMENT_NODE){var b=a.getAttribute("is"),c=v[b||a.localName];if(c){if(b&&c.tag==a.localName)return g(a,c);if(!b&&!c.extends)return g(a,c)}}}function q(b){var c=x.call(this,b);return a.upgradeAll(c),c}a||(a=window.CustomElements={flags:{}});var r=a.flags,s=Boolean(document.webkitRegister||document.register),t=!r.register&&s;if(t){document.register=document.register||document.webkitRegister;var u=function(){};a.registry={},a.upgradeElement=u,a.watchShadow=u,a.watchAllShadows=u,a.upgrade=u,a.upgradeAll=u,a.upgradeSubtree=u,a.observeDocument=u,a.upgradeDocument=u,a.takeRecords=u}else{var v={},w=document.createElement.bind(document),x=Node.prototype.cloneNode;document.register=b,document.createElement=o,Node.prototype.cloneNode=q,a.registry=v,a.upgrade=p}a.hasNative=s,a.useNative=t}(window.CustomElements),function(){function a(a){return"link"===a.localName&&a.getAttribute("rel")===b}var b=window.HTMLImports?HTMLImports.IMPORT_LINK_TYPE:"none",c={selectors:["link[rel="+b+"]"],map:{link:"parseLink"},parse:function(a){if(!a.__parsed){a.__parsed=!0;var b=a.querySelectorAll(c.selectors);d(b,function(a){c[c.map[a.localName]](a)}),CustomElements.upgradeDocument(a),CustomElements.observeDocument(a)}},parseLink:function(b){a(b)&&this.parseImport(b)},parseImport:function(a){a.content&&c.parse(a.content)}},d=Array.prototype.forEach.call.bind(Array.prototype.forEach);CustomElements.parser=c}(),function(){function a(){setTimeout(function(){CustomElements.parser.parse(document),CustomElements.upgradeDocument(document),CustomElements.ready=!0,CustomElements.readyTime=Date.now(),window.HTMLImports&&(CustomElements.elapsed=CustomElements.readyTime-HTMLImports.readyTime),document.body.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))},0)}if("function"!=typeof window.CustomEvent&&(window.CustomEvent=function(a){var b=document.createEvent("HTMLEvents");return b.initEvent(a,!0,!0),b}),"complete"===document.readyState)a();else{var b=window.HTMLImports?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(b,a)}}();
diff --git a/pkg/custom_element/test/custom_element_test.dart b/pkg/custom_element/test/custom_element_test.dart
index 341713a..3094518 100644
--- a/pkg/custom_element/test/custom_element_test.dart
+++ b/pkg/custom_element/test/custom_element_test.dart
@@ -13,16 +13,16 @@
main() {
useHtmlConfiguration();
- // Load the MutationObserver polyfill.
- HttpRequest.getString('/root_dart/pkg/mutation_observer/lib/'
- 'mutation_observer.js').then((code) {
- document.head.children.add(new ScriptElement()..text = code);
-
- customElementTests();
+ setUp(() {
+ // Load the MutationObserver polyfill if needed.
+ if (!MutationObserver.supported) {
+ var script = new ScriptElement()
+ ..src = '/packages/mutation_observer/mutation_observer.js';
+ document.head.append(script);
+ return script.onLoad.first;
+ }
});
-}
-customElementTests() {
test('register creates the element and calls lifecycle methods', () {
// Add element to the page.
var element = new Element.html('<fancy-button>foo bar</fancy-button>',
diff --git a/pkg/docgen/bin/dartdoc.py b/pkg/docgen/bin/dartdoc.py
index 948a24a..15f0de1 100644
--- a/pkg/docgen/bin/dartdoc.py
+++ b/pkg/docgen/bin/dartdoc.py
@@ -54,7 +54,7 @@
join(DIRECTORY, 'docgen.dart')]
docgen.extend(options.options.split())
ExecuteCommand(docgen)
- ExecuteCommand(['git', 'clone', '-b', 'dev',
+ ExecuteCommand(['git', 'clone', '-b', 'master',
'git://github.com/dart-lang/dartdoc-viewer.git'])
ExecuteCommand(['mv', 'docs', 'dartdoc-viewer/client/local'])
os.chdir('dartdoc-viewer/client/')
diff --git a/pkg/http/lib/src/utils.dart b/pkg/http/lib/src/utils.dart
index fdfa05b..28704dc 100644
--- a/pkg/http/lib/src/utils.dart
+++ b/pkg/http/lib/src/utils.dart
@@ -21,9 +21,9 @@
for (var pair in queryList.split("&")) {
var split = split1(pair, "=");
if (split.isEmpty) continue;
- var key = Uri.decodeQueryComponent(split[0], decode: encoding.decode);
+ var key = Uri.decodeQueryComponent(split[0], encoding: encoding);
var value = Uri.decodeQueryComponent(split.length > 1 ? split[1] : "",
- decode: encoding.decode);
+ encoding: encoding);
map[key] = value;
}
return map;
diff --git a/pkg/http_server/lib/src/http_body_impl.dart b/pkg/http_server/lib/src/http_body_impl.dart
index b1186405..f3ffa9e 100644
--- a/pkg/http_server/lib/src/http_body_impl.dart
+++ b/pkg/http_server/lib/src/http_body_impl.dart
@@ -121,7 +121,7 @@
return asText(ASCII)
.then((body) {
var map = Uri.splitQueryString(body.body,
- decode: (s) => defaultEncoding.decode(s));
+ encoding: defaultEncoding);
var result = {};
for (var key in map.keys) {
result[key] = map[key];
diff --git a/pkg/intl/lib/bidi_formatter.dart b/pkg/intl/lib/bidi_formatter.dart
index 6ae3982..0e5bee6 100644
--- a/pkg/intl/lib/bidi_formatter.dart
+++ b/pkg/intl/lib/bidi_formatter.dart
@@ -85,7 +85,11 @@
* Escapes HTML-special characters of [text] so that the result can be
* included verbatim in HTML source code, either in an element body or in an
* attribute value.
+ *
+ * *htmlEscape* is deprecated. Use [HtmlEscape] from the `dart:convert`
+ * package. *htmlEscape* will be removed the 30th of September 2013.
*/
+ // TODO(kevmoo) Remove this!
@deprecated
String htmlEscape(String text) => HTML_ESCAPE.convert(text);
diff --git a/pkg/intl/lib/extract_messages.dart b/pkg/intl/lib/extract_messages.dart
index 0679ff8..2a34a43 100644
--- a/pkg/intl/lib/extract_messages.dart
+++ b/pkg/intl/lib/extract_messages.dart
@@ -147,6 +147,7 @@
*/
void visitMethodDeclaration(MethodDeclaration node) {
parameters = node.parameters;
+ if (parameters == null) parameters = new FormalParameterList();
name = node.name.name;
super.visitMethodDeclaration(node);
}
@@ -157,6 +158,7 @@
*/
void visitFunctionDeclaration(FunctionDeclaration node) {
parameters = node.functionExpression.parameters;
+ if (parameters == null) parameters = new FormalParameterList();
name = node.name.name;
super.visitFunctionDeclaration(node);
}
diff --git a/pkg/intl/lib/generate_localized.dart b/pkg/intl/lib/generate_localized.dart
index 285e05e..0095c89 100644
--- a/pkg/intl/lib/generate_localized.dart
+++ b/pkg/intl/lib/generate_localized.dart
@@ -85,7 +85,7 @@
* We can't use a hyphen in a Dart library name, so convert the locale
* separator to an underscore.
*/
-String _libraryName(String x) => x.replaceAll('-', '_');
+String _libraryName(String x) => 'messages_' + x.replaceAll('-', '_');
/**
* Generate a file <[generated_file_prefix]>_messages_<[locale]>.dart
@@ -135,7 +135,7 @@
* function name.
*/
-library messages_${locale.replaceAll('-','_')};
+library ${_libraryName(locale)};
import 'package:$intlImportPath/intl.dart';
import 'package:$intlImportPath/message_lookup_by_library.dart';
diff --git a/pkg/intl/lib/src/intl_message.dart b/pkg/intl/lib/src/intl_message.dart
index 0eb67b1..2fe5fd0 100644
--- a/pkg/intl/lib/src/intl_message.dart
+++ b/pkg/intl/lib/src/intl_message.dart
@@ -239,18 +239,37 @@
*/
class VariableSubstitution extends Message {
VariableSubstitution(this._index, Message parent) : super(parent);
- VariableSubstitution.named(this._variableName, Message parent)
- : super(parent);
+
+ /**
+ * Create a substitution based on the name rather than the index. The name
+ * may have been used as all upper-case in the translation tool, so we
+ * save it separately and look it up case-insensitively once the parent
+ * (and its arguments) are definitely available.
+ */
+ VariableSubstitution.named(String name, Message parent)
+ : super(parent) {
+ _variableNameUpper = name.toUpperCase();
+ }
/** The index in the list of parameters of the containing function. */
int _index;
int get index {
if (_index != null) return _index;
if (arguments.isEmpty) return null;
- return _index = arguments.indexOf(_variableName);
+ // We may have been given an all-uppercase version of the name, so compare
+ // case-insensitive.
+ _index = arguments.map((x) => x.toUpperCase()).toList()
+ .indexOf(_variableNameUpper);
+ return _index;
}
/**
+ * The variable name we get from parsing. This may be an all uppercase version
+ * of the Dart argument name.
+ */
+ String _variableNameUpper;
+
+ /**
* The name of the variable in the parameter list of the containing function.
* Used when generating code for the interpolation.
*/
diff --git a/pkg/intl/test/message_extraction/foo_messages_all.dart b/pkg/intl/test/message_extraction/foo_messages_all.dart
index 829df89..1568a64 100644
--- a/pkg/intl/test/message_extraction/foo_messages_all.dart
+++ b/pkg/intl/test/message_extraction/foo_messages_all.dart
@@ -11,14 +11,14 @@
import 'package:intl/src/intl_helpers.dart';
import 'package:intl/intl.dart';
-import 'foo_messages_fr.dart' as fr;
-import 'foo_messages_de_DE.dart' as de_DE;
+import 'foo_messages_fr.dart' as messages_fr;
+import 'foo_messages_de_DE.dart' as messages_de_DE;
MessageLookupByLibrary _findExact(localeName) {
switch (localeName) {
- case 'fr' : return fr.messages;
- case 'de_DE' : return de_DE.messages;
+ case 'fr' : return messages_fr.messages;
+ case 'de_DE' : return messages_de_DE.messages;
default: return null;
}
}
diff --git a/pkg/intl/test/message_extraction/sample_with_messages.dart b/pkg/intl/test/message_extraction/sample_with_messages.dart
index 8fc65c7..f678a35 100644
--- a/pkg/intl/test/message_extraction/sample_with_messages.dart
+++ b/pkg/intl/test/message_extraction/sample_with_messages.dart
@@ -47,7 +47,7 @@
Intl.message("Interpolation is tricky when it ends a sentence like ${s}.",
name: 'trickyInterpolation', args: [s]);
-leadingQuotes() => Intl.message("\"So-called\"", name: 'leadingQuotes');
+get leadingQuotes => Intl.message("\"So-called\"", name: 'leadingQuotes');
// A message with characters not in the basic multilingual plane.
originalNotInBMP() => Intl.message("Ancient Greek hangman characters: 𐅆𐅇.",
@@ -135,7 +135,7 @@
printOut(messageVariable(1,2,3));
printOut(multiLine());
printOut(types(1, "b", ["c", "d"]));
- printOut(leadingQuotes());
+ printOut(leadingQuotes);
printOut(alwaysTranslated());
printOut(trickyInterpolation("this"));
var thing = new YouveGotMessages();
diff --git a/pkg/mdv/lib/mdv.dart b/pkg/mdv/lib/mdv.dart
index fe9d9c8..ee27616 100644
--- a/pkg/mdv/lib/mdv.dart
+++ b/pkg/mdv/lib/mdv.dart
@@ -14,9 +14,6 @@
import 'dart:html';
import 'package:observe/observe.dart';
-// TODO(jmesserly): get this from somewhere else. See http://dartbug.com/4161.
-import 'package:serialization/src/serialization_helpers.dart' show IdentityMap;
-
import 'src/list_diff.dart' show calculateSplices, ListChangeDelta;
part 'src/element.dart';
diff --git a/pkg/mdv/lib/src/template_iterator.dart b/pkg/mdv/lib/src/template_iterator.dart
index 6c87a42..8458165 100644
--- a/pkg/mdv/lib/src/template_iterator.dart
+++ b/pkg/mdv/lib/src/template_iterator.dart
@@ -344,9 +344,7 @@
return;
}
- // TODO(jmesserly): IdentityMap matches JS semantics, but it's O(N) right
- // now. See http://dartbug.com/4161.
- var instanceCache = new IdentityMap();
+ var instanceCache = new HashMap(equals: identical);
var removeDelta = 0;
for (var splice in splices) {
for (int i = 0; i < splice.removedCount; i++) {
diff --git a/pkg/mdv/pubspec.yaml b/pkg/mdv/pubspec.yaml
index c962aed..937a8cc 100644
--- a/pkg/mdv/pubspec.yaml
+++ b/pkg/mdv/pubspec.yaml
@@ -1,16 +1,14 @@
name: mdv
-author: "Web UI Team <web-ui-dev@dartlang.org>"
-homepage: https://github.com/dart-lang/web-ui
+author: Web UI Team <web-ui-dev@dartlang.org>
description: >
Model-Driven-Views (MDV) extends HTML and the DOM APIs to support a sensible
separation between the UI (DOM) of a document or application and its
underlying data (model).
Updates to the model are reflected in the DOM and user input into the DOM is
immediately assigned to the model.
+homepage: https://www.dartlang.org/polymer-dart/
dependencies:
logging: any
observe: any
- # This is only for IdentityMap. See http://dartbug.com/4161.
- serialization: any
dev_dependencies:
unittest: any
diff --git a/pkg/observe/lib/observe.dart b/pkg/observe/lib/observe.dart
index c1a2f80..8b972d3 100644
--- a/pkg/observe/lib/observe.dart
+++ b/pkg/observe/lib/observe.dart
@@ -65,8 +65,8 @@
* print('done!');
* }
*
- * [Tools](https://github.com/dart-lang/web-ui) exist to convert the first form
- * into the second form automatically, to get the best of both worlds.
+ * [Tools](https://www.dartlang.org/polymer-dart/) exist to convert the first
+ * form into the second form automatically, to get the best of both worlds.
*/
library observe;
diff --git a/pkg/observe/lib/src/observable.dart b/pkg/observe/lib/src/observable.dart
index 0127d6e..d39f84a 100644
--- a/pkg/observe/lib/src/observable.dart
+++ b/pkg/observe/lib/src/observable.dart
@@ -97,6 +97,8 @@
Map<Symbol, Object> _values;
List<ChangeRecord> _records;
+ static final _objectType = reflectClass(Object);
+
Stream<List<ChangeRecord>> get changes {
if (_changes == null) {
_changes = new StreamController.broadcast(sync: true,
@@ -114,19 +116,21 @@
var mirror = reflect(this);
var values = new Map<Symbol, Object>();
- // TODO(jmesserly): this should consider the superclass. Unfortunately
- // that is not possible right now because of:
- // http://code.google.com/p/dart/issues/detail?id=9434
- for (var field in mirror.type.variables.values) {
- if (field.isFinal || field.isStatic || field.isPrivate) continue;
+ // Note: we scan for @observable regardless of whether the base type
+ // actually includes this mixin. While perhaps too inclusive, it lets us
+ // avoid complex logic that walks "with" and "implements" clauses.
+ for (var type = mirror.type; type != _objectType; type = type.superclass) {
+ for (var field in type.variables.values) {
+ if (field.isFinal || field.isStatic || field.isPrivate) continue;
- for (var meta in field.metadata) {
- if (identical(observable, meta.reflectee)) {
- var name = field.simpleName;
- // Note: since this is a field, getting the value shouldn't execute
- // user code, so we don't need to worry about errors.
- values[name] = mirror.getField(name).reflectee;
- break;
+ for (var meta in field.metadata) {
+ if (identical(observable, meta.reflectee)) {
+ var name = field.simpleName;
+ // Note: since this is a field, getting the value shouldn't execute
+ // user code, so we don't need to worry about errors.
+ values[name] = mirror.getField(name).reflectee;
+ break;
+ }
}
}
}
diff --git a/pkg/observe/pubspec.yaml b/pkg/observe/pubspec.yaml
index 7324aef..8fbfb39 100644
--- a/pkg/observe/pubspec.yaml
+++ b/pkg/observe/pubspec.yaml
@@ -6,7 +6,7 @@
UI (DOM) of a document or application and its underlying data (model).
Updates to the model are reflected in the DOM and user input into the DOM is
immediately assigned to the model.
-homepage: https://github.com/dart-lang/web-ui
+homepage: https://www.dartlang.org/polymer-dart/
dependencies:
analyzer_experimental: any
barback: any
diff --git a/pkg/observe/test/observe_test.dart b/pkg/observe/test/observe_test.dart
index b181ccb..da58c3b 100644
--- a/pkg/observe/test/observe_test.dart
+++ b/pkg/observe/test/observe_test.dart
@@ -22,9 +22,11 @@
expect(dirty_check.allObservablesCount, 0);
});
- group('WatcherModel', () { _observeTests(watch: true); });
+ group('WatcherModel', () => _observeTests((x) => new WatcherModel(x)));
- group('ObservableBox', () { _observeTests(); });
+ group('ObservableBox', () => _observeTests((x) => new ObservableBox(x)));
+
+ group('ModelSubclass', () => _observeTests((x) => new ModelSubclass(x)));
group('dirtyCheck loops can be debugged', () {
var messages;
@@ -60,9 +62,8 @@
});
}
-void _observeTests({bool watch: false}) {
- final createModel = watch ? (x) => new WatcherModel(x)
- : (x) => new ObservableBox(x);
+void _observeTests(createModel(x)) {
+ final watch = createModel(null) is! ChangeNotifierMixin;
// Track the subscriptions so we can clean them up in tearDown.
List subs;
@@ -255,3 +256,7 @@
String toString() => '#<$runtimeType value: $value>';
}
+
+class ModelSubclass<T> extends WatcherModel<T> {
+ ModelSubclass([T initialValue]) : super(initialValue);
+}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 6460d2e..e398666 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -12,8 +12,7 @@
# Skip non-test files ending with "_test".
scheduled_test/lib/*: Skip
-polymer/lib/*: Skip
-polymer/example/*: Skip
+polymer/example/scoped_style/*: Skip
scheduled_test/test/scheduled_server_test: Pass, Fail, Slow, Crash # Issue 9231, 9582
scheduled_test/test/scheduled_process_test: Pass, Slow # Issue 9231
@@ -62,12 +61,29 @@
crypto/test/sha1_test: Fail # Issue 11407.
stack_trace/test/trace_test: Fail # http://dartbug.com/12380
crypto/test/sha256_test: Pass, Fail # Issue 12502
+crypto/test/hmac_sha256_test: Pass, Fail # Issue 12502
+polymer/test/events_test: Fail # Issue 12865, 13197
+polymer/test/event_path_test: Fail # Issue 12865, 13197
+
+[ $runtime == ff ]
+polymer/test/event_path_test: Fail # Issue 12865, 13197
+
+[ $runtime == ie9 || $runtime == ie10 ]
+polymer/test/events_test: Fail, Timeout # Issue 12865, 13197, 13260
+polymer/test/event_path_test: Fail, Timeout # Issue 12865, 13197, 13260
+
+[ $system == windows && ($runtime == chrome || $runtime == ff) ]
+polymer/test/events_test: Pass, Timeout # Issue 13260
+polymer/test/event_path_test: Pass, Timeout # Issue 13260
# Skip browser-specific tests on VM
[ $runtime == vm ]
path/test/browser_test: Fail, OK # Uses dart:html
intl/test/find_default_locale_browser_test: Skip
intl/test/date_time_format_http_request_test: Skip
+polymer/test/events_test: Fail, OK # Uses dart:html
+polymer/test/event_path_test: Fail, OK # Uses dart:html
+polymer/example: Fail, OK # Uses dart:html
[ $runtime == vm && $system == windows ]
docgen/test/single_library_test: Fail # Issue 11985
@@ -105,6 +121,7 @@
stack_trace/test/vm_test: Fail, OK # VM-specific traces
crypto/test/sha256_test: Slow, Pass
crypto/test/sha1_test: Slow, Pass
+polymer/example/component: Fail # Issue 13198
[ $browser ]
analyzer_experimental/test/error_test: Fail, OK # Uses dart:io.
@@ -133,7 +150,8 @@
oauth2/test/handle_access_token_response_test: Fail, OK # Uses dart:io.
observe/test/transform_test: Fail, OK # Uses dart:io.
path/test/io_test: Fail, OK # Uses dart:io.
-polymer/test/*: Fail, OK # Uses dart:io.
+polymer/test/build/*: Fail, OK # Uses dart:io.
+polymer/test/utils_test: Fail, OK # Uses dart:io.
watcher/test/*: Fail, OK # Uses dart:io.
scheduled_test/test/descriptor/async_test: Fail # http://dartbug.com/8440
diff --git a/pkg/polymer/build.dart b/pkg/polymer/build.dart
deleted file mode 100755
index 4599716..0000000
--- a/pkg/polymer/build.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2012, 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.
-
-/** Build logic that lets the Dart editor build examples in the background. */
-library build;
-import 'package:polymer/component_build.dart';
-import 'dart:io';
-
-void main() {
- build(new Options().arguments, [
- 'example/component/news/web/index.html',
- 'example/scoped_style/index.html']);
-}
diff --git a/pkg/polymer/example/component/news/test/expected/news_index_test.html.txt b/pkg/polymer/example/component/news/test/expected/news_index_test.html.txt
deleted file mode 100644
index 0850734..0000000
--- a/pkg/polymer/example/component/news/test/expected/news_index_test.html.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-Content-Type: text/plain
-<html><head>
- <title>Simple Web Components Example</title>
-
- <script src="packages/polymer/testing/testing.js"></script>
-<style>template,
-thead[template],
-tbody[template],
-tfoot[template],
-th[template],
-tr[template],
-td[template],
-caption[template],
-colgroup[template],
-col[template],
-option[template] {
- display: none;
-}</style></head>
-<body><script src="packages/shadow_dom/shadow_dom.min.js"></script>
-<script src="packages/browser/interop.js"></script>
-<polymer-element name="x-news" extends="ul">
- <template>
- <style scoped="">
- div.breaking {
- color: Red;
- font-size: 20px;
- border: 1px dashed Purple;
- }
- div.other {
- padding: 2px 0 0 0;
- border: 1px solid Cyan;
- }
- </style>
- <div class="breaking">
- <h2>Breaking Stories</h2>
- <ul>
- <content select=".breaking"></content>
- </ul>
- </div>
- <div class="other">
- <h2>Other News</h2>
- <ul>
- <content></content>
- </ul>
- </div>
- </template>
-
-</polymer-element>
-<h1>Simple Web Components Example</h1>
-<ul is="x-news"><shadow-root>
- <style scoped="">/* style hidden by testing.js */</style>
- <div class="breaking">
- <h2>Breaking Stories</h2>
- <ul>
- <content select=".breaking"></content>
- </ul>
- </div>
- <div class="other">
- <h2>Other News</h2>
- <ul>
- <content></content>
- </ul>
- </div>
- </shadow-root>
- <li><a href="//example.com/stories/1">A story</a></li>
- <li><a href="//example.com/stories/2">Another story</a></li>
- <li class="breaking"><a href="//example.com/stories/3">Also a story</a></li>
- <li><a href="//example.com/stories/4">Yet another story</a></li>
- <li><a href="//example.com/stories/4">Awesome story</a></li>
- <li class="breaking"><a href="//example.com/stories/5">Horrible story</a></li>
-</ul>
-
-
-
-<script type="application/dart" src="news_index_test.html_bootstrap.dart"></script></body></html>
diff --git a/pkg/polymer/example/component/news/test/news_index_test.dart b/pkg/polymer/example/component/news/test/news_index_test.dart
new file mode 100644
index 0000000..67ebbe3
--- /dev/null
+++ b/pkg/polymer/example/component/news/test/news_index_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2013, 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:html';
+import 'package:polymer/polymer.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+/**
+ * This test runs the news example and checks the state of the initial page.
+ */
+main() {
+ useHtmlConfiguration();
+
+ extractLinks(nodes) => nodes.where((n) => n is Element)
+ .map((n) => n.query('a').href.split('/').last).toList();
+
+ test('initial state', () {
+ final listComp = query('ul');
+ final items = listComp.queryAll('li');
+ expect(items.length, 6);
+ expect(extractLinks(items), ['1', '2', '3', '4', '4', '5']);
+ expect(listComp.xtag is PolymerElement, true,
+ reason: 'x-news should be created');
+
+ final contents = listComp.shadowRoot.queryAll('content');
+ expect(contents.length, 2, reason: 'news has 2 content tags');
+ expect(extractLinks(contents[0].getDistributedNodes()),
+ ['3', '5'], reason: 'breaking stories first');
+ expect(extractLinks(contents[1].getDistributedNodes()),
+ ['1', '2', '4', '4'], reason: 'other stories after breaking stories');
+ });
+}
diff --git a/pkg/polymer/example/component/news/test/news_index_test.html b/pkg/polymer/example/component/news/test/news_index_test.html
index 95bfadd..873b2d5 100644
--- a/pkg/polymer/example/component/news/test/news_index_test.html
+++ b/pkg/polymer/example/component/news/test/news_index_test.html
@@ -7,7 +7,8 @@
<head>
<title>Simple Web Components Example</title>
<link rel="import" href="../web/news-component.html">
- <script src="packages/polymer/testing/testing.js"></script>
+ <script src="packages/polymer/boot.js"></script>
+ <script src="packages/unittest/test_controller.js"></script>
</head>
<body>
<h1>Simple Web Components Example</h1>
@@ -19,11 +20,6 @@
<li><a href="//example.com/stories/4">Awesome story</a></li>
<li class="breaking"><a href="//example.com/stories/5">Horrible story</a></li>
</ul>
-<script type="application/dart">
-import 'dart:html';
-main() {
- window.postMessage('done', '*');
-}
-</script>
+<script type="application/dart" src="news_index_test.dart"></script>
</body>
</html>
diff --git a/pkg/polymer/example/component/news/test/test.dart b/pkg/polymer/example/component/news/test/test.dart
deleted file mode 100755
index a4c98a0..0000000
--- a/pkg/polymer/example/component/news/test/test.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2013, 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:polymer/testing/content_shell_test.dart';
-import 'package:unittest/compact_vm_config.dart';
-
-void main() {
- useCompactVMConfiguration();
- // Base directory, input, expected, output:
- renderTests('..', '.', 'expected', 'out');
-}
diff --git a/pkg/polymer/lib/builder.dart b/pkg/polymer/lib/builder.dart
new file mode 100644
index 0000000..eb5ee43
--- /dev/null
+++ b/pkg/polymer/lib/builder.dart
@@ -0,0 +1,292 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * Common logic to make it easy to run the polymer linter and deploy tool.
+ *
+ * The functions in this library are designed to make it easier to create
+ * `build.dart` files. A `build.dart` file is a Dart script that can be invoked
+ * from the command line, but that can also invoked automatically by the Dart
+ * Editor whenever a file in your project changes or when selecting some menu
+ * options, such as 'Reanalyze Sources'.
+ *
+ * To work correctly, place the `build.dart` in the root of your project (where
+ * pubspec.yaml lives). The file must be named exactly `build.dart`.
+ *
+ * It's quite likely that in the near future `build.dart` will be replaced with
+ * something else. For example, `pub deploy` will deal with deploying
+ * applications automatically, and the Dart Editor might provide other
+ * mechanisms to hook linters.
+ *
+ * There are three important functions exposed by this library [build], [lint],
+ * and [deploy]. The following examples show common uses of these functions when
+ * writing a `build.dart` file.
+ *
+ * **Example 1**: Uses build.dart to run the linter tool.
+ *
+ * import 'dart:io';
+ * import 'package:polymer/builder.dart';
+ *
+ * main() {
+ * lint();
+ * }
+ *
+ * **Example 2**: Runs the linter and creates a deployable version of the app
+ * every time.
+ *
+ * import 'dart:io';
+ * import 'package:polymer/builder.dart';
+ *
+ * main() {
+ * lint().then(() => deploy());
+ * }
+ *
+ * **Example 3**: Runs the linter, but conditionally does the deploy step. See
+ * [parseOptions] for a description of options parsed automatically by this
+ * helper library.
+ *
+ * import 'dart:io';
+ * import 'package:polymer/builder.dart';
+ *
+ * main() {
+ * var options = parseOptions();
+ * lint().then(() {
+ * if (options.forceDeploy) deploy();
+ * });
+ * }
+ *
+ * **Example 4**: Same as above, but uses [build] (which internally calls [lint]
+ * and [deploy]).
+ *
+ * import 'dart:io';
+ * import 'package:polymer/builder.dart';
+ *
+ * main() {
+ * build();
+ * }
+ *
+ * **Example 5**: Like the previous example, but indicates to the linter and
+ * deploy tool which files are actually used as entry point files. See the
+ * documentation of [build] below for more details.
+ *
+ * import 'dart:io';
+ * import 'package:polymer/builder.dart';
+ *
+ * main() {
+ * build(entryPoints: ['web/index.html']);
+ * }
+ */
+library polymer.builder;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/args.dart';
+
+import 'src/build/linter.dart';
+import 'src/build/runner.dart';
+import 'transformer.dart';
+
+
+/**
+ * Runs the polymer linter on any relevant file in your package, such as any
+ * .html file under 'lib/', 'asset/', and 'web/'. And, if requested, creates a
+ * directory suitable for deploying a Polymer application to a server.
+ *
+ * The [entryPoints] list contains files under web/ that should be treated as
+ * entry points. Each entry on this list is a relative path from the package
+ * root (for example 'web/index.html'). If null, all files under 'web/' are
+ * treated as possible entry points.
+ *
+ * Options are read from the command line arguments, but you can override them
+ * passing the [options] argument. The deploy operation is run only when the
+ * command-line argument `--deploy` is present, or equivalently when
+ * `options.forceDeploy` is true.
+ *
+ * The linter and deploy steps needs to know the name of the [currentPackage]
+ * and the location where to find the code for any package it depends on
+ * ([packageDirs]). This is inferred automatically, but can be overriden if
+ * those arguments are provided.
+ */
+Future build({List<String> entryPoints, CommandLineOptions options,
+ String currentPackage, Map<String, String> packageDirs}) {
+ if (options == null) options = _options;
+ return lint(entryPoints: entryPoints, options: options,
+ currentPackage: currentPackage, packageDirs: packageDirs).then((res) {
+ if (options.forceDeploy) {
+ return deploy(entryPoints: entryPoints, options: options,
+ currentPackage: currentPackage, packageDirs: packageDirs);
+ }
+ });
+}
+
+
+/**
+ * Runs the polymer linter on any relevant file in your package,
+ * such as any .html file under 'lib/', 'asset/', and 'web/'.
+ *
+ * The [entryPoints] list contains files under web/ that should be treated as
+ * entry points. Each entry on this list is a relative path from the package
+ * root (for example 'web/index.html'). If null, all files under 'web/' are
+ * treated as possible entry points.
+ *
+ * Options are read from the command line arguments, but you can override them
+ * passing the [options] argument.
+ *
+ * The linter needs to know the name of the [currentPackage] and the location
+ * where to find the code for any package it depends on ([packageDirs]). This is
+ * inferred automatically, but can be overriden if those arguments are provided.
+ */
+Future lint({List<String> entryPoints, CommandLineOptions options,
+ String currentPackage, Map<String, String> packageDirs}) {
+ if (options == null) options = _options;
+ if (currentPackage == null) currentPackage = readCurrentPackageFromPubspec();
+ var linterOptions = new TransformOptions(currentPackage, entryPoints);
+ var formatter = options.machineFormat ? jsonFormatter : consoleFormatter;
+ var linter = new Linter(linterOptions, formatter);
+ return runBarback(new BarbackOptions([[linter]], null,
+ currentPackage: currentPackage, packageDirs: packageDirs)).then((assets) {
+ var messages = {};
+ var futures = [];
+ for (var asset in assets) {
+ var id = asset.id;
+ if (id.package == currentPackage && id.path.endsWith('.messages')) {
+ futures.add(asset.readAsString().then((content) {
+ if (content.isEmpty) return;
+ messages[id] = content;
+ }));
+ }
+ }
+
+ return Future.wait(futures).then((_) {
+ // Print messages sorting by package and filepath.
+ var orderedKeys = messages.keys.toList();
+ orderedKeys.sort((a, b) {
+ int packageCompare = a.package.compareTo(b.package);
+ if (packageCompare != 0) return packageCompare;
+ return a.path.compareTo(b.path);
+ });
+
+ for (var key in orderedKeys) {
+ print(messages[key]);
+ }
+ });
+ });
+}
+
+/**
+ * Creates a directory suitable for deploying a Polymer application to a server.
+ *
+ * **Note**: this function will be replaced in the future by the `pub deploy`
+ * command.
+ *
+ * The [entryPoints] list contains files under web/ that should be treated as
+ * entry points. Each entry on this list is a relative path from the package
+ * root (for example 'web/index.html'). If null, all files under 'web/' are
+ * treated as possible entry points.
+ *
+ * Options are read from the command line arguments, but you can override them
+ * passing the [options] list.
+ *
+ * The deploy step needs to know the name of the [currentPackage] and the
+ * location where to find the code for any package it depends on
+ * ([packageDirs]). This is inferred automatically, but can be overriden if
+ * those arguments are provided.
+ */
+Future deploy({List<String> entryPoints, CommandLineOptions options,
+ String currentPackage, Map<String, String> packageDirs}) {
+ if (options == null) options = _options;
+ if (currentPackage == null) currentPackage = readCurrentPackageFromPubspec();
+ var barbackOptions = new BarbackOptions(
+ createDeployPhases(new TransformOptions(currentPackage, entryPoints)),
+ options.outDir, currentPackage: currentPackage,
+ packageDirs: packageDirs);
+ return runBarback(barbackOptions)
+ .then((_) => print('Done! All files written to "${options.outDir}"'));
+}
+
+
+/**
+ * Options that may be used either in build.dart or by the linter and deploy
+ * tools.
+ */
+class CommandLineOptions {
+ /** Files marked as changed. */
+ final List<String> changedFiles;
+
+ /** Files marked as removed. */
+ final List<String> removedFiles;
+
+ /** Whether to clean intermediate artifacts, if any. */
+ final bool clean;
+
+ /** Whether to do a full build (as if all files have changed). */
+ final bool full;
+
+ /** Whether to print results using a machine parseable format. */
+ final bool machineFormat;
+
+ /** Whether the force deploy option was passed in the command line. */
+ final bool forceDeploy;
+
+ /** Location where to generate output files. */
+ final String outDir;
+
+ CommandLineOptions(this.changedFiles, this.removedFiles, this.clean,
+ this.full, this.machineFormat, this.forceDeploy, this.outDir);
+}
+
+/** Options parsed directly from the command line arguments. */
+CommandLineOptions _options = parseOptions();
+
+/**
+ * Parse command-line arguments and return a [CommandLineOptions] object. The
+ * following flags are parsed by this method.
+ *
+ * * `--changed file-path`: notify of a file change.
+ * * `--removed file-path`: notify that a file was removed.
+ * * `--clean`: remove temporary artifacts (if any)
+ * * `--full`: build everything, similar to marking every file as changed
+ * * `--machine`: produce output that can be parsed by tools, such as the Dart
+ * Editor.
+ * * `--deploy`: force deploy.
+ * * `--help`: print documentation for each option and exit.
+ *
+ * Currently not all the flags are used by [lint] or [deploy] above, but they
+ * are available so they can be used from your `build.dart`. For instance, see
+ * the top-level library documentation for an example that uses the force-deploy
+ * option to conditionally call [deploy].
+ *
+ * If this documentation becomes out of date, the best way to discover which
+ * flags are supported is to invoke this function from your build.dart, and run
+ * it with the `--help` command-line flag.
+ */
+CommandLineOptions parseOptions([List<String> args]) {
+ var parser = new ArgParser()
+ ..addOption('changed', help: 'The file has changed since the last build.',
+ allowMultiple: true)
+ ..addOption('removed', help: 'The file was removed since the last build.',
+ allowMultiple: true)
+ ..addFlag('clean', negatable: false,
+ help: 'Remove any build artifacts (if any).')
+ ..addFlag('full', negatable: false, help: 'perform a full build')
+ ..addFlag('machine', negatable: false,
+ help: 'Produce warnings in a machine parseable format.')
+ ..addFlag('deploy', negatable: false,
+ help: 'Whether to force deploying.')
+ ..addOption('out', abbr: 'o', help: 'Directory to generate files into.',
+ defaultsTo: 'out')
+ ..addFlag('help', abbr: 'h',
+ negatable: false, help: 'Displays this help and exit.');
+ var res = parser.parse(args == null ? new Options().arguments : args);
+ if (res['help']) {
+ print('A build script that invokes the polymer linter and deploy tools.');
+ print('Usage: dart build.dart [options]');
+ print('\nThese are valid options expected by build.dart:');
+ print(parser.getUsage());
+ exit(0);
+ }
+ return new CommandLineOptions(res['changed'], res['removed'], res['clean'],
+ res['full'], res['machine'], res['deploy'], res['out']);
+}
diff --git a/pkg/polymer/lib/component_build.dart b/pkg/polymer/lib/component_build.dart
index 7184787..6a923f0 100644
--- a/pkg/polymer/lib/component_build.dart
+++ b/pkg/polymer/lib/component_build.dart
@@ -2,105 +2,21 @@
// 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.
-/**
- * Common logic to make it easy to create a `build.dart` for your project.
- *
- * The `build.dart` script is invoked automatically by the Editor whenever a
- * file in the project changes. It must be placed in the root of a project
- * (where pubspec.yaml lives) and should be named exactly 'build.dart'.
- *
- * A common `build.dart` would look as follows:
- *
- * import 'dart:io';
- * import 'package:polymer/component_build.dart';
- *
- * main() => build(new Options().arguments, ['web/index.html']);
- */
+/** This library is deprecated. Please use `builder.dart` instead. */
+@deprecated
library build_utils;
-import 'dart:async';
-import 'dart:convert';
-import 'dart:io';
-import 'package:args/args.dart';
+import 'package:meta/meta.dart';
-import 'dwc.dart' as dwc;
-import 'src/utils.dart';
-import 'src/compiler_options.dart';
+import 'builder.dart' as builder;
/**
- * Set up 'build.dart' to compile with the dart web components compiler every
- * [entryPoints] listed. On clean commands, the directory where [entryPoints]
- * live will be scanned for generated files to delete them.
+ * This function is deprecated. Please use `build` from `builder.dart`
+ * instead.
*/
-Future<List<dwc.AnalysisResults>> build(List<String> arguments,
- List<String> entryPoints,
+@deprecated
+Future build(List<String> arguments, List<String> entryPoints,
{bool printTime: true, bool shouldPrint: true}) {
- bool useColors = stdioType(stdout) == StdioType.TERMINAL;
- return asyncTime('Total time', () {
- var args = _processArgs(arguments);
- var tasks = new FutureGroup();
- var lastTask = new Future.value(null);
- tasks.add(lastTask);
-
- var changedFiles = args["changed"];
- var removedFiles = args["removed"];
- var cleanBuild = args["clean"];
- var machineFormat = args["machine"];
- // Also trigger a full build if the script was run from the command line
- // with no arguments
- var fullBuild = args["full"] || (!machineFormat && changedFiles.isEmpty &&
- removedFiles.isEmpty && !cleanBuild);
-
- var options = CompilerOptions.parse(args.rest, checkUsage: false);
-
- if (fullBuild || !changedFiles.isEmpty || !removedFiles.isEmpty) {
- for (var file in entryPoints) {
- var dwcArgs = new List.from(args.rest);
- if (machineFormat) dwcArgs.add('--json_format');
- if (!useColors) dwcArgs.add('--no-colors');
- dwcArgs.add(file);
- // Chain tasks to that we run one at a time.
- lastTask = lastTask.then((_) => dwc.run(dwcArgs, printTime: printTime,
- shouldPrint: shouldPrint));
- if (machineFormat) {
- lastTask = lastTask.then((res) {
- appendMessage(Map jsonMessage) {
- var message = JSON.encode([jsonMessage]);
- if (shouldPrint) print(message);
- res.messages.add(message);
- }
- return res;
- });
- }
- tasks.add(lastTask);
- }
- }
- return tasks.future.then((r) => r.where((v) => v != null));
- }, printTime: printTime, useColors: useColors);
-}
-
-/** Process the command-line arguments. */
-ArgResults _processArgs(List<String> arguments) {
- var parser = new ArgParser()
- ..addOption("changed", help: "the file has changed since the last build",
- allowMultiple: true)
- ..addOption("removed", help: "the file was removed since the last build",
- allowMultiple: true)
- ..addFlag("clean", negatable: false, help: "currently a noop, may be used "
- "in the future to remove any build artifacts")
- ..addFlag("full", negatable: false, help: "perform a full build")
- ..addFlag("machine", negatable: false,
- help: "produce warnings in a machine parseable format")
- ..addFlag("help", abbr: 'h',
- negatable: false, help: "displays this help and exit");
- var args = parser.parse(arguments);
- if (args["help"]) {
- print('A build script that invokes the web-ui compiler (dwc).');
- print('Usage: dart build.dart [options] [-- [dwc-options]]');
- print('\nThese are valid options expected by build.dart:');
- print(parser.getUsage());
- print('\nThese are valid options expected by dwc:');
- dwc.run(['-h']).then((_) => exit(0));
- }
- return args;
+ return builder.build(
+ entryPoints: entryPoints, options: parseOptions(arguments));
}
diff --git a/pkg/polymer/lib/deploy.dart b/pkg/polymer/lib/deploy.dart
index 4c5b564..8445ac8 100644
--- a/pkg/polymer/lib/deploy.dart
+++ b/pkg/polymer/lib/deploy.dart
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
/**
+ * **Note**: If you already have a `build.dart` in your application, we
+ * recommend to use the `package:polymer/builder.dart` library instead.
+
* Temporary deploy command used to create a version of the app that can be
* compiled with dart2js and deployed. Following pub layout conventions, this
* script will treat any HTML file under a package 'web/' and 'test/'
@@ -20,34 +23,33 @@
library polymer.deploy;
import 'dart:async';
-import 'dart:convert';
import 'dart:io';
-import 'package:barback/barback.dart';
-import 'package:path/path.dart' as path;
-import 'package:polymer/src/transform.dart' show phases;
-import 'package:stack_trace/stack_trace.dart';
-import 'package:yaml/yaml.dart';
import 'package:args/args.dart';
+import 'package:path/path.dart' as path;
+import 'src/build/runner.dart';
+import 'transformer.dart';
main() {
var args = _parseArgs(new Options().arguments);
- if (args == null) return;
+ if (args == null) exit(1);
var test = args['test'];
- if (test != null) {
- _initForTest(test);
- }
-
- print('polymer/deploy.dart: creating a deploy target for "$_currentPackage"');
var outDir = args['out'];
- _run(outDir, test != null).then(
- (_) => print('Done! All files written to "$outDir"'));
+ var options = (test == null)
+ ? new BarbackOptions(createDeployPhases(new TransformOptions()), outDir)
+ : _createTestOptions(test, outDir);
+ if (options == null) exit(1);
+
+ print('polymer/deploy.dart: creating a deploy target for '
+ '"${options.currentPackage}"');
+
+ runBarback(options)
+ .then((_) => print('Done! All files written to "$outDir"'))
+ .catchError(_reportErrorAndExit);
}
-// TODO(jmesserly): the current deploy/barback architecture is very unfriendly
-// to deploying a single test. We need to fix it somehow but it isn't clear yet.
-void _initForTest(String testFile) {
+BarbackOptions _createTestOptions(String testFile, String outDir) {
var testDir = path.normalize(path.dirname(testFile));
// A test must be allowed to import things in the package.
@@ -57,11 +59,15 @@
if (pubspecDir == null) {
print('error: pubspec.yaml file not found, please run this script from '
'your package root directory or a subdirectory.');
- exit(1);
+ return null;
}
- _currentPackage = '_test';
- _packageDirs = {'_test' : pubspecDir};
+ var phases = createDeployPhases(new TransformOptions(
+ '_test', [path.relative(testFile, from: pubspecDir)]));
+ return new BarbackOptions(phases, outDir,
+ currentPackage: '_test',
+ packageDirs: {'_test' : pubspecDir},
+ transformTests: true);
}
String _findDirWithFile(String dir, String filename) {
@@ -74,208 +80,18 @@
return dir;
}
-/**
- * API exposed for testing purposes. Runs this deploy command but prentend that
- * the sources under [webDir] belong to package 'test'.
- */
-Future runForTest(String webDir, String outDir) {
- _currentPackage = 'test';
-
- // associate package dirs with their location in the repo:
- _packageDirs = {'test' : '.'};
- _addPackages('..');
- _addPackages('../third_party');
- _addPackages('../../third_party/pkg');
- return _run(webDir, outDir);
+void _reportErrorAndExit(e) {
+ var trace = getAttachedStackTrace(e);
+ print('Uncaught error: $e');
+ if (trace != null) print(trace);
+ exit(1);
}
-_addPackages(String dir) {
- for (var packageDir in new Directory(dir).listSync().map((d) => d.path)) {
- _packageDirs[path.basename(packageDir)] = packageDir;
- }
-}
-
-Future _run(String outDir, bool includeTests) {
- var barback = new Barback(new _PolymerDeployProvider());
- _initializeBarback(barback, includeTests);
- _attachListeners(barback);
- return _emitAllFiles(barback, 'web', outDir).then(
- (_) => includeTests ? _emitAllFiles(barback, 'test', outDir) : null);
-}
-
-/** Tell barback which transformers to use and which assets to process. */
-void _initializeBarback(Barback barback, bool includeTests) {
- var assets = [];
- void addAssets(String package, String subDir) {
- for (var filepath in _listDir(package, subDir)) {
- assets.add(new AssetId(package, filepath));
- }
- }
-
- for (var package in _packageDirs.keys) {
- // Do not process packages like 'polymer' where there is nothing to do.
- if (_ignoredPackages.contains(package)) continue;
- barback.updateTransformers(package, phases);
-
- // notify barback to process anything under 'lib' and 'asset'
- addAssets(package, 'lib');
- addAssets(package, 'asset');
- }
-
- // In case of the current package, include also 'web'.
- addAssets(_currentPackage, 'web');
- if (includeTests) addAssets(_currentPackage, 'test');
-
- barback.updateSources(assets);
-}
-
-/** Return the relative path of each file under [subDir] in a [package]. */
-Iterable<String> _listDir(String package, String subDir) {
- var packageDir = _packageDirs[package];
- if (packageDir == null) return const [];
- var dir = new Directory(path.join(packageDir, subDir));
- if (!dir.existsSync()) return const [];
- return dir.listSync(recursive: true, followLinks: false)
- .where((f) => f is File)
- .map((f) => path.relative(f.path, from: packageDir));
-}
-
-/** Attach error listeners on [barback] so we can report errors. */
-void _attachListeners(Barback barback) {
- // Listen for errors and results
- barback.errors.listen((e) {
- var trace = getAttachedStackTrace(e);
- if (trace != null) {
- print(Trace.format(trace));
- }
- print('error running barback: $e');
- exit(1);
- });
-
- barback.results.listen((result) {
- if (!result.succeeded) {
- print("build failed with errors: ${result.errors}");
- exit(1);
- }
- });
-}
-
-/** Ensure [dirpath] exists. */
-void _ensureDir(var dirpath) {
- new Directory(dirpath).createSync(recursive: true);
-}
-
-/**
- * Emits all outputs of [barback] and copies files that we didn't process (like
- * polymer's libraries).
- */
-Future _emitAllFiles(Barback barback, String webDir, String outDir) {
- return barback.getAllAssets().then((assets) {
- // Copy all the assets we transformed
- var futures = [];
- for (var asset in assets) {
- var id = asset.id;
- var filepath;
- if (id.package == _currentPackage && id.path.startsWith('$webDir/')) {
- filepath = path.join(outDir, id.path);
- } else if (id.path.startsWith('lib/')) {
- filepath = path.join(outDir, webDir, 'packages', id.package,
- id.path.substring(4));
- } else {
- // TODO(sigmund): do something about other assets?
- continue;
- }
-
- _ensureDir(path.dirname(filepath));
- var writer = new File(filepath).openWrite();
- futures.add(writer.addStream(asset.read()).then((_) => writer.close()));
- }
- return Future.wait(futures);
- }).then((_) {
- // Copy also all the files we didn't process
- var futures = [];
- for (var package in _ignoredPackages) {
- for (var relpath in _listDir(package, 'lib')) {
- var inpath = path.join(_packageDirs[package], relpath);
- var outpath = path.join(outDir, webDir, 'packages', package,
- relpath.substring(4));
- _ensureDir(path.dirname(outpath));
-
- var writer = new File(outpath).openWrite();
- futures.add(writer.addStream(new File(inpath).openRead())
- .then((_) => writer.close()));
- }
- }
- return Future.wait(futures);
- });
-}
-
-/** A simple provider that reads files directly from the pub cache. */
-class _PolymerDeployProvider implements PackageProvider {
-
- Iterable<String> get packages => _packageDirs.keys;
- _PolymerDeployProvider();
-
- Future<Asset> getAsset(AssetId id) =>
- new Future.value(new Asset.fromPath(id, path.join(
- _packageDirs[id.package],
- // Assets always use the posix style paths
- path.joinAll(path.posix.split(id.path)))));
-}
-
-
-/** The current package extracted from the pubspec.yaml file. */
-String _currentPackage = () {
- var pubspec = new File('pubspec.yaml');
- if (!pubspec.existsSync()) {
- print('error: pubspec.yaml file not found, please run this script from '
- 'your package root directory.');
- return null;
- }
- return loadYaml(pubspec.readAsStringSync())['name'];
-}();
-
-/**
- * Maps package names to the path in the file system where to find the sources
- * of such package. This map will contain an entry for the current package and
- * everything it depends on (extracted via `pub list-pacakge-dirs`).
- */
-Map<String, String> _packageDirs = () {
- var pub = path.join(path.dirname(new Options().executable),
- Platform.isWindows ? 'pub.bat' : 'pub');
- var result = Process.runSync(pub, ['list-package-dirs']);
- if (result.exitCode != 0) {
- print("unexpected error invoking 'pub':");
- print(result.stdout);
- print(result.stderr);
- exit(result.exitCode);
- }
- var map = JSON.decode(result.stdout)["packages"];
- map.forEach((k, v) { map[k] = path.dirname(v); });
- map[_currentPackage] = '.';
- return map;
-}();
-
-/**
- * Internal packages used by polymer which we can copy directly to the output
- * folder without having to process them with barback.
- */
-// TODO(sigmund): consider computing this list by recursively parsing
-// pubspec.yaml files in the [_packageDirs].
-final Set<String> _ignoredPackages =
- (const [ 'analyzer_experimental', 'args', 'barback', 'browser', 'csslib',
- 'custom_element', 'fancy_syntax', 'html5lib', 'html_import', 'js',
- 'logging', 'mdv', 'meta', 'mutation_observer', 'observe', 'path',
- 'polymer', 'polymer_expressions', 'serialization', 'shadow_dom',
- 'source_maps', 'stack_trace', 'unittest',
- 'unmodifiable_collection', 'yaml'
- ]).toSet();
-
ArgResults _parseArgs(arguments) {
var parser = new ArgParser()
..addFlag('help', abbr: 'h', help: 'Displays this help message.',
defaultsTo: false, negatable: false)
- ..addOption('out', abbr: 'o', help: 'Directory where to generated files.',
+ ..addOption('out', abbr: 'o', help: 'Directory to generate files into.',
defaultsTo: 'out')
..addOption('test', help: 'Deploy the test at the given path.\n'
'Note: currently this will deploy all tests in its directory,\n'
@@ -295,6 +111,7 @@
}
_showUsage(parser) {
- print('Usage: dart package:polymer/deploy.dart [options]');
+ print('Usage: dart --package-root=packages/ '
+ 'package:polymer/deploy.dart [options]');
print(parser.getUsage());
}
diff --git a/pkg/polymer/lib/dwc.dart b/pkg/polymer/lib/dwc.dart
deleted file mode 100644
index b0cc4db..0000000
--- a/pkg/polymer/lib/dwc.dart
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/** The entry point to the compiler. Used to implement `bin/dwc.dart`. */
-library dwc;
-
-import 'dart:async';
-import 'dart:io';
-import 'package:logging/logging.dart' show Level;
-
-import 'src/compiler.dart';
-import 'src/file_system/console.dart';
-import 'src/messages.dart';
-import 'src/compiler_options.dart';
-import 'src/utils.dart';
-
-void main() {
- run(new Options().arguments).then((result) {
- exit(result.success ? 0 : 1);
- });
-}
-
-/** Contains the result of a compiler run. */
-class AnalysisResults {
-
- /** False when errors were found by our polymer analyzer. */
- final bool success;
-
- /** Error and warning messages collected by the analyzer. */
- final List<String> messages;
-
- AnalysisResults(this.success, this.messages);
-}
-
-/**
- * Runs the polymer analyzer with the command-line options in [args].
- * See [CompilerOptions] for the definition of valid arguments.
- */
-// TODO(sigmund): rename to analyze? and rename file as analyzer.dart
-Future<AnalysisResults> run(List<String> args, {bool printTime,
- bool shouldPrint: true}) {
- var options = CompilerOptions.parse(args);
- if (options == null) return new Future.value(new AnalysisResults(true, []));
- if (printTime == null) printTime = options.verbose;
-
- return asyncTime('Total time spent on ${options.inputFile}', () {
- var messages = new Messages(options: options, shouldPrint: shouldPrint);
- var compiler = new Compiler(new ConsoleFileSystem(), options, messages);
- return compiler.run().then((_) {
- var success = messages.messages.every((m) => m.level != Level.SEVERE);
- var msgs = options.jsonFormat
- ? messages.messages.map((m) => m.toJson())
- : messages.messages.map((m) => m.toString());
- return new AnalysisResults(success, msgs.toList());
- });
- }, printTime: printTime, useColors: options.useColors);
-}
diff --git a/pkg/polymer/lib/polymer_element.dart b/pkg/polymer/lib/polymer_element.dart
index 14d4096..3f42390 100644
--- a/pkg/polymer/lib/polymer_element.dart
+++ b/pkg/polymer/lib/polymer_element.dart
@@ -16,7 +16,7 @@
import 'package:observe/src/microtask.dart';
import 'package:polymer_expressions/polymer_expressions.dart';
-import 'src/utils_observe.dart' show toCamelCase, toHyphenedName;
+import 'src/utils.dart' show toCamelCase, toHyphenedName;
/**
* Registers a [PolymerElement]. This is similar to [registerCustomElement]
diff --git a/pkg/polymer/lib/src/analyzer.dart b/pkg/polymer/lib/src/analyzer.dart
deleted file mode 100644
index b8011a0..0000000
--- a/pkg/polymer/lib/src/analyzer.dart
+++ /dev/null
@@ -1,481 +0,0 @@
-// Copyright (c) 2013, 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.
-
-/**
- * Part of the template compilation that concerns with extracting information
- * from the HTML parse tree.
- */
-library analyzer;
-
-import 'package:html5lib/dom.dart';
-import 'package:html5lib/dom_parsing.dart';
-
-import 'custom_tag_name.dart';
-import 'files.dart';
-import 'info.dart';
-import 'messages.dart';
-
-/**
- * Finds custom elements in this file and the list of referenced files with
- * component declarations. This is the first pass of analysis on a file.
- *
- * Adds emitted error/warning messages to [messages], if [messages] is
- * supplied.
- */
-FileInfo analyzeDefinitions(GlobalInfo global, UrlInfo inputUrl,
- Document document, String packageRoot, Messages messages) {
- var result = new FileInfo(inputUrl);
- var loader = new _ElementLoader(global, result, packageRoot, messages);
- loader.visit(document);
- return result;
-}
-
-/**
- * Extract relevant information from all files found from the root document.
- *
- * Adds emitted error/warning messages to [messages], if [messages] is
- * supplied.
- */
-void analyzeFile(SourceFile file, Map<String, FileInfo> info,
- Iterator<int> uniqueIds, GlobalInfo global,
- Messages messages, emulateScopedCss) {
- var fileInfo = info[file.path];
- var analyzer = new _Analyzer(fileInfo, uniqueIds, global, messages,
- emulateScopedCss);
- analyzer._normalize(fileInfo, info);
- analyzer.visit(file.document);
-}
-
-
-/** A visitor that walks the HTML to extract all the relevant information. */
-class _Analyzer extends TreeVisitor {
- final FileInfo _fileInfo;
- LibraryInfo _currentInfo;
- Iterator<int> _uniqueIds;
- GlobalInfo _global;
- Messages _messages;
-
- int _generatedClassNumber = 0;
-
- /**
- * Whether to keep indentation spaces. Break lines and indentation spaces
- * within templates are preserved in HTML. When users specify the attribute
- * 'indentation="remove"' on a template tag, we'll trim those indentation
- * spaces that occur within that tag and its decendants. If any decendant
- * specifies 'indentation="preserve"', then we'll switch back to the normal
- * behavior.
- */
- bool _keepIndentationSpaces = true;
-
- final bool _emulateScopedCss;
-
- _Analyzer(this._fileInfo, this._uniqueIds, this._global, this._messages,
- this._emulateScopedCss) {
- _currentInfo = _fileInfo;
- }
-
- void visitElement(Element node) {
- if (node.tagName == 'script') {
- // We already extracted script tags in previous phase.
- return;
- }
-
- if (node.tagName == 'style') {
- // We've already parsed the CSS.
- // If this is a component remove the style node.
- if (_currentInfo is ComponentInfo && _emulateScopedCss) node.remove();
- return;
- }
-
- _bindCustomElement(node);
-
- var lastInfo = _currentInfo;
- if (node.tagName == 'polymer-element') {
- // If element is invalid _ElementLoader already reported an error, but
- // we skip the body of the element here.
- var name = node.attributes['name'];
- if (name == null) return;
-
- ComponentInfo component = _fileInfo.components[name];
- if (component == null) return;
-
- _analyzeComponent(component);
-
- _currentInfo = component;
-
- // Remove the <element> tag from the tree
- node.remove();
- }
-
- node.attributes.forEach((name, value) {
- if (name.startsWith('on')) {
- _validateEventHandler(node, name, value);
- } else if (name == 'pseudo' && _currentInfo is ComponentInfo) {
- // Any component's custom pseudo-element(s) defined?
- _processPseudoAttribute(node, value.split(' '));
- }
- });
-
- var keepSpaces = _keepIndentationSpaces;
- if (node.tagName == 'template' &&
- node.attributes.containsKey('indentation')) {
- var value = node.attributes['indentation'];
- if (value != 'remove' && value != 'preserve') {
- _messages.warning(
- "Invalid value for 'indentation' ($value). By default we preserve "
- "the indentation. Valid values are either 'remove' or 'preserve'.",
- node.sourceSpan);
- }
- _keepIndentationSpaces = value != 'remove';
- }
-
- // Invoke super to visit children.
- super.visitElement(node);
-
- _keepIndentationSpaces = keepSpaces;
- _currentInfo = lastInfo;
- }
-
- void _analyzeComponent(ComponentInfo component) {
- var baseTag = component.extendsTag;
- component.extendsComponent = baseTag == null ? null
- : _fileInfo.components[baseTag];
- if (component.extendsComponent == null && isCustomTag(baseTag)) {
- _messages.warning(
- 'custom element with tag name ${component.extendsTag} not found.',
- component.element.sourceSpan);
- }
- }
-
- void _bindCustomElement(Element node) {
- // <fancy-button>
- var component = _fileInfo.components[node.tagName];
- if (component == null) {
- // TODO(jmesserly): warn for unknown element tags?
-
- // <button is="fancy-button">
- var componentName = node.attributes['is'];
- if (componentName != null) {
- component = _fileInfo.components[componentName];
- } else if (isCustomTag(node.tagName)) {
- componentName = node.tagName;
- }
- if (component == null && componentName != null &&
- componentName != 'polymer-element') {
- _messages.warning(
- 'custom element with tag name $componentName not found.',
- node.sourceSpan);
- }
- }
-
- if (component != null) {
- var baseTag = component.baseExtendsTag;
- var nodeTag = node.tagName;
- var hasIsAttribute = node.attributes.containsKey('is');
-
- if (baseTag != null && !hasIsAttribute) {
- _messages.warning(
- 'custom element "${component.tagName}" extends from "$baseTag", but'
- ' this tag will not include the default properties of "$baseTag". '
- 'To fix this, either write this tag as <$baseTag '
- 'is="${component.tagName}"> or remove the "extends" attribute from '
- 'the custom element declaration.', node.sourceSpan);
- } else if (hasIsAttribute) {
- if (baseTag == null) {
- _messages.warning(
- 'custom element "${component.tagName}" doesn\'t declare any type '
- 'extensions. To fix this, either rewrite this tag as '
- '<${component.tagName}> or add \'extends="$nodeTag"\' to '
- 'the custom element declaration.', node.sourceSpan);
- } else if (baseTag != nodeTag) {
- _messages.warning(
- 'custom element "${component.tagName}" extends from "$baseTag". '
- 'Did you mean to write <$baseTag is="${component.tagName}">?',
- node.sourceSpan);
- }
- }
- }
- }
-
- void _processPseudoAttribute(Node node, List<String> values) {
- List mangledValues = [];
- for (var pseudoElement in values) {
- if (_global.pseudoElements.containsKey(pseudoElement)) continue;
-
- _uniqueIds.moveNext();
- var newValue = "${pseudoElement}_${_uniqueIds.current}";
- _global.pseudoElements[pseudoElement] = newValue;
- // Mangled name of pseudo-element.
- mangledValues.add(newValue);
-
- if (!pseudoElement.startsWith('x-')) {
- // TODO(terry): The name must start with x- otherwise it's not a custom
- // pseudo-element. May want to relax since components no
- // longer need to start with x-. See isse #509 on
- // pseudo-element prefix.
- _messages.warning("Custom pseudo-element must be prefixed with 'x-'.",
- node.sourceSpan);
- }
- }
-
- // Update the pseudo attribute with the new mangled names.
- node.attributes['pseudo'] = mangledValues.join(' ');
- }
-
- /**
- * Support for inline event handlers that take expressions.
- * For example: `on-double-click=myHandler($event, todo)`.
- */
- void _validateEventHandler(Element node, String name, String value) {
- if (!name.startsWith('on-')) {
- // TODO(jmesserly): do we need an option to suppress this warning?
- _messages.warning('Event handler $name will be interpreted as an inline '
- 'JavaScript event handler. Use the form '
- 'on-event-name="handlerName" if you want a Dart handler '
- 'that will automatically update the UI based on model changes.',
- node.sourceSpan);
- }
-
- if (value.contains('.') || value.contains('(')) {
- // TODO(sigmund): should we allow more if we use fancy-syntax?
- _messages.warning('Invalid event handler body "$value". Declare a method '
- 'in your custom element "void handlerName(event, detail, target)" '
- 'and use the form on-event-name="handlerName".',
- node.sourceSpan);
- }
- }
-
- /**
- * Normalizes references in [info]. On the [analyzeDefinitions] phase, the
- * analyzer extracted names of files and components. Here we link those names
- * to actual info classes. In particular:
- * * we initialize the [FileInfo.components] map in [info] by importing all
- * [declaredComponents],
- * * we scan all [info.componentLinks] and import their
- * [info.declaredComponents], using [files] to map the href to the file
- * info. Names in [info] will shadow names from imported files.
- */
- void _normalize(FileInfo info, Map<String, FileInfo> files) {
- for (var component in info.declaredComponents) {
- _addComponent(info, component);
- }
-
- for (var link in info.componentLinks) {
- var file = files[link.resolvedPath];
- // We already issued an error for missing files.
- if (file == null) continue;
- file.declaredComponents.forEach((c) => _addComponent(info, c));
- }
- }
-
- /** Adds a component's tag name to the names in scope for [fileInfo]. */
- void _addComponent(FileInfo fileInfo, ComponentInfo component) {
- var existing = fileInfo.components[component.tagName];
- if (existing != null) {
- if (existing == component) {
- // This is the same exact component as the existing one.
- return;
- }
-
- if (existing is ComponentInfo && component is! ComponentInfo) {
- // Components declared in [fileInfo] shadow component names declared in
- // imported files.
- return;
- }
-
- if (existing.hasConflict) {
- // No need to report a second error for the same name.
- return;
- }
-
- existing.hasConflict = true;
-
- if (component is ComponentInfo) {
- _messages.error('duplicate custom element definition for '
- '"${component.tagName}".', existing.sourceSpan);
- _messages.error('duplicate custom element definition for '
- '"${component.tagName}" (second location).', component.sourceSpan);
- } else {
- _messages.error('imported duplicate custom element definitions '
- 'for "${component.tagName}".', existing.sourceSpan);
- _messages.error('imported duplicate custom element definitions '
- 'for "${component.tagName}" (second location).',
- component.sourceSpan);
- }
- } else {
- fileInfo.components[component.tagName] = component;
- }
- }
-}
-
-/** A visitor that finds `<link rel="import">` and `<element>` tags. */
-class _ElementLoader extends TreeVisitor {
- final GlobalInfo _global;
- final FileInfo _fileInfo;
- LibraryInfo _currentInfo;
- String _packageRoot;
- bool _inHead = false;
- Messages _messages;
-
- /**
- * Adds emitted warning/error messages to [_messages]. [_messages]
- * must not be null.
- */
- _ElementLoader(this._global, this._fileInfo, this._packageRoot,
- this._messages) {
- _currentInfo = _fileInfo;
- }
-
- void visitElement(Element node) {
- switch (node.tagName) {
- case 'link': visitLinkElement(node); break;
- case 'element':
- _messages.warning('<element> elements are not supported, use'
- ' <polymer-element> instead', node.sourceSpan);
- break;
- case 'polymer-element':
- visitElementElement(node);
- break;
- case 'script': visitScriptElement(node); break;
- case 'head':
- var savedInHead = _inHead;
- _inHead = true;
- super.visitElement(node);
- _inHead = savedInHead;
- break;
- default: super.visitElement(node); break;
- }
- }
-
- /**
- * Process `link rel="import"` as specified in:
- * <https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/components/index.html#link-type-component>
- */
- void visitLinkElement(Element node) {
- var rel = node.attributes['rel'];
- if (rel != 'component' && rel != 'components' &&
- rel != 'import' && rel != 'stylesheet') return;
-
- if (!_inHead) {
- _messages.warning('link rel="$rel" only valid in '
- 'head.', node.sourceSpan);
- return;
- }
-
- if (rel == 'component' || rel == 'components') {
- _messages.warning('import syntax is changing, use '
- 'rel="import" instead of rel="$rel".', node.sourceSpan);
- }
-
- var href = node.attributes['href'];
- if (href == null || href == '') {
- _messages.warning('link rel="$rel" missing href.',
- node.sourceSpan);
- return;
- }
-
- bool isStyleSheet = rel == 'stylesheet';
- var urlInfo = UrlInfo.resolve(href, _fileInfo.inputUrl, node.sourceSpan,
- _packageRoot, _messages, ignoreAbsolute: isStyleSheet);
- if (urlInfo == null) return;
- if (isStyleSheet) {
- _fileInfo.styleSheetHrefs.add(urlInfo);
- } else {
- _fileInfo.componentLinks.add(urlInfo);
- }
- }
-
- void visitElementElement(Element node) {
- // TODO(jmesserly): what do we do in this case? It seems like an <element>
- // inside a Shadow DOM should be scoped to that <template> tag, and not
- // visible from the outside.
- if (_currentInfo is ComponentInfo) {
- _messages.error('Nested component definitions are not yet supported.',
- node.sourceSpan);
- return;
- }
-
- var tagName = node.attributes['name'];
- var extendsTag = node.attributes['extends'];
-
- if (tagName == null) {
- _messages.error('Missing tag name of the component. Please include an '
- 'attribute like \'name="your-tag-name"\'.',
- node.sourceSpan);
- return;
- }
-
- var component = new ComponentInfo(node, tagName, extendsTag);
- _fileInfo.declaredComponents.add(component);
- _addComponent(component);
-
- var lastInfo = _currentInfo;
- _currentInfo = component;
- super.visitElement(node);
- _currentInfo = lastInfo;
- }
-
- /** Adds a component's tag name to the global list. */
- void _addComponent(ComponentInfo component) {
- var existing = _global.components[component.tagName];
- if (existing != null) {
- if (existing.hasConflict) {
- // No need to report a second error for the same name.
- return;
- }
-
- existing.hasConflict = true;
-
- _messages.error('duplicate custom element definition for '
- '"${component.tagName}".', existing.sourceSpan);
- _messages.error('duplicate custom element definition for '
- '"${component.tagName}" (second location).', component.sourceSpan);
- } else {
- _global.components[component.tagName] = component;
- }
- }
-
- void visitScriptElement(Element node) {
- var scriptType = node.attributes['type'];
- var src = node.attributes["src"];
-
- if (scriptType == null) {
- // Note: in html5 leaving off type= is fine, but it defaults to
- // text/javascript. Because this might be a common error, we warn about it
- // in two cases:
- // * an inline script tag in a web component
- // * a script src= if the src file ends in .dart (component or not)
- //
- // The hope is that neither of these cases should break existing valid
- // code, but that they'll help component authors avoid having their Dart
- // code accidentally interpreted as JavaScript by the browser.
- if (src == null && _currentInfo is ComponentInfo) {
- _messages.warning('script tag in component with no type will '
- 'be treated as JavaScript. Did you forget type="application/dart"?',
- node.sourceSpan);
- }
- if (src != null && src.endsWith('.dart')) {
- _messages.warning('script tag with .dart source file but no type will '
- 'be treated as JavaScript. Did you forget type="application/dart"?',
- node.sourceSpan);
- }
- return;
- }
-
- if (scriptType != 'application/dart') return;
-
- if (src != null) {
- if (!src.endsWith('.dart')) {
- _messages.warning('"application/dart" scripts should '
- 'use the .dart file extension.',
- node.sourceSpan);
- }
-
- if (node.innerHtml.trim() != '') {
- _messages.error('script tag has "src" attribute and also has script '
- 'text.', node.sourceSpan);
- }
- }
- }
-}
diff --git a/pkg/polymer/lib/src/transform/code_extractor.dart b/pkg/polymer/lib/src/build/code_extractor.dart
similarity index 93%
rename from pkg/polymer/lib/src/transform/code_extractor.dart
rename to pkg/polymer/lib/src/build/code_extractor.dart
index a46300e..5e9135d 100644
--- a/pkg/polymer/lib/src/transform/code_extractor.dart
+++ b/pkg/polymer/lib/src/build/code_extractor.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/** Transfomer that extracts inlined script code into separate assets. */
-library polymer.src.transformers;
+library polymer.src.build.code_extractor;
import 'dart:async';
@@ -20,7 +20,11 @@
* Transformer that extracts Dart code inlined in HTML script tags and outputs a
* separate file for each.
*/
-class InlineCodeExtractor extends Transformer {
+class InlineCodeExtractor extends Transformer with PolymerTransformer {
+ final TransformOptions options;
+
+ InlineCodeExtractor(this.options);
+
/** Only run this transformer on .html files. */
final String allowedExtensions = ".html";
diff --git a/pkg/polymer/lib/src/transform/common.dart b/pkg/polymer/lib/src/build/common.dart
similarity index 67%
rename from pkg/polymer/lib/src/transform/common.dart
rename to pkg/polymer/lib/src/build/common.dart
index e18a053..6233aa3 100644
--- a/pkg/polymer/lib/src/transform/common.dart
+++ b/pkg/polymer/lib/src/build/common.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/** Common methods used by transfomers. */
-library polymer.src.transform.common;
+library polymer.src.build.common;
import 'dart:async';
@@ -34,23 +34,50 @@
return document;
}
-Future<Document> readPrimaryAsHtml(Transform transform) {
- var asset = transform.primaryInput;
- var id = asset.id;
- return asset.readAsString().then((content) {
- return _parseHtml(content, id.path, transform.logger,
- checkDocType: isPrimaryHtml(id));
- });
+/** Additional options used by polymer transformers */
+class TransformOptions {
+ String currentPackage;
+ List<String> entryPoints;
+
+ TransformOptions([this.currentPackage, entryPoints])
+ : entryPoints = entryPoints == null ? null
+ : entryPoints.map(_systemToAssetPath).toList();
+
+ /** Whether an asset with [id] is an entry point HTML file. */
+ bool isHtmlEntryPoint(AssetId id) {
+ if (id.extension != '.html') return false;
+
+ // Note: [id.path] is a relative path from the root of a package.
+ if (currentPackage == null || entryPoints == null) {
+ return id.path.startsWith('web/') || id.path.startsWith('test/');
+ }
+
+ return id.package == currentPackage && entryPoints.contains(id.path);
+ }
}
-Future<Document> readAsHtml(AssetId id, Transform transform) {
- var primaryId = transform.primaryInput.id;
- var url = (id.package == primaryId.package) ? id.path
- : assetUrlFor(id, primaryId, transform.logger, allowAssetUrl: true);
- return transform.readInputAsString(id).then((content) {
- return _parseHtml(content, url, transform.logger,
- checkDocType: isPrimaryHtml(id));
- });
+/** Mixin for polymer transformers. */
+abstract class PolymerTransformer {
+ TransformOptions get options;
+
+ Future<Document> readPrimaryAsHtml(Transform transform) {
+ var asset = transform.primaryInput;
+ var id = asset.id;
+ return asset.readAsString().then((content) {
+ return _parseHtml(content, id.path, transform.logger,
+ checkDocType: options.isHtmlEntryPoint(id));
+ });
+ }
+
+ Future<Document> readAsHtml(AssetId id, Transform transform) {
+ var primaryId = transform.primaryInput.id;
+ var url = (id.package == primaryId.package) ? id.path
+ : assetUrlFor(id, primaryId, transform.logger, allowAssetUrl: true);
+ return transform.readInputAsString(id).then((content) {
+ return _parseHtml(content, url, transform.logger,
+ checkDocType: options.isHtmlEntryPoint(id));
+ });
+ }
}
/** Create an [AssetId] for a [url] seen in the [source] asset. */
@@ -92,11 +119,6 @@
return new AssetId(package, targetPath);
}
-/** Whether an asset with [id] is considered a primary entry point HTML file. */
-bool isPrimaryHtml(AssetId id) => id.extension == '.html' &&
- // Note: [id.path] is a relative path from the root of a package.
- (id.path.startsWith('web/') || id.path.startsWith('test/'));
-
/**
* Generate the import url for a file described by [id], referenced by a file
* with [sourceId].
@@ -128,3 +150,10 @@
return builder.relative(builder.join('/', id.path),
from: builder.join('/', builder.dirname(sourceId.path)));
}
+
+
+/** Convert system paths to asset paths (asset paths are posix style). */
+String _systemToAssetPath(String assetPath) {
+ if (path.Style.platform != path.Style.windows) return assetPath;
+ return path.posix.joinAll(path.split(assetPath));
+}
diff --git a/pkg/polymer/lib/src/transform/import_inliner.dart b/pkg/polymer/lib/src/build/import_inliner.dart
similarity index 95%
rename from pkg/polymer/lib/src/transform/import_inliner.dart
rename to pkg/polymer/lib/src/build/import_inliner.dart
index 889ce20..987fe9f 100644
--- a/pkg/polymer/lib/src/transform/import_inliner.dart
+++ b/pkg/polymer/lib/src/build/import_inliner.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/** Transfomer that inlines polymer-element definitions from html imports. */
-library polymer.src.transform.import_inliner;
+library polymer.src.build.import_inliner;
import 'dart:async';
@@ -16,10 +16,14 @@
/** Recursively inlines polymer-element definitions from html imports. */
// TODO(sigmund): make sure we match semantics of html-imports for tags other
// than polymer-element (see dartbug.com/12613).
-class ImportedElementInliner extends Transformer {
+class ImportedElementInliner extends Transformer with PolymerTransformer {
+ final TransformOptions options;
+
+ ImportedElementInliner(this.options);
+
/** Only run on entry point .html files. */
Future<bool> isPrimary(Asset input) =>
- new Future.value(isPrimaryHtml(input.id));
+ new Future.value(options.isHtmlEntryPoint(input.id));
Future apply(Transform transform) {
var seen = new Set<AssetId>();
diff --git a/pkg/polymer/lib/src/linter.dart b/pkg/polymer/lib/src/build/linter.dart
similarity index 91%
rename from pkg/polymer/lib/src/linter.dart
rename to pkg/polymer/lib/src/build/linter.dart
index f443603..f65741a 100644
--- a/pkg/polymer/lib/src/linter.dart
+++ b/pkg/polymer/lib/src/build/linter.dart
@@ -6,8 +6,9 @@
* Logic to validate that developers are correctly using Polymer constructs.
* This is mainly used to produce warnings for feedback in the editor.
*/
-library polymer.src.linter;
+library polymer.src.build.linter;
+import 'dart:io';
import 'dart:async';
import 'dart:mirrors';
import 'dart:convert' show JSON;
@@ -16,7 +17,7 @@
import 'package:html5lib/dom.dart';
import 'package:html5lib/dom_parsing.dart';
-import 'transform/common.dart';
+import 'common.dart';
typedef String MessageFormatter(String kind, String message, Span span);
@@ -25,13 +26,15 @@
* show on the editor or the command line. Leaves sources unchanged, but creates
* a new asset containing all the warnings.
*/
-class Linter extends Transformer {
+class Linter extends Transformer with PolymerTransformer {
+ final TransformOptions options;
+
/** Only run on .html files. */
final String allowedExtensions = '.html';
final MessageFormatter _formatter;
- Linter([this._formatter]);
+ Linter(this.options, [this._formatter]);
Future apply(Transform transform) {
var wrapper = new _LoggerInterceptor(transform, _formatter);
@@ -120,7 +123,7 @@
final MessageFormatter _formatter;
_LoggerInterceptor(this._original, MessageFormatter formatter)
- : _formatter = formatter == null ? _defaultFormatter : formatter;
+ : _formatter = formatter == null ? consoleFormatter : formatter;
TransformLogger get logger => this;
@@ -137,10 +140,10 @@
}
/**
- * Default formatter that generates messages using a format that can be parsed
+ * Formatter that generates messages using a format that can be parsed
* by tools, such as the Dart Editor, for reporting error messages.
*/
-String _defaultFormatter(String kind, String message, Span span) {
+String jsonFormatter(String kind, String message, Span span) {
return JSON.encode((span == null)
? [{'method': 'warning', 'params': {'message': message}}]
: [{'method': kind,
@@ -153,6 +156,26 @@
}}]);
}
+/**
+ * Formatter that generates messages that are easy to read on the console (used
+ * by default).
+ */
+String consoleFormatter(String kind, String message, Span span) {
+ var useColors = stdioType(stdout) == StdioType.TERMINAL;
+ var levelColor = (kind == 'error') ? _RED_COLOR : _MAGENTA_COLOR;
+ var output = new StringBuffer();
+ if (useColors) output.write(levelColor);
+ output..write(kind)..write(' ');
+ if (useColors) output.write(_NO_COLOR);
+ if (span == null) {
+ output.write(message);
+ } else {
+ output.write(span.getLocationMessage(message,
+ useColors: useColors,
+ color: levelColor));
+ }
+ return output.toString();
+}
/**
* Information needed about other polymer-element tags in order to validate
@@ -415,3 +438,7 @@
if (name == null || !name.contains('-')) return false;
return !_invalidTagNames.containsKey(name);
}
+
+final String _RED_COLOR = '\u001b[31m';
+final String _MAGENTA_COLOR = '\u001b[35m';
+final String _NO_COLOR = '\u001b[0m';
diff --git a/pkg/polymer/lib/src/transform/polyfill_injector.dart b/pkg/polymer/lib/src/build/polyfill_injector.dart
similarity index 91%
rename from pkg/polymer/lib/src/transform/polyfill_injector.dart
rename to pkg/polymer/lib/src/build/polyfill_injector.dart
index 6b95113..7c3d3cf 100644
--- a/pkg/polymer/lib/src/transform/polyfill_injector.dart
+++ b/pkg/polymer/lib/src/build/polyfill_injector.dart
@@ -6,7 +6,7 @@
* Final phase of the polymer transformation: includes any additional polyfills
* that may needed by the deployed app.
*/
-library polymer.src.transform.polyfill_injector;
+library polymer.src.build.polyfill_injector;
import 'dart:async';
@@ -21,10 +21,14 @@
* script tag that loads the shadow_dom polyfill and interop.js (used for the
* css shimming).
*/
-class PolyfillInjector extends Transformer {
+class PolyfillInjector extends Transformer with PolymerTransformer {
+ final TransformOptions options;
+
+ PolyfillInjector(this.options);
+
/** Only run on entry point .html files. */
Future<bool> isPrimary(Asset input) =>
- new Future.value(isPrimaryHtml(input.id));
+ new Future.value(options.isHtmlEntryPoint(input.id));
Future apply(Transform transform) {
return readPrimaryAsHtml(transform).then((document) {
diff --git a/pkg/polymer/lib/src/build/runner.dart b/pkg/polymer/lib/src/build/runner.dart
new file mode 100644
index 0000000..d940c18
--- /dev/null
+++ b/pkg/polymer/lib/src/build/runner.dart
@@ -0,0 +1,312 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Definitions used to run the polymer linter and deploy tools without using
+ * pub serve or pub deploy.
+ */
+library polymer.src.build.runner;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:barback/barback.dart';
+import 'package:path/path.dart' as path;
+import 'package:stack_trace/stack_trace.dart';
+import 'package:yaml/yaml.dart';
+
+
+/** Collects different parameters needed to configure and run barback. */
+class BarbackOptions {
+ /** Phases of transformers to run. */
+ final List<List<Transformer>> phases;
+
+ /** Package to treat as the current package in barback. */
+ final String currentPackage;
+
+ /**
+ * Mapping between package names and the path in the file system where
+ * to find the sources of such package.
+ */
+ final Map<String, String> packageDirs;
+
+ /** Whether to run transformers on the test folder. */
+ final bool transformTests;
+
+ /** Whether to apply transformers on polymer dependencies. */
+ final bool transformPolymerDependencies;
+
+ /** Directory where to generate code, if any. */
+ final String outDir;
+
+ BarbackOptions(this.phases, this.outDir, {currentPackage, packageDirs,
+ this.transformTests: false, this.transformPolymerDependencies: false})
+ : currentPackage = (currentPackage != null
+ ? currentPackage : readCurrentPackageFromPubspec()),
+ packageDirs = (packageDirs != null
+ ? packageDirs : _readPackageDirsFromPub(currentPackage));
+
+}
+
+/**
+ * Creates a barback system as specified by [options] and runs it. Returns a
+ * future that contains the list of assets generated after barback runs to
+ * completion.
+ */
+Future<AssetSet> runBarback(BarbackOptions options) {
+ var barback = new Barback(new _PolymerPackageProvider(options.packageDirs));
+ _initBarback(barback, options);
+ _attachListeners(barback);
+ if (options.outDir == null) return barback.getAllAssets();
+ return _emitAllFiles(barback, options);
+}
+
+/** Extract the current package from the pubspec.yaml file. */
+String readCurrentPackageFromPubspec() {
+ var pubspec = new File('pubspec.yaml');
+ if (!pubspec.existsSync()) {
+ print('error: pubspec.yaml file not found, please run this script from '
+ 'your package root directory.');
+ return null;
+ }
+ return loadYaml(pubspec.readAsStringSync())['name'];
+}
+
+/**
+ * Extract a mapping between package names and the path in the file system where
+ * to find the sources of such package. This map will contain an entry for the
+ * current package and everything it depends on (extracted via `pub
+ * list-pacakge-dirs`).
+ */
+Map<String, String> _readPackageDirsFromPub(String currentPackage) {
+ var dartExec = new Options().executable;
+ // If dartExec == dart, then dart and pub are in standard PATH.
+ var sdkDir = dartExec == 'dart' ? '' : path.dirname(dartExec);
+ var pub = path.join(sdkDir, Platform.isWindows ? 'pub.bat' : 'pub');
+ var result = Process.runSync(pub, ['list-package-dirs']);
+ if (result.exitCode != 0) {
+ print("unexpected error invoking 'pub':");
+ print(result.stdout);
+ print(result.stderr);
+ exit(result.exitCode);
+ }
+ var map = JSON.decode(result.stdout)["packages"];
+ map.forEach((k, v) { map[k] = path.dirname(v); });
+ map[currentPackage] = '.';
+ return map;
+}
+
+/** Internal packages used by polymer. */
+// TODO(sigmund): consider computing this list by recursively parsing
+// pubspec.yaml files in the `Options.packageDirs`.
+final Set<String> _polymerPackageDependencies = [
+ 'analyzer_experimental', 'args', 'barback', 'browser', 'csslib',
+ 'custom_element', 'fancy_syntax', 'html5lib', 'html_import', 'js',
+ 'logging', 'mdv', 'meta', 'mutation_observer', 'observe', 'path', 'polymer',
+ 'polymer_expressions', 'serialization', 'shadow_dom', 'source_maps',
+ 'stack_trace', 'unittest', 'unmodifiable_collection', 'yaml'].toSet();
+
+/** Return the relative path of each file under [subDir] in [package]. */
+Iterable<String> _listPackageDir(String package, String subDir,
+ BarbackOptions options) {
+ var packageDir = options.packageDirs[package];
+ if (packageDir == null) return const [];
+ var dir = new Directory(path.join(packageDir, subDir));
+ if (!dir.existsSync()) return const [];
+ return dir.listSync(recursive: true, followLinks: false)
+ .where((f) => f is File)
+ .map((f) => path.relative(f.path, from: packageDir));
+}
+
+/** A simple provider that reads files directly from the pub cache. */
+class _PolymerPackageProvider implements PackageProvider {
+ Map<String, String> packageDirs;
+ Iterable<String> get packages => packageDirs.keys;
+
+ _PolymerPackageProvider(this.packageDirs);
+
+ Future<Asset> getAsset(AssetId id) => new Future.value(
+ new Asset.fromPath(id, path.join(packageDirs[id.package],
+ _toSystemPath(id.path))));
+}
+
+/** Convert asset paths to system paths (Assets always use the posix style). */
+String _toSystemPath(String assetPath) {
+ if (path.Style.platform != path.Style.windows) return assetPath;
+ return path.joinAll(path.posix.split(assetPath));
+}
+
+/** Tell barback which transformers to use and which assets to process. */
+void _initBarback(Barback barback, BarbackOptions options) {
+ var assets = [];
+ void addAssets(String package, String subDir) {
+ for (var filepath in _listPackageDir(package, subDir, options)) {
+ assets.add(new AssetId(package, filepath));
+ }
+ }
+
+ for (var package in options.packageDirs.keys) {
+ // There is nothing to do in the 'polymer' package and its dependencies.
+ if (!options.transformPolymerDependencies &&
+ _polymerPackageDependencies.contains(package)) continue;
+ barback.updateTransformers(package, options.phases);
+
+ // Notify barback to process anything under 'lib' and 'asset'.
+ addAssets(package, 'lib');
+ addAssets(package, 'asset');
+ }
+
+ // In case of the current package, include also 'web'.
+ addAssets(options.currentPackage, 'web');
+ if (options.transformTests) addAssets(options.currentPackage, 'test');
+
+ barback.updateSources(assets);
+}
+
+/** Attach error listeners on [barback] so we can report errors. */
+void _attachListeners(Barback barback) {
+ // Listen for errors and results
+ barback.errors.listen((e) {
+ var trace = getAttachedStackTrace(e);
+ if (trace != null) {
+ print(Trace.format(trace));
+ }
+ print('error running barback: $e');
+ exit(1);
+ });
+
+ barback.results.listen((result) {
+ if (!result.succeeded) {
+ print("build failed with errors: ${result.errors}");
+ exit(1);
+ }
+ });
+}
+
+/**
+ * Emits all outputs of [barback] and copies files that we didn't process (like
+ * polymer's libraries).
+ */
+Future _emitAllFiles(Barback barback, BarbackOptions options) {
+ return barback.getAllAssets().then((assets) {
+ // Delete existing output folder before we generate anything
+ var dir = new Directory(options.outDir);
+ if (dir.existsSync()) dir.deleteSync(recursive: true);
+ return _emitPackagesDir(options)
+ .then((_) => _emitTransformedFiles(assets, options))
+ .then((_) => _addPackagesSymlinks(assets, options))
+ .then((_) => assets);
+ });
+}
+
+Future _emitTransformedFiles(AssetSet assets, BarbackOptions options) {
+ // Copy all the assets we transformed
+ var futures = [];
+ var currentPackage = options.currentPackage;
+ var transformTests = options.transformTests;
+ var outPackages = path.join(options.outDir, 'packages');
+ for (var asset in assets) {
+ var id = asset.id;
+ var dir = _firstDir(id.path);
+ if (dir == null) continue;
+
+ var filepath;
+ if (dir == 'lib') {
+ // Put lib files directly under the packages folder (e.g. 'lib/foo.dart'
+ // will be emitted at out/packages/package_name/foo.dart).
+ filepath = path.join(outPackages, id.package,
+ _toSystemPath(id.path.substring(4)));
+ } else if (id.package == currentPackage &&
+ (dir == 'web' || (transformTests && dir == 'test'))) {
+ filepath = path.join(options.outDir, _toSystemPath(id.path));
+ } else {
+ // TODO(sigmund): do something about other assets?
+ continue;
+ }
+
+ futures.add(_writeAsset(filepath, asset));
+ }
+ return Future.wait(futures);
+}
+
+/**
+ * Adds a package symlink from each directory under `out/web/foo/` to
+ * `out/packages`.
+ */
+Future _addPackagesSymlinks(AssetSet assets, BarbackOptions options) {
+ var outPackages = path.join(options.outDir, 'packages');
+ var currentPackage = options.currentPackage;
+ for (var asset in assets) {
+ var id = asset.id;
+ if (id.package != currentPackage) continue;
+ var firstDir = _firstDir(id.path);
+ if (firstDir == null) continue;
+
+ if (firstDir == 'web' || (options.transformTests && firstDir == 'test')) {
+ var dir = path.join(options.outDir, path.dirname(_toSystemPath(id.path)));
+ var linkPath = path.join(dir, 'packages');
+ var link = new Link(linkPath);
+ if (!link.existsSync()) {
+ var targetPath = Platform.operatingSystem == 'windows'
+ ? path.normalize(path.absolute(outPackages))
+ : path.normalize(path.relative(outPackages, from: dir));
+ link.createSync(targetPath);
+ }
+ }
+ }
+}
+
+/**
+ * Emits a 'packages' directory directly under `out/packages` with the contents
+ * of every file that was not transformed by barback.
+ */
+Future _emitPackagesDir(BarbackOptions options) {
+ if (options.transformPolymerDependencies) return new Future.value(null);
+ var outPackages = path.join(options.outDir, 'packages');
+ _ensureDir(outPackages);
+
+ // Copy all the files we didn't process
+ var futures = [];
+ var dirs = options.packageDirs;
+ for (var package in _polymerPackageDependencies) {
+ for (var relpath in _listPackageDir(package, 'lib', options)) {
+ var inpath = path.join(dirs[package], relpath);
+ var outpath = path.join(outPackages, package, relpath.substring(4));
+ futures.add(_copyFile(inpath, outpath));
+ }
+ }
+ return Future.wait(futures);
+}
+
+/** Ensure [dirpath] exists. */
+void _ensureDir(String dirpath) {
+ new Directory(dirpath).createSync(recursive: true);
+}
+
+/**
+ * Returns the first directory name on a url-style path, or null if there are no
+ * slashes.
+ */
+String _firstDir(String url) {
+ var firstSlash = url.indexOf('/');
+ if (firstSlash == -1) return null;
+ return url.substring(0, firstSlash);
+}
+
+/** Copy a file from [inpath] to [outpath]. */
+Future _copyFile(String inpath, String outpath) {
+ _ensureDir(path.dirname(outpath));
+ var writer = new File(outpath).openWrite();
+ return writer.addStream(new File(inpath).openRead())
+ .then((_) => writer.close());
+}
+
+/** Write contents of an [asset] into a file at [filepath]. */
+Future _writeAsset(String filepath, Asset asset) {
+ _ensureDir(path.dirname(filepath));
+ var writer = new File(filepath).openWrite();
+ return writer.addStream(asset.read()).then((_) => writer.close());
+}
diff --git a/pkg/polymer/lib/src/transform/script_compactor.dart b/pkg/polymer/lib/src/build/script_compactor.dart
similarity index 93%
rename from pkg/polymer/lib/src/transform/script_compactor.dart
rename to pkg/polymer/lib/src/build/script_compactor.dart
index c247f2d..a20fcd6 100644
--- a/pkg/polymer/lib/src/transform/script_compactor.dart
+++ b/pkg/polymer/lib/src/build/script_compactor.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/** Transfomer that combines multiple dart script tags into a single one. */
-library polymer.src.transform.script_compactor;
+library polymer.src.build.script_compactor;
import 'dart:async';
@@ -27,10 +27,14 @@
* invoke the main method on each of these libraries and register any polymer
* elements annotated with `@CustomTag`.
*/
-class ScriptCompactor extends Transformer {
+class ScriptCompactor extends Transformer with PolymerTransformer {
+ final TransformOptions options;
+
+ ScriptCompactor(this.options);
+
/** Only run on entry point .html files. */
Future<bool> isPrimary(Asset input) =>
- new Future.value(isPrimaryHtml(input.id));
+ new Future.value(options.isHtmlEntryPoint(input.id));
Future apply(Transform transform) {
var id = transform.primaryInput.id;
@@ -47,7 +51,7 @@
continue;
}
var last = src.split('/').last;
- if (last == 'dart.js' || last == 'testing.js') {
+ if (last == 'dart.js') {
dartLoaderTag = tag;
}
}
diff --git a/pkg/polymer/lib/src/compiler.dart b/pkg/polymer/lib/src/compiler.dart
deleted file mode 100644
index 9d5d10d..0000000
--- a/pkg/polymer/lib/src/compiler.dart
+++ /dev/null
@@ -1,360 +0,0 @@
-// Copyright (c) 2012, 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.
-
-library compiler;
-
-import 'dart:async';
-import 'dart:collection' show SplayTreeMap;
-
-import 'package:csslib/visitor.dart' show StyleSheet, treeToDebugString;
-import 'package:html5lib/dom.dart';
-import 'package:html5lib/parser.dart';
-
-import 'analyzer.dart';
-import 'css_analyzer.dart' show analyzeCss, findUrlsImported,
- findImportsInStyleSheet, parseCss;
-import 'css_emitters.dart' show rewriteCssUris,
- emitComponentStyleSheet, emitOriginalCss, emitStyleSheet;
-import 'file_system.dart';
-import 'files.dart';
-import 'info.dart';
-import 'messages.dart';
-import 'compiler_options.dart';
-import 'utils.dart';
-
-/**
- * Parses an HTML file [contents] and returns a DOM-like tree.
- * Note that [contents] will be a [String] if coming from a browser-based
- * [FileSystem], or it will be a [List<int>] if running on the command line.
- *
- * Adds emitted error/warning to [messages], if [messages] is supplied.
- */
-Document parseHtml(contents, String sourcePath, Messages messages,
- bool checkDocType) {
- var parser = new HtmlParser(contents, generateSpans: true,
- sourceUrl: sourcePath);
- var document = parser.parse();
-
- // Note: errors aren't fatal in HTML (unless strict mode is on).
- // So just print them as warnings.
- for (var e in parser.errors) {
- if (checkDocType || e.errorCode != 'expected-doctype-but-got-start-tag') {
- messages.warning(e.message, e.span);
- }
- }
- return document;
-}
-
-/** Compiles an application written with Dart web components. */
-class Compiler {
- final FileSystem fileSystem;
- final CompilerOptions options;
- final List<SourceFile> files = <SourceFile>[];
-
- String _mainPath;
- String _packageRoot;
- String _resetCssFile;
- StyleSheet _cssResetStyleSheet;
- Messages _messages;
-
- FutureGroup _tasks;
- Set _processed;
-
- /** Information about source [files] given their href. */
- final Map<String, FileInfo> info = new SplayTreeMap<String, FileInfo>();
-
- final GlobalInfo global = new GlobalInfo();
-
- /** Creates a compiler with [options] using [fileSystem]. */
- Compiler(this.fileSystem, this.options, this._messages) {
- _mainPath = options.inputFile;
- var mainDir = path.dirname(_mainPath);
- var baseDir = options.baseDir != null ? options.baseDir : mainDir;
- _packageRoot = options.packageRoot != null ? options.packageRoot
- : path.join(path.dirname(_mainPath), 'packages');
-
- if (options.resetCssFile != null) {
- _resetCssFile = options.resetCssFile;
- if (path.isRelative(_resetCssFile)) {
- // If CSS reset file path is relative from our current path.
- _resetCssFile = path.resolve(_resetCssFile);
- }
- }
-
- // Normalize paths - all should be relative or absolute paths.
- if (path.isAbsolute(_mainPath) || path.isAbsolute(baseDir)
- || path.isAbsolute(_packageRoot)) {
- if (path.isRelative(_mainPath)) _mainPath = path.resolve(_mainPath);
- if (path.isRelative(baseDir)) baseDir = path.resolve(baseDir);
- if (path.isRelative(_packageRoot)) {
- _packageRoot = path.resolve(_packageRoot);
- }
- }
- }
-
- /** Compile the application starting from the given input file. */
- Future run() {
- if (path.basename(_mainPath).endsWith('.dart')) {
- _messages.error("Please provide an HTML file as your entry point.",
- null);
- return new Future.value(null);
- }
- return _parseAndDiscover(_mainPath).then((_) {
- _analyze();
-
- // Analyze all CSS files.
- _time('Analyzed Style Sheets', '', () =>
- analyzeCss(_packageRoot, files, info,
- global.pseudoElements, _messages,
- warningsAsErrors: options.warningsAsErrors));
- });
- }
-
- /**
- * Asynchronously parse [inputFile] and transitively discover web components
- * to load and parse. Returns a future that completes when all files are
- * processed.
- */
- Future _parseAndDiscover(String inputFile) {
- _tasks = new FutureGroup();
- _processed = new Set();
- _processed.add(inputFile);
- _tasks.add(_parseHtmlFile(new UrlInfo(inputFile, inputFile, null), true));
- return _tasks.future;
- }
-
- void _processHtmlFile(UrlInfo inputUrl, SourceFile file) {
- if (file == null) return;
-
- bool isEntryPoint = _processed.length == 1;
-
- files.add(file);
-
- var fileInfo = _time('Analyzed definitions', inputUrl.url, () {
- return analyzeDefinitions(global, inputUrl, file.document, _packageRoot,
- _messages);
- });
- info[inputUrl.resolvedPath] = fileInfo;
-
- if (isEntryPoint && _resetCssFile != null) {
- _processed.add(_resetCssFile);
- _tasks.add(_parseCssFile(new UrlInfo(_resetCssFile, _resetCssFile,
- null)));
- }
-
- // Load component files referenced by [file].
- for (var link in fileInfo.componentLinks) {
- _loadFile(link, _parseHtmlFile);
- }
-
- // Load stylesheet files referenced by [file].
- for (var link in fileInfo.styleSheetHrefs) {
- _loadFile(link, _parseCssFile);
- }
-
- // Process any @imports inside of a <style> tag.
- var urlInfos = findUrlsImported(fileInfo, fileInfo.inputUrl, _packageRoot,
- file.document, _messages, options);
- for (var urlInfo in urlInfos) {
- _loadFile(urlInfo, _parseCssFile);
- }
-
- // Load .dart files being referenced in components.
- for (var component in fileInfo.declaredComponents) {
- // Process any @imports inside of the <style> tag in a component.
- var urlInfos = findUrlsImported(component, fileInfo.inputUrl,
- _packageRoot, component.element, _messages, options);
- for (var urlInfo in urlInfos) {
- _loadFile(urlInfo, _parseCssFile);
- }
- }
- }
-
- /**
- * Helper function to load [urlInfo] and parse it using [loadAndParse] if it
- * hasn't been loaded before.
- */
- void _loadFile(UrlInfo urlInfo, Future loadAndParse(UrlInfo inputUrl)) {
- if (urlInfo == null) return;
- var resolvedPath = urlInfo.resolvedPath;
- if (!_processed.contains(resolvedPath)) {
- _processed.add(resolvedPath);
- _tasks.add(loadAndParse(urlInfo));
- }
- }
-
- /** Parse an HTML file. */
- Future _parseHtmlFile(UrlInfo inputUrl, [bool checkDocType = false]) {
- var filePath = inputUrl.resolvedPath;
- return fileSystem.readTextOrBytes(filePath)
- .catchError((e) => _readError(e, inputUrl))
- .then((source) {
- if (source == null) return;
- var file = new SourceFile(filePath);
- file.document = _time('Parsed', filePath,
- () => parseHtml(source, filePath, _messages, checkDocType));
- _processHtmlFile(inputUrl, file);
- });
- }
-
- /** Parse a stylesheet file. */
- Future _parseCssFile(UrlInfo inputUrl) {
- if (!options.emulateScopedCss) {
- return new Future<SourceFile>.value(null);
- }
- var filePath = inputUrl.resolvedPath;
- return fileSystem.readText(filePath)
- .catchError((e) => _readError(e, inputUrl, isWarning: true))
- .then((code) {
- if (code == null) return;
- var file = new SourceFile(filePath, type: SourceFile.STYLESHEET);
- file.code = code;
- _processCssFile(inputUrl, file);
- });
- }
-
-
- SourceFile _readError(error, UrlInfo inputUrl, {isWarning: false}) {
- var message = 'unable to open file "${inputUrl.resolvedPath}"';
- if (options.verbose) {
- message = '$message. original message:\n $error';
- }
- if (isWarning) {
- _messages.warning(message, inputUrl.sourceSpan);
- } else {
- _messages.error(message, inputUrl.sourceSpan);
- }
- return null;
- }
-
- void _processCssFile(UrlInfo inputUrl, SourceFile cssFile) {
- if (cssFile == null) return;
-
- files.add(cssFile);
-
- var fileInfo = new FileInfo(inputUrl);
- info[inputUrl.resolvedPath] = fileInfo;
-
- var styleSheet = parseCss(cssFile.code, _messages, options);
- if (inputUrl.url == _resetCssFile) {
- _cssResetStyleSheet = styleSheet;
- } else if (styleSheet != null) {
- _resolveStyleSheetImports(inputUrl, cssFile.path, styleSheet);
- fileInfo.styleSheets.add(styleSheet);
- }
- }
-
- /** Load and parse all style sheets referenced with an @imports. */
- void _resolveStyleSheetImports(UrlInfo inputUrl, String processingFile,
- StyleSheet styleSheet) {
- var urlInfos = _time('CSS imports', processingFile, () =>
- findImportsInStyleSheet(styleSheet, _packageRoot, inputUrl, _messages));
-
- for (var urlInfo in urlInfos) {
- if (urlInfo == null) break;
- // Load any @imported stylesheet files referenced in this style sheet.
- _loadFile(urlInfo, _parseCssFile);
- }
- }
-
- /** Run the analyzer on every input html file. */
- void _analyze() {
- var uniqueIds = new IntIterator();
- for (var file in files) {
- if (file.isHtml) {
- _time('Analyzed contents', file.path, () =>
- analyzeFile(file, info, uniqueIds, global, _messages,
- options.emulateScopedCss));
- }
- }
- }
-
- // TODO(jmesserly): refactor this and other CSS related transforms out of
- // Compiler.
- /**
- * Generate an CSS file for all style sheets (main and components).
- * Returns true if a file was generated, otherwise false.
- */
- bool _emitAllCss() {
- if (!options.emulateScopedCss) return false;
-
- var buff = new StringBuffer();
-
- // Emit all linked style sheet files first.
- for (var file in files) {
- var css = new StringBuffer();
- var fileInfo = info[file.path];
- if (file.isStyleSheet) {
- for (var styleSheet in fileInfo.styleSheets) {
- css.write(
- '/* Auto-generated from style sheet href = ${file.path} */\n'
- '/* DO NOT EDIT. */\n\n');
- css.write(emitStyleSheet(styleSheet, fileInfo));
- css.write('\n\n');
- }
- }
- }
-
- // Emit all CSS for each component (style scoped).
- for (var file in files) {
- if (file.isHtml) {
- var fileInfo = info[file.path];
- for (var component in fileInfo.declaredComponents) {
- for (var styleSheet in component.styleSheets) {
- // Translate any URIs in CSS.
- if (buff.isEmpty) {
- buff.write(
- '/* Auto-generated from components style tags. */\n'
- '/* DO NOT EDIT. */\n\n');
- }
- buff.write(
- '/* ==================================================== \n'
- ' Component ${component.tagName} stylesheet \n'
- ' ==================================================== */\n');
-
- var tagName = component.tagName;
- if (!component.hasAuthorStyles) {
- if (_cssResetStyleSheet != null) {
- // If component doesn't have apply-author-styles then we need to
- // reset the CSS the styles for the component (if css-reset file
- // option was passed).
- buff.write('\n/* Start CSS Reset */\n');
- var style;
- if (options.emulateScopedCss) {
- style = emitComponentStyleSheet(_cssResetStyleSheet, tagName);
- } else {
- style = emitOriginalCss(_cssResetStyleSheet);
- }
- buff.write(style);
- buff.write('/* End CSS Reset */\n\n');
- }
- }
- if (options.emulateScopedCss) {
- buff.write(emitComponentStyleSheet(styleSheet, tagName));
- } else {
- buff.write(emitOriginalCss(styleSheet));
- }
- buff.write('\n\n');
- }
- }
- }
- }
-
- if (buff.isEmpty) return false;
- return true;
- }
-
- _time(String logMessage, String filePath, callback(),
- {bool printTime: false}) {
- var message = new StringBuffer();
- message.write(logMessage);
- var filename = path.basename(filePath);
- for (int i = (60 - logMessage.length - filename.length); i > 0 ; i--) {
- message.write(' ');
- }
- message.write(filename);
- return time(message.toString(), callback,
- printTime: options.verbose || printTime);
- }
-}
diff --git a/pkg/polymer/lib/src/compiler_options.dart b/pkg/polymer/lib/src/compiler_options.dart
deleted file mode 100644
index 2d446ef..0000000
--- a/pkg/polymer/lib/src/compiler_options.dart
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) 2012, 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.
-
-library polymer.src.compiler_options;
-
-import 'package:args/args.dart';
-
-class CompilerOptions {
- /** Report warnings as errors. */
- final bool warningsAsErrors;
-
- /** True to show informational messages. The `--verbose` flag. */
- final bool verbose;
-
- /** Remove any generated files. */
- final bool clean;
-
- /** Whether to use colors to print messages on the terminal. */
- final bool useColors;
-
- /** Force mangling any generated name (even when --out is provided). */
- final bool forceMangle;
-
- /** Generate component's dart code, but not the main entry point file. */
- final bool componentsOnly;
-
- /** File to process by the compiler. */
- String inputFile;
-
- /** Directory where all sources are found. */
- final String baseDir;
-
- /** Directory where all output will be generated. */
- final String outputDir;
-
- /** Directory where to look for 'package:' imports. */
- final String packageRoot;
-
- /**
- * Adjust resource URLs in the output HTML to point back to the original
- * location in the file system. Commonly this is enabled during development,
- * but disabled for deployment.
- */
- final bool rewriteUrls;
-
- /**
- * Whether to print error messages using the json format understood by the
- * Dart editor.
- */
- final bool jsonFormat;
-
- /** Emulate scoped styles using a CSS polyfill. */
- final bool emulateScopedCss;
-
- /** Use CSS file for CSS Reset. */
- final String resetCssFile;
-
- // We could make this faster, if it ever matters.
- factory CompilerOptions() => parse(['']);
-
- CompilerOptions.fromArgs(ArgResults args)
- : warningsAsErrors = args['warnings_as_errors'],
- verbose = args['verbose'],
- clean = args['clean'],
- useColors = args['colors'],
- baseDir = args['basedir'],
- outputDir = args['out'],
- packageRoot = args['package-root'],
- rewriteUrls = args['rewrite-urls'],
- forceMangle = args['unique_output_filenames'],
- jsonFormat = args['json_format'],
- componentsOnly = args['components_only'],
- emulateScopedCss = args['scoped-css'],
- resetCssFile = args['css-reset'],
- inputFile = args.rest.length > 0 ? args.rest[0] : null;
-
- /**
- * Returns the compiler options parsed from [arguments]. Set [checkUsage] to
- * false to suppress checking of correct usage or printing help messages.
- */
- // TODO(sigmund): convert all flags to use dashes instead of underscores
- static CompilerOptions parse(List<String> arguments,
- {bool checkUsage: true}) {
- var parser = new ArgParser()
- ..addFlag('verbose', abbr: 'v')
- ..addFlag('clean', help: 'Remove all generated files',
- defaultsTo: false, negatable: false)
- ..addFlag('warnings_as_errors', abbr: 'e',
- help: 'Warnings handled as errors',
- defaultsTo: false, negatable: false)
- ..addFlag('colors', help: 'Display errors/warnings in colored text',
- defaultsTo: true)
- ..addFlag('rewrite-urls',
- help: 'Adjust every resource url to point to the original location in'
- ' the filesystem.\nThis on by default during development and can be'
- ' disabled to make the generated code easier to deploy.',
- defaultsTo: true)
- ..addFlag('unique_output_filenames', abbr: 'u',
- help: 'Use unique names for all generated files, so they will not '
- 'have the\nsame name as your input files, even if they are in a'
- ' different directory',
- defaultsTo: false, negatable: false)
- ..addFlag('json_format',
- help: 'Print error messsages in a json format easy to parse by tools,'
- ' such as the Dart editor',
- defaultsTo: false, negatable: false)
- ..addFlag('components_only',
- help: 'Generate only the code for component classes, do not generate '
- 'HTML files or the main bootstrap code.',
- defaultsTo: false, negatable: false)
- ..addFlag('scoped-css', help: 'Emulate scoped styles with CSS polyfill',
- defaultsTo: false)
- ..addOption('css-reset', abbr: 'r', help: 'CSS file used to reset CSS')
- // TODO(sigmund): remove this flag
- ..addFlag('deploy', help: '(deprecated) currently a noop',
- defaultsTo: false, negatable: false)
- ..addOption('out', abbr: 'o', help: 'Directory where to generate files'
- ' (defaults to the same directory as the source file)')
- ..addOption('basedir', help: 'Base directory where to find all source '
- 'files (defaults to the source file\'s directory)')
- ..addOption('package-root', help: 'Where to find "package:" imports'
- '(defaults to the "packages/" subdirectory next to the source file)')
- ..addFlag('help', abbr: 'h', help: 'Displays this help message',
- defaultsTo: false, negatable: false);
- try {
- var results = parser.parse(arguments);
- if (checkUsage && (results['help'] || results.rest.length == 0)) {
- showUsage(parser);
- return null;
- }
- return new CompilerOptions.fromArgs(results);
- } on FormatException catch (e) {
- print(e.message);
- showUsage(parser);
- return null;
- }
- }
-
- static showUsage(parser) {
- print('Usage: dwc [options...] input.html');
- print(parser.getUsage());
- }
-}
diff --git a/pkg/polymer/lib/src/css_analyzer.dart b/pkg/polymer/lib/src/css_analyzer.dart
deleted file mode 100644
index 0b96825..0000000
--- a/pkg/polymer/lib/src/css_analyzer.dart
+++ /dev/null
@@ -1,503 +0,0 @@
-// Copyright (c) 2013, 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.
-
-/** Portion of the analyzer dealing with CSS sources. */
-library polymer.src.css_analyzer;
-
-import 'package:csslib/parser.dart' as css;
-import 'package:csslib/visitor.dart';
-import 'package:html5lib/dom.dart';
-import 'package:html5lib/dom_parsing.dart';
-
-import 'info.dart';
-import 'files.dart' show SourceFile;
-import 'messages.dart';
-import 'compiler_options.dart';
-
-void analyzeCss(String packageRoot, List<SourceFile> files,
- Map<String, FileInfo> info, Map<String, String> pseudoElements,
- Messages messages, {warningsAsErrors: false}) {
- var analyzer = new _AnalyzerCss(packageRoot, info, pseudoElements, messages,
- warningsAsErrors);
- for (var file in files) analyzer.process(file);
- analyzer.normalize();
-}
-
-class _AnalyzerCss {
- final String packageRoot;
- final Map<String, FileInfo> info;
- final Map<String, String> _pseudoElements;
- final Messages _messages;
- final bool _warningsAsErrors;
-
- Set<StyleSheet> allStyleSheets = new Set<StyleSheet>();
-
- /**
- * [_pseudoElements] list of known pseudo attributes found in HTML, any
- * CSS pseudo-elements 'name::custom-element' is mapped to the manged name
- * associated with the pseudo-element key.
- */
- _AnalyzerCss(this.packageRoot, this.info, this._pseudoElements,
- this._messages, this._warningsAsErrors);
-
- /**
- * Run the analyzer on every file that is a style sheet or any component that
- * has a style tag.
- */
- void process(SourceFile file) {
- var fileInfo = info[file.path];
- if (file.isStyleSheet || fileInfo.styleSheets.length > 0) {
- var styleSheets = processVars(fileInfo.inputUrl, fileInfo);
-
- // Add to list of all style sheets analyzed.
- allStyleSheets.addAll(styleSheets);
- }
-
- // Process any components.
- for (var component in fileInfo.declaredComponents) {
- var all = processVars(fileInfo.inputUrl, component);
-
- // Add to list of all style sheets analyzed.
- allStyleSheets.addAll(all);
- }
-
- processCustomPseudoElements();
- }
-
- void normalize() {
- // Remove all var definitions for all style sheets analyzed.
- for (var tree in allStyleSheets) new _RemoveVarDefinitions().visitTree(tree);
- }
-
- List<StyleSheet> processVars(inputUrl, libraryInfo) {
- // Get list of all stylesheet(s) dependencies referenced from this file.
- var styleSheets = _dependencies(inputUrl, libraryInfo).toList();
-
- var errors = [];
- css.analyze(styleSheets, errors: errors, options:
- [_warningsAsErrors ? '--warnings_as_errors' : '', 'memory']);
-
- // Print errors as warnings.
- for (var e in errors) {
- _messages.warning(e.message, e.span);
- }
-
- // Build list of all var definitions.
- Map varDefs = new Map();
- for (var tree in styleSheets) {
- var allDefs = (new _VarDefinitions()..visitTree(tree)).found;
- allDefs.forEach((key, value) {
- varDefs[key] = value;
- });
- }
-
- // Resolve all definitions to a non-VarUsage (terminal expression).
- varDefs.forEach((key, value) {
- for (var expr in (value.expression as Expressions).expressions) {
- var def = _findTerminalVarDefinition(varDefs, value);
- varDefs[key] = def;
- }
- });
-
- // Resolve all var usages.
- for (var tree in styleSheets) new _ResolveVarUsages(varDefs).visitTree(tree);
-
- return styleSheets;
- }
-
- processCustomPseudoElements() {
- var polyFiller = new _PseudoElementExpander(_pseudoElements);
- for (var tree in allStyleSheets) {
- polyFiller.visitTree(tree);
- }
- }
-
- /**
- * Given a component or file check if any stylesheets referenced. If so then
- * return a list of all referenced stylesheet dependencies (@imports or <link
- * rel="stylesheet" ..>).
- */
- Set<StyleSheet> _dependencies(inputUrl, libraryInfo, {Set<StyleSheet> seen}) {
- if (seen == null) seen = new Set();
-
- for (var styleSheet in libraryInfo.styleSheets) {
- if (!seen.contains(styleSheet)) {
- // TODO(terry): VM uses expandos to implement hashes. Currently, it's a
- // linear (not constant) time cost (see dartbug.com/5746).
- // If this bug isn't fixed and performance show's this a
- // a problem we'll need to implement our own hashCode or
- // use a different key for better perf.
- // Add the stylesheet.
- seen.add(styleSheet);
-
- // Any other imports in this stylesheet?
- var urlInfos = findImportsInStyleSheet(styleSheet, packageRoot,
- inputUrl, _messages);
-
- // Process other imports in this stylesheets.
- for (var importSS in urlInfos) {
- var importInfo = info[importSS.resolvedPath];
- if (importInfo != null) {
- // Add all known stylesheets processed.
- seen.addAll(importInfo.styleSheets);
- // Find dependencies for stylesheet referenced with a
- // @import
- for (var ss in importInfo.styleSheets) {
- var urls = findImportsInStyleSheet(ss, packageRoot, inputUrl,
- _messages);
- for (var url in urls) {
- var fileInfo = info[url.resolvedPath];
- _dependencies(fileInfo.inputUrl, fileInfo, seen: seen);
- }
- }
- }
- }
- }
- }
-
- return seen;
- }
-}
-
-/**
- * Find var- definitions in a style sheet.
- * [found] list of known definitions.
- */
-class _VarDefinitions extends Visitor {
- final Map<String, VarDefinition> found = new Map();
-
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- visitVarDefinition(VarDefinition node) {
- // Replace with latest variable definition.
- found[node.definedName] = node;
- super.visitVarDefinition(node);
- }
-
- void visitVarDefinitionDirective(VarDefinitionDirective node) {
- visitVarDefinition(node.def);
- }
-}
-
-/**
- * Resolve any CSS expression which contains a var() usage to the ultimate real
- * CSS expression value e.g.,
- *
- * var-one: var(two);
- * var-two: #ff00ff;
- *
- * .test {
- * color: var(one);
- * }
- *
- * then .test's color would be #ff00ff
- */
-class _ResolveVarUsages extends Visitor {
- final Map<String, VarDefinition> varDefs;
- bool inVarDefinition = false;
- bool inUsage = false;
- Expressions currentExpressions;
-
- _ResolveVarUsages(this.varDefs);
-
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- void visitVarDefinition(VarDefinition varDef) {
- inVarDefinition = true;
- super.visitVarDefinition(varDef);
- inVarDefinition = false;
- }
-
- void visitExpressions(Expressions node) {
- currentExpressions = node;
- super.visitExpressions(node);
- currentExpressions = null;
- }
-
- void visitVarUsage(VarUsage node) {
- // Don't process other var() inside of a varUsage. That implies that the
- // default is a var() too. Also, don't process any var() inside of a
- // varDefinition (they're just place holders until we've resolved all real
- // usages.
- if (!inUsage && !inVarDefinition && currentExpressions != null) {
- var expressions = currentExpressions.expressions;
- var index = expressions.indexOf(node);
- assert(index >= 0);
- var def = varDefs[node.name];
- if (def != null) {
- // Found a VarDefinition use it.
- _resolveVarUsage(currentExpressions.expressions, index, def);
- } else if (node.defaultValues.any((e) => e is VarUsage)) {
- // Don't have a VarDefinition need to use default values resolve all
- // default values.
- var terminalDefaults = [];
- for (var defaultValue in node.defaultValues) {
- terminalDefaults.addAll(resolveUsageTerminal(defaultValue));
- }
- expressions.replaceRange(index, index + 1, terminalDefaults);
- } else {
- // No VarDefinition but default value is a terminal expression; use it.
- expressions.replaceRange(index, index + 1, node.defaultValues);
- }
- }
-
- inUsage = true;
- super.visitVarUsage(node);
- inUsage = false;
- }
-
- List<Expression> resolveUsageTerminal(VarUsage usage) {
- var result = [];
-
- var varDef = varDefs[usage.name];
- var expressions;
- if (varDef == null) {
- // VarDefinition not found try the defaultValues.
- expressions = usage.defaultValues;
- } else {
- // Use the VarDefinition found.
- expressions = (varDef.expression as Expressions).expressions;
- }
-
- for (var expr in expressions) {
- if (expr is VarUsage) {
- // Get terminal value.
- result.addAll(resolveUsageTerminal(expr));
- }
- }
-
- // We're at a terminal just return the VarDefinition expression.
- if (result.isEmpty && varDef != null) {
- result = (varDef.expression as Expressions).expressions;
- }
-
- return result;
- }
-
- _resolveVarUsage(List<Expressions> expressions, int index,
- VarDefinition def) {
- var defExpressions = (def.expression as Expressions).expressions;
- expressions.replaceRange(index, index + 1, defExpressions);
- }
-}
-
-/** Remove all var definitions. */
-class _RemoveVarDefinitions extends Visitor {
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- void visitStyleSheet(StyleSheet ss) {
- ss.topLevels.removeWhere((e) => e is VarDefinitionDirective);
- super.visitStyleSheet(ss);
- }
-
- void visitDeclarationGroup(DeclarationGroup node) {
- node.declarations.removeWhere((e) => e is VarDefinition);
- super.visitDeclarationGroup(node);
- }
-}
-
-/**
- * Process all selectors looking for a pseudo-element in a selector. If the
- * name is found in our list of known pseudo-elements. Known pseudo-elements
- * are built when parsing a component looking for an attribute named "pseudo".
- * The value of the pseudo attribute is the name of the custom pseudo-element.
- * The name is mangled so Dart/JS can't directly access the pseudo-element only
- * CSS can access a custom pseudo-element (and see issue #510, querying needs
- * access to custom pseudo-elements).
- *
- * Change the custom pseudo-element to be a child of the pseudo attribute's
- * mangled custom pseudo element name. e.g,
- *
- * .test::x-box
- *
- * would become:
- *
- * .test > *[pseudo="x-box_2"]
- */
-class _PseudoElementExpander extends Visitor {
- final Map<String, String> _pseudoElements;
-
- _PseudoElementExpander(this._pseudoElements);
-
- void visitTree(StyleSheet tree) => visitStyleSheet(tree);
-
- visitSelector(Selector node) {
- var selectors = node.simpleSelectorSequences;
- for (var index = 0; index < selectors.length; index++) {
- var selector = selectors[index].simpleSelector;
- if (selector is PseudoElementSelector) {
- if (_pseudoElements.containsKey(selector.name)) {
- // Pseudo Element is a custom element.
- var mangledName = _pseudoElements[selector.name];
-
- var span = selectors[index].span;
-
- var attrSelector = new AttributeSelector(
- new Identifier('pseudo', span), css.TokenKind.EQUALS,
- mangledName, span);
- // The wildcard * namespace selector.
- var wildCard = new ElementSelector(new Wildcard(span), span);
- selectors[index] = new SimpleSelectorSequence(wildCard, span,
- css.TokenKind.COMBINATOR_GREATER);
- selectors.insert(++index,
- new SimpleSelectorSequence(attrSelector, span));
- }
- }
- }
- }
-}
-
-List<UrlInfo> findImportsInStyleSheet(StyleSheet styleSheet,
- String packageRoot, UrlInfo inputUrl, Messages messages) {
- var visitor = new _CssImports(packageRoot, inputUrl, messages);
- visitor.visitTree(styleSheet);
- return visitor.urlInfos;
-}
-
-/**
- * Find any imports in the style sheet; normalize the style sheet href and
- * return a list of all fully qualified CSS files.
- */
-class _CssImports extends Visitor {
- final String packageRoot;
-
- /** Input url of the css file, used to normalize relative import urls. */
- final UrlInfo inputUrl;
-
- /** List of all imported style sheets. */
- final List<UrlInfo> urlInfos = [];
-
- final Messages _messages;
-
- _CssImports(this.packageRoot, this.inputUrl, this._messages);
-
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- void visitImportDirective(ImportDirective node) {
- var urlInfo = UrlInfo.resolve(node.import, inputUrl,
- node.span, packageRoot, _messages, ignoreAbsolute: true);
- if (urlInfo == null) return;
- urlInfos.add(urlInfo);
- }
-}
-
-StyleSheet parseCss(String content, Messages messages,
- CompilerOptions options) {
- if (content.trim().isEmpty) return null;
-
- var errors = [];
-
- // TODO(terry): Add --checked when fully implemented and error handling.
- var stylesheet = css.parse(content, errors: errors, options:
- [options.warningsAsErrors ? '--warnings_as_errors' : '', 'memory']);
-
- // Note: errors aren't fatal in HTML (unless strict mode is on).
- // So just print them as warnings.
- for (var e in errors) {
- messages.warning(e.message, e.span);
- }
-
- return stylesheet;
-}
-
-/** Find terminal definition (non VarUsage implies real CSS value). */
-VarDefinition _findTerminalVarDefinition(Map<String, VarDefinition> varDefs,
- VarDefinition varDef) {
- var expressions = varDef.expression as Expressions;
- for (var expr in expressions.expressions) {
- if (expr is VarUsage) {
- var usageName = (expr as VarUsage).name;
- var foundDef = varDefs[usageName];
-
- // If foundDef is unknown check if defaultValues; if it exist then resolve
- // to terminal value.
- if (foundDef == null) {
- // We're either a VarUsage or terminal definition if in varDefs;
- // either way replace VarUsage with it's default value because the
- // VarDefinition isn't found.
- var defaultValues = (expr as VarUsage).defaultValues;
- var replaceExprs = expressions.expressions;
- assert(replaceExprs.length == 1);
- replaceExprs.replaceRange(0, 1, defaultValues);
- return varDef;
- }
- if (foundDef is VarDefinition) {
- return _findTerminalVarDefinition(varDefs, foundDef);
- }
- } else {
- // Return real CSS property.
- return varDef;
- }
- }
-
- // Didn't point to a var definition that existed.
- return varDef;
-}
-
-/**
- * Find urls imported inside style tags under [info]. If [info] is a FileInfo
- * then process only style tags in the body (don't process any style tags in a
- * component). If [info] is a ComponentInfo only process style tags inside of
- * the element are processed. For an [info] of type FileInfo [node] is the
- * file's document and for an [info] of type ComponentInfo then [node] is the
- * component's element tag.
- */
-List<UrlInfo> findUrlsImported(LibraryInfo info, UrlInfo inputUrl,
- String packageRoot, Node node, Messages messages, CompilerOptions options) {
- // Process any @imports inside of the <style> tag.
- var styleProcessor =
- new _CssStyleTag(packageRoot, info, inputUrl, messages, options);
- styleProcessor.visit(node);
- return styleProcessor.imports;
-}
-
-/* Process CSS inside of a style tag. */
-class _CssStyleTag extends TreeVisitor {
- final String _packageRoot;
-
- /** Either a FileInfo or ComponentInfo. */
- final LibraryInfo _info;
- final Messages _messages;
- final CompilerOptions _options;
-
- /**
- * Path of the declaring file, for a [_info] of type FileInfo it's the file's
- * path for a type ComponentInfo it's the declaring file path.
- */
- final UrlInfo _inputUrl;
-
- /** List of @imports found. */
- List<UrlInfo> imports = [];
-
- _CssStyleTag(this._packageRoot, this._info, this._inputUrl, this._messages,
- this._options);
-
- void visitElement(Element node) {
- // Don't process any style tags inside of element if we're processing a
- // FileInfo. The style tags inside of a component defintion will be
- // processed when _info is a ComponentInfo.
- if (node.tagName == 'polymer-element' && _info is FileInfo) return;
- if (node.tagName == 'style') {
- // Parse the contents of the scoped style tag.
- var styleSheet = parseCss(node.nodes.single.value, _messages, _options);
- if (styleSheet != null) {
- _info.styleSheets.add(styleSheet);
-
- // Find all imports return list of @imports in this style tag.
- var urlInfos = findImportsInStyleSheet(styleSheet, _packageRoot,
- _inputUrl, _messages);
- imports.addAll(urlInfos);
- }
- }
- super.visitElement(node);
- }
-}
diff --git a/pkg/polymer/lib/src/css_emitters.dart b/pkg/polymer/lib/src/css_emitters.dart
deleted file mode 100644
index a4d4b7d..0000000
--- a/pkg/polymer/lib/src/css_emitters.dart
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (c) 2013, 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.
-
-library polymer.src.css_emitters;
-
-import 'package:csslib/visitor.dart' show Visitor, CssPrinter, ElementSelector,
- UriTerm, Selector, HostDirective, SimpleSelectorSequence, StyleSheet;
-
-import 'info.dart';
-
-
-/** Emit the contents of the style tag outside of a component. */
-String emitStyleSheet(StyleSheet ss, FileInfo file) =>
- (new _CssEmitter(file.components.keys.toSet())
- ..visitTree(ss, pretty: true)).toString();
-
-/** Emit a component's style tag content emulating scoped css. */
-String emitComponentStyleSheet(StyleSheet ss, String tagName) =>
- (new _ComponentCssEmitter(tagName)..visitTree(ss, pretty: true)).toString();
-
-String emitOriginalCss(StyleSheet css) =>
- (new CssPrinter()..visitTree(css)).toString();
-
-/** Only x-tag name element selectors are emitted as [is="x-"]. */
-class _CssEmitter extends CssPrinter {
- final Set _componentsTag;
- _CssEmitter(this._componentsTag);
-
- void visitElementSelector(ElementSelector node) {
- // If element selector is a component's tag name, then change selector to
- // find element who's is attribute's the component's name.
- if (_componentsTag.contains(node.name)) {
- emit('[is="${node.name}"]');
- return;
- }
- super.visitElementSelector(node);
- }
-}
-
-/**
- * Emits a css stylesheet applying rules to emulate scoped css. The rules adjust
- * element selectors to include the component's tag name.
- */
-class _ComponentCssEmitter extends CssPrinter {
- final String _componentTagName;
- bool _inHostDirective = false;
- bool _selectorStartInHostDirective = false;
-
- _ComponentCssEmitter(this._componentTagName);
-
- /** Is the element selector an x-tag name. */
- bool _isSelectorElementXTag(Selector node) {
- if (node.simpleSelectorSequences.length > 0) {
- var selector = node.simpleSelectorSequences[0].simpleSelector;
- return selector is ElementSelector && selector.name == _componentTagName;
- }
- return false;
- }
-
- void visitSelector(Selector node) {
- // If the selector starts with an x-tag name don't emit it twice.
- if (!_isSelectorElementXTag(node)) {
- if (_inHostDirective) {
- // Style the element that's hosting the component, therefore don't emit
- // the descendent combinator (first space after the [is="x-..."]).
- emit('[is="$_componentTagName"]');
- // Signal that first simpleSelector must be checked.
- _selectorStartInHostDirective = true;
- } else {
- // Emit its scoped as a descendent (space at end).
- emit('[is="$_componentTagName"] ');
- }
- }
- super.visitSelector(node);
- }
-
- /**
- * If first simple selector of a ruleset in a @host directive is a wildcard
- * then don't emit the wildcard.
- */
- void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
- if (_selectorStartInHostDirective) {
- _selectorStartInHostDirective = false;
- if (node.simpleSelector.isWildcard) {
- // Skip the wildcard if first item in the sequence.
- return;
- }
- assert(node.isCombinatorNone);
- }
-
- super.visitSimpleSelectorSequence(node);
- }
-
- void visitElementSelector(ElementSelector node) {
- // If element selector is the component's tag name, then change selector to
- // find element who's is attribute is the component's name.
- if (_componentTagName == node.name) {
- emit('[is="$_componentTagName"]');
- return;
- }
- super.visitElementSelector(node);
- }
-
- /**
- * If we're polyfilling scoped styles the @host directive is stripped. Any
- * ruleset(s) processed in an @host will fixup the first selector. See
- * visitSelector and visitSimpleSelectorSequence in this class, they adjust
- * the selectors so it styles the element hosting the compopnent.
- */
- void visitHostDirective(HostDirective node) {
- _inHostDirective = true;
- emit('/* @host */');
- for (var ruleset in node.rulesets) {
- ruleset.visit(this);
- }
- _inHostDirective = false;
- emit('/* end of @host */\n');
- }
-}
diff --git a/pkg/polymer/lib/src/custom_tag_name.dart b/pkg/polymer/lib/src/custom_tag_name.dart
deleted file mode 100644
index 637b937..0000000
--- a/pkg/polymer/lib/src/custom_tag_name.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2013, 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.
-
-library polymer.src.custom_tag_name;
-
-/**
- * Returns true if this is a valid custom element name. See:
- * <https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-custom-element-name>
- */
-bool isCustomTag(String name) {
- if (name == null || !name.contains('-')) return false;
-
- // These names have meaning in SVG or MathML, so they aren't allowed as custom
- // tags.
- var invalidNames = const {
- 'annotation-xml': '',
- 'color-profile': '',
- 'font-face': '',
- 'font-face-src': '',
- 'font-face-uri': '',
- 'font-face-format': '',
- 'font-face-name': '',
- 'missing-glyph': '',
- };
- return !invalidNames.containsKey(name);
-}
diff --git a/pkg/polymer/lib/src/file_system.dart b/pkg/polymer/lib/src/file_system.dart
deleted file mode 100644
index c6af55c..0000000
--- a/pkg/polymer/lib/src/file_system.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/** Abstraction for file systems and utility functions to manipulate paths. */
-library file_system;
-
-import 'dart:async';
-
-/**
- * Abstraction around file system access to work in a variety of different
- * environments.
- */
-abstract class FileSystem {
- /**
- * Apply all pending writes. Until this method is called, writeString is not
- * guaranteed to have any observable impact.
- */
- Future flush();
-
- /**
- * Reads bytes if possible, but falls back to text if running in a browser.
- * Return type is either [Future<List<int>>] or [Future<String>].
- */
- Future readTextOrBytes(String path);
-
- /* Like [readTextOrBytes], but decodes bytes as UTF-8. Used for Dart code. */
- Future<String> readText(String path);
-
- /**
- * Writes [text] to file at [path]. Call flush to insure that changes are
- * visible.
- */
- void writeString(String path, String text);
-}
diff --git a/pkg/polymer/lib/src/file_system/console.dart b/pkg/polymer/lib/src/file_system/console.dart
deleted file mode 100644
index 346eb4b..0000000
--- a/pkg/polymer/lib/src/file_system/console.dart
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2012, 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.
-
-library console;
-
-import 'dart:async';
-import 'dart:convert';
-import 'dart:io';
-import 'package:polymer/src/file_system.dart';
-
-/** File system implementation for console VM (i.e. no browser). */
-class ConsoleFileSystem implements FileSystem {
-
- /** Pending futures for file write requests. */
- final _pending = <String, Future>{};
-
- Future flush() => Future.wait(_pending.values.toList());
-
- void writeString(String path, String text) {
- if(!_pending.containsKey(path)) {
- _pending[path] = new File(path).open(mode: FileMode.WRITE)
- .then((file) => file.writeString(text))
- .then((file) => file.close())
- .whenComplete(() { _pending.remove(path); });
- }
- }
-
- // TODO(jmesserly): even better would be to pass the RandomAccessFile directly
- // to html5lib. This will require a further restructuring of FileSystem.
- // Probably it just needs "readHtml" and "readText" methods.
- Future<List<int>> readTextOrBytes(String path) {
- return new File(path).open().then(
- (file) => file.length().then((length) {
- // TODO(jmesserly): is this guaranteed to read all of the bytes?
- var buffer = new List<int>(length);
- return file.readInto(buffer, 0, length)
- .then((_) => file.close())
- .then((_) => buffer);
- }));
- }
-
- // TODO(jmesserly): do we support any encoding other than UTF-8 for Dart?
- Future<String> readText(String path) {
- return readTextOrBytes(path).then(UTF8.decode);
- }
-}
diff --git a/pkg/polymer/lib/src/files.dart b/pkg/polymer/lib/src/files.dart
deleted file mode 100644
index 9fea763..0000000
--- a/pkg/polymer/lib/src/files.dart
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2012, 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.
-
-library files;
-
-import 'package:html5lib/dom.dart';
-
-/** An input file to process by the template compiler. */
-class SourceFile {
- static const int HTML = 1;
- static const int DART = 2;
- static const int STYLESHEET = 3;
-
- final String path;
- final int type;
-
- Document document;
-
- /** Dart code or contents of a linked style sheet. */
- String code;
-
- SourceFile(this.path, {this.type: HTML});
-
- bool get isDart => type == DART;
- bool get isHtml => type == HTML;
- bool get isStyleSheet => type == STYLESHEET;
-
- String toString() => "#<SourceFile $path>";
-}
-
-/** An output file to generated by the template compiler. */
-class OutputFile {
- final String path;
- final String contents;
-
- /**
- * Path to the source file that was transformed into this OutputFile, `null`
- * for files that are generated and do not correspond to an input
- * [SourceFile].
- */
- final String source;
-
- OutputFile(this.path, this.contents, {this.source});
-
- String toString() => "#<OutputFile $path>";
-}
diff --git a/pkg/polymer/lib/src/info.dart b/pkg/polymer/lib/src/info.dart
deleted file mode 100644
index 5fa901f..0000000
--- a/pkg/polymer/lib/src/info.dart
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright (c) 2013, 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.
-
-/**
- * Datatypes holding information extracted by the analyzer and used by later
- * phases of the compiler.
- */
-library polymer.src.info;
-
-import 'dart:collection' show SplayTreeMap, LinkedHashMap;
-
-import 'package:csslib/visitor.dart';
-import 'package:html5lib/dom.dart';
-import 'package:source_maps/span.dart' show Span;
-
-import 'messages.dart';
-import 'utils.dart';
-
-/**
- * Information that is global. Roughly corresponds to `window` and `document`.
- */
-class GlobalInfo {
- /**
- * Pseudo-element names exposed in a component via a pseudo attribute.
- * The name is only available from CSS (not Dart code) so they're mangled.
- * The same pseudo-element in different components maps to the same
- * mangled name (as the pseudo-element is scoped inside of the component).
- */
- final Map<String, String> pseudoElements = <String, String>{};
-
- /** All components declared in the application. */
- final Map<String, ComponentInfo> components = new SplayTreeMap();
-}
-
-/**
- * Information for any library-like input. We consider each HTML file a library,
- * and each component declaration a library as well. Hence we use this as a base
- * class for both [FileInfo] and [ComponentInfo]. Both HTML files and components
- * can have .dart code provided by the user for top-level user scripts and
- * component-level behavior code. This code can either be inlined in the HTML
- * file or included in a script tag with the "src" attribute.
- */
-abstract class LibraryInfo {
- /** Parsed cssSource. */
- List<StyleSheet> styleSheets = [];
-}
-
-/** Information extracted at the file-level. */
-class FileInfo extends LibraryInfo {
- /** Relative path to this file from the compiler's base directory. */
- final UrlInfo inputUrl;
-
- /**
- * All custom element definitions in this file. This may contain duplicates.
- * Normally you should use [components] for lookup.
- */
- final List<ComponentInfo> declaredComponents = new List<ComponentInfo>();
-
- /**
- * All custom element definitions defined in this file or imported via
- *`<link rel='components'>` tag. Maps from the tag name to the component
- * information. This map is sorted by the tag name.
- */
- final Map<String, ComponentInfo> components =
- new SplayTreeMap<String, ComponentInfo>();
-
- /** Files imported with `<link rel="import">` */
- final List<UrlInfo> componentLinks = <UrlInfo>[];
-
- /** Files imported with `<link rel="stylesheet">` */
- final List<UrlInfo> styleSheetHrefs = <UrlInfo>[];
-
- FileInfo(this.inputUrl);
-}
-
-
-/** Information about a web component definition declared locally. */
-class ComponentInfo extends LibraryInfo {
-
- /** The component tag name, defined with the `name` attribute on `element`. */
- final String tagName;
-
- /**
- * The tag name that this component extends, defined with the `extends`
- * attribute on `element`.
- */
- final String extendsTag;
-
- /**
- * The component info associated with the [extendsTag] name, if any.
- * This will be `null` if the component extends a built-in HTML tag, or
- * if the analyzer has not run yet.
- */
- ComponentInfo extendsComponent;
-
- /** The declaring `<element>` tag. */
- final Node element;
-
- /**
- * True if [tagName] was defined by more than one component. If this happened
- * we will skip over the component.
- */
- bool hasConflict = false;
-
- ComponentInfo(this.element, this.tagName, this.extendsTag);
-
- /**
- * Gets the HTML tag extended by the base of the component hierarchy.
- * Equivalent to [extendsTag] if this inherits directly from an HTML element,
- * in other words, if [extendsComponent] is null.
- */
- String get baseExtendsTag =>
- extendsComponent == null ? extendsTag : extendsComponent.baseExtendsTag;
-
- Span get sourceSpan => element.sourceSpan;
-
- /** Is apply-author-styles enabled. */
- bool get hasAuthorStyles =>
- element.attributes.containsKey('apply-author-styles');
-
- String toString() => '#<ComponentInfo $tagName>';
-}
-
-
-/**
- * Information extracted about a URL that refers to another file. This is
- * mainly introduced to be able to trace back where URLs come from when
- * reporting errors.
- */
-class UrlInfo {
- /** Original url. */
- final String url;
-
- /** Path that the URL points to. */
- final String resolvedPath;
-
- /** Original source location where the URL was extracted from. */
- final Span sourceSpan;
-
- UrlInfo(this.url, this.resolvedPath, this.sourceSpan);
-
- /**
- * Resolve a path from an [url] found in a file located at [inputUrl].
- * Returns null for absolute [url]. Unless [ignoreAbsolute] is true, reports
- * an error message if the url is an absolute url.
- */
- static UrlInfo resolve(String url, UrlInfo inputUrl, Span span,
- String packageRoot, Messages messages, {bool ignoreAbsolute: false}) {
-
- var uri = Uri.parse(url);
- if (uri.host != '' || (uri.scheme != '' && uri.scheme != 'package')) {
- if (!ignoreAbsolute) {
- messages.error('absolute paths not allowed here: "$url"', span);
- }
- return null;
- }
-
- var target;
- if (url.startsWith('package:')) {
- target = path.join(packageRoot, url.substring(8));
- } else if (path.isAbsolute(url)) {
- if (!ignoreAbsolute) {
- messages.error('absolute paths not allowed here: "$url"', span);
- }
- return null;
- } else {
- target = path.join(path.dirname(inputUrl.resolvedPath), url);
- url = pathToUrl(path.normalize(path.join(
- path.dirname(inputUrl.url), url)));
- }
- target = path.normalize(target);
-
- return new UrlInfo(url, target, span);
- }
-
- bool operator ==(UrlInfo other) =>
- url == other.url && resolvedPath == other.resolvedPath;
-
- int get hashCode => resolvedPath.hashCode;
-
- String toString() => "#<UrlInfo url: $url, resolvedPath: $resolvedPath>";
-}
diff --git a/pkg/polymer/lib/src/messages.dart b/pkg/polymer/lib/src/messages.dart
deleted file mode 100644
index b0a5ebd..0000000
--- a/pkg/polymer/lib/src/messages.dart
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2012, 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.
-
-library messages;
-
-import 'dart:convert';
-
-import 'package:barback/barback.dart' show TransformLogger;
-import 'package:source_maps/span.dart' show Span;
-import 'package:logging/logging.dart' show Level;
-
-import 'compiler_options.dart';
-import 'utils.dart';
-
-/** Map between error levels and their display color. */
-final Map<Level, String> _ERROR_COLORS = (() {
- var colorsMap = new Map<Level, String>();
- colorsMap[Level.SEVERE] = RED_COLOR;
- colorsMap[Level.WARNING] = MAGENTA_COLOR;
- colorsMap[Level.INFO] = GREEN_COLOR;
- return colorsMap;
-})();
-
-/** A single message from the compiler. */
-class Message {
- final Level level;
- final String message;
- final Span span;
- final bool useColors;
-
- Message(this.level, this.message, {this.span, this.useColors: false});
-
- String get kind => level == Level.SEVERE ? 'error' :
- (level == Level.WARNING ? 'warning' : 'info');
-
- String toString() {
- var output = new StringBuffer();
- bool colors = useColors && _ERROR_COLORS.containsKey(level);
- var levelColor = _ERROR_COLORS[level];
- if (colors) output.write(levelColor);
- output..write(kind)..write(' ');
- if (colors) output.write(NO_COLOR);
-
- if (span == null) {
- output.write(message);
- } else {
- output.write(span.getLocationMessage(message, useColors: colors,
- color: levelColor));
- }
-
- return output.toString();
- }
-
- String toJson() {
- if (span == null) return toString();
- return JSON.encode([{
- 'method': kind,
- 'params': {
- 'file': span.sourceUrl,
- 'message': message,
- 'line': span.start.line + 1,
- 'charStart': span.start.offset,
- 'charEnd': span.end.offset,
- }
- }]);
- }
-}
-
-/**
- * This class tracks and prints information, warnings, and errors emitted by the
- * compiler.
- */
-class Messages implements TransformLogger {
- final CompilerOptions options;
- final bool shouldPrint;
-
- final List<Message> messages = <Message>[];
-
- Messages({CompilerOptions options, this.shouldPrint: true})
- : options = options != null ? options : new CompilerOptions();
-
- /**
- * Creates a new instance of [Messages] which doesn't write messages to
- * the console.
- */
- Messages.silent(): this(shouldPrint: false);
-
- /**
- * True if we have an error that prevents correct codegen.
- * For example, if we failed to read an input file.
- */
- bool get hasErrors => messages.any((m) => m.level == Level.SEVERE);
-
- // Convenience methods for testing
- int get length => messages.length;
-
- Message operator[](int index) => messages[index];
-
- void clear() {
- messages.clear();
- }
-
- /** [message] is considered a static compile-time error by the Dart lang. */
- void error(String message, [Span span]) {
- var msg = new Message(Level.SEVERE, message, span: span,
- useColors: options.useColors);
-
- messages.add(msg);
- printMessage(msg);
- }
-
- /** [message] is considered a type warning by the Dart lang. */
- void warning(String message, [Span span]) {
- if (options.warningsAsErrors) {
- error(message, span);
- } else {
- var msg = new Message(Level.WARNING, message,
- span: span, useColors: options.useColors);
-
- messages.add(msg);
- printMessage(msg);
- }
- }
-
- /// the list of error messages. Empty list, if there are no error messages.
- List<Message> get errors =>
- messages.where((m) => m.level == Level.SEVERE).toList();
-
- /// the list of warning messages. Empty list if there are no warning messages.
- List<Message> get warnings =>
- messages.where((m) => m.level == Level.WARNING).toList();
-
- /**
- * [message] at [span] will tell the user about what the compiler
- * is doing.
- */
- void info(String message, [Span span]) {
- var msg = new Message(Level.INFO, message, span: span,
- useColors: options.useColors);
-
- messages.add(msg);
- if (options.verbose) printMessage(msg);
- }
-
- void printMessage(msg) {
- if (shouldPrint) print(options.jsonFormat ? msg.toJson() : msg);
- }
-}
diff --git a/pkg/polymer/lib/src/transform.dart b/pkg/polymer/lib/src/transform.dart
deleted file mode 100644
index d2bf910..0000000
--- a/pkg/polymer/lib/src/transform.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2013, 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.
-
-/** Transfomers used for pub-serve and pub-deploy. */
-// TODO(sigmund): move into a plugin directory when pub supports it.
-library polymer.src.transform;
-
-import 'package:observe/transform.dart';
-import 'transform/code_extractor.dart';
-import 'transform/import_inliner.dart';
-import 'transform/script_compactor.dart';
-import 'transform/polyfill_injector.dart';
-
-export 'transform/code_extractor.dart';
-export 'transform/import_inliner.dart';
-export 'transform/script_compactor.dart';
-export 'transform/polyfill_injector.dart';
-
-/** Phases to deploy a polymer application. */
-var phases = [
- [new InlineCodeExtractor()],
- [new ObservableTransformer()],
- [new ImportedElementInliner()],
- [new ScriptCompactor()],
- [new PolyfillInjector()]
-];
diff --git a/pkg/polymer/lib/src/utils.dart b/pkg/polymer/lib/src/utils.dart
index da79dc2..59fdc73 100644
--- a/pkg/polymer/lib/src/utils.dart
+++ b/pkg/polymer/lib/src/utils.dart
@@ -1,177 +1,34 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2013, 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.
library polymer.src.utils;
-import 'dart:async';
-import 'package:path/path.dart' show Builder;
-export 'utils_observe.dart' show toCamelCase, toHyphenedName;
-
/**
- * An instance of the pathos library builder. We could just use the default
- * builder in pathos, but we add this indirection to make it possible to run
- * unittest for windows paths.
+ * Converts a string name with hyphens into an identifier, by removing hyphens
+ * and capitalizing the following letter. Optionally [startUppercase] to
+ * captialize the first letter.
*/
-Builder path = new Builder();
-
-/** Convert a OS specific path into a url. */
-String pathToUrl(String relPath) =>
- (path.separator == '/') ? relPath : path.split(relPath).join('/');
-
-/**
- * Invokes [callback], logs how long it took to execute in ms, and returns
- * whatever [callback] returns. The log message will be printed if [printTime]
- * is true.
- */
-time(String logMessage, callback(),
- {bool printTime: false, bool useColors: false}) {
- final watch = new Stopwatch();
- watch.start();
- var result = callback();
- watch.stop();
- final duration = watch.elapsedMilliseconds;
- if (printTime) {
- _printMessage(logMessage, duration, useColors);
- }
- return result;
-}
-
-/**
- * Invokes [callback], logs how long it takes from the moment [callback] is
- * executed until the future it returns is completed. Returns the future
- * returned by [callback]. The log message will be printed if [printTime]
- * is true.
- */
-Future asyncTime(String logMessage, Future callback(),
- {bool printTime: false, bool useColors: false}) {
- final watch = new Stopwatch();
- watch.start();
- return callback()..then((_) {
- watch.stop();
- final duration = watch.elapsedMilliseconds;
- if (printTime) {
- _printMessage(logMessage, duration, useColors);
+String toCamelCase(String hyphenedName, {bool startUppercase: false}) {
+ var segments = hyphenedName.split('-');
+ int start = startUppercase ? 0 : 1;
+ for (int i = start; i < segments.length; i++) {
+ var segment = segments[i];
+ if (segment.length > 0) {
+ // Character between 'a'..'z' mapped to 'A'..'Z'
+ segments[i] = '${segment[0].toUpperCase()}${segment.substring(1)}';
}
- });
+ }
+ return segments.join('');
}
-void _printMessage(String logMessage, int duration, bool useColors) {
- var buf = new StringBuffer();
- buf.write(logMessage);
- for (int i = logMessage.length; i < 60; i++) buf.write(' ');
- buf.write(' -- ');
- if (useColors) {
- buf.write(GREEN_COLOR);
+/** Reverse of [toCamelCase]. */
+String toHyphenedName(String word) {
+ var sb = new StringBuffer();
+ for (int i = 0; i < word.length; i++) {
+ var lower = word[i].toLowerCase();
+ if (word[i] != lower && i > 0) sb.write('-');
+ sb.write(lower);
}
- if (duration < 10) buf.write(' ');
- if (duration < 100) buf.write(' ');
- buf..write(duration)..write(' ms');
- if (useColors) {
- buf.write(NO_COLOR);
- }
- print(buf.toString());
-}
-
-// Color constants used for generating messages.
-final String GREEN_COLOR = '\u001b[32m';
-final String RED_COLOR = '\u001b[31m';
-final String MAGENTA_COLOR = '\u001b[35m';
-final String NO_COLOR = '\u001b[0m';
-
-/** A future that waits until all added [Future]s complete. */
-// TODO(sigmund): this should be part of the futures/core libraries.
-class FutureGroup {
- static const _FINISHED = -1;
-
- int _pending = 0;
- Future _failedTask;
- final Completer<List> _completer = new Completer<List>();
- final List results = [];
-
- /** Gets the task that failed, if any. */
- Future get failedTask => _failedTask;
-
- /**
- * Wait for [task] to complete.
- *
- * If this group has already been marked as completed, you'll get a
- * [StateError].
- *
- * If this group has a [failedTask], new tasks will be ignored, because the
- * error has already been signaled.
- */
- void add(Future task) {
- if (_failedTask != null) return;
- if (_pending == _FINISHED) throw new StateError("Future already completed");
-
- _pending++;
- var i = results.length;
- results.add(null);
- task.then((res) {
- results[i] = res;
- if (_failedTask != null) return;
- _pending--;
- if (_pending == 0) {
- _pending = _FINISHED;
- _completer.complete(results);
- }
- }, onError: (e) {
- if (_failedTask != null) return;
- _failedTask = task;
- _completer.completeError(e, getAttachedStackTrace(e));
- });
- }
-
- Future<List> get future => _completer.future;
-}
-
-
-/**
- * Escapes [text] for use in a Dart string.
- * [single] specifies single quote `'` vs double quote `"`.
- * [triple] indicates that a triple-quoted string, such as `'''` or `"""`.
- */
-String escapeDartString(String text, {bool single: true, bool triple: false}) {
- // Note: don't allocate anything until we know we need it.
- StringBuffer result = null;
-
- for (int i = 0; i < text.length; i++) {
- int code = text.codeUnitAt(i);
- var replace = null;
- switch (code) {
- case 92/*'\\'*/: replace = r'\\'; break;
- case 36/*r'$'*/: replace = r'\$'; break;
- case 34/*'"'*/: if (!single) replace = r'\"'; break;
- case 39/*"'"*/: if (single) replace = r"\'"; break;
- case 10/*'\n'*/: if (!triple) replace = r'\n'; break;
- case 13/*'\r'*/: if (!triple) replace = r'\r'; break;
-
- // Note: we don't escape unicode characters, under the assumption that
- // writing the file in UTF-8 will take care of this.
-
- // TODO(jmesserly): do we want to replace any other non-printable
- // characters (such as \f) for readability?
- }
-
- if (replace != null && result == null) {
- result = new StringBuffer(text.substring(0, i));
- }
-
- if (result != null) result.write(replace != null ? replace : text[i]);
- }
-
- return result == null ? text : result.toString();
-}
-
-/** Iterates through an infinite sequence, starting from zero. */
-class IntIterator implements Iterator<int> {
- int _next = -1;
-
- int get current => _next < 0 ? null : _next;
-
- bool moveNext() {
- _next++;
- return true;
- }
+ return sb.toString();
}
diff --git a/pkg/polymer/lib/src/utils_observe.dart b/pkg/polymer/lib/src/utils_observe.dart
deleted file mode 100644
index b568d0c..0000000
--- a/pkg/polymer/lib/src/utils_observe.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2013, 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.
-
-library polymer.src.utils_observe;
-
-/**
- * Converts a string name with hyphens into an identifier, by removing hyphens
- * and capitalizing the following letter. Optionally [startUppercase] to
- * captialize the first letter.
- */
-String toCamelCase(String hyphenedName, {bool startUppercase: false}) {
- var segments = hyphenedName.split('-');
- int start = startUppercase ? 0 : 1;
- for (int i = start; i < segments.length; i++) {
- var segment = segments[i];
- if (segment.length > 0) {
- // Character between 'a'..'z' mapped to 'A'..'Z'
- segments[i] = '${segment[0].toUpperCase()}${segment.substring(1)}';
- }
- }
- return segments.join('');
-}
-
-String toHyphenedName(String word) {
- var sb = new StringBuffer();
- for (int i = 0; i < word.length; i++) {
- var lower = word[i].toLowerCase();
- if (word[i] != lower && i > 0) sb.write('-');
- sb.write(lower);
- }
- return sb.toString();
-}
diff --git a/pkg/polymer/lib/testing/content_shell_test.dart b/pkg/polymer/lib/testing/content_shell_test.dart
deleted file mode 100644
index cc60912..0000000
--- a/pkg/polymer/lib/testing/content_shell_test.dart
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2013, 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.
-
-/**
- * Helper library to run tests in content_shell
- */
-library polymer.testing.end2end;
-
-import 'dart:io';
-import 'package:args/args.dart';
-import 'package:path/path.dart' as path;
-import 'package:unittest/unittest.dart';
-import 'package:polymer/deploy.dart' as deploy;
-
-
-/**
- * Compiles [testFile] with the web-ui compiler, and then runs the output as a
- * unit test in content_shell.
- */
-void endToEndTests(String inputDir, String outDir, {List<String> arguments}) {
- _testHelper(new _TestOptions(inputDir, inputDir, null, outDir,
- arguments: arguments));
-}
-
-/**
- * Compiles [testFile] with the web-ui compiler, and then runs the output as a
- * render test in content_shell.
- */
-void renderTests(String webDir, String inputDir, String expectedDir,
- String outDir, {List<String> arguments, String script, String pattern,
- bool deleteDir: true}) {
- _testHelper(new _TestOptions(webDir, inputDir, expectedDir, outDir,
- arguments: arguments, script: script, pattern: pattern,
- deleteDir: deleteDir));
-}
-
-void _testHelper(_TestOptions options) {
- expect(options, isNotNull);
-
- var paths = new Directory(options.inputDir).listSync()
- .where((f) => f is File).map((f) => f.path)
- .where((p) => p.endsWith('_test.html') && options.pattern.hasMatch(p));
-
- if (paths.isEmpty) return;
-
- // First clear the output folder. Otherwise we can miss bugs when we fail to
- // generate a file.
- var dir = new Directory(options.outDir);
- if (dir.existsSync() && options.deleteDir) {
- print('Cleaning old output for ${path.normalize(options.outDir)}');
- dir.deleteSync(recursive: true);
- }
- dir.createSync(recursive: true);
-
- for (var filePath in paths) {
- var filename = path.basename(filePath);
- test('compile $filename', () {
- return deploy.runForTest(options.webDir, options.outDir);
- });
- }
-
- var filenames = paths.map(path.basename).toList();
- // Sort files to match the order in which run.sh runs diff.
- filenames.sort();
-
- var finalOutDir = path.join(options.outDir, options.inputDir);
- var outBaseDir = path.join(options.outDir, options.webDir);
-
- runTests(String search) {
- var output;
-
- for (var filename in filenames) {
- test('content_shell run $filename$search', () {
- var lnArgs = ['-s', '$outBaseDir/packages', '$finalOutDir/packages'];
- return Process.run('ln', lnArgs).then((_) {
- var args = ['--dump-render-tree',
- 'file://$finalOutDir/$filename$search'];
- var env = {'DART_FLAGS': '--checked'};
- return Process.run('content_shell', args, environment: env)
- .then((res) {
- expect(res.exitCode, 0, reason: 'content_shell exit code: '
- '${res.exitCode}. Contents of stderr: \n${res.stderr}');
- var outs = res.stdout.split('#EOF\n')
- .where((s) => !s.trim().isEmpty).toList();
- expect(outs.length, 1);
- output = outs.first;
- });
- });
- });
-
- test('verify $filename $search', () {
- expect(output, isNotNull, reason:
- 'Output not available, maybe content_shell failed to run.');
- var outPath = path.join(options.outDir, '$filename.txt');
- new File(outPath).writeAsStringSync(output);
- if (options.isRenderTest) {
- var expectedPath = path.join(options.expectedDir, '$filename.txt');
- var expected = new File(expectedPath).readAsStringSync();
- expect(output, expected, reason: 'unexpected output for <$filename>');
- } else {
- bool passes = matches(
- new RegExp('All .* tests passed')).matches(output, {});
- expect(passes, true, reason: 'unit test failed:\n$output');
- }
- });
- }
- }
-
- bool compiled = false;
- ensureCompileToJs() {
- if (compiled) return;
- compiled = true;
-
- for (var filename in filenames) {
- test('dart2js $filename', () {
- // TODO(jmesserly): this depends on DWC's output scheme.
- // Alternatively we could use html5lib to find the script tag.
- var inPath = '${filename}_bootstrap.dart';
- var outPath = '${inPath}.js';
-
- inPath = path.join(finalOutDir, inPath);
- outPath = path.join(finalOutDir, outPath);
-
- expect(Process.run('dart2js', ['-o$outPath', inPath]).then((res) {
- expect(res.exitCode, 0, reason: 'dart2js exit code: '
- '${res.exitCode}. Contents of stderr: \n${res.stderr}. '
- 'Contents of stdout: \n${res.stdout}.');
- expect(new File(outPath).existsSync(), true, reason: 'input file '
- '$inPath should have been compiled to $outPath.');
- }), completes);
- });
- }
- }
-
- if (options.runAsDart) {
- runTests('');
- }
- if (options.runAsJs) {
- ensureCompileToJs();
- runTests('?js=1');
- }
- if (options.forcePolyfillShadowDom) {
- ensureCompileToJs();
- runTests('?js=1&shadowdomjs=1');
- }
-}
-
-class _TestOptions {
- final String webDir;
- final String inputDir;
-
- final String expectedDir;
- bool get isRenderTest => expectedDir != null;
-
- final String outDir;
- final bool deleteDir;
-
- final bool runAsDart;
- final bool runAsJs;
- final bool forcePolyfillShadowDom;
-
- final List<String> compilerArgs;
- final RegExp pattern;
-
- factory _TestOptions(String webDir, String inputDir, String expectedDir,
- String outDir, {List<String> arguments, String script, String pattern,
- bool deleteDir: true}) {
- if (arguments == null) arguments = new Options().arguments;
- if (script == null) script = new Options().script;
-
- var args = _parseArgs(arguments, script);
- if (args == null) return null;
- var compilerArgs = args.rest;
- var filePattern;
- if (pattern != null) {
- filePattern = new RegExp(pattern);
- } else if (compilerArgs.length > 0) {
- filePattern = new RegExp(compilerArgs[0]);
- compilerArgs = compilerArgs.sublist(1);
- } else {
- filePattern = new RegExp('.');
- }
-
- var scriptDir = path.absolute(path.dirname(script));
- webDir = path.relative(path.join(scriptDir, webDir));
- inputDir = path.relative(path.join(scriptDir, inputDir));
- outDir = path.join(scriptDir, outDir);
- if (expectedDir != null) {
- expectedDir = path.join(scriptDir, expectedDir);
- }
-
- return new _TestOptions._(webDir, inputDir, expectedDir, outDir, deleteDir,
- args['dart'] == true, args['js'] == true, args['shadowdom'] == true,
- compilerArgs, filePattern);
- }
-
- _TestOptions._(this.webDir, this.inputDir, this.expectedDir, this.outDir,
- this.deleteDir, this.runAsDart, this.runAsJs,
- this.forcePolyfillShadowDom, this.compilerArgs, this.pattern);
-}
-
-ArgResults _parseArgs(List<String> arguments, String script) {
- var parser = new ArgParser()
- ..addFlag('dart', abbr: 'd', help: 'run on Dart VM', defaultsTo: true)
- ..addFlag('js', abbr: 'j', help: 'run compiled dart2js', defaultsTo: true)
- ..addFlag('shadowdom', abbr: 's',
- help: 'run dart2js and polyfilled ShadowDOM', defaultsTo: true)
- ..addFlag('help', abbr: 'h', help: 'Displays this help message',
- defaultsTo: false, negatable: false);
-
- showUsage() {
- print('Usage: $script [options...] [test_name_regexp]');
- print(parser.getUsage());
- return null;
- }
-
- try {
- var results = parser.parse(arguments);
- if (results['help']) return showUsage();
- return results;
- } on FormatException catch (e) {
- print(e.message);
- return showUsage();
- }
-}
diff --git a/pkg/polymer/lib/testing/testing.js b/pkg/polymer/lib/testing/testing.js
deleted file mode 100644
index 3c22f05..0000000
--- a/pkg/polymer/lib/testing/testing.js
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright (c) 2012, 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.
-
-(function() {
- var undoReplaceScripts = [];
-
- var flags = {};
- // populate flags from location
- location.search.slice(1).split('&').forEach(function(o) {
- o = o.split('=');
- o[0] && (flags[o[0]] = o[1] || true);
- });
-
- // Webkit is migrating from layoutTestController to testRunner, we use
- // layoutTestController as a fallback until that settles in.
- var runner = window.testRunner || window.layoutTestController;
-
- if (runner) {
- runner.dumpAsText();
- runner.waitUntilDone();
- }
-
- function dumpDOM() {
- // Undo any scripts that were modified.
- undoReplaceScripts.forEach(function(undo) { undo(); });
-
- function expandShadowRoot(node) {
- for (var n = node.firstChild; n; n = n.nextSibling) {
- expandShadowRoot(n);
- }
- var shadow = node.shadowRoot || node.webkitShadowRoot ||
- node.olderShadowRoot;
-
- if (shadow) {
- expandShadowRoot(shadow);
-
- var name = 'shadow-root';
- if (shadow == node.olderShadowRoot) name = 'older-' + name;
-
- var fakeShadow = document.createElement(name);
- while (shadow.firstChild) fakeShadow.appendChild(shadow.firstChild);
- node.insertBefore(fakeShadow, node.firstChild);
- }
- }
-
- // TODO(jmesserly): use querySelector to workaround unwrapped "document".
- expandShadowRoot(document.querySelector('body'));
-
- // Clean up all of the junk added to the DOM by js-interop and shadow CSS
- // TODO(jmesserly): it seems like we're leaking lots of dart-port attributes
- // for the document elemenet
- function cleanTree(node) {
- for (var n = node.firstChild; n; n = n.nextSibling) {
- cleanTree(n);
- }
-
- // TODO(terry): Need to remove attributes in the dart-port: namespace
- // these are added for JS interop. See bug
- // https://code.google.com/p/dart/issues/detail?id=12645
- // The new dart:js shouldn't need these attrs for dart2js or
- // Dartium (won't need with native support) then remove the
- // below code.
- // Remove JS interop dart-port attributes,
- if (node.tagName == 'HTML' && node.attributes) {
- for (var i = node.attributes.length; i--; i >= 0) {
- var attrNode = node.attributes[i];
- if (attrNode && attrNode.name.indexOf('dart-port:') == 0) {
- node.removeAttributeNode(attrNode);
- }
- }
- }
-
- // We remove the contents of style tags so that we can compare both runs
- // with and without the runtmie css shim. We keep the STYLE right under
- // HEAD, because it is not affected by the shim.
- if (node.tagName == 'STYLE') {
- if (node.attributes['shadowcssshim'] != null) {
- node.parentNode.removeChild(node);
- } else if (node.parentNode.tagName != "HEAD") {
- node.textContent = '/* style hidden by testing.js */'
- }
- }
-
- if (node.tagName == 'SCRIPT') {
- if (node.textContent.indexOf('_DART_TEMPORARY_ATTACHED') >= 0) {
- node.parentNode.removeChild(node);
- } else {
- // Remove the JS Interop script.
- var typeAttr = node.getAttributeNode("type");
- if (typeAttr && typeAttr.value == "text/javascript") {
- if (node.textContent.indexOf(
- "(function() {\n // Proxy support for js.dart.\n\n") == 0) {
- node.parentNode.removeChild(node);
- }
- }
- }
- }
- }
-
- // TODO(jmesserly): use querySelector to workaround unwrapped "document".
- cleanTree(document.querySelector('html'));
-
- var out = document.createElement('pre');
- out.textContent = document.documentElement.outerHTML;
- document.body.innerHTML = '';
- document.body.appendChild(out);
- }
-
- function messageHandler(e) {
- if (e.data == 'done' && runner) {
- // On success, dump the DOM. Convert shadowRoot contents into
- // <shadow-root>
- dumpDOM();
- runner.notifyDone();
- }
- }
-
- window.addEventListener('message', messageHandler, false);
-
- function errorHandler(e) {
- if (runner) {
- window.setTimeout(function() { runner.notifyDone(); }, 0);
- }
- window.console.log('FAIL');
- }
-
- window.addEventListener('error', errorHandler, false);
-
- if (navigator.webkitStartDart && !flags.js) {
- // TODO(jmesserly): fix this so we don't need to copy from browser/dart.js
- if (!navigator.webkitStartDart()) {
- document.body.innerHTML = 'This build has expired. Please download a new Dartium at http://www.dartlang.org/dartium/index.html';
- }
- } else {
- if (flags.shadowdomjs) {
- // Allow flags to force polyfill of ShadowDOM so we can test it.
- window.__forceShadowDomPolyfill = true;
- }
-
- // TODO:
- // - Support in-browser compilation.
- // - Handle inline Dart scripts.
- window.addEventListener("DOMContentLoaded", function (e) {
- // Fall back to compiled JS. Run through all the scripts and
- // replace them if they have a type that indicate that they source
- // in Dart code.
- //
- // <script type="application/dart" src="..."></script>
- //
- var scripts = document.getElementsByTagName("script");
- var length = scripts.length;
- for (var i = 0; i < length; ++i) {
- var script = scripts[i];
- if (script.type == "application/dart") {
- // Remap foo.dart to foo.dart.js.
- if (script.src && script.src != '') {
- var jsScript = document.createElement('script');
- jsScript.src = script.src.replace(/\.dart(?=\?|$)/, '.dart.js');
- var parent = script.parentNode;
- // TODO(vsm): Find a solution for issue 8455 that works with more
- // than one script.
- document.currentScript = jsScript;
-
- undoReplaceScripts.push(function() {
- parent.replaceChild(script, jsScript);
- });
- parent.replaceChild(jsScript, script);
- }
- }
- }
- }, false);
- }
-
-})();
diff --git a/pkg/polymer/lib/transformer.dart b/pkg/polymer/lib/transformer.dart
new file mode 100644
index 0000000..5f4dc02
--- /dev/null
+++ b/pkg/polymer/lib/transformer.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, 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.
+
+/** Transfomers used for pub-serve and pub-deploy. */
+// TODO(sigmund): move into a plugin directory when pub supports it.
+library polymer.src.transform;
+
+import 'package:observe/transform.dart';
+import 'src/build/code_extractor.dart';
+import 'src/build/import_inliner.dart';
+import 'src/build/script_compactor.dart';
+import 'src/build/polyfill_injector.dart';
+import 'src/build/common.dart';
+
+export 'src/build/code_extractor.dart';
+export 'src/build/import_inliner.dart';
+export 'src/build/script_compactor.dart';
+export 'src/build/polyfill_injector.dart';
+export 'src/build/common.dart' show TransformOptions;
+
+/** Creates phases to deploy a polymer application. */
+List<List<Transformer>> createDeployPhases(TransformOptions options) {
+ return [
+ [new InlineCodeExtractor(options)],
+ [new ObservableTransformer()],
+ [new ImportedElementInliner(options)],
+ [new ScriptCompactor(options)],
+ [new PolyfillInjector(options)]
+ ];
+}
diff --git a/pkg/polymer/pubspec.yaml b/pkg/polymer/pubspec.yaml
index eb03098..fd765fd 100644
--- a/pkg/polymer/pubspec.yaml
+++ b/pkg/polymer/pubspec.yaml
@@ -4,7 +4,7 @@
Polymer.dart is a new type of library for the web, built on top of Web
Components, and designed to leverage the evolving web platform on modern
browsers.
-homepage: https://www.dartlang.org
+homepage: https://www.dartlang.org/polymer-dart/
dependencies:
analyzer_experimental: any
args: any
diff --git a/pkg/polymer/test/transform/all_phases_test.dart b/pkg/polymer/test/build/all_phases_test.dart
similarity index 97%
rename from pkg/polymer/test/transform/all_phases_test.dart
rename to pkg/polymer/test/build/all_phases_test.dart
index 81de2f9..e35347a 100644
--- a/pkg/polymer/test/transform/all_phases_test.dart
+++ b/pkg/polymer/test/build/all_phases_test.dart
@@ -2,15 +2,16 @@
// 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.
-library polymer.test.transform.all_phases_test;
+library polymer.test.build.all_phases_test;
-import 'package:polymer/src/transform.dart';
+import 'package:polymer/transformer.dart';
import 'package:unittest/compact_vm_config.dart';
import 'common.dart';
void main() {
useCompactVMConfiguration();
+ var phases = createDeployPhases(new TransformOptions());
testPhases('no changes', phases, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
@@ -211,7 +212,7 @@
set $fieldName(int value) {
__\$$fieldName = notifyPropertyChange(const Symbol('$fieldName'), __\$$fieldName, value);
}
-
+
$className($fieldName) : __\$$fieldName = $fieldName;
}
''';
diff --git a/pkg/polymer/test/transform/code_extractor_test.dart b/pkg/polymer/test/build/code_extractor_test.dart
similarity index 85%
rename from pkg/polymer/test/transform/code_extractor_test.dart
rename to pkg/polymer/test/build/code_extractor_test.dart
index 72aefb9..3130faf 100644
--- a/pkg/polymer/test/transform/code_extractor_test.dart
+++ b/pkg/polymer/test/build/code_extractor_test.dart
@@ -2,24 +2,25 @@
// 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.
-library polymer.test.transform.code_extractor_test;
+library polymer.test.build.code_extractor_test;
-import 'package:polymer/src/transform/code_extractor.dart';
+import 'package:polymer/src/build/code_extractor.dart';
+import 'package:polymer/src/build/common.dart';
import 'package:unittest/compact_vm_config.dart';
import 'common.dart';
void main() {
useCompactVMConfiguration();
+ var phases = [[new InlineCodeExtractor(new TransformOptions())]];
- testPhases('no changes', [[new InlineCodeExtractor()]], {
+ testPhases('no changes', phases, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
}, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
});
- testPhases('single script, no library in script',
- [[new InlineCodeExtractor()]], {
+ testPhases('single script, no library in script', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart">main() { }</script>',
@@ -33,7 +34,7 @@
'library web_test_html_0;\nmain() { }',
});
- testPhases('single script, with library', [[new InlineCodeExtractor()]], {
+ testPhases('single script, with library', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart">library f;\nmain() { }</script>',
@@ -47,8 +48,7 @@
'library f;\nmain() { }',
});
- testPhases('under lib/ directory also transformed',
- [[new InlineCodeExtractor()]], {
+ testPhases('under lib/ directory also transformed', phases, {
'a|lib/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart">library f;\nmain() { }</script>',
@@ -62,7 +62,7 @@
'library f;\nmain() { }',
});
- testPhases('multiple scripts', [[new InlineCodeExtractor()]], {
+ testPhases('multiple scripts', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart">library a1;\nmain1() { }</script>'
@@ -81,7 +81,7 @@
'library a2;\nmain2() { }',
});
- testPhases('multiple deeper scripts', [[new InlineCodeExtractor()]], {
+ testPhases('multiple deeper scripts', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart">main1() { }</script>'
diff --git a/pkg/polymer/test/transform/common.dart b/pkg/polymer/test/build/common.dart
similarity index 91%
rename from pkg/polymer/test/transform/common.dart
rename to pkg/polymer/test/build/common.dart
index f1d8500..b855938 100644
--- a/pkg/polymer/test/transform/common.dart
+++ b/pkg/polymer/test/build/common.dart
@@ -2,7 +2,7 @@
// 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.
-library polymer.test.transfom.common;
+library polymer.test.build.common;
import 'dart:async';
@@ -16,6 +16,10 @@
return new AssetId(s.substring(0, index), s.substring(index + 1));
}
+String _removeTrailingWhitespace(String str) =>
+ str.splitMapJoin('\n',
+ onNonMatch: (s) => s.replaceAll(new RegExp(r'\s+$'), ''));
+
/**
* A helper package provider that has files stored in memory, also wraps
* [Barback] to simply our tests.
@@ -74,6 +78,8 @@
Future check(String assetIdString, String content) {
return this[assetIdString].then((value) {
+ value = _removeTrailingWhitespace(value);
+ content = _removeTrailingWhitespace(content);
expect(value, content, reason: 'Final output of $assetIdString differs.');
});
}
diff --git a/pkg/polymer/test/transform/import_inliner_test.dart b/pkg/polymer/test/build/import_inliner_test.dart
similarity index 92%
rename from pkg/polymer/test/transform/import_inliner_test.dart
rename to pkg/polymer/test/build/import_inliner_test.dart
index fbc405f..8654107 100644
--- a/pkg/polymer/test/transform/import_inliner_test.dart
+++ b/pkg/polymer/test/build/import_inliner_test.dart
@@ -2,9 +2,10 @@
// 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.
-library polymer.test.transform.import_inliner_test;
+library polymer.test.build.import_inliner_test;
-import 'package:polymer/src/transform/import_inliner.dart';
+import 'package:polymer/src/build/common.dart';
+import 'package:polymer/src/build/import_inliner.dart';
import 'package:unittest/compact_vm_config.dart';
import 'package:unittest/unittest.dart';
@@ -12,13 +13,14 @@
void main() {
useCompactVMConfiguration();
- testPhases('no changes', [[new ImportedElementInliner()]], {
+ var phases = [[new ImportedElementInliner(new TransformOptions())]];
+ testPhases('no changes', phases, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
}, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
});
- testPhases('empty import', [[new ImportedElementInliner()]], {
+ testPhases('empty import', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="">' // empty href
@@ -36,7 +38,7 @@
'</head><body></body></html>',
});
- testPhases('shallow, no elements', [[new ImportedElementInliner()]], {
+ testPhases('shallow, no elements', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="test2.html">'
@@ -53,7 +55,7 @@
'</head></html>',
});
- testPhases('shallow, elements, one import', [[new ImportedElementInliner()]],
+ testPhases('shallow, elements, one import', phases,
{
'a|web/test.html':
'<!DOCTYPE html><html><head>'
@@ -73,7 +75,7 @@
'</head><body><polymer-element>2</polymer-element></html>',
});
- testPhases('no transformation outside web/', [[new ImportedElementInliner()]],
+ testPhases('no transformation outside web/', phases,
{
'a|lib/test.html':
'<!DOCTYPE html><html><head>'
@@ -92,7 +94,7 @@
'</head><body><polymer-element>2</polymer-element></html>',
});
- testPhases('shallow, elements, many', [[new ImportedElementInliner()]],
+ testPhases('shallow, elements, many', phases,
{
'a|web/test.html':
'<!DOCTYPE html><html><head>'
@@ -120,7 +122,7 @@
'</head><body><polymer-element>3</polymer-element></html>',
});
- testPhases('deep, elements, one per file', [[new ImportedElementInliner()]], {
+ testPhases('deep, elements, one per file', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="test2.html">'
@@ -158,7 +160,7 @@
'</head><body><polymer-element>4</polymer-element></html>',
});
- testPhases('deep, elements, many imports', [[new ImportedElementInliner()]], {
+ testPhases('deep, elements, many imports', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="test2a.html">'
@@ -233,7 +235,7 @@
'</body></html>',
});
- testPhases('imports cycle, 1-step lasso', [[new ImportedElementInliner()]], {
+ testPhases('imports cycle, 1-step lasso', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="test_1.html">'
@@ -264,7 +266,7 @@
'<polymer-element>2</polymer-element></body></html>',
});
- testPhases('imports cycle, 2-step lasso', [[new ImportedElementInliner()]], {
+ testPhases('imports cycle, 2-step lasso', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="test_1.html">'
@@ -308,7 +310,7 @@
'<polymer-element>3</polymer-element></body></html>',
});
- testPhases('imports cycle, self cycle', [[new ImportedElementInliner()]], {
+ testPhases('imports cycle, self cycle', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="test_1.html">'
@@ -328,7 +330,7 @@
'<polymer-element>1</polymer-element></body></html>',
});
- testPhases('imports DAG', [[new ImportedElementInliner()]], {
+ testPhases('imports DAG', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<link rel="import" href="test_1.html">'
diff --git a/pkg/polymer/test/linter_test.dart b/pkg/polymer/test/build/linter_test.dart
similarity index 98%
rename from pkg/polymer/test/linter_test.dart
rename to pkg/polymer/test/build/linter_test.dart
index 1b4c348..c2eb75e 100644
--- a/pkg/polymer/test/linter_test.dart
+++ b/pkg/polymer/test/build/linter_test.dart
@@ -4,12 +4,13 @@
library polymer.test.linter_test;
+import 'package:polymer/src/build/common.dart';
+import 'package:polymer/src/build/linter.dart';
import 'package:source_maps/span.dart';
-import 'package:polymer/src/linter.dart';
import 'package:unittest/compact_vm_config.dart';
import 'package:unittest/unittest.dart';
-import 'transform/common.dart';
+import 'common.dart';
void main() {
useCompactVMConfiguration();
@@ -23,7 +24,7 @@
_testLinter('in web', {
'a|web/test.html': '<html></html>',
}, {
- 'a|web/test.html.messages':
+ 'a|web/test.html.messages':
'warning: Unexpected start tag (html). Expected DOCTYPE. '
'(web/test.html 0 0)',
});
@@ -400,7 +401,7 @@
}
_testLinter(String name, Map inputFiles, Map outputMessages) {
- var linter = new Linter(_testFormatter);
+ var linter = new Linter(new TransformOptions(), _testFormatter);
var outputFiles = {};
inputFiles.forEach((k, v) => outputFiles[k] = v);
outputMessages.forEach((k, v) => outputFiles[k] = v);
diff --git a/pkg/polymer/test/transform/polyfill_injector_test.dart b/pkg/polymer/test/build/polyfill_injector_test.dart
similarity index 80%
rename from pkg/polymer/test/transform/polyfill_injector_test.dart
rename to pkg/polymer/test/build/polyfill_injector_test.dart
index e8bbb89..124b21b 100644
--- a/pkg/polymer/test/transform/polyfill_injector_test.dart
+++ b/pkg/polymer/test/build/polyfill_injector_test.dart
@@ -2,23 +2,25 @@
// 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.
-library polymer.test.transform.polyfill_injector_test;
+library polymer.test.build.polyfill_injector_test;
-import 'package:polymer/src/transform.dart';
+import 'package:polymer/src/build/common.dart';
+import 'package:polymer/src/build/polyfill_injector.dart';
import 'package:unittest/compact_vm_config.dart';
import 'common.dart';
void main() {
useCompactVMConfiguration();
+ var phases = [[new PolyfillInjector(new TransformOptions())]];
- testPhases('no changes', [[new PolyfillInjector()]], {
+ testPhases('no changes', phases, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
}, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
});
- testPhases('no changes under lib ', [[new PolyfillInjector()]], {
+ testPhases('no changes under lib ', phases, {
'a|lib/test.html':
'<!DOCTYPE html><html><head></head><body>'
'<script type="application/dart" src="a.dart"></script>',
@@ -28,7 +30,7 @@
'<script type="application/dart" src="a.dart"></script>',
});
- testPhases('with some script', [[new PolyfillInjector()]], {
+ testPhases('with some script', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head></head><body>'
'<script type="application/dart" src="a.dart"></script>',
@@ -40,7 +42,7 @@
'</body></html>',
});
- testPhases('interop/shadow dom already present', [[new PolyfillInjector()]], {
+ testPhases('interop/shadow dom already present', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head></head><body>'
'<script type="application/dart" src="a.dart"></script>'
diff --git a/pkg/polymer/test/transform/script_compactor_test.dart b/pkg/polymer/test/build/script_compactor_test.dart
similarity index 87%
rename from pkg/polymer/test/transform/script_compactor_test.dart
rename to pkg/polymer/test/build/script_compactor_test.dart
index 1588bd1..7f0c390 100644
--- a/pkg/polymer/test/transform/script_compactor_test.dart
+++ b/pkg/polymer/test/build/script_compactor_test.dart
@@ -2,23 +2,25 @@
// 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.
-library polymer.test.transform.script_compactor_test;
+library polymer.test.build.script_compactor_test;
-import 'package:polymer/src/transform/script_compactor.dart';
+import 'package:polymer/src/build/common.dart';
+import 'package:polymer/src/build/script_compactor.dart';
import 'package:unittest/compact_vm_config.dart';
import 'common.dart';
void main() {
useCompactVMConfiguration();
+ var phases = [[new ScriptCompactor(new TransformOptions())]];
- testPhases('no changes', [[new ScriptCompactor()]], {
+ testPhases('no changes', phases, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
}, {
'a|web/test.html': '<!DOCTYPE html><html></html>',
});
- testPhases('no changes outside web/', [[new ScriptCompactor()]], {
+ testPhases('no changes outside web/', phases, {
'a|lib/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart" src="a.dart"></script>',
@@ -28,7 +30,7 @@
'<script type="application/dart" src="a.dart"></script>',
});
- testPhases('single script', [[new ScriptCompactor()]], {
+ testPhases('single script', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart" src="a.dart"></script>',
@@ -56,7 +58,7 @@
'''.replaceAll('\n ', '\n'),
});
- testPhases('several scripts', [[new ScriptCompactor()]], {
+ testPhases('several scripts', phases, {
'a|web/test.html':
'<!DOCTYPE html><html><head>'
'<script type="application/dart" src="a.dart"></script>'
diff --git a/pkg/polymer/test/compiler_test.dart b/pkg/polymer/test/compiler_test.dart
deleted file mode 100644
index abe5e32..0000000
--- a/pkg/polymer/test/compiler_test.dart
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/** End-to-end tests for the [Compiler] API. */
-library compiler_test;
-
-import 'package:logging/logging.dart' show Level;
-import 'package:path/path.dart' as path;
-import 'package:polymer/src/messages.dart';
-import 'package:unittest/compact_vm_config.dart';
-import 'package:unittest/unittest.dart';
-
-import 'testing.dart';
-
-main() {
- useCompactVMConfiguration();
-
- test('recursive dependencies', () {
- var messages = new Messages.silent();
- var compiler = createCompiler({
- 'index.html': '<head>'
- '<link rel="import" href="foo.html">'
- '<link rel="import" href="bar.html">'
- '<body><x-foo></x-foo><x-bar></x-bar>'
- '<script type="application/dart">main() {}</script>',
- 'foo.html': '<head><link rel="import" href="bar.html">'
- '<body><polymer-element name="x-foo" constructor="Foo">'
- '<template><x-bar>',
- 'bar.html': '<head><link rel="import" href="foo.html">'
- '<body><polymer-element name="x-bar" constructor="Boo">'
- '<template><x-foo>',
- }, messages);
-
- compiler.run().then(expectAsync1((e) {
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, equals({
- 'index.html': 1,
- 'foo.html': 1,
- 'bar.html': 1
- }), reason: 'Actual:\n ${fs.readCount}');
- }));
- });
-
- group('missing files', () {
- test('main script', () {
- var messages = new Messages.silent();
- var compiler = createCompiler({
- 'index.html': '<head></head><body>'
- '<script type="application/dart" src="notfound.dart"></script>'
- '</body>',
- }, messages);
-
- compiler.run().then(expectAsync1((e) {
- var msgs = messages.messages.where((m) =>
- m.message.contains('unable')).toList();
- expect(msgs.length, 0);
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, { 'index.html': 1 });
- }));
- });
-
- test('component html', () {
- var messages = new Messages.silent();
- var compiler = createCompiler({
- 'index.html': '<head>'
- '<link rel="import" href="notfound.html">'
- '<body><x-foo>'
- '<script type="application/dart">main() {}</script>',
- }, messages);
-
- compiler.run().then(expectAsync1((e) {
- var msgs = messages.messages.where((m) =>
- m.message.contains('unable')).toList();
-
- expect(msgs.length, 1);
- expect(msgs[0].level, Level.SEVERE);
- expect(msgs[0].message, contains('unable to open file'));
- expect(msgs[0].span, isNotNull);
- expect(msgs[0].span.sourceUrl, 'index.html');
-
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, { 'index.html': 1, 'notfound.html': 1 });
- }));
- });
-
- test('component script', () {
- var messages = new Messages.silent();
- var compiler = createCompiler({
- 'index.html': '<head>'
- '<link rel="import" href="foo.html">'
- '<body><x-foo></x-foo>'
- '<script type="application/dart">main() {}</script>'
- '</body>',
- 'foo.html': '<body><polymer-element name="x-foo" constructor="Foo">'
- '<template></template>'
- '<script type="application/dart" src="notfound.dart"></script>',
- }, messages);
-
- compiler.run().then(expectAsync1((e) {
- var msgs = messages.messages.where((m) =>
- m.message.contains('unable')).toList();
- expect(msgs.length, 0);
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount,
- { 'index.html': 1, 'foo.html': 1 });
- }));
- });
- });
-}
diff --git a/pkg/polymer/test/css_test.dart b/pkg/polymer/test/css_test.dart
deleted file mode 100644
index 9f4c734..0000000
--- a/pkg/polymer/test/css_test.dart
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright (c) 2013, 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.
-
-library css_test;
-
-import 'package:path/path.dart' as path;
-import 'package:polymer/src/messages.dart';
-import 'package:polymer/src/utils.dart' as utils;
-import 'package:unittest/compact_vm_config.dart';
-import 'package:unittest/unittest.dart';
-
-import 'testing.dart';
-
-test_simple_var() {
- Map createFiles() {
- return {
- 'index.html':
- '<!DOCTYPE html>'
- '<html lang="en">'
- '<head>'
- '<meta charset="utf-8">'
- '</head>'
- '<body>'
- '<style>'
- '@main_color: var(b);'
- '@b: var(c);'
- '@c: red;'
- '</style>'
- '<style>'
- '.test { color: var(main_color); }'
- '</style>'
- '<script type="application/dart">main() {}</script>'
- '</body>'
- '</html>',
- };
- }
-
- var messages = new Messages.silent();
- var compiler = createCompiler(createFiles(), messages, errors: true,
- scopedCss: true);
-
- compiler.run().then(expectAsync1((e) {
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, equals({
- 'index.html': 1,
- }), reason: 'Actual:\n ${fs.readCount}');
-
- var htmlInfo = compiler.info['index.html'];
- expect(htmlInfo.styleSheets.length, 2);
- expect(prettyPrintCss(htmlInfo.styleSheets[0]), '');
- expect(prettyPrintCss(htmlInfo.styleSheets[1]), '.test { color: red; }');
- }));
-}
-
-test_var() {
- Map createFiles() {
- return {
- 'index.html':
- '<!DOCTYPE html>'
- '<html lang="en">'
- '<head>'
- '<meta charset="utf-8">'
- '</head>'
- '<body>'
- '<style>'
- '@main-color: var(b);'
- '@b: var(c);'
- '@c: red;'
- '@d: var(main-color-1, green);'
- '@border-pen: solid;'
- '@inset: 5px;'
- '@frame-color: solid orange;'
- '@big-border: 2px 2px 2px;'
- '@border-stuff: 3px dashed var(main-color);'
- '@border-2: 3px var(big-border) dashed var(main-color-1, green);'
- '@blue-border: bold var(not-found, 1px 10px blue)'
- '</style>'
- '<style>'
- '.test-1 { color: var(main-color-1, blue); }'
- '.test-2 { color: var(main-color-1, var(main-color)); }'
- '.test-3 { color: var(d, yellow); }'
- '.test-4 { color: var(d-1, yellow); }'
- '.test-5 { color: var(d-1, var(d)); }'
- '.test-6 { border: var(inset) var(border-pen) var(d); }'
- '.test-7 { border: 10px var(border-pen) var(d); }'
- '.test-8 { border: 20px var(border-pen) yellow; }'
- '.test-9 { border: 30px dashed var(d); }'
- '.test-10 { border: 40px var(frame-color);}'
- '.test-11 { border: 40px var(frame-color-1, blue);}'
- '.test-12 { border: 40px var(frame-color-1, solid blue);}'
- '.test-13 {'
- 'border: 40px var(x1, var(x2, var(x3, var(frame-color)));'
- '}'
- '.test-14 { border: 40px var(x1, var(frame-color); }'
- '.test-15 { border: 40px var(x1, solid blue);}'
- '.test-16 { border: 1px 1px 2px 3px var(frame-color);}'
- '.test-17 { border: 1px 1px 2px 3px var(x1, solid blue);}'
- '.test-18 { border: 1px 1px 2px var(border-stuff);}'
- '.test-19 { border: var(big-border) var(border-stuff);}'
- '.test-20 { border: var(border-2);}'
- '.test-21 { border: var(blue-border);}'
- '</style>'
- '<script type="application/dart">main() {}</script>'
- '</body>'
- '</html>',
- };
- }
-
- var messages = new Messages.silent();
- var compiler = createCompiler(createFiles(), messages, errors: true,
- scopedCss: true);
-
- compiler.run().then(expectAsync1((e) {
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, equals({
- 'index.html': 1,
- }), reason: 'Actual:\n ${fs.readCount}');
-
- var htmlInfo = compiler.info['index.html'];
- expect(htmlInfo.styleSheets.length, 2);
- expect(prettyPrintCss(htmlInfo.styleSheets[0]), '');
- expect(prettyPrintCss(htmlInfo.styleSheets[1]),
- '.test-1 { color: blue; } '
- '.test-2 { color: red; } '
- '.test-3 { color: green; } '
- '.test-4 { color: yellow; } '
- '.test-5 { color: green; } '
- '.test-6 { border: 5px solid green; } '
- '.test-7 { border: 10px solid green; } '
- '.test-8 { border: 20px solid yellow; } '
- '.test-9 { border: 30px dashed green; } '
- '.test-10 { border: 40px solid orange; } '
- '.test-11 { border: 40px blue; } '
- '.test-12 { border: 40px solid blue; } '
- '.test-13 { border: 40px solid orange; } '
- '.test-14 { border: 40px solid orange; } '
- '.test-15 { border: 40px solid blue; } '
- '.test-16 { border: 1px 1px 2px 3px solid orange; } '
- '.test-17 { border: 1px 1px 2px 3px solid blue; } '
- '.test-18 { border: 1px 1px 2px 3px dashed red; } '
- '.test-19 { border: 2px 2px 2px 3px dashed red; } '
- '.test-20 { border: 3px 2px 2px 2px dashed green; } '
- '.test-21 { border: bold 1px 10px blue; }');
- }));
-}
-
-test_simple_import() {
- Map createFiles() {
- return {
- 'foo.css': r'''@main_color: var(b);
- @b: var(c);
- @c: red;''',
- 'index.html':
- '<!DOCTYPE html>'
- '<html lang="en">'
- '<head>'
- '<meta charset="utf-8">'
- '</head>'
- '<body>'
- '<style>'
- '@import "foo.css";'
- '.test { color: var(main_color); }'
- '</style>'
- '<script type="application/dart">main() {}</script>'
- '</body>'
- '</html>',
- };
- }
-
- var messages = new Messages.silent();
- var compiler = createCompiler(createFiles(), messages, errors: true,
- scopedCss: true);
-
- compiler.run().then(expectAsync1((e) {
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, equals({
- 'foo.css': 1,
- 'index.html': 1,
- }), reason: 'Actual:\n ${fs.readCount}');
-
- var cssInfo = compiler.info['foo.css'];
- expect(cssInfo.styleSheets.length, 1);
- expect(prettyPrintCss(cssInfo.styleSheets[0]), '');
-
- var htmlInfo = compiler.info['index.html'];
- expect(htmlInfo.styleSheets.length, 1);
- expect(prettyPrintCss(htmlInfo.styleSheets[0]),
- '@import url(foo.css); .test { color: red; }');
- }));
-}
-
-test_imports() {
- Map createFiles() {
- return {
- 'first.css':
- '@import "third.css";'
- '@main-width: var(main-width-b);'
- '@main-width-b: var(main-width-c);'
- '@main-width-c: var(wide-width);',
- 'second.css':
- '@import "fourth.css";'
- '@main-color: var(main-color-b);'
- '@main-color-b: var(main-color-c);'
- '@main-color-c: var(color-value);',
- 'third.css':
- '@wide-width: var(wide-width-b);'
- '@wide-width-b: var(wide-width-c);'
- '@wide-width-c: 100px;',
- 'fourth.css':
- '@color-value: var(color-value-b);'
- '@color-value-b: var(color-value-c);'
- '@color-value-c: red;',
- 'index.html':
- '<!DOCTYPE html>'
- '<html lang="en">'
- '<head>'
- '<meta charset="utf-8">'
- '<link rel="stylesheet" href="first.css">'
- '</head>'
- '<body>'
- '<style>'
- '@import "first.css";'
- '@import "second.css";'
- '.test-1 { color: var(main-color); }'
- '.test-2 { width: var(main-width); }'
- '</style>'
- '<script type="application/dart">main() {}</script>'
- '</body>'
- '</html>',
- };
- }
-
- var messages = new Messages.silent();
- var compiler = createCompiler(createFiles(), messages, errors: true,
- scopedCss: true);
-
- compiler.run().then(expectAsync1((e) {
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, equals({
- 'first.css': 1,
- 'second.css': 1,
- 'third.css': 1,
- 'fourth.css': 1,
- 'index.html': 1,
- }), reason: 'Actual:\n ${fs.readCount}');
-
- var firstInfo = compiler.info['first.css'];
- expect(firstInfo.styleSheets.length, 1);
- expect(prettyPrintCss(firstInfo.styleSheets[0]), '@import url(third.css);');
-
- var secondInfo = compiler.info['second.css'];
- expect(secondInfo.styleSheets.length, 1);
- expect(prettyPrintCss(secondInfo.styleSheets[0]),
- '@import url(fourth.css);');
-
- var thirdInfo = compiler.info['third.css'];
- expect(thirdInfo.styleSheets.length, 1);
- expect(prettyPrintCss(thirdInfo.styleSheets[0]), '');
-
- var fourthInfo = compiler.info['fourth.css'];
- expect(fourthInfo.styleSheets.length, 1);
- expect(prettyPrintCss(fourthInfo.styleSheets[0]), '');
-
- var htmlInfo = compiler.info['index.html'];
- expect(htmlInfo.styleSheets.length, 1);
- expect(prettyPrintCss(htmlInfo.styleSheets[0]),
- '@import url(first.css); '
- '@import url(second.css); '
- '.test-1 { color: red; } '
- '.test-2 { width: 100px; }');
- }));
-}
-
-test_component_var() {
- Map createFiles() {
- return {
- 'index.html': '<!DOCTYPE html>'
- '<html lang="en">'
- '<head>'
- '<meta charset="utf-8">'
- '<link rel="import" href="foo.html">'
- '</head>'
- '<body>'
- '<x-foo></x-foo>'
- '<script type="application/dart">main() {}</script>'
- '</body>'
- '</html>',
- 'foo.html': '<!DOCTYPE html>'
- '<html lang="en">'
- '<head>'
- '<meta charset="utf-8">'
- '</head>'
- '<body>'
- '<polymer-element name="x-foo" constructor="Foo">'
- '<template>'
- '<style scoped>'
- '@import "foo.css";'
- '.main { color: var(main_color); }'
- '.test-background { '
- 'background: url(http://www.foo.com/bar.png);'
- '}'
- '</style>'
- '</template>'
- '</polymer-element>'
- '</body>'
- '</html>',
- 'foo.css': r'''@main_color: var(b);
- @b: var(c);
- @c: red;
-
- @one: var(two);
- @two: var(one);
-
- @four: var(five);
- @five: var(six);
- @six: var(four);
-
- @def-1: var(def-2);
- @def-2: var(def-3);
- @def-3: var(def-2);''',
- };
- }
-
- test('var- and Less @define', () {
- var messages = new Messages.silent();
- var compiler = createCompiler(createFiles(), messages, errors: true,
- scopedCss: true);
-
- compiler.run().then(expectAsync1((e) {
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, equals({
- 'index.html': 1,
- 'foo.html': 1,
- 'foo.css': 1
- }), reason: 'Actual:\n ${fs.readCount}');
-
- var cssInfo = compiler.info['foo.css'];
- expect(cssInfo.styleSheets.length, 1);
- var htmlInfo = compiler.info['foo.html'];
- expect(htmlInfo.styleSheets.length, 0);
- expect(htmlInfo.declaredComponents.length, 1);
- expect(htmlInfo.declaredComponents[0].styleSheets.length, 1);
-
- // TODO(sigmund,terry): reenable
- // if (file.path == 'out/index.html.css') {
- // expect(file.contents,
- // '/* Auto-generated from components style tags. */\n'
- // '/* DO NOT EDIT. */\n\n'
- // '/* ==================================================== \n'
- // ' Component x-foo stylesheet \n'
- // ' ==================================================== */\n'
- // '@import "foo.css";\n'
- // '[is="x-foo"] .main {\n'
- // ' color: #f00;\n'
- // '}\n'
- // '[is="x-foo"] .test-background {\n'
- // ' background: url("http://www.foo.com/bar.png");\n'
- // '}\n\n');
- // } else if (file.path == 'out/foo.css') {
- // expect(file.contents,
- // '/* Auto-generated from style sheet href = foo.css */\n'
- // '/* DO NOT EDIT. */\n\n\n\n');
- // }
-
- // Check for warning messages about var- cycles in no expected order.
- expect(messages.messages.length, 8);
- int testBitMap = 0;
- for (var errorMessage in messages.messages) {
- var message = errorMessage.message;
- if (message.contains('var cycle detected var-def-1')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 11);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@def-1: var(def-2)');
- testBitMap |= 1 << 0;
- } else if (message.contains('var cycle detected var-five')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 8);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@five: var(six)');
- testBitMap |= 1 << 1;
- } else if (message.contains('var cycle detected var-six')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 9);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@six: var(four)');
- testBitMap |= 1 << 2;
- } else if (message.contains('var cycle detected var-def-3')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 13);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@def-3: var(def-2)');
- testBitMap |= 1 << 3;
- } else if (message.contains('var cycle detected var-two')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 5);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@two: var(one)');
- testBitMap |= 1 << 4;
- } else if (message.contains('var cycle detected var-def-2')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 12);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@def-2: var(def-3)');
- testBitMap |= 1 << 5;
- } else if (message.contains('var cycle detected var-one')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 4);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@one: var(two)');
- testBitMap |= 1 << 6;
- } else if (message.contains('var cycle detected var-four')) {
- expect(errorMessage.span, isNotNull);
- expect(errorMessage.span.start.line, 7);
- expect(errorMessage.span.start.column, 22);
- expect(errorMessage.span.text, '@four: var(five)');
- testBitMap |= 1 << 7;
- }
- }
- expect(testBitMap, equals((1 << 8) - 1));
- }));
- });
-}
-
-test_pseudo_element() {
- var messages = new Messages.silent();
- var compiler = createCompiler({
- 'index.html': '<head>'
- '<link rel="import" href="foo.html">'
- '<style>'
- '.test::x-foo { background-color: red; }'
- '.test::x-foo1 { color: blue; }'
- '.test::x-foo2 { color: green; }'
- '</style>'
- '<body>'
- '<x-foo class=test></x-foo>'
- '<x-foo></x-foo>'
- '<script type="application/dart">main() {}</script>',
- 'foo.html': '<head>'
- '<body><polymer-element name="x-foo" constructor="Foo">'
- '<template>'
- '<div pseudo="x-foo">'
- '<div>Test</div>'
- '</div>'
- '<div pseudo="x-foo1 x-foo2">'
- '<div>Test</div>'
- '</div>'
- '</template>',
- }, messages, scopedCss: true);
-
- compiler.run().then(expectAsync1((e) {
- MockFileSystem fs = compiler.fileSystem;
- expect(fs.readCount, equals({
- 'index.html': 1,
- 'foo.html': 1,
- }), reason: 'Actual:\n ${fs.readCount}');
-
- // TODO(sigmund, terry): reenable
- // expect(compiler.output.last.contents, contains(
- // '<div pseudo="x-foo_0">'
- // '<div>Test</div>'
- // '</div>'
- // '<div pseudo="x-foo1_1 x-foo2_2">'
- // '<div>Test</div>'
- // '</div>'));
- // expect(compiler.output.last.contents, contains(
- // '<style>.test > *[pseudo="x-foo_0"] {\n'
- // ' background-color: #f00;\n'
- // '}\n'
- // '.test > *[pseudo="x-foo1_1"] {\n'
- // ' color: #00f;\n'
- // '}\n'
- // '.test > *[pseudo="x-foo2_2"] {\n'
- // ' color: #008000;\n'
- // '}'
- // '</style>'));
- }));
-}
-
-main() {
- useCompactVMConfiguration();
-
- group('css', () {
- setUp(() {
- utils.path = new path.Builder(style: path.Style.posix);
- });
-
- tearDown(() {
- utils.path = new path.Builder();
- });
-
- test('test_simple_var', test_simple_var);
- test('test_var', test_var);
- test('test_simple_import', test_simple_import);
- test('test_imports', test_imports);
- group('test_component_var', test_component_var);
- test('test_pseudo_element', test_pseudo_element);
- });
-}
diff --git a/pkg/polymer/test/data/unit/web/event_path_test.html b/pkg/polymer/test/data/unit/web/event_path_test.html
deleted file mode 100644
index 811479d..0000000
--- a/pkg/polymer/test/data/unit/web/event_path_test.html
+++ /dev/null
@@ -1,142 +0,0 @@
-<!doctype html>
-<!--
- Copyright (c) 2013, 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.
--->
-<html>
- <head>
- <title>event path</title>
- <script src="packages/polymer/testing/testing.js"></script>
- <script src="packages/unittest/test_controller.js"></script>
- <!--
- Test ported from:
- https://github.com/Polymer/polymer/blob/7936ff8/test/html/event-path.html
-
- This test actually doesn't test the polymer's event layer. It just ensures
- that tests are propagated in the right order when using Shadow DOM.
- -->
- </head>
- <body>
-
- <polymer-element name="x-selector">
- <template>
- <div id="selectorDiv">
- <content id="selectorContent"></content>
- </div>
- </template>
- <script type="application/dart">
- import 'package:polymer/polymer.dart';
- @CustomTag("x-selector")
- class XSelector extends PolymerElement {}
- </script>
- </polymer-element>
-
- <polymer-element name="x-overlay">
- <template>
- <content id="overlayContent"></content>
- </template>
- <script type="application/dart">
- import 'package:polymer/polymer.dart';
- @CustomTag("x-overlay")
- class XOverlay extends PolymerElement {}
- </script>
- </polymer-element>
-
- <polymer-element name="x-menu" extends="x-selector">
- <template>
- <div id="menuDiv">
- <shadow id="menuShadow"></shadow>
- </div>
- </template>
- <script type="application/dart">
- import 'package:polymer/polymer.dart';
- @CustomTag("x-menu")
- class XMenu extends PolymerElement {}
- </script>
- </polymer-element>
-
- <polymer-element name="x-menu-button">
- <template>
- <div>
- <x-overlay id="overlay">
- <div id="menuButtonDiv">
- <x-menu id="menu">
- <content id="menuButtonContent"></content>
- </x-menu>
- </div>
- </x-overlay>
- </div>
- </template>
- <script type="application/dart">
- import 'package:polymer/polymer.dart';
- @CustomTag("x-menu-button")
- class XMenuButton extends PolymerElement {}
- </script>
- </polymer-element>
-
- <x-menu-button id="menuButton">
- <div id="item1"><div id="source"></div>Item1</div>
- <div id="item2">Item2</div>
- </x-menu-button>
-
-
- <script type="application/dart">
- import 'dart:html';
- import 'dart:async';
- import 'package:unittest/unittest.dart';
- import 'package:unittest/html_config.dart';
-
- main() {
- useHtmlConfiguration();
- test('bubbling in the right order', () {
- // TODO(sigmund): this should change once we port over the
- // 'WebComponentsReady' event.
- runAsync(expectAsync0(() {
- var item1 = query('#item1');
- var menuButton = query('#menuButton');
- // Note: polymer uses automatic node finding (menuButton.$.menu)
- // also note that their node finding code also reachs into the ids
- // from the parent shadow (menu.$.selectorContent instead of
- // menu.$.menuShadow.$.selectorContent)
- var menu = menuButton.shadowRoot.query('#menu');
- var selector = menu.shadowRoot.query("#menuShadow");
- var overlay = menuButton.shadowRoot.query('#overlay');
- var expectedPath = <Node>[
- item1,
- menuButton.shadowRoot.query('#menuButtonContent'),
- selector.olderShadowRoot.query('#selectorContent'),
- selector.olderShadowRoot.query('#selectorDiv'),
- menu.shadowRoot.query('#menuShadow').olderShadowRoot,
- menu.shadowRoot.query('#menuShadow'),
- menu.shadowRoot.query('#menuDiv'),
- menu.shadowRoot,
- menu,
- menuButton.shadowRoot.query('#menuButtonDiv'),
- // TODO(sigmund): this test is currently broken because currently
- // registerElement is sensitive to the order in which each custom
- // element is registered. When fixed, we should be able to add the
- // following three targets:
- // overlay.shadowRoot.query('#overlayContent'),
- // overlay.shadowRoot,
- // overlay,
- menuButton.shadowRoot,
- menuButton
- ];
- var x = 0;
- for (int i = 0; i < expectedPath.length; i++) {
- var node = expectedPath[i];
- expect(node, isNotNull, reason: "Should not be null at $i");
- node.on['x'].listen(expectAsync1((e) {
- expect(e.currentTarget, node);
- expect(x++, i);
- }));
- }
-
- item1.dispatchEvent(new Event('x', canBubble: true));
- }));
- });
- }
- </script>
- </body>
-</html>
diff --git a/pkg/polymer/test/data/unit/web/events_test.html b/pkg/polymer/test/data/unit/web/events_test.html
deleted file mode 100644
index cbf33d1..0000000
--- a/pkg/polymer/test/data/unit/web/events_test.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!doctype html>
-<!--
- Copyright (c) 2013, 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.
--->
-<html>
- <head>
- <title>event path</title>
- <script src="packages/polymer/testing/testing.js"></script>
- <script src="packages/unittest/test_controller.js"></script>
- <!--
- Test ported from:
- https://github.com/Polymer/polymer/blob/7936ff8/test/js/events.js
-
- TODO(sigmund): when we have support for mutation observers, render all of
- the test in Dart (like events.js does in JS)
- -->
- </head>
- <body>
-
- <polymer-element name="test-a" on-click="clickHandler">
- <template></template>
- <script type="application/dart">
- import 'package:polymer/polymer.dart';
-
- @CustomTag("test-a")
- class TestA extends PolymerElement {
- List clicks = [];
- void clickHandler() {
- clicks.add('host click on: $localName (id $id)');
- }
- }
- </script>
- </polymer-element>
-
- <polymer-element name="test-b">
- <template>
- <div>
- <span id="b-1">1</span>
- <span id="b-2" on-click="clickHandler">2</span>
- </div>
- </template>
- <script type="application/dart">
- import 'package:polymer/polymer.dart';
-
- @CustomTag("test-b")
- class TestB extends PolymerElement {
- List clicks = [];
- void clickHandler(event, detail, target) {
- clicks.add('local click under $localName (id $id) on ${target.id}');
- }
- }
- </script>
- </polymer-element>
-
- <test-a id="a"></test-a>
- <test-b id="b"></test-b>
-
- <script type="application/dart">
- import 'dart:html';
- import 'dart:async';
- import 'package:unittest/unittest.dart';
- import 'package:unittest/html_config.dart';
-
- main() {
- useHtmlConfiguration();
-
- test('host event', () {
- // Note: this test is currently the only event in
- // polymer/test/js/events.js at commit #7936ff8
- Timer.run(expectAsync0(() {
- var testA = query('#a');
- expect(testA.xtag.clicks, isEmpty);
- testA.click();
- expect(testA.xtag.clicks, ['host click on: test-a (id a)']);
- }));
- });
-
- test('local event', () {
- Timer.run(expectAsync0(() {
- var testB = query('#b');
- expect(testB.xtag.clicks, isEmpty);
- testB.click();
- expect(testB.xtag.clicks, []);
- var b1 = testB.shadowRoot.query('#b-1');
- b1.click();
- expect(testB.xtag.clicks, []);
- var b2 = testB.shadowRoot.query('#b-2');
- b2.click();
- expect(testB.xtag.clicks, ['local click under test-b (id b) on b-2']);
- }));
- });
- }
- </script>
- </body>
-</html>
diff --git a/pkg/polymer/test/event_path_test.dart b/pkg/polymer/test/event_path_test.dart
new file mode 100644
index 0000000..550731c
--- /dev/null
+++ b/pkg/polymer/test/event_path_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2013, 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.
+
+library polymer.test.web.event_path_test;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+main() {
+ useHtmlConfiguration();
+ test('bubbling in the right order', () {
+ // TODO(sigmund): this should change once we port over the
+ // 'WebComponentsReady' event.
+ runAsync(expectAsync0(() {
+ var item1 = query('#item1');
+ var menuButton = query('#menuButton');
+ // Note: polymer uses automatic node finding (menuButton.$.menu)
+ // also note that their node finding code also reachs into the ids
+ // from the parent shadow (menu.$.selectorContent instead of
+ // menu.$.menuShadow.$.selectorContent)
+ var menu = menuButton.shadowRoot.query('#menu');
+ var selector = menu.shadowRoot.query("#menuShadow");
+ var overlay = menuButton.shadowRoot.query('#overlay');
+ var expectedPath = <Node>[
+ item1,
+ menuButton.shadowRoot.query('#menuButtonContent'),
+ selector.olderShadowRoot.query('#selectorContent'),
+ selector.olderShadowRoot.query('#selectorDiv'),
+ menu.shadowRoot.query('#menuShadow').olderShadowRoot,
+ menu.shadowRoot.query('#menuShadow'),
+ menu.shadowRoot.query('#menuDiv'),
+ menu.shadowRoot,
+ menu,
+ menuButton.shadowRoot.query('#menuButtonDiv'),
+ // TODO(sigmund): this test is currently broken because currently
+ // registerElement is sensitive to the order in which each custom
+ // element is registered. When fixed, we should be able to add the
+ // following three targets:
+ // overlay.shadowRoot.query('#overlayContent'),
+ // overlay.shadowRoot,
+ // overlay,
+ menuButton.shadowRoot,
+ menuButton
+ ];
+ var x = 0;
+ for (int i = 0; i < expectedPath.length; i++) {
+ var node = expectedPath[i];
+ expect(node, isNotNull, reason: "Should not be null at $i");
+ node.on['x'].listen(expectAsync1((e) {
+ expect(e.currentTarget, node);
+ expect(x++, i);
+ }));
+ }
+
+ item1.dispatchEvent(new Event('x', canBubble: true));
+ }));
+ });
+}
diff --git a/pkg/polymer/test/event_path_test.html b/pkg/polymer/test/event_path_test.html
new file mode 100644
index 0000000..3e5af21
--- /dev/null
+++ b/pkg/polymer/test/event_path_test.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<!--
+ Copyright (c) 2013, 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.
+-->
+<html>
+ <head>
+ <title>event path</title>
+ <script src="packages/polymer/boot.js"></script>
+ <script src="packages/unittest/test_controller.js"></script>
+ <!--
+ Test ported from:
+ https://github.com/Polymer/polymer/blob/7936ff8/test/html/event-path.html
+
+ This test actually doesn't test the polymer's event layer. It just ensures
+ that tests are propagated in the right order when using Shadow DOM.
+ -->
+ </head>
+ <body>
+
+ <polymer-element name="x-selector">
+ <template>
+ <div id="selectorDiv">
+ <content id="selectorContent"></content>
+ </div>
+ </template>
+ <script type="application/dart">
+ import 'package:polymer/polymer.dart';
+ @CustomTag("x-selector")
+ class XSelector extends PolymerElement {}
+ </script>
+ </polymer-element>
+
+ <polymer-element name="x-overlay">
+ <template>
+ <content id="overlayContent"></content>
+ </template>
+ <script type="application/dart">
+ import 'package:polymer/polymer.dart';
+ @CustomTag("x-overlay")
+ class XOverlay extends PolymerElement {}
+ </script>
+ </polymer-element>
+
+ <polymer-element name="x-menu" extends="x-selector">
+ <template>
+ <div id="menuDiv">
+ <shadow id="menuShadow"></shadow>
+ </div>
+ </template>
+ <script type="application/dart">
+ import 'package:polymer/polymer.dart';
+ @CustomTag("x-menu")
+ class XMenu extends PolymerElement {}
+ </script>
+ </polymer-element>
+
+ <polymer-element name="x-menu-button">
+ <template>
+ <div>
+ <x-overlay id="overlay">
+ <div id="menuButtonDiv">
+ <x-menu id="menu">
+ <content id="menuButtonContent"></content>
+ </x-menu>
+ </div>
+ </x-overlay>
+ </div>
+ </template>
+ <script type="application/dart">
+ import 'package:polymer/polymer.dart';
+ @CustomTag("x-menu-button")
+ class XMenuButton extends PolymerElement {}
+ </script>
+ </polymer-element>
+
+ <x-menu-button id="menuButton">
+ <div id="item1"><div id="source"></div>Item1</div>
+ <div id="item2">Item2</div>
+ </x-menu-button>
+
+
+ <script type="application/dart" src="event_path_test.dart"></script>
+ </body>
+</html>
diff --git a/pkg/polymer/test/events_test.dart b/pkg/polymer/test/events_test.dart
new file mode 100644
index 0000000..9199329
--- /dev/null
+++ b/pkg/polymer/test/events_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2013, 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.
+
+library polymer.test.web.events_test;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+main() {
+ useHtmlConfiguration();
+
+ test('host event', () {
+ // Note: this test is currently the only event in
+ // polymer/test/js/events.js at commit #7936ff8
+ Timer.run(expectAsync0(() {
+ var testA = query('#a');
+ expect(testA.xtag.clicks, isEmpty);
+ testA.click();
+ expect(testA.xtag.clicks, ['host click on: test-a (id a)']);
+ }));
+ });
+
+ test('local event', () {
+ Timer.run(expectAsync0(() {
+ var testB = query('#b');
+ expect(testB.xtag.clicks, isEmpty);
+ testB.click();
+ expect(testB.xtag.clicks, []);
+ var b1 = testB.shadowRoot.query('#b-1');
+ b1.click();
+ expect(testB.xtag.clicks, []);
+ var b2 = testB.shadowRoot.query('#b-2');
+ b2.click();
+ expect(testB.xtag.clicks, ['local click under test-b (id b) on b-2']);
+ }));
+ });
+}
diff --git a/pkg/polymer/test/events_test.html b/pkg/polymer/test/events_test.html
new file mode 100644
index 0000000..b077163
--- /dev/null
+++ b/pkg/polymer/test/events_test.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<!--
+ Copyright (c) 2013, 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.
+-->
+<html>
+ <head>
+ <title>event path</title>
+ <script src="packages/polymer/boot.js"></script>
+ <script src="packages/unittest/test_controller.js"></script>
+ <!--
+ Test ported from:
+ https://github.com/Polymer/polymer/blob/7936ff8/test/js/events.js
+
+ TODO(sigmund): when we have support for mutation observers, render all of
+ the test in Dart (like events.js does in JS)
+ -->
+ </head>
+ <body>
+
+ <polymer-element name="test-a" on-click="clickHandler">
+ <template></template>
+ <script type="application/dart">
+ import 'package:polymer/polymer.dart';
+
+ @CustomTag("test-a")
+ class TestA extends PolymerElement {
+ List clicks = [];
+ void clickHandler() {
+ clicks.add('host click on: $localName (id $id)');
+ }
+ }
+ </script>
+ </polymer-element>
+
+ <polymer-element name="test-b">
+ <template>
+ <div>
+ <span id="b-1">1</span>
+ <span id="b-2" on-click="clickHandler">2</span>
+ </div>
+ </template>
+ <script type="application/dart">
+ import 'package:polymer/polymer.dart';
+
+ @CustomTag("test-b")
+ class TestB extends PolymerElement {
+ List clicks = [];
+ void clickHandler(event, detail, target) {
+ clicks.add('local click under $localName (id $id) on ${target.id}');
+ }
+ }
+ </script>
+ </polymer-element>
+
+ <test-a id="a"></test-a>
+ <test-b id="b"></test-b>
+
+ <script type="application/dart" src="events_test.dart"></script>
+ </body>
+</html>
diff --git a/pkg/polymer/test/run.sh b/pkg/polymer/test/run.sh
index 3d04337..7c20556 100755
--- a/pkg/polymer/test/run.sh
+++ b/pkg/polymer/test/run.sh
@@ -1,131 +1,25 @@
#!/bin/bash
-# Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+# Copyright (c) 2013, 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.
-# Usage: call directly in the commandline as test/run.sh ensuring that you have
-# both 'dart' and 'content_shell' in your path. Filter tests by passing a
-# pattern as an argument to this script.
-
-# TODO(sigmund): replace with a real test runner
+# Usage: run from a Dart SVN checkout after building.
# bail on error
set -e
-# print commands executed by this script
-# set -x
-
+# Note: test.dart needs to be run from the root of the Dart checkout
DIR=$( cd $( dirname "${BASH_SOURCE[0]}" ) && pwd )
-# Note: dartanalyzer and some tests needs to be run from the root directory
-pushd $DIR/.. > /dev/null
+pushd $DIR/../../.. > /dev/null
-export DART_FLAGS="--checked"
+echo "*** Running unit tests for Polymer.dart and its dependencies."
-# Search for the first argument that doesn't look like an option ('--foo')
-function first_non_option {
- while [[ $# -gt 0 ]]; do
- if [[ $1 != --* ]]; then # Note: --* is a regex
- echo $1
- return
- fi
- shift
- done
-}
+SUITES="pkg/polymer samples/third_party/todomvc"
-TEST_PATTERN=$(first_non_option $@)
+CONFIG="-m release -r vm,drt,ff,chrome -c none,dart2js,dartanalyzer --checked $*"
-SDK_DIR=$(cd ../../out/ReleaseIA32/dart-sdk/; pwd)
-package_root=$SDK_DIR/../packages
-dart="$SDK_DIR/bin/dart --package-root=$package_root"
-dartanalyzer="$SDK_DIR/bin/dartanalyzer --package-root=$package_root"
-
-function fail {
- return 1
-}
-
-function show_diff {
- diff -u -N $1 $2 | \
- sed -e "s/^\(+.*\)/[32m\1[0m/" |\
- sed -e "s/^\(-.*\)/[31m\1[0m/"
- return 1
-}
-
-function update {
- read -p "Would you like to update the expectations? [y/N]: " answer
- if [[ $answer == 'y' || $answer == 'Y' ]]; then
- cp $2 $1
- return 0
- fi
- return 1
-}
-
-function pass {
- echo -e "[32mOK[0m"
-}
-
-function compare {
- # use a standard diff, if they are not identical, format the diff nicely to
- # see what's the error and prompt to see if they wish to update it. If they
- # do, continue running more tests.
- diff -q $1 $2 && pass || show_diff $1 $2 || update $1 $2
-}
-
-if [[ ($TEST_PATTERN == "") ]]; then
- echo Analyzing analyzer for warnings or type errors
- $dartanalyzer --hints --fatal-warnings --fatal-type-errors lib/dwc.dart
-
- echo Analyzing deploy-compiler for warnings or type errors
- $dartanalyzer --hints --fatal-warnings --fatal-type-errors lib/deploy.dart
-
- echo -e "\nAnalyzing runtime for warnings or type errors"
- $dartanalyzer --hints --fatal-warnings --fatal-type-errors lib/polymer.dart
-
- popd > /dev/null
-fi
-
-function compare_all {
-# TODO(jmesserly): bash and dart regexp might not be 100% the same. Ideally we
-# could do all the heavy lifting in Dart code, and keep this script as a thin
-# wrapper that sets `--enable-type-checks --enable-asserts`
- for input in $DIR/../example/component/news/test/*_test.html; do
- if [[ ($TEST_PATTERN == "") || ($input =~ $TEST_PATTERN) ]]; then
- FILENAME=`basename $input`
- DIRNAME=`dirname $input`
- if [[ `basename $DIRNAME` == 'input' ]]; then
- DIRNAME=`dirname $DIRNAME`
- fi
- echo -e -n "Checking diff for $FILENAME "
- DUMP="test/data/out/example/test/$FILENAME.txt"
- EXPECTATION="$DIRNAME/expected/$FILENAME.txt"
-
- compare $EXPECTATION $DUMP
- fi
- done
- echo -e "[31mSome tests failed[0m"
- fail
-}
-
-if [[ ($TEST_PATTERN == "") ]]; then
- echo -e "\nTesting build.dart... "
- $dart $DART_FLAGS build.dart
- # Run it the way the editor does. Hide stdout because it is in noisy machine
- # format. Show stderr in case something breaks.
- # NOTE: not using --checked because the editor doesn't use it, and to workaround
- # http://dartbug.com/9637
- $dart build.dart --machine --clean > /dev/null
- $dart build.dart --machine --full > /dev/null
-fi
-
-echo -e "\nRunning unit tests... "
-$dart $DART_FLAGS test/run_all.dart $@ || compare_all
-
-# Run Dart analyzer to check that we're generating warning clean code.
-# It's a bit slow, so only do this for one test.
-OUT_PATTERN="$DIR/../example/component/news/test/out/test/*$TEST_PATTERN*_bootstrap.dart"
-if [[ `ls $OUT_PATTERN 2>/dev/null` != "" ]]; then
- echo -e "\nAnalyzing generated code for warnings or type errors."
- ls $OUT_PATTERN 2>/dev/null | $dartanalyzer \
- --fatal-warnings --fatal-type-errors -batch
-fi
+CMD="xvfb-run ./tools/test.py $CONFIG $SUITES"
+echo "*** $CMD"
+$CMD
echo -e "[32mAll tests pass[0m"
diff --git a/pkg/polymer/test/run_all.dart b/pkg/polymer/test/run_all.dart
deleted file mode 100644
index 6191492..0000000
--- a/pkg/polymer/test/run_all.dart
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/**
- * This is a helper for run.sh. We try to run all of the Dart code in one
- * instance of the Dart VM to reduce warm-up time.
- */
-library run_impl;
-
-import 'dart:io';
-import 'package:unittest/compact_vm_config.dart';
-import 'package:unittest/unittest.dart';
-import 'package:polymer/testing/content_shell_test.dart';
-
-import 'compiler_test.dart' as compiler_test;
-import 'css_test.dart' as css_test;
-import 'transform/all_phases_test.dart' as all_phases_test;
-import 'transform/code_extractor_test.dart' as code_extractor_test;
-import 'transform/import_inliner_test.dart' as import_inliner_test;
-import 'transform/polyfill_injector_test.dart' as polyfill_injector_test;
-import 'transform/script_compactor_test.dart' as script_compactor_test;
-import 'utils_test.dart' as utils_test;
-import 'linter_test.dart' as linter_test;
-
-main() {
- var args = new Options().arguments;
- var pattern = new RegExp(args.length > 0 ? args[0] : '.');
-
- useCompactVMConfiguration();
-
- void addGroup(testFile, testMain) {
- if (pattern.hasMatch(testFile)) {
- group(testFile.replaceAll('_test.dart', ':'), testMain);
- }
- }
-
- addGroup('linter_test.dart', linter_test.main);
- addGroup('compiler_test.dart', compiler_test.main);
- addGroup('css_test.dart', css_test.main);
- addGroup('utils_test.dart', utils_test.main);
- addGroup('transform/code_extractor_test.dart', code_extractor_test.main);
- addGroup('transform/import_inliner_test.dart', import_inliner_test.main);
- addGroup('transform/script_compactor_test.dart', script_compactor_test.main);
- addGroup('transform/polyfill_injector_test.dart',
- polyfill_injector_test.main);
- addGroup('transform/all_phases_test.dart', all_phases_test.main);
-
- endToEndTests('data/unit/web', 'data/out');
-
- // Note: if you're adding more render test suites, make sure to update run.sh
- // as well for convenient baseline diff/updating.
-
- // TODO(jmesserly): figure out why this fails in content_shell but works in
- // Dartium and Firefox when using the ShadowDOM polyfill.
- exampleTest('../example/component/news', ['--no-shadowdom']..addAll(args));
-}
-
-void exampleTest(String path, [List args]) {
- // TODO(sigmund): renderTests currently contatenates [path] with the out
- // folder. This can be a problem with relative paths that go up (like todomvc
- // above, which has '../../../'). If we continue running tests with
- // test/run.sh, we should fix this. For now we work around this problem by
- // using a long path 'data/out/example/test'. That way we avoid dumping output
- // in the source-tree.
- renderTests(path, '$path/test', '$path/test/expected',
- 'data/out/example/test', arguments: args);
-}
diff --git a/pkg/polymer/test/testing.dart b/pkg/polymer/test/testing.dart
deleted file mode 100644
index c89b416..0000000
--- a/pkg/polymer/test/testing.dart
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/** Common definitions used for setting up the test environment. */
-library testing;
-
-import 'dart:async';
-import 'dart:io';
-
-import 'package:csslib/visitor.dart';
-import 'package:html5lib/dom.dart';
-import 'package:html5lib/parser.dart';
-import 'package:polymer/src/analyzer.dart';
-import 'package:polymer/src/compiler.dart';
-import 'package:polymer/src/file_system.dart';
-import 'package:polymer/src/info.dart';
-import 'package:polymer/src/messages.dart';
-import 'package:polymer/src/compiler_options.dart';
-import 'package:polymer/src/files.dart';
-import 'package:polymer/src/utils.dart';
-
-
-Document parseDocument(String html) => parse(html);
-
-Element parseSubtree(String html) => parseFragment(html).nodes[0];
-
-FileInfo analyzeDefinitionsInTree(Document doc, Messages messages,
- {String packageRoot: 'packages'}) {
-
- return analyzeDefinitions(new GlobalInfo(), new UrlInfo('', '', null),
- doc, packageRoot, messages);
-}
-
-/** Parses files in [fileContents], with [mainHtmlFile] being the main file. */
-List<SourceFile> parseFiles(Map<String, String> fileContents,
- [String mainHtmlFile = 'index.html']) {
-
- var result = <SourceFile>[];
- fileContents.forEach((filename, contents) {
- var src = new SourceFile(filename);
- src.document = parse(contents);
- result.add(src);
- });
-
- return result;
-}
-
-/** Analyze all files. */
-Map<String, FileInfo> analyzeFiles(List<SourceFile> files,
- {Messages messages, String packageRoot: 'packages'}) {
- messages = messages == null ? new Messages.silent() : messages;
- var result = new Map<String, FileInfo>();
-
- // analyze definitions
- var global = new GlobalInfo();
- for (var file in files) {
- var path = file.path;
- result[path] = analyzeDefinitions(global, new UrlInfo(path, path, null),
- file.document, packageRoot, messages);
- }
-
- // analyze file contents
- var uniqueIds = new IntIterator();
- var pseudoElements = new Map();
- for (var file in files) {
- analyzeFile(file, result, uniqueIds, pseudoElements, messages, true);
- }
- return result;
-}
-
-Compiler createCompiler(Map files, Messages messages, {bool errors: false,
- bool scopedCss: false}) {
- List baseOptions = ['--no-colors', '-o', 'out', '--deploy', 'index.html'];
- if (errors) baseOptions.insert(0, '--warnings_as_errors');
- if (scopedCss) baseOptions.insert(0, '--scoped-css');
- var options = CompilerOptions.parse(baseOptions);
- var fs = new MockFileSystem(files);
- return new Compiler(fs, options, messages);
-}
-
-String prettyPrintCss(StyleSheet styleSheet) =>
- ((new CssPrinter())..visitTree(styleSheet)).toString();
-
-/**
- * Abstraction around file system access to work in a variety of different
- * environments.
- */
-class MockFileSystem extends FileSystem {
- final Map _files;
- final Map readCount = {};
-
- MockFileSystem(this._files);
-
- Future readTextOrBytes(String filename) => readText(filename);
-
- Future<String> readText(String path) {
- readCount[path] = readCount.putIfAbsent(path, () => 0) + 1;
- var file = _files[path];
- if (file != null) {
- return new Future.value(file);
- } else {
- return new Future.error(
- new FileException('MockFileSystem: $path not found'));
- }
- }
-
- // Compiler doesn't call these
- void writeString(String outfile, String text) {}
- Future flush() {}
-}
diff --git a/pkg/polymer/test/utils_test.dart b/pkg/polymer/test/utils_test.dart
index 393d215..335e538 100644
--- a/pkg/polymer/test/utils_test.dart
+++ b/pkg/polymer/test/utils_test.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/** Tests for some of the utility helper functions used by the compiler. */
-library utils_test;
+library polymer.test.utils_test;
import 'package:unittest/compact_vm_config.dart';
import 'package:unittest/unittest.dart';
diff --git a/pkg/polymer_expressions/lib/eval.dart b/pkg/polymer_expressions/lib/eval.dart
index 7ed7b5a..7341e05 100644
--- a/pkg/polymer_expressions/lib/eval.dart
+++ b/pkg/polymer_expressions/lib/eval.dart
@@ -259,7 +259,7 @@
String toString() => _expr.toString();
}
-class Updater extends RecursiveVisitor<ExpressionObserver> {
+class Updater extends RecursiveVisitor {
final Scope scope;
Updater(this.scope);
@@ -469,9 +469,12 @@
var f = _BINARY_OPERATORS[operator];
if (operator == '&&' || operator == '||') {
_value = f(_toBool(left._value), _toBool(right._value));
+ } else if (operator == '==' || operator == '!=') {
+ _value = f(left._value, right._value);
+ } else if (left._value == null || right._value == null) {
+ _value = null;
} else {
- _value = (left._value == null || right._value == null)
- ? null : f(left._value, right._value);
+ _value = f(left._value, right._value);
}
}
diff --git a/pkg/polymer_expressions/lib/parser.dart b/pkg/polymer_expressions/lib/parser.dart
index cc06681..9a16300 100644
--- a/pkg/polymer_expressions/lib/parser.dart
+++ b/pkg/polymer_expressions/lib/parser.dart
@@ -196,6 +196,10 @@
_advance();
return _astFactory.literal(false);
}
+ if (_token.value == 'null') {
+ _advance();
+ return _astFactory.literal(null);
+ }
var identifier = _parseIdentifier();
var args = _parseArguments();
if (args == null) {
diff --git a/pkg/polymer_expressions/lib/visitor.dart b/pkg/polymer_expressions/lib/visitor.dart
index a11a0d4..03341c2 100644
--- a/pkg/polymer_expressions/lib/visitor.dart
+++ b/pkg/polymer_expressions/lib/visitor.dart
@@ -6,8 +6,8 @@
import 'expression.dart';
-abstract class Visitor<E extends Expression> {
- visit(E s) => s.accept(this);
+abstract class Visitor {
+ visit(Expression s) => s.accept(this);
visitEmptyExpression(EmptyExpression e);
visitParenthesizedExpression(ParenthesizedExpression e);
visitInvoke(Invoke i);
@@ -20,8 +20,8 @@
visitInExpression(InExpression c);
}
-abstract class RecursiveVisitor<E> extends Visitor<E> {
- visitExpression(E e);
+abstract class RecursiveVisitor extends Visitor {
+ visitExpression(Expression e);
visitEmptyExpression(EmptyExpression e) => visitExpression(e);
diff --git a/pkg/polymer_expressions/test/eval_test.dart b/pkg/polymer_expressions/test/eval_test.dart
index b2d9123..ece4396 100644
--- a/pkg/polymer_expressions/test/eval_test.dart
+++ b/pkg/polymer_expressions/test/eval_test.dart
@@ -47,6 +47,10 @@
expectEval('false', false);
});
+ test('should return a literal null', () {
+ expectEval('null', null);
+ });
+
test('should return a literal map', () {
expectEval('{"a": 1}', equals(new Map.from({'a': 1})));
expectEval('{"a": 1}', containsPair('a', 1));
@@ -70,8 +74,10 @@
expectEval('1 == 1', true);
expectEval('1 == 2', false);
+ expectEval('1 == null', false);
expectEval('1 != 1', false);
expectEval('1 != 2', true);
+ expectEval('1 != null', true);
expectEval('1 > 1', false);
expectEval('1 > 2', false);
@@ -162,6 +168,10 @@
});
test('should treat null as false', () {
+ expectEval('!null', true);
+ expectEval('true && null', false);
+ expectEval('null || false', false);
+
expectEval('!a', true, null, {'a': null});
expectEval('a && b', false, null, {'a': null, 'b': true});
diff --git a/pkg/polymer_expressions/test/parser_test.dart b/pkg/polymer_expressions/test/parser_test.dart
index 21b11fb..66c1d04 100644
--- a/pkg/polymer_expressions/test/parser_test.dart
+++ b/pkg/polymer_expressions/test/parser_test.dart
@@ -27,6 +27,15 @@
expectParse('"abc"', literal('abc'));
});
+ test('should parse a bool literal', () {
+ expectParse('true', literal(true));
+ expectParse('false', literal(false));
+ });
+
+ test('should parse a null literal', () {
+ expectParse('null', literal(null));
+ });
+
test('should parse an integer literal', () {
expectParse('123', literal(123));
});
diff --git a/pkg/serialization/lib/src/reader_writer.dart b/pkg/serialization/lib/src/reader_writer.dart
index b40196e..9203185 100644
--- a/pkg/serialization/lib/src/reader_writer.dart
+++ b/pkg/serialization/lib/src/reader_writer.dart
@@ -40,7 +40,7 @@
* but also serves to record which objects we have already seen.
*/
final Map<dynamic, Reference> references =
- new IdentityMap<Object, Reference>();
+ new HashMap<Object, Reference>(equals: identical);
/**
* The state of objects that need to be serialized is stored here.
diff --git a/pkg/serialization/lib/src/serialization_helpers.dart b/pkg/serialization/lib/src/serialization_helpers.dart
index ec89857..d9ad747 100644
--- a/pkg/serialization/lib/src/serialization_helpers.dart
+++ b/pkg/serialization/lib/src/serialization_helpers.dart
@@ -180,64 +180,3 @@
final _wrappedObject;
const _Sentinel(this._wrappedObject);
}
-
-/**
- * This is used in the implementation of [IdentityMap]. We wrap all the keys
- * in an [_IdentityMapKey] that compares using the identity of the wrapped
- * objects. It also treats equal primitive values as identical
- * to conserve space.
- */
-class _IdentityMapKey {
- _IdentityMapKey(this._value);
- var _value;
-
- /**
- * Check if an object is primitive to know if we should compare it using
- * equality or identity. We don't test null/true/false where it's the same.
- */
- _isPrimitive(x) => x is String || x is num;
-
- operator ==(_IdentityMapKey w) =>
- _isPrimitive(_value) ? _value == w._value : identical(_value, w._value);
- get hashCode => _value.hashCode;
- get object => _value;
-}
-
-/**
- * This provides an identity map. We wrap all the objects in
- * an [_IdentityMapKey] that compares using the identity of the
- * wrapped objects. It also treats equal primitive values as identical
- * to conserve space.
- */
-class IdentityMap<K, V> extends LinkedHashMap<K, V> {
-// TODO(alanknight): Replace with a system identity-based map once
-// one is available. Issue 4161.
-// TODO(lrn): Replace with identity map when custom hash maps are introduced
-// (which is soon).
-
- // Check before wrapping because some methods may call others, e.g. on
- // dart2js putIfAbsent calls containsKey, so without this we wrap forever.
- _wrap(Object key) =>
- (key is _IdentityMapKey) ? key : new _IdentityMapKey(key);
- _unwrap(_IdentityMapKey wrapper) => wrapper.object;
-
- Iterable<K> get keys => super.keys.map((x) => _unwrap(x));
- Iterable<V> get values => super.values;
-
- void forEach(void f(K key, V value)) {
- super.forEach((k, v) => f(_unwrap(k), v));
- }
-
- V operator [](K key) => super[_wrap(key)];
-
- void operator []=(K key, V value) {
- super[_wrap(key)] = value;
- }
-
- V putIfAbsent(K key, Function ifAbsent) =>
- super.putIfAbsent(_wrap(key), ifAbsent);
-
- bool containsKey(Object key) => super.containsKey(_wrap(key));
-
- V remove(Object key) => super.remove(_wrap(key));
-}
diff --git a/pkg/serialization/test/polyfill_identity_map_test.dart b/pkg/serialization/test/polyfill_identity_map_test.dart
deleted file mode 100644
index bfc8fef..0000000
--- a/pkg/serialization/test/polyfill_identity_map_test.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2012, 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.
-
-/**
- * Provide a trivial test for identity based hashed collections, which we
- * provide here until an implementation is available in the regular libraries.
- */
-// TODO(alanknight): Remove once identity-hashed collections are available.
-// Issue 4161.
-library identity_set_test;
-
-import 'package:unittest/unittest.dart';
-import 'package:serialization/src/serialization_helpers.dart';
-
-class Foo {
- var x;
- Foo(this.x);
- int get hashCode => x.hashCode;
- bool operator ==(a) => a.x == x;
-}
-
-main() {
- test('basic', () {
- var one = new Foo(3);
- var two = new Foo(3);
- var map = new Map();
- var identityMap = new IdentityMap();
- map[one] = one;
- map[two] = two;
- identityMap[one] = one;
- identityMap[two] = two;
- expect(map.length, 1);
- expect(identityMap.length, 2);
- for (var each in identityMap.values) {
- expect(each, one);
- }
- });
-
- test('uniquing primitives', () {
- var map = new IdentityMap();
- var one = 'one';
- var two = new String.fromCharCodes(one.codeUnits);
- map[one] = 1;
- expect(map[two], 1);
- expect(map[one], 1);
- });
-}
diff --git a/pkg/shadow_dom/lib/shadow_dom.debug.js b/pkg/shadow_dom/lib/shadow_dom.debug.js
index 07b283e..b889a5e 100644
--- a/pkg/shadow_dom/lib/shadow_dom.debug.js
+++ b/pkg/shadow_dom/lib/shadow_dom.debug.js
@@ -16,28 +16,1350 @@
}
})();
+// Copyright 2012 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(global) {
+ 'use strict';
+
+ function detectObjectObserve() {
+ if (typeof Object.observe !== 'function' ||
+ typeof Array.observe !== 'function') {
+ return false;
+ }
+
+ var gotSplice = false;
+ function callback(records) {
+ if (records[0].type === 'splice' && records[1].type === 'splice')
+ gotSplice = true;
+ }
+
+ var test = [0];
+ Array.observe(test, callback);
+ test[1] = 1;
+ test.length = 0;
+ Object.deliverChangeRecords(callback);
+ return gotSplice;
+ }
+
+ var hasObserve = detectObjectObserve();
+
+ function detectEval() {
+ // don't test for eval if document has CSP securityPolicy object and we can see that
+ // eval is not supported. This avoids an error message in console even when the exception
+ // is caught
+ if (global.document &&
+ 'securityPolicy' in global.document &&
+ !global.document.securityPolicy.allowsEval) {
+ return false;
+ }
+
+ try {
+ var f = new Function('', 'return true;');
+ return f();
+ } catch (ex) {
+ return false;
+ }
+ }
+
+ var hasEval = detectEval();
+
+ function isIndex(s) {
+ return +s === s >>> 0;
+ }
+
+ function toNumber(s) {
+ return +s;
+ }
+
+ function isObject(obj) {
+ return obj === Object(obj);
+ }
+
+ var numberIsNaN = global.Number.isNaN || function isNaN(value) {
+ return typeof value === 'number' && global.isNaN(value);
+ }
+
+ function areSameValue(left, right) {
+ if (left === right)
+ return left !== 0 || 1 / left === 1 / right;
+ if (numberIsNaN(left) && numberIsNaN(right))
+ return true;
+
+ return left !== left && right !== right;
+ }
+
+ var createObject = ('__proto__' in {}) ?
+ function(obj) { return obj; } :
+ function(obj) {
+ var proto = obj.__proto__;
+ if (!proto)
+ return obj;
+ var newObject = Object.create(proto);
+ Object.getOwnPropertyNames(obj).forEach(function(name) {
+ Object.defineProperty(newObject, name,
+ Object.getOwnPropertyDescriptor(obj, name));
+ });
+ return newObject;
+ };
+
+ var identStart = '[\$_a-zA-Z]';
+ var identPart = '[\$_a-zA-Z0-9]';
+ var ident = identStart + '+' + identPart + '*';
+ var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)';
+ var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')';
+ var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementIndex + ')*';
+ var pathRegExp = new RegExp('^' + path + '$');
+
+ function isPathValid(s) {
+ if (typeof s != 'string')
+ return false;
+ s = s.trim();
+
+ if (s == '')
+ return true;
+
+ if (s[0] == '.')
+ return false;
+
+ return pathRegExp.test(s);
+ }
+
+ var constructorIsPrivate = {};
+
+ function Path(s, privateToken) {
+ if (privateToken !== constructorIsPrivate)
+ throw Error('Use Path.get to retrieve path objects');
+
+ if (s.trim() == '')
+ return this;
+
+ if (isIndex(s)) {
+ this.push(s);
+ return this;
+ }
+
+ s.split(/\s*\.\s*/).filter(function(part) {
+ return part;
+ }).forEach(function(part) {
+ this.push(part);
+ }, this);
+
+ if (hasEval && !hasObserve && this.length) {
+ this.getValueFrom = this.compiledGetValueFromFn();
+ }
+ }
+
+ // TODO(rafaelw): Make simple LRU cache
+ var pathCache = {};
+
+ function getPath(pathString) {
+ if (pathString instanceof Path)
+ return pathString;
+
+ if (pathString == null)
+ pathString = '';
+
+ if (typeof pathString !== 'string')
+ pathString = String(pathString);
+
+ var path = pathCache[pathString];
+ if (path)
+ return path;
+ if (!isPathValid(pathString))
+ return invalidPath;
+ var path = new Path(pathString, constructorIsPrivate);
+ pathCache[pathString] = path;
+ return path;
+ }
+
+ Path.get = getPath;
+
+ Path.prototype = createObject({
+ __proto__: [],
+ valid: true,
+
+ toString: function() {
+ return this.join('.');
+ },
+
+ getValueFrom: function(obj, observedSet) {
+ for (var i = 0; i < this.length; i++) {
+ if (obj == null)
+ return;
+ if (observedSet)
+ observedSet.observe(obj);
+ obj = obj[this[i]];
+ }
+ return obj;
+ },
+
+ compiledGetValueFromFn: function() {
+ var accessors = this.map(function(ident) {
+ return isIndex(ident) ? '["' + ident + '"]' : '.' + ident;
+ });
+
+ var str = '';
+ var pathString = 'obj';
+ str += 'if (obj != null';
+ var i = 0;
+ for (; i < (this.length - 1); i++) {
+ var ident = this[i];
+ pathString += accessors[i];
+ str += ' &&\n ' + pathString + ' != null';
+ }
+ str += ')\n';
+
+ pathString += accessors[i];
+
+ str += ' return ' + pathString + ';\nelse\n return undefined;';
+ return new Function('obj', str);
+ },
+
+ setValueFrom: function(obj, value) {
+ if (!this.length)
+ return false;
+
+ for (var i = 0; i < this.length - 1; i++) {
+ if (!isObject(obj))
+ return false;
+ obj = obj[this[i]];
+ }
+
+ if (!isObject(obj))
+ return false;
+
+ obj[this[i]] = value;
+ return true;
+ }
+ });
+
+ var invalidPath = new Path('', constructorIsPrivate);
+ invalidPath.valid = false;
+ invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
+
+ var MAX_DIRTY_CHECK_CYCLES = 1000;
+
+ function dirtyCheck(observer) {
+ var cycles = 0;
+ while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check()) {
+ observer.report();
+ cycles++;
+ }
+ if (global.testingExposeCycleCount)
+ global.dirtyCheckCycleCount = cycles;
+ }
+
+ function objectIsEmpty(object) {
+ for (var prop in object)
+ return false;
+ return true;
+ }
+
+ function diffIsEmpty(diff) {
+ return objectIsEmpty(diff.added) &&
+ objectIsEmpty(diff.removed) &&
+ objectIsEmpty(diff.changed);
+ }
+
+ function diffObjectFromOldObject(object, oldObject) {
+ var added = {};
+ var removed = {};
+ var changed = {};
+ var oldObjectHas = {};
+
+ for (var prop in oldObject) {
+ var newValue = object[prop];
+
+ if (newValue !== undefined && newValue === oldObject[prop])
+ continue;
+
+ if (!(prop in object)) {
+ removed[prop] = undefined;
+ continue;
+ }
+
+ if (newValue !== oldObject[prop])
+ changed[prop] = newValue;
+ }
+
+ for (var prop in object) {
+ if (prop in oldObject)
+ continue;
+
+ added[prop] = object[prop];
+ }
+
+ if (Array.isArray(object) && object.length !== oldObject.length)
+ changed.length = object.length;
+
+ return {
+ added: added,
+ removed: removed,
+ changed: changed
+ };
+ }
+
+ function copyObject(object, opt_copy) {
+ var copy = opt_copy || (Array.isArray(object) ? [] : {});
+ for (var prop in object) {
+ copy[prop] = object[prop];
+ };
+ if (Array.isArray(object))
+ copy.length = object.length;
+ return copy;
+ }
+
+ function Observer(object, callback, target, token) {
+ this.closed = false;
+ this.object = object;
+ this.callback = callback;
+ // TODO(rafaelw): Hold this.target weakly when WeakRef is available.
+ this.target = target;
+ this.token = token;
+ this.reporting = true;
+ if (hasObserve) {
+ var self = this;
+ this.boundInternalCallback = function(records) {
+ self.internalCallback(records);
+ };
+ }
+
+ addToAll(this);
+ }
+
+ Observer.prototype = {
+ internalCallback: function(records) {
+ if (this.closed)
+ return;
+ if (this.reporting && this.check(records)) {
+ this.report();
+ if (this.testingResults)
+ this.testingResults.anyChanged = true;
+ }
+ },
+
+ close: function() {
+ if (this.closed)
+ return;
+ if (this.object && typeof this.object.close === 'function')
+ this.object.close();
+
+ this.disconnect();
+ this.object = undefined;
+ this.closed = true;
+ },
+
+ deliver: function(testingResults) {
+ if (this.closed)
+ return;
+ if (hasObserve) {
+ this.testingResults = testingResults;
+ Object.deliverChangeRecords(this.boundInternalCallback);
+ this.testingResults = undefined;
+ } else {
+ dirtyCheck(this);
+ }
+ },
+
+ report: function() {
+ if (!this.reporting)
+ return;
+
+ this.sync(false);
+ if (this.callback) {
+ this.reportArgs.push(this.token);
+ this.invokeCallback(this.reportArgs);
+ }
+ this.reportArgs = undefined;
+ },
+
+ invokeCallback: function(args) {
+ try {
+ this.callback.apply(this.target, args);
+ } catch (ex) {
+ Observer._errorThrownDuringCallback = true;
+ console.error('Exception caught during observer callback: ' + (ex.stack || ex));
+ }
+ },
+
+ reset: function() {
+ if (this.closed)
+ return;
+
+ if (hasObserve) {
+ this.reporting = false;
+ Object.deliverChangeRecords(this.boundInternalCallback);
+ this.reporting = true;
+ }
+
+ this.sync(true);
+ }
+ }
+
+ var collectObservers = !hasObserve || global.forceCollectObservers;
+ var allObservers;
+ Observer._allObserversCount = 0;
+
+ if (collectObservers) {
+ allObservers = [];
+ }
+
+ function addToAll(observer) {
+ if (!collectObservers)
+ return;
+
+ allObservers.push(observer);
+ Observer._allObserversCount++;
+ }
+
+ var runningMicrotaskCheckpoint = false;
+
+ var hasDebugForceFullDelivery = typeof Object.deliverAllChangeRecords == 'function';
+
+ global.Platform = global.Platform || {};
+
+ global.Platform.performMicrotaskCheckpoint = function() {
+ if (runningMicrotaskCheckpoint)
+ return;
+
+ if (hasDebugForceFullDelivery) {
+ Object.deliverAllChangeRecords();
+ return;
+ }
+
+ if (!collectObservers)
+ return;
+
+ runningMicrotaskCheckpoint = true;
+
+ var cycles = 0;
+ var results = {};
+
+ do {
+ cycles++;
+ var toCheck = allObservers;
+ allObservers = [];
+ results.anyChanged = false;
+
+ for (var i = 0; i < toCheck.length; i++) {
+ var observer = toCheck[i];
+ if (observer.closed)
+ continue;
+
+ if (hasObserve) {
+ observer.deliver(results);
+ } else if (observer.check()) {
+ results.anyChanged = true;
+ observer.report();
+ }
+
+ allObservers.push(observer);
+ }
+ } while (cycles < MAX_DIRTY_CHECK_CYCLES && results.anyChanged);
+
+ if (global.testingExposeCycleCount)
+ global.dirtyCheckCycleCount = cycles;
+
+ Observer._allObserversCount = allObservers.length;
+ runningMicrotaskCheckpoint = false;
+ };
+
+ if (collectObservers) {
+ global.Platform.clearObservers = function() {
+ allObservers = [];
+ };
+ }
+
+ function ObjectObserver(object, callback, target, token) {
+ Observer.call(this, object, callback, target, token);
+ this.connect();
+ this.sync(true);
+ }
+
+ ObjectObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ connect: function() {
+ if (hasObserve)
+ Object.observe(this.object, this.boundInternalCallback);
+ },
+
+ sync: function(hard) {
+ if (!hasObserve)
+ this.oldObject = copyObject(this.object);
+ },
+
+ check: function(changeRecords) {
+ var diff;
+ var oldValues;
+ if (hasObserve) {
+ if (!changeRecords)
+ return false;
+
+ oldValues = {};
+ diff = diffObjectFromChangeRecords(this.object, changeRecords,
+ oldValues);
+ } else {
+ oldValues = this.oldObject;
+ diff = diffObjectFromOldObject(this.object, this.oldObject);
+ }
+
+ if (diffIsEmpty(diff))
+ return false;
+
+ this.reportArgs =
+ [diff.added || {}, diff.removed || {}, diff.changed || {}];
+ this.reportArgs.push(function(property) {
+ return oldValues[property];
+ });
+
+ return true;
+ },
+
+ disconnect: function() {
+ if (!hasObserve)
+ this.oldObject = undefined;
+ else if (this.object)
+ Object.unobserve(this.object, this.boundInternalCallback);
+ }
+ });
+
+ function ArrayObserver(array, callback, target, token) {
+ if (!Array.isArray(array))
+ throw Error('Provided object is not an Array');
+ ObjectObserver.call(this, array, callback, target, token);
+ }
+
+ ArrayObserver.prototype = createObject({
+ __proto__: ObjectObserver.prototype,
+
+ connect: function() {
+ if (hasObserve)
+ Array.observe(this.object, this.boundInternalCallback);
+ },
+
+ sync: function() {
+ if (!hasObserve)
+ this.oldObject = this.object.slice();
+ },
+
+ check: function(changeRecords) {
+ var splices;
+ if (hasObserve) {
+ if (!changeRecords)
+ return false;
+ splices = projectArraySplices(this.object, changeRecords);
+ } else {
+ splices = calcSplices(this.object, 0, this.object.length,
+ this.oldObject, 0, this.oldObject.length);
+ }
+
+ if (!splices || !splices.length)
+ return false;
+
+ this.reportArgs = [splices];
+ return true;
+ }
+ });
+
+ ArrayObserver.applySplices = function(previous, current, splices) {
+ splices.forEach(function(splice) {
+ var spliceArgs = [splice.index, splice.removed.length];
+ var addIndex = splice.index;
+ while (addIndex < splice.index + splice.addedCount) {
+ spliceArgs.push(current[addIndex]);
+ addIndex++;
+ }
+
+ Array.prototype.splice.apply(previous, spliceArgs);
+ });
+ };
+
+ function ObservedSet(callback) {
+ this.arr = [];
+ this.callback = callback;
+ this.isObserved = true;
+ }
+
+ var objProto = Object.getPrototypeOf({});
+ var arrayProto = Object.getPrototypeOf([]);
+ ObservedSet.prototype = {
+ reset: function() {
+ this.isObserved = !this.isObserved;
+ },
+
+ observe: function(obj) {
+ if (!isObject(obj) || obj === objProto || obj === arrayProto)
+ return;
+ var i = this.arr.indexOf(obj);
+ if (i >= 0 && this.arr[i+1] === this.isObserved)
+ return;
+
+ if (i < 0) {
+ i = this.arr.length;
+ this.arr[i] = obj;
+ Object.observe(obj, this.callback);
+ }
+
+ this.arr[i+1] = this.isObserved;
+ this.observe(Object.getPrototypeOf(obj));
+ },
+
+ cleanup: function() {
+ var i = 0, j = 0;
+ var isObserved = this.isObserved;
+ while(j < this.arr.length) {
+ var obj = this.arr[j];
+ if (this.arr[j + 1] == isObserved) {
+ if (i < j) {
+ this.arr[i] = obj;
+ this.arr[i + 1] = isObserved;
+ }
+ i += 2;
+ } else {
+ Object.unobserve(obj, this.callback);
+ }
+ j += 2;
+ }
+
+ this.arr.length = i;
+ }
+ };
+
+ function PathObserver(object, path, callback, target, token, valueFn,
+ setValueFn) {
+ var path = path instanceof Path ? path : getPath(path);
+ if (!path || !path.length || !isObject(object)) {
+ this.value_ = path ? path.getValueFrom(object) : undefined;
+ this.value = valueFn ? valueFn(this.value_) : this.value_;
+ this.closed = true;
+ return;
+ }
+
+ Observer.call(this, object, callback, target, token);
+ this.valueFn = valueFn;
+ this.setValueFn = setValueFn;
+ this.path = path;
+
+ this.connect();
+ this.sync(true);
+ }
+
+ PathObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ connect: function() {
+ if (hasObserve)
+ this.observedSet = new ObservedSet(this.boundInternalCallback);
+ },
+
+ disconnect: function() {
+ this.value = undefined;
+ this.value_ = undefined;
+ if (this.observedSet) {
+ this.observedSet.reset();
+ this.observedSet.cleanup();
+ this.observedSet = undefined;
+ }
+ },
+
+ check: function() {
+ // Note: Extracting this to a member function for use here and below
+ // regresses dirty-checking path perf by about 25% =-(.
+ if (this.observedSet)
+ this.observedSet.reset();
+
+ this.value_ = this.path.getValueFrom(this.object, this.observedSet);
+
+ if (this.observedSet)
+ this.observedSet.cleanup();
+
+ if (areSameValue(this.value_, this.oldValue_))
+ return false;
+
+ this.value = this.valueFn ? this.valueFn(this.value_) : this.value_;
+ this.reportArgs = [this.value, this.oldValue];
+ return true;
+ },
+
+ sync: function(hard) {
+ if (hard) {
+ if (this.observedSet)
+ this.observedSet.reset();
+
+ this.value_ = this.path.getValueFrom(this.object, this.observedSet);
+ this.value = this.valueFn ? this.valueFn(this.value_) : this.value_;
+
+ if (this.observedSet)
+ this.observedSet.cleanup();
+ }
+
+ this.oldValue_ = this.value_;
+ this.oldValue = this.value;
+ },
+
+ setValue: function(newValue) {
+ if (!this.path)
+ return;
+ if (typeof this.setValueFn === 'function')
+ newValue = this.setValueFn(newValue);
+ this.path.setValueFrom(this.object, newValue);
+ }
+ });
+
+ function CompoundPathObserver(callback, target, token, valueFn) {
+ Observer.call(this, undefined, callback, target, token);
+ this.valueFn = valueFn;
+
+ this.observed = [];
+ this.values = [];
+ this.started = false;
+ }
+
+ CompoundPathObserver.prototype = createObject({
+ __proto__: PathObserver.prototype,
+
+ addPath: function(object, path) {
+ if (this.started)
+ throw Error('Cannot add more paths once started.');
+
+ var path = path instanceof Path ? path : getPath(path);
+ var value = path ? path.getValueFrom(object) : undefined;
+
+ this.observed.push(object, path);
+ this.values.push(value);
+ },
+
+ start: function() {
+ this.connect();
+ this.sync(true);
+ },
+
+ getValues: function() {
+ if (this.observedSet)
+ this.observedSet.reset();
+
+ var anyChanged = false;
+ for (var i = 0; i < this.observed.length; i = i+2) {
+ var path = this.observed[i+1];
+ if (!path)
+ continue;
+ var object = this.observed[i];
+ var value = path.getValueFrom(object, this.observedSet);
+ var oldValue = this.values[i/2];
+ if (!areSameValue(value, oldValue)) {
+ this.values[i/2] = value;
+ anyChanged = true;
+ }
+ }
+
+ if (this.observedSet)
+ this.observedSet.cleanup();
+
+ return anyChanged;
+ },
+
+ check: function() {
+ if (!this.getValues())
+ return;
+
+ this.value = this.valueFn(this.values);
+
+ if (areSameValue(this.value, this.oldValue))
+ return false;
+
+ this.reportArgs = [this.value, this.oldValue];
+ return true;
+ },
+
+ sync: function(hard) {
+ if (hard) {
+ this.getValues();
+ this.value = this.valueFn(this.values);
+ }
+
+ this.oldValue = this.value;
+ },
+
+ close: function() {
+ if (this.observed) {
+ for (var i = 0; i < this.observed.length; i = i + 2) {
+ var object = this.observed[i];
+ if (object && typeof object.close === 'function')
+ object.close();
+ }
+ this.observed = undefined;
+ this.values = undefined;
+ }
+
+ Observer.prototype.close.call(this);
+ }
+ });
+
+ var knownRecordTypes = {
+ 'new': true,
+ 'updated': true,
+ 'deleted': true
+ };
+
+ function notifyFunction(object, name) {
+ if (typeof Object.observe !== 'function')
+ return;
+
+ var notifier = Object.getNotifier(object);
+ return function(type, oldValue) {
+ var changeRecord = {
+ object: object,
+ type: type,
+ name: name
+ };
+ if (arguments.length === 2)
+ changeRecord.oldValue = oldValue;
+ notifier.notify(changeRecord);
+ }
+ }
+
+ // TODO(rafaelw): It should be possible for the Object.observe case to have
+ // every PathObserver used by defineProperty share a single Object.observe
+ // callback, and thus get() can simply call observer.deliver() and any changes
+ // to any dependent value will be observed.
+ PathObserver.defineProperty = function(object, name, descriptor) {
+ // TODO(rafaelw): Validate errors
+ var obj = descriptor.object;
+ var path = getPath(descriptor.path);
+ var notify = notifyFunction(object, name);
+
+ var observer = new PathObserver(obj, descriptor.path,
+ function(newValue, oldValue) {
+ if (notify)
+ notify('updated', oldValue);
+ }
+ );
+
+ Object.defineProperty(object, name, {
+ get: function() {
+ return path.getValueFrom(obj);
+ },
+ set: function(newValue) {
+ path.setValueFrom(obj, newValue);
+ },
+ configurable: true
+ });
+
+ return {
+ close: function() {
+ var oldValue = path.getValueFrom(obj);
+ if (notify)
+ observer.deliver();
+ observer.close();
+ Object.defineProperty(object, name, {
+ value: oldValue,
+ writable: true,
+ configurable: true
+ });
+ }
+ };
+ }
+
+ function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
+ var added = {};
+ var removed = {};
+
+ for (var i = 0; i < changeRecords.length; i++) {
+ var record = changeRecords[i];
+ if (!knownRecordTypes[record.type]) {
+ console.error('Unknown changeRecord type: ' + record.type);
+ console.error(record);
+ continue;
+ }
+
+ if (!(record.name in oldValues))
+ oldValues[record.name] = record.oldValue;
+
+ if (record.type == 'updated')
+ continue;
+
+ if (record.type == 'new') {
+ if (record.name in removed)
+ delete removed[record.name];
+ else
+ added[record.name] = true;
+
+ continue;
+ }
+
+ // type = 'deleted'
+ if (record.name in added) {
+ delete added[record.name];
+ delete oldValues[record.name];
+ } else {
+ removed[record.name] = true;
+ }
+ }
+
+ for (var prop in added)
+ added[prop] = object[prop];
+
+ for (var prop in removed)
+ removed[prop] = undefined;
+
+ var changed = {};
+ for (var prop in oldValues) {
+ if (prop in added || prop in removed)
+ continue;
+
+ var newValue = object[prop];
+ if (oldValues[prop] !== newValue)
+ changed[prop] = newValue;
+ }
+
+ return {
+ added: added,
+ removed: removed,
+ changed: changed
+ };
+ }
+
+ function newSplice(index, removed, addedCount) {
+ return {
+ index: index,
+ removed: removed,
+ addedCount: addedCount
+ };
+ }
+
+ var EDIT_LEAVE = 0;
+ var EDIT_UPDATE = 1;
+ var EDIT_ADD = 2;
+ var EDIT_DELETE = 3;
+
+ function ArraySplice() {}
+
+ ArraySplice.prototype = {
+
+ // Note: This function is *based* on the computation of the Levenshtein
+ // "edit" distance. The one change is that "updates" are treated as two
+ // edits - not one. With Array splices, an update is really a delete
+ // followed by an add. By retaining this, we optimize for "keeping" the
+ // maximum array items in the original array. For example:
+ //
+ // 'xxxx123' -> '123yyyy'
+ //
+ // With 1-edit updates, the shortest path would be just to update all seven
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
+ // leaves the substring '123' intact.
+ calcEditDistances: function(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ // "Deletion" columns
+ var rowCount = oldEnd - oldStart + 1;
+ var columnCount = currentEnd - currentStart + 1;
+ var distances = new Array(rowCount);
+
+ // "Addition" rows. Initialize null column.
+ for (var i = 0; i < rowCount; i++) {
+ distances[i] = new Array(columnCount);
+ distances[i][0] = i;
+ }
+
+ // Initialize null row
+ for (var j = 0; j < columnCount; j++)
+ distances[0][j] = j;
+
+ for (var i = 1; i < rowCount; i++) {
+ for (var j = 1; j < columnCount; j++) {
+ if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
+ distances[i][j] = distances[i - 1][j - 1];
+ else {
+ var north = distances[i - 1][j] + 1;
+ var west = distances[i][j - 1] + 1;
+ distances[i][j] = north < west ? north : west;
+ }
+ }
+ }
+
+ return distances;
+ },
+
+ // This starts at the final weight, and walks "backward" by finding
+ // the minimum previous weight recursively until the origin of the weight
+ // matrix.
+ spliceOperationsFromEditDistances: function(distances) {
+ var i = distances.length - 1;
+ var j = distances[0].length - 1;
+ var current = distances[i][j];
+ var edits = [];
+ while (i > 0 || j > 0) {
+ if (i == 0) {
+ edits.push(EDIT_ADD);
+ j--;
+ continue;
+ }
+ if (j == 0) {
+ edits.push(EDIT_DELETE);
+ i--;
+ continue;
+ }
+ var northWest = distances[i - 1][j - 1];
+ var west = distances[i - 1][j];
+ var north = distances[i][j - 1];
+
+ var min;
+ if (west < north)
+ min = west < northWest ? west : northWest;
+ else
+ min = north < northWest ? north : northWest;
+
+ if (min == northWest) {
+ if (northWest == current) {
+ edits.push(EDIT_LEAVE);
+ } else {
+ edits.push(EDIT_UPDATE);
+ current = northWest;
+ }
+ i--;
+ j--;
+ } else if (min == west) {
+ edits.push(EDIT_DELETE);
+ i--;
+ current = west;
+ } else {
+ edits.push(EDIT_ADD);
+ j--;
+ current = north;
+ }
+ }
+
+ edits.reverse();
+ return edits;
+ },
+
+ /**
+ * Splice Projection functions:
+ *
+ * A splice map is a representation of how a previous array of items
+ * was transformed into a new array of items. Conceptually it is a list of
+ * tuples of
+ *
+ * <index, removed, addedCount>
+ *
+ * which are kept in ascending index order of. The tuple represents that at
+ * the |index|, |removed| sequence of items were removed, and counting forward
+ * from |index|, |addedCount| items were added.
+ */
+
+ /**
+ * Lacking individual splice mutation information, the minimal set of
+ * splices can be synthesized given the previous state and final state of an
+ * array. The basic approach is to calculate the edit distance matrix and
+ * choose the shortest path through it.
+ *
+ * Complexity: O(l * p)
+ * l: The length of the current array
+ * p: The length of the old array
+ */
+ calcSplices: function(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ var prefixCount = 0;
+ var suffixCount = 0;
+
+ var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+ if (currentStart == 0 && oldStart == 0)
+ prefixCount = this.sharedPrefix(current, old, minLength);
+
+ if (currentEnd == current.length && oldEnd == old.length)
+ suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+
+ currentStart += prefixCount;
+ oldStart += prefixCount;
+ currentEnd -= suffixCount;
+ oldEnd -= suffixCount;
+
+ if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
+ return [];
+
+ if (currentStart == currentEnd) {
+ var splice = newSplice(currentStart, [], 0);
+ while (oldStart < oldEnd)
+ splice.removed.push(old[oldStart++]);
+
+ return [ splice ];
+ } else if (oldStart == oldEnd)
+ return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+
+ var ops = this.spliceOperationsFromEditDistances(
+ this.calcEditDistances(current, currentStart, currentEnd,
+ old, oldStart, oldEnd));
+
+ var splice = undefined;
+ var splices = [];
+ var index = currentStart;
+ var oldIndex = oldStart;
+ for (var i = 0; i < ops.length; i++) {
+ switch(ops[i]) {
+ case EDIT_LEAVE:
+ if (splice) {
+ splices.push(splice);
+ splice = undefined;
+ }
+
+ index++;
+ oldIndex++;
+ break;
+ case EDIT_UPDATE:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.addedCount++;
+ index++;
+
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ case EDIT_ADD:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.addedCount++;
+ index++;
+ break;
+ case EDIT_DELETE:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ }
+ }
+
+ if (splice) {
+ splices.push(splice);
+ }
+ return splices;
+ },
+
+ sharedPrefix: function(current, old, searchLength) {
+ for (var i = 0; i < searchLength; i++)
+ if (!this.equals(current[i], old[i]))
+ return i;
+ return searchLength;
+ },
+
+ sharedSuffix: function(current, old, searchLength) {
+ var index1 = current.length;
+ var index2 = old.length;
+ var count = 0;
+ while (count < searchLength && this.equals(current[--index1], old[--index2]))
+ count++;
+
+ return count;
+ },
+
+ calculateSplices: function(current, previous) {
+ return this.calcSplices(current, 0, current.length, previous, 0,
+ previous.length);
+ },
+
+ equals: function(currentValue, previousValue) {
+ return currentValue === previousValue;
+ }
+ };
+
+ var arraySplice = new ArraySplice();
+
+ function calcSplices(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ return arraySplice.calcSplices(current, currentStart, currentEnd,
+ old, oldStart, oldEnd);
+ }
+
+ function intersect(start1, end1, start2, end2) {
+ // Disjoint
+ if (end1 < start2 || end2 < start1)
+ return -1;
+
+ // Adjacent
+ if (end1 == start2 || end2 == start1)
+ return 0;
+
+ // Non-zero intersect, span1 first
+ if (start1 < start2) {
+ if (end1 < end2)
+ return end1 - start2; // Overlap
+ else
+ return end2 - start2; // Contained
+ } else {
+ // Non-zero intersect, span2 first
+ if (end2 < end1)
+ return end2 - start1; // Overlap
+ else
+ return end1 - start1; // Contained
+ }
+ }
+
+ function mergeSplice(splices, index, removed, addedCount) {
+
+ var splice = newSplice(index, removed, addedCount);
+
+ var inserted = false;
+ var insertionOffset = 0;
+
+ for (var i = 0; i < splices.length; i++) {
+ var current = splices[i];
+ current.index += insertionOffset;
+
+ if (inserted)
+ continue;
+
+ var intersectCount = intersect(splice.index,
+ splice.index + splice.removed.length,
+ current.index,
+ current.index + current.addedCount);
+
+ if (intersectCount >= 0) {
+ // Merge the two splices
+
+ splices.splice(i, 1);
+ i--;
+
+ insertionOffset -= current.addedCount - current.removed.length;
+
+ splice.addedCount += current.addedCount - intersectCount;
+ var deleteCount = splice.removed.length +
+ current.removed.length - intersectCount;
+
+ if (!splice.addedCount && !deleteCount) {
+ // merged splice is a noop. discard.
+ inserted = true;
+ } else {
+ var removed = current.removed;
+
+ if (splice.index < current.index) {
+ // some prefix of splice.removed is prepended to current.removed.
+ var prepend = splice.removed.slice(0, current.index - splice.index);
+ Array.prototype.push.apply(prepend, removed);
+ removed = prepend;
+ }
+
+ if (splice.index + splice.removed.length > current.index + current.addedCount) {
+ // some suffix of splice.removed is appended to current.removed.
+ var append = splice.removed.slice(current.index + current.addedCount - splice.index);
+ Array.prototype.push.apply(removed, append);
+ }
+
+ splice.removed = removed;
+ if (current.index < splice.index) {
+ splice.index = current.index;
+ }
+ }
+ } else if (splice.index < current.index) {
+ // Insert splice here.
+
+ inserted = true;
+
+ splices.splice(i, 0, splice);
+ i++;
+
+ var offset = splice.addedCount - splice.removed.length
+ current.index += offset;
+ insertionOffset += offset;
+ }
+ }
+
+ if (!inserted)
+ splices.push(splice);
+ }
+
+ function createInitialSplices(array, changeRecords) {
+ var splices = [];
+
+ for (var i = 0; i < changeRecords.length; i++) {
+ var record = changeRecords[i];
+ switch(record.type) {
+ case 'splice':
+ mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
+ break;
+ case 'new':
+ case 'updated':
+ case 'deleted':
+ if (!isIndex(record.name))
+ continue;
+ var index = toNumber(record.name);
+ if (index < 0)
+ continue;
+ mergeSplice(splices, index, [record.oldValue], 1);
+ break;
+ default:
+ console.error('Unexpected record type: ' + JSON.stringify(record));
+ break;
+ }
+ }
+
+ return splices;
+ }
+
+ function projectArraySplices(array, changeRecords) {
+ var splices = [];
+
+ createInitialSplices(array, changeRecords).forEach(function(splice) {
+ if (splice.addedCount == 1 && splice.removed.length == 1) {
+ if (splice.removed[0] !== array[splice.index])
+ splices.push(splice);
+
+ return
+ };
+
+ splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
+ splice.removed, 0, splice.removed.length));
+ });
+
+ return splices;
+ }
+
+ global.Observer = Observer;
+ global.Observer.hasObjectObserve = hasObserve;
+ global.ArrayObserver = ArrayObserver;
+ global.ArrayObserver.calculateSplices = function(current, previous) {
+ return arraySplice.calculateSplices(current, previous);
+ };
+
+ global.ArraySplice = ArraySplice;
+ global.ObjectObserver = ObjectObserver;
+ global.PathObserver = PathObserver;
+ global.CompoundPathObserver = CompoundPathObserver;
+ global.Path = Path;
+})(typeof global !== 'undefined' && global ? global : this);
+
/*
* Copyright 2012 The Polymer Authors. All rights reserved.
- * Use of this source code is goverened by a BSD-style
+ * Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
-// SideTable is a weak map where possible. If WeakMap is not available the
-// association is stored as an expando property.
-var SideTable;
+// If WeakMap is not available, the association is stored as an expando property on the "key".
// TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox
-if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') < 0) {
- SideTable = WeakMap;
-} else {
+if (typeof WeakMap === 'undefined' || navigator.userAgent.indexOf('Firefox/') > -1) {
(function() {
var defineProperty = Object.defineProperty;
var counter = Date.now() % 1e9;
- SideTable = function() {
+ var WeakMap = function() {
this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
};
- SideTable.prototype = {
+ WeakMap.prototype = {
set: function(key, value) {
var entry = key[this.name];
if (entry && entry[0] === key)
@@ -53,7 +1375,9 @@
delete: function(key) {
this.set(key, undefined);
}
- }
+ };
+
+ window.WeakMap = WeakMap;
})();
}
@@ -66,11 +1390,23 @@
(function(scope) {
'use strict';
- var wrapperTable = new SideTable();
- var constructorTable = new SideTable();
- var nativePrototypeTable = new SideTable();
+ var constructorTable = new WeakMap();
+ var nativePrototypeTable = new WeakMap();
var wrappers = Object.create(null);
+ // Don't test for eval if document has CSP securityPolicy object and we can
+ // see that eval is not supported. This avoids an error message in console
+ // even when the exception is caught
+ var hasEval = !('securityPolicy' in document) ||
+ document.securityPolicy.allowsEval;
+ if (hasEval) {
+ try {
+ var f = new Function('', 'return true;');
+ hasEval = f();
+ } catch (ex) {
+ }
+ }
+
function assert(b) {
if (!b)
throw new Error('Assertion failed');
@@ -150,6 +1486,25 @@
return /^on[a-z]+$/.test(name);
}
+ function getGetter(name) {
+ return hasEval ?
+ new Function('return this.impl.' + name) :
+ function() { return this.impl[name]; };
+ }
+
+ function getSetter(name) {
+ return hasEval ?
+ new Function('v', 'this.impl.' + name + ' = v') :
+ function(v) { this.impl[name] = v; };
+ }
+
+ function getMethod(name) {
+ return hasEval ?
+ new Function('return this.impl.' + name +
+ '.apply(this.impl, arguments)') :
+ function() { return this.impl[name].apply(this.impl, arguments); };
+ }
+
function installProperty(source, target, allowMethod) {
Object.getOwnPropertyNames(source).forEach(function(name) {
if (name in target)
@@ -170,29 +1525,21 @@
}
var getter, setter;
if (allowMethod && typeof descriptor.value === 'function') {
- target[name] = function() {
- return this.impl[name].apply(this.impl, arguments);
- };
+ target[name] = getMethod(name);
return;
}
var isEvent = isEventHandlerName(name);
- if (isEvent) {
+ if (isEvent)
getter = scope.getEventHandlerGetter(name);
- } else {
- getter = function() {
- return this.impl[name];
- };
- }
+ else
+ getter = getGetter(name);
if (descriptor.writable || descriptor.set) {
- if (isEvent) {
+ if (isEvent)
setter = scope.getEventHandlerSetter(name);
- } else {
- setter = function(value) {
- this.impl[name] = value;
- };
- }
+ else
+ setter = getSetter(name);
}
Object.defineProperty(target, name, {
@@ -296,10 +1643,8 @@
return null;
assert(isNative(impl));
- var wrapper = wrapperTable.get(impl);
- if (!wrapper)
- wrapperTable.set(impl, wrapper = new (getWrapperConstructor(impl))(impl));
- return wrapper;
+ return impl.polymerWrapper_ ||
+ (impl.polymerWrapper_ = new (getWrapperConstructor(impl))(impl));
}
/**
@@ -343,7 +1688,7 @@
return;
assert(isNative(node));
assert(wrapper === undefined || isWrapper(wrapper));
- wrapperTable.set(node, wrapper);
+ node.polymerWrapper_ = wrapper;
}
function defineGetter(constructor, name, getter) {
@@ -411,17 +1756,17 @@
var wrap = scope.wrap;
var wrappers = scope.wrappers;
- var wrappedFuns = new SideTable();
- var listenersTable = new SideTable();
- var handledEventsTable = new SideTable();
- var targetTable = new SideTable();
- var currentTargetTable = new SideTable();
- var relatedTargetTable = new SideTable();
- var eventPhaseTable = new SideTable();
- var stopPropagationTable = new SideTable();
- var stopImmediatePropagationTable = new SideTable();
- var eventHandlersTable = new SideTable();
- var eventPathTable = new SideTable();
+ var wrappedFuns = new WeakMap();
+ var listenersTable = new WeakMap();
+ var handledEventsTable = new WeakMap();
+ var targetTable = new WeakMap();
+ var currentTargetTable = new WeakMap();
+ var relatedTargetTable = new WeakMap();
+ var eventPhaseTable = new WeakMap();
+ var stopPropagationTable = new WeakMap();
+ var stopImmediatePropagationTable = new WeakMap();
+ var eventHandlersTable = new WeakMap();
+ var eventPathTable = new WeakMap();
function isShadowRoot(node) {
return node instanceof wrappers.ShadowRoot;
@@ -578,7 +1923,6 @@
return enclosedBy(rootOfNode(host), b);
}
return false;
-
}
function isMutationEvent(type) {
@@ -1042,7 +2386,6 @@
}
},
dispatchEvent: function(event) {
- scope.renderAllPending();
var target = getTargetToListenAt(this);
return target.dispatchEvent_(unwrap(event));
}
@@ -1211,7 +2554,7 @@
* This updates the internal pointers for node, previousNode and nextNode.
*/
function collectNodes(node, parentNode, previousNode, nextNode) {
- if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
+ if (!(node instanceof DocumentFragment)) {
if (node.parentNode)
node.parentNode.removeChild(node);
node.parentNode_ = parentNode;
@@ -1245,6 +2588,24 @@
return nodes;
}
+ function collectNodesNoNeedToUpdatePointers(node) {
+ if (node instanceof DocumentFragment) {
+ var nodes = [];
+ var i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ nodes[i++] = child;
+ }
+ return nodes;
+ }
+ return [node];
+ }
+
+ function nodesWereAdded(nodes) {
+ for (var i = 0; i < nodes.length; i++) {
+ nodes[i].nodeWasAdded_();
+ }
+ }
+
function ensureSameOwnerDocument(parent, child) {
var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ?
parent : parent.ownerDocument;
@@ -1373,10 +2734,12 @@
appendChild: function(childWrapper) {
assertIsNodeWrapper(childWrapper);
+ var nodes;
+
if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) {
var previousNode = this.lastChild;
var nextNode = null;
- var nodes = collectNodes(childWrapper, this, previousNode, nextNode);
+ nodes = collectNodes(childWrapper, this, previousNode, nextNode);
this.lastChild_ = nodes[nodes.length - 1];
if (!previousNode)
@@ -1384,11 +2747,12 @@
originalAppendChild.call(this.impl, unwrapNodesForInsertion(this, nodes));
} else {
+ nodes = collectNodesNoNeedToUpdatePointers(childWrapper)
ensureSameOwnerDocument(this, childWrapper);
originalAppendChild.call(this.impl, unwrap(childWrapper));
}
- childWrapper.nodeWasAdded_();
+ nodesWereAdded(nodes);
return childWrapper;
},
@@ -1402,10 +2766,12 @@
assertIsNodeWrapper(refWrapper);
assert(refWrapper.parentNode === this);
+ var nodes;
+
if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) {
var previousNode = refWrapper.previousSibling;
var nextNode = refWrapper;
- var nodes = collectNodes(childWrapper, this, previousNode, nextNode);
+ nodes = collectNodes(childWrapper, this, previousNode, nextNode);
if (this.firstChild === refWrapper)
this.firstChild_ = nodes[0];
@@ -1423,12 +2789,13 @@
adoptNodesIfNeeded(this, nodes);
}
} else {
+ nodes = collectNodesNoNeedToUpdatePointers(childWrapper);
ensureSameOwnerDocument(this, childWrapper);
originalInsertBefore.call(this.impl, unwrap(childWrapper),
unwrap(refWrapper));
}
- childWrapper.nodeWasAdded_();
+ nodesWereAdded(nodes);
return childWrapper;
},
@@ -1485,6 +2852,7 @@
}
var oldChildNode = unwrap(oldChildWrapper);
+ var nodes;
if (this.invalidateShadowRenderer() ||
invalidateParent(newChildWrapper)) {
@@ -1492,8 +2860,7 @@
var nextNode = oldChildWrapper.nextSibling;
if (nextNode === newChildWrapper)
nextNode = newChildWrapper.nextSibling;
- var nodes = collectNodes(newChildWrapper, this,
- previousNode, nextNode);
+ nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
if (this.firstChild === oldChildWrapper)
this.firstChild_ = nodes[0];
@@ -1511,12 +2878,13 @@
oldChildNode);
}
} else {
+ nodes = collectNodesNoNeedToUpdatePointers(newChildWrapper);
ensureSameOwnerDocument(this, newChildWrapper);
originalReplaceChild.call(this.impl, unwrap(newChildWrapper),
oldChildNode);
}
- newChildWrapper.nodeWasAdded_();
+ nodesWereAdded(nodes);
return oldChildWrapper;
},
@@ -1635,19 +3003,10 @@
// This only wraps, it therefore only operates on the composed DOM and not
// the logical DOM.
return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode));
- },
-
- // TODO(jmesserly): this is a workaround for
- // https://github.com/Polymer/ShadowDOM/issues/200
- get ownerDocument() {
- scope.renderAllPending();
- return wrap(this.impl.ownerDocument);
}
});
- // TODO(jmesserly): this is commented out to workaround:
- // https://github.com/Polymer/ShadowDOM/issues/200
- //defineWrapGetter(Node, 'ownerDocument');
+ defineWrapGetter(Node, 'ownerDocument');
// We use a DocumentFragment as a base and then delete the properties of
// DocumentFragment.prototype from the wrapper Node. Since delete makes
@@ -1858,7 +3217,7 @@
var registerWrapper = scope.registerWrapper;
var wrappers = scope.wrappers;
- var shadowRootTable = new SideTable();
+ var shadowRootTable = new WeakMap();
var OriginalElement = window.Element;
@@ -2224,8 +3583,8 @@
var unwrap = scope.unwrap;
var wrap = scope.wrap;
- var contentTable = new SideTable();
- var templateContentsOwnerTable = new SideTable();
+ var contentTable = new WeakMap();
+ var templateContentsOwnerTable = new WeakMap();
// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
function getTemplateContentsOwner(doc) {
@@ -2362,8 +3721,8 @@
var setInnerHTML = scope.setInnerHTML;
var unwrap = scope.unwrap;
- var shadowHostTable = new SideTable();
- var nextOlderShadowTreeTable = new SideTable();
+ var shadowHostTable = new WeakMap();
+ var nextOlderShadowTreeTable = new WeakMap();
function ShadowRoot(hostWrapper) {
var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment());
@@ -2417,6 +3776,7 @@
(function(scope) {
'use strict';
+ var Element = scope.wrappers.Element;
var HTMLContentElement = scope.wrappers.HTMLContentElement;
var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
var Node = scope.wrappers.Node;
@@ -2460,79 +3820,59 @@
updateWrapperDown(parentNodeWrapper);
}
- // This object groups DOM operations. This is supposed to be the DOM as the
- // browser/render tree sees it.
- // When changes are done to the visual DOM the logical DOM needs to be updated
- // to reflect the correct tree.
- function removeAllChildNodes(parentNodeWrapper) {
+ function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
var parentNode = unwrap(parentNodeWrapper);
- updateAllChildNodes(parentNodeWrapper);
- if (parentNode.firstChild)
- parentNode.textContent = '';
- }
+ var newChild = unwrap(newChildWrapper);
+ var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
- function appendChild(parentNodeWrapper, childWrapper) {
- var parentNode = unwrap(parentNodeWrapper);
- var child = unwrap(childWrapper);
- if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
- updateAllChildNodes(childWrapper);
+ remove(newChildWrapper);
+ updateWrapperUpAndSideways(newChildWrapper);
+ if (!refChildWrapper) {
+ parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
+ if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild)
+ parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
+
+ var lastChildWrapper = wrap(parentNode.lastChild);
+ if (lastChildWrapper)
+ lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
} else {
- remove(childWrapper);
- updateWrapperUpAndSideways(childWrapper);
+ if (parentNodeWrapper.firstChild === refChildWrapper)
+ parentNodeWrapper.firstChild_ = refChildWrapper;
+
+ refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
}
- parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
- if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild)
- parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
-
- var lastChildWrapper = wrap(parentNode.lastChild);
- if (lastChildWrapper) {
- lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
- }
-
- parentNode.appendChild(child);
- }
-
- function removeChild(parentNodeWrapper, childWrapper) {
- var parentNode = unwrap(parentNodeWrapper);
- var child = unwrap(childWrapper);
-
- updateWrapperUpAndSideways(childWrapper);
-
- if (childWrapper.previousSibling)
- childWrapper.previousSibling.nextSibling_ = childWrapper;
- if (childWrapper.nextSibling)
- childWrapper.nextSibling.previousSibling_ = childWrapper;
-
- if (parentNodeWrapper.lastChild === childWrapper)
- parentNodeWrapper.lastChild_ = childWrapper;
- if (parentNodeWrapper.firstChild === childWrapper)
- parentNodeWrapper.firstChild_ = childWrapper;
-
- parentNode.removeChild(child);
+ parentNode.insertBefore(newChild, refChild);
}
function remove(nodeWrapper) {
var node = unwrap(nodeWrapper)
var parentNode = node.parentNode;
- if (parentNode)
- removeChild(wrap(parentNode), nodeWrapper);
+ if (!parentNode)
+ return;
+
+ var parentNodeWrapper = wrap(parentNode);
+ updateWrapperUpAndSideways(nodeWrapper);
+
+ if (nodeWrapper.previousSibling)
+ nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
+ if (nodeWrapper.nextSibling)
+ nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
+
+ if (parentNodeWrapper.lastChild === nodeWrapper)
+ parentNodeWrapper.lastChild_ = nodeWrapper;
+ if (parentNodeWrapper.firstChild === nodeWrapper)
+ parentNodeWrapper.firstChild_ = nodeWrapper;
+
+ parentNode.removeChild(node);
}
- var distributedChildNodesTable = new SideTable();
- var eventParentsTable = new SideTable();
- var insertionParentTable = new SideTable();
- var rendererForHostTable = new SideTable();
- var shadowDOMRendererTable = new SideTable();
-
- var reprCounter = 0;
-
- function repr(node) {
- if (!node.displayName)
- node.displayName = node.nodeName + '-' + ++reprCounter;
- return node.displayName;
- }
+ var distributedChildNodesTable = new WeakMap();
+ var eventParentsTable = new WeakMap();
+ var insertionParentTable = new WeakMap();
+ var rendererForHostTable = new WeakMap();
+ var shadowDOMRendererTable = new WeakMap();
function distributeChildToInsertionPoint(child, insertionPoint) {
getDistributedChildNodes(insertionPoint).push(child);
@@ -2569,9 +3909,7 @@
*/
function visit(tree, predicate, visitor) {
// This operates on logical DOM.
- var nodes = getChildNodesSnapshot(tree);
- for (var i = 0; i < nodes.length; i++) {
- var node = nodes[i];
+ for (var node = tree.firstChild; node; node = node.nextSibling) {
if (predicate(node)) {
if (visitor(node) === false)
return;
@@ -2622,14 +3960,14 @@
if (!select)
return true;
- if (node.nodeType !== Node.ELEMENT_NODE)
+ if (!(node instanceof Element))
return false;
// TODO(arv): This does not seem right. Need to check for a simple selector.
if (!selectorMatchRegExp.test(select))
return false;
- if (select[0] === ':' &&!allowedPseudoRegExp.test(select))
+ if (select[0] === ':' && !allowedPseudoRegExp.test(select))
return false;
try {
@@ -2651,18 +3989,15 @@
var renderTimer;
function renderAllPending() {
- renderTimer = null;
- pendingDirtyRenderers.forEach(function(owner) {
- owner.render();
- });
+ for (var i = 0; i < pendingDirtyRenderers.length; i++) {
+ pendingDirtyRenderers[i].render();
+ }
pendingDirtyRenderers = [];
}
- function ShadowRenderer(host) {
- this.host = host;
- this.dirty = false;
- this.invalidateAttributes();
- this.associateNode(host);
+ function handleRequestAnimationFrame() {
+ renderTimer = null;
+ renderAllPending();
}
/**
@@ -2691,10 +4026,92 @@
return getRendererForHost(getHostForShadowRoot(shadowRoot));
}
+ var spliceDiff = new ArraySplice();
+ spliceDiff.equals = function(renderNode, rawNode) {
+ return unwrap(renderNode.node) === rawNode;
+ };
+
+ /**
+ * RenderNode is used as an in memory "render tree". When we render the
+ * composed tree we create a tree of RenderNodes, then we diff this against
+ * the real DOM tree and make minimal changes as needed.
+ */
+ function RenderNode(node) {
+ this.skip = false;
+ this.node = node;
+ this.childNodes = [];
+ }
+
+ RenderNode.prototype = {
+ append: function(node) {
+ var rv = new RenderNode(node);
+ this.childNodes.push(rv);
+ return rv;
+ },
+
+ sync: function(opt_added) {
+ if (this.skip)
+ return;
+
+ var nodeWrapper = this.node;
+ // plain array of RenderNodes
+ var newChildren = this.childNodes;
+ // plain array of real nodes.
+ var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
+ var added = opt_added || new WeakMap();
+
+ var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
+
+ var newIndex = 0, oldIndex = 0;
+ var lastIndex = 0;
+ for (var i = 0; i < splices.length; i++) {
+ var splice = splices[i];
+ for (; lastIndex < splice.index; lastIndex++) {
+ oldIndex++;
+ newChildren[newIndex++].sync(added);
+ }
+
+ var removedCount = splice.removed.length;
+ for (var j = 0; j < removedCount; j++) {
+ var wrapper = wrap(oldChildren[oldIndex++]);
+ if (!added.get(wrapper))
+ remove(wrapper);
+ }
+
+ var addedCount = splice.addedCount;
+ var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
+ for (var j = 0; j < addedCount; j++) {
+ var newChildRenderNode = newChildren[newIndex++];
+ var newChildWrapper = newChildRenderNode.node;
+ insertBefore(nodeWrapper, newChildWrapper, refNode);
+
+ // Keep track of added so that we do not remove the node after it
+ // has been added.
+ added.set(newChildWrapper, true);
+
+ newChildRenderNode.sync(added);
+ }
+
+ lastIndex += addedCount;
+ }
+
+ for (var i = lastIndex; i < newChildren.length; i++) {
+ newChildren[i++].sync(added);
+ }
+ }
+ };
+
+ function ShadowRenderer(host) {
+ this.host = host;
+ this.dirty = false;
+ this.invalidateAttributes();
+ this.associateNode(host);
+ }
+
ShadowRenderer.prototype = {
// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#rendering-shadow-trees
- render: function() {
+ render: function(opt_renderNode) {
if (!this.dirty)
return;
@@ -2702,14 +4119,18 @@
this.treeComposition();
var host = this.host;
- var shadowDOM = host.shadowRoot;
+ var shadowRoot = host.shadowRoot;
- this.removeAllChildNodes(this.host);
+ this.associateNode(host);
+ var topMostRenderer = !renderNode;
+ var renderNode = opt_renderNode || new RenderNode(host);
- var shadowDOMChildNodes = getChildNodesSnapshot(shadowDOM);
- shadowDOMChildNodes.forEach(function(node) {
- this.renderNode(host, shadowDOM, node, false);
- }, this);
+ for (var node = shadowRoot.firstChild; node; node = node.nextSibling) {
+ this.renderNode(shadowRoot, renderNode, node, false);
+ }
+
+ if (topMostRenderer)
+ renderNode.sync();
this.dirty = false;
},
@@ -2720,81 +4141,81 @@
pendingDirtyRenderers.push(this);
if (renderTimer)
return;
- renderTimer = window[request](renderAllPending, 0);
+ renderTimer = window[request](handleRequestAnimationFrame, 0);
}
},
- renderNode: function(visualParent, tree, node, isNested) {
+ renderNode: function(shadowRoot, renderNode, node, isNested) {
if (isShadowHost(node)) {
- this.appendChild(visualParent, node);
+ renderNode = renderNode.append(node);
var renderer = getRendererForHost(node);
renderer.dirty = true; // Need to rerender due to reprojection.
- renderer.render();
+ renderer.render(renderNode);
} else if (isInsertionPoint(node)) {
- this.renderInsertionPoint(visualParent, tree, node, isNested);
+ this.renderInsertionPoint(shadowRoot, renderNode, node, isNested);
} else if (isShadowInsertionPoint(node)) {
- this.renderShadowInsertionPoint(visualParent, tree, node);
+ this.renderShadowInsertionPoint(shadowRoot, renderNode, node);
} else {
- this.renderAsAnyDomTree(visualParent, tree, node, isNested);
+ this.renderAsAnyDomTree(shadowRoot, renderNode, node, isNested);
}
},
- renderAsAnyDomTree: function(visualParent, tree, node, isNested) {
- this.appendChild(visualParent, node);
+ renderAsAnyDomTree: function(shadowRoot, renderNode, node, isNested) {
+ renderNode = renderNode.append(node);
if (isShadowHost(node)) {
- render(node);
+ var renderer = getRendererForHost(node);
+ renderNode.skip = !renderer.dirty;
+ renderer.render(renderNode);
} else {
- var parent = node;
- var logicalChildNodes = getChildNodesSnapshot(parent);
- // We associate the parent of a content/shadow with the renderer
- // because we may need to remove stale childNodes.
- if (shadowDOMRendererTable.get(parent))
- this.removeAllChildNodes(parent);
- logicalChildNodes.forEach(function(node) {
- this.renderNode(parent, tree, node, isNested);
- }, this);
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ this.renderNode(shadowRoot, renderNode, child, isNested);
+ }
}
},
- renderInsertionPoint: function(visualParent, tree, insertionPoint, isNested) {
+ renderInsertionPoint: function(shadowRoot, renderNode, insertionPoint,
+ isNested) {
var distributedChildNodes = getDistributedChildNodes(insertionPoint);
if (distributedChildNodes.length) {
- this.removeAllChildNodes(insertionPoint);
+ this.associateNode(insertionPoint);
- distributedChildNodes.forEach(function(child) {
+ for (var i = 0; i < distributedChildNodes.length; i++) {
+ var child = distributedChildNodes[i];
if (isInsertionPoint(child) && isNested)
- this.renderInsertionPoint(visualParent, tree, child, isNested);
+ this.renderInsertionPoint(shadowRoot, renderNode, child, isNested);
else
- this.renderAsAnyDomTree(visualParent, tree, child, isNested);
- }, this);
+ this.renderAsAnyDomTree(shadowRoot, renderNode, child, isNested);
+ }
} else {
- this.renderFallbackContent(visualParent, insertionPoint);
+ this.renderFallbackContent(shadowRoot, renderNode, insertionPoint);
}
- this.remove(insertionPoint);
+ this.associateNode(insertionPoint.parentNode);
},
- renderShadowInsertionPoint: function(visualParent, tree, shadowInsertionPoint) {
- var nextOlderTree = tree.olderShadowRoot;
+ renderShadowInsertionPoint: function(shadowRoot, renderNode,
+ shadowInsertionPoint) {
+ var nextOlderTree = shadowRoot.olderShadowRoot;
if (nextOlderTree) {
assignToInsertionPoint(nextOlderTree, shadowInsertionPoint);
- this.remove(shadowInsertionPoint);
- var shadowDOMChildNodes = getChildNodesSnapshot(nextOlderTree);
- shadowDOMChildNodes.forEach(function(node) {
- this.renderNode(visualParent, nextOlderTree, node, true);
- }, this);
+ this.associateNode(shadowInsertionPoint.parentNode);
+ for (var node = nextOlderTree.firstChild;
+ node;
+ node = node.nextSibling) {
+ this.renderNode(nextOlderTree, renderNode, node, true);
+ }
} else {
- this.renderFallbackContent(visualParent, shadowInsertionPoint);
+ this.renderFallbackContent(shadowRoot, renderNode,
+ shadowInsertionPoint);
}
},
- renderFallbackContent: function (visualParent, fallbackHost) {
- var logicalChildNodes = getChildNodesSnapshot(fallbackHost);
+ renderFallbackContent: function(shadowRoot, renderNode, fallbackHost) {
this.associateNode(fallbackHost);
- this.remove(fallbackHost);
- logicalChildNodes.forEach(function(node) {
- this.appendChild(visualParent, node);
- }, this);
+ this.associateNode(fallbackHost.parentNode);
+ for (var node = fallbackHost.firstChild; node; node = node.nextSibling) {
+ this.renderAsAnyDomTree(shadowRoot, renderNode, node, false);
+ }
},
/**
@@ -2837,7 +4258,6 @@
// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#dfn-distribution-algorithm
distribute: function(tree, pool) {
- var anyRemoved = false;
var self = this;
visit(tree, isActiveInsertionPoint,
@@ -2853,17 +4273,9 @@
if (matchesCriteria(node, insertionPoint)) { // 1.2.2
distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2.1
pool[i] = undefined; // 1.2.2.2
- anyRemoved = true;
}
}
});
-
- if (!anyRemoved)
- return pool;
-
- return pool.filter(function(item) {
- return item !== undefined;
- });
},
// http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#dfn-tree-composition
@@ -2871,8 +4283,10 @@
var shadowHost = this.host;
var tree = shadowHost.shadowRoot; // 1.
var pool = []; // 2.
- var shadowHostChildNodes = getChildNodesSnapshot(shadowHost);
- shadowHostChildNodes.forEach(function(child) { // 3.
+
+ for (var child = shadowHost.firstChild;
+ child;
+ child = child.nextSibling) { // 3.
if (isInsertionPoint(child)) { // 3.2.
var reprojected = getDistributedChildNodes(child); // 3.2.1.
// if reprojected is undef... reset it?
@@ -2882,7 +4296,7 @@
} else {
pool.push(child); // 3.3.
}
- });
+ }
var shadowInsertionPoint, point;
while (tree) { // 4.
@@ -2894,7 +4308,7 @@
});
point = shadowInsertionPoint;
- pool = this.distribute(tree, pool); // 4.2.
+ this.distribute(tree, pool); // 4.2.
if (point) { // 4.3.
var nextOlderTree = tree.olderShadowRoot; // 4.3.1.
if (!nextOlderTree) {
@@ -2910,23 +4324,6 @@
}
},
- appendChild: function(parent, child) {
- // this.associateNode(child);
- this.associateNode(parent);
- appendChild(parent, child);
- },
-
- remove: function(node) {
- // this.associateNode(node);
- this.associateNode(node.parentNode);
- remove(node);
- },
-
- removeAllChildNodes: function(parent) {
- this.associateNode(parent);
- removeAllChildNodes(parent);
- },
-
associateNode: function(node) {
shadowDOMRendererTable.set(node, this);
}
@@ -2934,21 +4331,21 @@
function isInsertionPoint(node) {
// Should this include <shadow>?
- return node.localName === 'content';
+ return node instanceof HTMLContentElement;
}
function isActiveInsertionPoint(node) {
// <content> inside another <content> or <shadow> is considered inactive.
- return node.localName === 'content';
+ return node instanceof HTMLContentElement;
}
function isShadowInsertionPoint(node) {
- return node.localName === 'shadow';
+ return node instanceof HTMLShadowElement;
}
function isActiveShadowInsertionPoint(node) {
// <shadow> inside another <content> or <shadow> is considered inactive.
- return node.localName === 'shadow';
+ return node instanceof HTMLShadowElement;
}
function isShadowHost(shadowHost) {
@@ -3025,9 +4422,8 @@
// Exposed for testing
scope.visual = {
- removeAllChildNodes: removeAllChildNodes,
- appendChild: appendChild,
- removeChild: removeChild
+ insertBefore: insertBefore,
+ remove: remove,
};
})(this.ShadowDOMPolyfill);
@@ -3109,7 +4505,7 @@
var wrapEventTargetMethods = scope.wrapEventTargetMethods;
var wrapNodeList = scope.wrapNodeList;
- var implementationTable = new SideTable();
+ var implementationTable = new WeakMap();
function Document(node) {
Node.call(this, node);
@@ -3384,6 +4780,7 @@
var unwrap = scope.unwrap;
var unwrapIfNeeded = scope.unwrapIfNeeded;
var wrap = scope.wrap;
+ var renderAllPending = scope.renderAllPending;
var OriginalWindow = window.Window;
@@ -3394,6 +4791,7 @@
var originalGetComputedStyle = window.getComputedStyle;
OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
+ renderAllPending();
return originalGetComputedStyle.call(this || window, unwrapIfNeeded(el),
pseudo);
};
@@ -3745,11 +5143,9 @@
// Fix up class names for Firefox.
// For some of them (like HTMLFormElement and HTMLInputElement),
// the "constructor" property of the unwrapped nodes points at the
- // wrapper.
- // Note: it is safe to check for the GeneratedWrapper string because
- // we know it is some kind of Shadow DOM wrapper object.
- var ctor = obj.constructor;
- if (ctor && ctor.name == 'GeneratedWrapper') {
+ // same constructor as the wrapper.
+ var ctor = obj.constructor
+ if (ctor === unwrapped.constructor) {
var name = ctor._ShadowDOMPolyfill$cacheTag_;
if (!name) {
name = Object.prototype.toString.call(unwrapped);
diff --git a/pkg/shadow_dom/lib/shadow_dom.min.js b/pkg/shadow_dom/lib/shadow_dom.min.js
index 7d3ff1b..16bf09b 100644
--- a/pkg/shadow_dom/lib/shadow_dom.min.js
+++ b/pkg/shadow_dom/lib/shadow_dom.min.js
@@ -1,2 +1,2 @@
-if(!HTMLElement.prototype.createShadowRoot&&!HTMLElement.prototype.webkitCreateShadowRoot||window.__forceShadowDomPolyfill){!function(){Element.prototype.webkitCreateShadowRoot&&(Element.prototype.webkitCreateShadowRoot=function(){return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot()})}();var SideTable;"undefined"!=typeof WeakMap&&navigator.userAgent.indexOf("Firefox/")<0?SideTable=WeakMap:!function(){var a=Object.defineProperty,b=Date.now()%1e9;SideTable=function(){this.name="__st"+(1e9*Math.random()>>>0)+(b++ +"__")},SideTable.prototype={set:function(b,c){var d=b[this.name];d&&d[0]===b?d[1]=c:a(b,this.name,{value:[b,c],writable:!0})},get:function(a){var b;return(b=a[this.name])&&b[0]===a?b[1]:void 0},"delete":function(a){this.set(a,void 0)}}}();var ShadowDOMPolyfill={};!function(a){"use strict";function b(a){if(!a)throw new Error("Assertion failed")}function c(a,b){return Object.getOwnPropertyNames(b).forEach(function(c){Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))}),a}function d(a,b){return Object.getOwnPropertyNames(b).forEach(function(c){switch(c){case"arguments":case"caller":case"length":case"name":case"prototype":case"toString":return}Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))}),a}function e(a,b){for(var c=0;c<b.length;c++)if(b[c]in a)return b[c]}function f(a){var b=a.__proto__||Object.getPrototypeOf(a),c=A.get(b);if(c)return c;var d=f(b),e=o(d);return l(b,e,a),e}function g(a,b){j(a,b,!0)}function h(a,b){j(b,a,!1)}function i(a){return/^on[a-z]+$/.test(a)}function j(b,c,d){Object.getOwnPropertyNames(b).forEach(function(e){if(!(e in c)){D&&b.__lookupGetter__(e);var f;try{f=Object.getOwnPropertyDescriptor(b,e)}catch(g){f=E}var h,j;if(d&&"function"==typeof f.value)return c[e]=function(){return this.impl[e].apply(this.impl,arguments)},void 0;var k=i(e);h=k?a.getEventHandlerGetter(e):function(){return this.impl[e]},(f.writable||f.set)&&(j=k?a.getEventHandlerSetter(e):function(a){this.impl[e]=a}),Object.defineProperty(c,e,{get:h,set:j,configurable:f.configurable,enumerable:f.enumerable})}})}function k(a,b,c){var e=a.prototype;l(e,b,c),d(b,a)}function l(a,c,d){var e=c.prototype;b(void 0===A.get(a)),A.set(a,c),B.set(e,a),g(a,e),d&&h(e,d)}function m(a,b){return A.get(b.prototype)===a}function n(a){var b=Object.getPrototypeOf(a),c=f(b),d=o(c);return l(b,d,a),d}function o(a){function b(b){a.call(this,b)}return b.prototype=Object.create(a.prototype),b.prototype.constructor=b,b}function p(a){return a instanceof C.EventTarget||a instanceof C.Event||a instanceof C.Range||a instanceof C.DOMImplementation}function q(a){return a instanceof H||a instanceof G||a instanceof I||a instanceof J||a instanceof F}function r(a){if(null===a)return null;b(q(a));var c=z.get(a);return c||z.set(a,c=new(f(a))(a)),c}function s(a){return null===a?null:(b(p(a)),a.impl)}function t(a){return a&&p(a)?s(a):a}function u(a){return a&&!p(a)?r(a):a}function v(a,c){null!==c&&(b(q(a)),b(void 0===c||p(c)),z.set(a,c))}function w(a,b,c){Object.defineProperty(a.prototype,b,{get:c,configurable:!0,enumerable:!0})}function x(a,b){w(a,b,function(){return r(this.impl[b])})}function y(a,b){a.forEach(function(a){b.forEach(function(b){a.prototype[b]=function(){var a=u(this);return a[b].apply(a,arguments)}})})}var z=new SideTable,A=new SideTable,B=new SideTable,C=Object.create(null);Object.getOwnPropertyNames(window);var D=/Firefox/.test(navigator.userAgent),E={get:function(){},set:function(){},configurable:!0,enumerable:!0},F=DOMImplementation,G=Event,H=Node,I=Window,J=Range;a.assert=b,a.constructorTable=A,a.defineGetter=w,a.defineWrapGetter=x,a.forwardMethodsToWrapper=y,a.isWrapperFor=m,a.mixin=c,a.nativePrototypeTable=B,a.oneOf=e,a.registerObject=n,a.registerWrapper=k,a.rewrap=v,a.unwrap=s,a.unwrapIfNeeded=t,a.wrap=r,a.wrapIfNeeded=u,a.wrappers=C}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){return a instanceof N.ShadowRoot}function c(a){var b=a.localName;return"content"===b||"shadow"===b}function d(a){return!!a.shadowRoot}function e(a){var b;return a.parentNode||(b=a.defaultView)&&M(b)||null}function f(f,g,h){if(h.length)return h.shift();if(b(f))return j(f)||a.getHostForShadowRoot(f);var i=a.eventParentsTable.get(f);if(i){for(var k=1;k<i.length;k++)h[k-1]=i[k];return i[0]}if(g&&c(f)){var l=f.parentNode;if(l&&d(l))for(var m=a.getShadowTrees(l),n=j(g),k=0;k<m.length;k++)if(m[k].contains(n))return n}return e(f)}function g(a){for(var d=[],e=a,g=[],i=[];e;){var j=null;if(c(e)){j=h(d);var k=d[d.length-1]||e;d.push(k)}else d.length||d.push(e);var l=d[d.length-1];g.push({target:l,currentTarget:e}),b(e)&&d.pop(),e=f(e,j,i)}return g}function h(a){for(var b=a.length-1;b>=0;b--)if(!c(a[b]))return a[b];return null}function i(d,e){for(var g=[];d;){for(var i=[],j=e,l=void 0;j;){var n=null;if(i.length){if(c(j)&&(n=h(i),k(l))){var o=i[i.length-1];i.push(o)}}else i.push(j);if(m(j,d))return i[i.length-1];b(j)&&i.pop(),l=j,j=f(j,n,g)}d=b(d)?a.getHostForShadowRoot(d):d.parentNode}}function j(b){return a.insertionParentTable.get(b)}function k(a){return j(a)}function l(a){for(var b;b=a.parentNode;)a=b;return a}function m(a,b){return l(a)===l(b)}function n(b,c){if(b===c)return!0;if(b instanceof N.ShadowRoot){var d=a.getHostForShadowRoot(b);return n(l(d),c)}return!1}function o(a){switch(a){case"DOMAttrModified":case"DOMAttributeNameChanged":case"DOMCharacterDataModified":case"DOMElementNameChanged":case"DOMNodeInserted":case"DOMNodeInsertedIntoDocument":case"DOMNodeRemoved":case"DOMNodeRemovedFromDocument":case"DOMSubtreeModified":return!0}return!1}function p(b){if(!P.get(b)){P.set(b,!0),o(b.type)||a.renderAllPending();var c=M(b.target),d=M(b);return q(d,c)}}function q(a,b){var c=g(b);return"load"===a.type&&2===c.length&&c[0].target instanceof N.Document&&c.shift(),X.set(a,c),r(a,c)&&s(a,c)&&t(a,c),T.set(a,w.NONE),R.set(a,null),a.defaultPrevented}function r(a,b){for(var c,d=b.length-1;d>0;d--){var e=b[d].target,f=b[d].currentTarget;if(e!==f&&(c=w.CAPTURING_PHASE,!u(b[d],a,c)))return!1}return!0}function s(a,b){var c=w.AT_TARGET;return u(b[0],a,c)}function t(a,b){for(var c,d=a.bubbles,e=1;e<b.length;e++){var f=b[e].target,g=b[e].currentTarget;if(f===g)c=w.AT_TARGET;else{if(!d||V.get(a))continue;c=w.BUBBLING_PHASE}if(!u(b[e],a,c))return}}function u(a,b,c){var d=a.target,e=a.currentTarget,f=O.get(e);if(!f)return!0;if("relatedTarget"in b){var g=L(b),h=M(g.relatedTarget),j=i(e,h);if(j===d)return!0;S.set(b,j)}T.set(b,c);var k=b.type,l=!1;Q.set(b,d),R.set(b,e);for(var m=0;m<f.length;m++){var n=f[m];if(n.removed)l=!0;else if(!(n.type!==k||!n.capture&&c===w.CAPTURING_PHASE||n.capture&&c===w.BUBBLING_PHASE))try{if("function"==typeof n.handler?n.handler.call(e,b):n.handler.handleEvent(b),V.get(b))return!1}catch(o){window.onerror?window.onerror(o.message):console.error(o)}}if(l){var p=f.slice();f.length=0;for(var m=0;m<p.length;m++)p[m].removed||f.push(p[m])}return!U.get(b)}function v(a,b,c){this.type=a,this.handler=b,this.capture=Boolean(c)}function w(a,b){return a instanceof Y?(this.impl=a,void 0):M(A(Y,"Event",a,b))}function x(a){return a&&a.relatedTarget?Object.create(a,{relatedTarget:{value:L(a.relatedTarget)}}):a}function y(a,b,c){var d=window[a],e=function(b,c){return b instanceof d?(this.impl=b,void 0):M(A(d,a,b,c))};return e.prototype=Object.create(b.prototype),c&&J(e.prototype,c),d&&(d.prototype["init"+a]?K(d,e,document.createEvent(a)):K(d,e,new d("temp"))),e}function z(a,b){return function(){arguments[b]=L(arguments[b]);var c=L(this);c[a].apply(c,arguments)}}function A(a,b,c,d){if(gb)return new a(c,x(d));var e=L(document.createEvent(b)),f=fb[b],g=[c];return Object.keys(f).forEach(function(a){var b=null!=d&&a in d?d[a]:f[a];"relatedTarget"===a&&(b=L(b)),g.push(b)}),e["init"+b].apply(e,g),e}function B(a){return"function"==typeof a?!0:a&&a.handleEvent}function C(a){this.impl=a}function D(b){return b instanceof N.ShadowRoot&&(b=a.getHostForShadowRoot(b)),L(b)}function E(a){I(a,jb)}function F(b,c,d,e){a.renderAllPending();for(var f=M(kb.call(c.impl,d,e)),h=g(f,this),i=0;i<h.length;i++){var j=h[i];if(j.currentTarget===b)return j.target}return null}function G(a){return function(){var b=W.get(this);return b&&b[a]&&b[a].value||null}}function H(a){var b=a.slice(2);return function(c){var d=W.get(this);d||(d=Object.create(null),W.set(this,d));var e=d[a];if(e&&this.removeEventListener(b,e.wrapped,!1),"function"==typeof c){var f=function(b){var d=c.call(this,b);d===!1?b.preventDefault():"onbeforeunload"===a&&"string"==typeof d&&(b.returnValue=d)};this.addEventListener(b,f,!1),d[a]={value:c,wrapped:f}}}}var I=a.forwardMethodsToWrapper,J=a.mixin,K=a.registerWrapper,L=a.unwrap,M=a.wrap,N=a.wrappers;new SideTable;var O=new SideTable,P=new SideTable,Q=new SideTable,R=new SideTable,S=new SideTable,T=new SideTable,U=new SideTable,V=new SideTable,W=new SideTable,X=new SideTable;v.prototype={equals:function(a){return this.handler===a.handler&&this.type===a.type&&this.capture===a.capture},get removed(){return null===this.handler},remove:function(){this.handler=null}};var Y=window.Event;w.prototype={get target(){return Q.get(this)},get currentTarget(){return R.get(this)},get eventPhase(){return T.get(this)},get path(){var a=new N.NodeList,b=X.get(this);if(b){for(var c=0,d=b.length-1,e=l(R.get(this)),f=0;d>=f;f++){var g=b[f].currentTarget,h=l(g);n(e,h)&&(f!==d||g instanceof N.Node)&&(a[c++]=g)}a.length=c}return a},stopPropagation:function(){U.set(this,!0)},stopImmediatePropagation:function(){U.set(this,!0),V.set(this,!0)}},K(Y,w,document.createEvent("Event"));var Z=y("UIEvent",w),$=y("CustomEvent",w),_={get relatedTarget(){return S.get(this)||M(L(this).relatedTarget)}},ab=J({initMouseEvent:z("initMouseEvent",14)},_),bb=J({initFocusEvent:z("initFocusEvent",5)},_),cb=y("MouseEvent",Z,ab),db=y("FocusEvent",Z,bb),eb=y("MutationEvent",w,{initMutationEvent:z("initMutationEvent",3),get relatedNode(){return M(this.impl.relatedNode)}}),fb=Object.create(null),gb=function(){try{new window.MouseEvent("click")}catch(a){return!1}return!0}();if(!gb){var hb=function(a,b,c){if(c){var d=fb[c];b=J(J({},d),b)}fb[a]=b};hb("Event",{bubbles:!1,cancelable:!1}),hb("CustomEvent",{detail:null},"Event"),hb("UIEvent",{view:null,detail:0},"Event"),hb("MouseEvent",{screenX:0,screenY:0,clientX:0,clientY:0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:null},"UIEvent"),hb("FocusEvent",{relatedTarget:null},"UIEvent")}var ib=window.EventTarget,jb=["addEventListener","removeEventListener","dispatchEvent"];[Node,Window].forEach(function(a){var b=a.prototype;jb.forEach(function(a){Object.defineProperty(b,a+"_",{value:b[a]})})}),C.prototype={addEventListener:function(a,b,c){if(B(b)){var d=new v(a,b,c),e=O.get(this);if(e){for(var f=0;f<e.length;f++)if(d.equals(e[f]))return}else e=[],O.set(this,e);e.push(d);var g=D(this);g.addEventListener_(a,p,!0)}},removeEventListener:function(a,b,c){c=Boolean(c);var d=O.get(this);if(d){for(var e=0,f=!1,g=0;g<d.length;g++)d[g].type===a&&d[g].capture===c&&(e++,d[g].handler===b&&(f=!0,d[g].remove()));if(f&&1===e){var h=D(this);h.removeEventListener_(a,p,!0)}}},dispatchEvent:function(b){a.renderAllPending();var c=D(this);return c.dispatchEvent_(L(b))}},ib&&K(ib,C);var kb=document.elementFromPoint;a.adjustRelatedTarget=i,a.elementFromPoint=F,a.getEventHandlerGetter=G,a.getEventHandlerSetter=H,a.wrapEventTargetMethods=E,a.wrappers.CustomEvent=$,a.wrappers.Event=w,a.wrappers.EventTarget=C,a.wrappers.FocusEvent=db,a.wrappers.MouseEvent=cb,a.wrappers.MutationEvent=eb,a.wrappers.UIEvent=Z}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a,b){Object.defineProperty(a,b,{enumerable:!1})}function c(){this.length=0,b(this,"length")}function d(a){if(null==a)return a;for(var b=new c,d=0,e=a.length;e>d;d++)b[d]=f(a[d]);return b.length=e,b}function e(a,b){a.prototype[b]=function(){return d(this.impl[b].apply(this.impl,arguments))}}var f=a.wrap;c.prototype={item:function(a){return this[a]}},b(c.prototype,"item"),a.wrappers.NodeList=c,a.addWrapNodeListMethod=e,a.wrapNodeList=d}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){l(a instanceof i)}function c(a,b,c,d){if(a.nodeType!==i.DOCUMENT_FRAGMENT_NODE)return a.parentNode&&a.parentNode.removeChild(a),a.parentNode_=b,a.previousSibling_=c,a.nextSibling_=d,c&&(c.nextSibling_=a),d&&(d.previousSibling_=a),[a];for(var e,f=[];e=a.firstChild;)a.removeChild(e),f.push(e),e.parentNode_=b;for(var g=0;g<f.length;g++)f[g].previousSibling_=f[g-1]||c,f[g].nextSibling_=f[g+1]||d;return c&&(c.nextSibling_=f[0]),d&&(d.previousSibling_=f[f.length-1]),f}function d(a,b){var c=a.nodeType===i.DOCUMENT_NODE?a:a.ownerDocument;c!==b.ownerDocument&&c.adoptNode(b)}function e(b,c){if(c.length){var d=b.ownerDocument;if(d!==c[0].ownerDocument)for(var e=0;e<c.length;e++)a.adoptNodeNoRemove(c[e],d)}}function f(a,b){e(a,b);var c=b.length;if(1===c)return o(b[0]);for(var d=o(a.ownerDocument.createDocumentFragment()),f=0;c>f;f++)d.appendChild(o(b[f]));return d}function g(a){if(a.invalidateShadowRenderer()){for(var b=a.firstChild;b;){l(b.parentNode===a);var c=b.nextSibling,d=o(b),e=d.parentNode;e&&v.call(e,d),b.previousSibling_=b.nextSibling_=b.parentNode_=null,b=c}a.firstChild_=a.lastChild_=null}else for(var c,f=o(a),g=f.firstChild;g;)c=g.nextSibling,v.call(f,g),g=c}function h(a){var b=a.parentNode;return b&&b.invalidateShadowRenderer()}function i(a){l(a instanceof r),j.call(this,a),this.parentNode_=void 0,this.firstChild_=void 0,this.lastChild_=void 0,this.nextSibling_=void 0,this.previousSibling_=void 0}var j=a.wrappers.EventTarget,k=a.wrappers.NodeList;a.defineWrapGetter;var l=a.assert,m=a.mixin,n=a.registerWrapper,o=a.unwrap,p=a.wrap,q=a.wrapIfNeeded,r=window.Node,s=r.prototype.appendChild,t=r.prototype.insertBefore,u=r.prototype.replaceChild,v=r.prototype.removeChild,w=r.prototype.compareDocumentPosition;i.prototype=Object.create(j.prototype),m(i.prototype,{appendChild:function(a){if(b(a),this.invalidateShadowRenderer()||h(a)){var e=this.lastChild,g=null,i=c(a,this,e,g);this.lastChild_=i[i.length-1],e||(this.firstChild_=i[0]),s.call(this.impl,f(this,i))}else d(this,a),s.call(this.impl,o(a));return a.nodeWasAdded_(),a},insertBefore:function(a,g){if(!g)return this.appendChild(a);if(b(a),b(g),l(g.parentNode===this),this.invalidateShadowRenderer()||h(a)){var i=g.previousSibling,j=g,k=c(a,this,i,j);this.firstChild===g&&(this.firstChild_=k[0]);var m=o(g),n=m.parentNode;n?t.call(n,f(this,k),m):e(this,k)}else d(this,a),t.call(this.impl,o(a),o(g));return a.nodeWasAdded_(),a},removeChild:function(a){if(b(a),a.parentNode!==this)throw new Error("NotFoundError");var c=o(a);if(this.invalidateShadowRenderer()){var d=this.firstChild,e=this.lastChild,f=a.nextSibling,g=a.previousSibling,h=c.parentNode;h&&v.call(h,c),d===a&&(this.firstChild_=f),e===a&&(this.lastChild_=g),g&&(g.nextSibling_=f),f&&(f.previousSibling_=g),a.previousSibling_=a.nextSibling_=a.parentNode_=void 0}else v.call(this.impl,c);return a},replaceChild:function(a,e){if(b(a),b(e),e.parentNode!==this)throw new Error("NotFoundError");var g=o(e);if(this.invalidateShadowRenderer()||h(a)){var i=e.previousSibling,j=e.nextSibling;j===a&&(j=a.nextSibling);var k=c(a,this,i,j);this.firstChild===e&&(this.firstChild_=k[0]),this.lastChild===e&&(this.lastChild_=k[k.length-1]),e.previousSibling_=e.nextSibling_=e.parentNode_=void 0,g.parentNode&&u.call(g.parentNode,f(this,k),g)}else d(this,a),u.call(this.impl,o(a),g);return a.nodeWasAdded_(),e},nodeWasAdded_:function(){},hasChildNodes:function(){return null===this.firstChild},get parentNode(){return void 0!==this.parentNode_?this.parentNode_:p(this.impl.parentNode)},get firstChild(){return void 0!==this.firstChild_?this.firstChild_:p(this.impl.firstChild)},get lastChild(){return void 0!==this.lastChild_?this.lastChild_:p(this.impl.lastChild)},get nextSibling(){return void 0!==this.nextSibling_?this.nextSibling_:p(this.impl.nextSibling)},get previousSibling(){return void 0!==this.previousSibling_?this.previousSibling_:p(this.impl.previousSibling)},get parentElement(){for(var a=this.parentNode;a&&a.nodeType!==i.ELEMENT_NODE;)a=a.parentNode;return a},get textContent(){for(var a="",b=this.firstChild;b;b=b.nextSibling)a+=b.textContent;return a},set textContent(a){if(this.invalidateShadowRenderer()){if(g(this),""!==a){var b=this.impl.ownerDocument.createTextNode(a);this.appendChild(b)}}else this.impl.textContent=a},get childNodes(){for(var a=new k,b=0,c=this.firstChild;c;c=c.nextSibling)a[b++]=c;return a.length=b,a},cloneNode:function(a){if(!this.invalidateShadowRenderer())return p(this.impl.cloneNode(a));var b=p(this.impl.cloneNode(!1));if(a)for(var c=this.firstChild;c;c=c.nextSibling)b.appendChild(c.cloneNode(!0));return b},contains:function(a){if(!a)return!1;if(a=q(a),a===this)return!0;var b=a.parentNode;return b?this.contains(b):!1},compareDocumentPosition:function(a){return w.call(this.impl,o(a))},get ownerDocument(){return a.renderAllPending(),p(this.impl.ownerDocument)}}),n(r,i,document.createDocumentFragment()),delete i.prototype.querySelector,delete i.prototype.querySelectorAll,i.prototype=m(Object.create(j.prototype),i.prototype),a.wrappers.Node=i}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a,c){for(var d,e=a.firstElementChild;e;){if(e.matches(c))return e;if(d=b(e,c))return d;e=e.nextElementSibling}return null}function c(a,b,d){for(var e=a.firstElementChild;e;)e.matches(b)&&(d[d.length++]=e),c(e,b,d),e=e.nextElementSibling;return d}var d={querySelector:function(a){return b(this,a)},querySelectorAll:function(a){return c(this,a,new NodeList)}},e={getElementsByTagName:function(a){return this.querySelectorAll(a)},getElementsByClassName:function(a){return this.querySelectorAll("."+a)},getElementsByTagNameNS:function(a,b){if("*"===a)return this.getElementsByTagName(b);for(var c=new NodeList,d=this.getElementsByTagName(b),e=0,f=0;e<d.length;e++)d[e].namespaceURI===a&&(c[f++]=d[e]);return c.length=f,c}};a.GetElementsByInterface=e,a.SelectorsInterface=d}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){for(;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}function c(a){for(;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}var d=a.wrappers.NodeList,e={get firstElementChild(){return b(this.firstChild)},get lastElementChild(){return c(this.lastChild)},get childElementCount(){for(var a=0,b=this.firstElementChild;b;b=b.nextElementSibling)a++;return a},get children(){for(var a=new d,b=0,c=this.firstElementChild;c;c=c.nextElementSibling)a[b++]=c;return a.length=b,a}},f={get nextElementSibling(){return b(this.nextSibling)},get previousElementSibling(){return c(this.previousSibling)}};a.ChildNodeInterface=f,a.ParentNodeInterface=e}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){d.call(this,a)}var c=a.ChildNodeInterface,d=a.wrappers.Node,e=a.mixin,f=a.registerWrapper,g=window.CharacterData;b.prototype=Object.create(d.prototype),e(b.prototype,{get textContent(){return this.data},set textContent(a){this.data=a}}),e(b.prototype,c),f(g,b,document.createTextNode("")),a.wrappers.CharacterData=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(b,c){var d=b.parentNode;if(d&&d.shadowRoot){var e=a.getRendererForHost(d);e.dependsOnAttribute(c)&&e.invalidate()}}function c(a){g.call(this,a)}function d(a,c,d){var e=d||c;Object.defineProperty(a,c,{get:function(){return this.impl[c]},set:function(a){this.impl[c]=a,b(this,e)},configurable:!0,enumerable:!0})}var e=a.ChildNodeInterface,f=a.GetElementsByInterface,g=a.wrappers.Node,h=a.ParentNodeInterface,i=a.SelectorsInterface;a.addWrapNodeListMethod;var j=a.mixin,k=a.oneOf,l=a.registerWrapper,m=a.wrappers,n=new SideTable,o=window.Element,p=k(o.prototype,["matches","mozMatchesSelector","msMatchesSelector","webkitMatchesSelector"]),q=o.prototype[p];c.prototype=Object.create(g.prototype),j(c.prototype,{createShadowRoot:function(){var b=new m.ShadowRoot(this);n.set(this,b);var c=a.getRendererForHost(this);return c.invalidate(),b},get shadowRoot(){return n.get(this)||null},setAttribute:function(a,c){this.impl.setAttribute(a,c),b(this,a)},removeAttribute:function(a){this.impl.removeAttribute(a),b(this,a)},matches:function(a){return q.call(this.impl,a)}}),c.prototype[p]=function(a){return this.matches(a)},o.prototype.webkitCreateShadowRoot&&(c.prototype.webkitCreateShadowRoot=c.prototype.createShadowRoot),d(c.prototype,"id"),d(c.prototype,"className","class"),j(c.prototype,e),j(c.prototype,f),j(c.prototype,h),j(c.prototype,i),l(o,c),a.matchesName=p,a.wrappers.Element=c}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){switch(a){case"&":return"&";case"<":return"<";case'"':return"""}}function c(a){return a.replace(p,b)}function d(a){switch(a.nodeType){case Node.ELEMENT_NODE:for(var b,d=a.tagName.toLowerCase(),f="<"+d,g=a.attributes,h=0;b=g[h];h++)f+=" "+b.name+'="'+c(b.value)+'"';return f+=">",q[d]?f:f+e(a)+"</"+d+">";case Node.TEXT_NODE:return c(a.nodeValue);case Node.COMMENT_NODE:return"<!--"+c(a.nodeValue)+"-->";default:throw console.error(a),new Error("not implemented")}}function e(a){for(var b="",c=a.firstChild;c;c=c.nextSibling)b+=d(c);return b}function f(a,b,c){var d=c||"div";a.textContent="";var e=n(a.ownerDocument.createElement(d));e.innerHTML=b;for(var f;f=e.firstChild;)a.appendChild(o(f))}function g(a){j.call(this,a)}function h(b){k(g,b,function(){return a.renderAllPending(),this.impl[b]})}function i(b){Object.defineProperty(g.prototype,b,{value:function(){return a.renderAllPending(),this.impl[b].apply(this.impl,arguments)},configurable:!0,enumerable:!0})}var j=a.wrappers.Element,k=a.defineGetter,l=a.mixin,m=a.registerWrapper,n=a.unwrap,o=a.wrap,p=/&|<|"/g,q={area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},r=window.HTMLElement;g.prototype=Object.create(j.prototype),l(g.prototype,{get innerHTML(){return e(this)},set innerHTML(a){this.invalidateShadowRenderer()?f(this,a,this.tagName):this.impl.innerHTML=a},get outerHTML(){return d(this)},set outerHTML(a){var b=this.parentNode;b&&(b.invalidateShadowRenderer(),this.impl.outerHTML=a)}}),["clientHeight","clientLeft","clientTop","clientWidth","offsetHeight","offsetLeft","offsetTop","offsetWidth","scrollHeight","scrollLeft","scrollTop","scrollWidth"].forEach(h),["getBoundingClientRect","getClientRects","scrollIntoView"].forEach(i),m(r,g,document.createElement("b")),a.wrappers.HTMLElement=g,a.getInnerHTML=e,a.setInnerHTML=f}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){c.call(this,a)}var c=a.wrappers.HTMLElement,d=a.mixin,e=a.registerWrapper,f=window.HTMLContentElement;b.prototype=Object.create(c.prototype),d(b.prototype,{get select(){return this.getAttribute("select")},set select(a){this.setAttribute("select",a)},setAttribute:function(a,b){c.prototype.setAttribute.call(this,a,b),"select"===String(a).toLowerCase()&&this.invalidateShadowRenderer(!0)}}),f&&e(f,b),a.wrappers.HTMLContentElement=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){c.call(this,a)}var c=a.wrappers.HTMLElement,d=a.mixin,e=a.registerWrapper,f=window.HTMLShadowElement;b.prototype=Object.create(c.prototype),d(b.prototype,{}),f&&e(f,b),a.wrappers.HTMLShadowElement=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){if(!a.defaultView)return a;var b=m.get(a);if(!b){for(b=a.implementation.createHTMLDocument("");b.lastChild;)b.removeChild(b.lastChild);m.set(a,b)}return b}function c(a){for(var c,d=b(a.ownerDocument),e=j(d.createDocumentFragment());c=a.firstChild;)e.appendChild(c);return e}function d(a){if(e.call(this,a),!n){var b=c(a);l.set(this,k(b))}}var e=a.wrappers.HTMLElement,f=a.getInnerHTML,g=a.mixin,h=a.registerWrapper,i=a.setInnerHTML,j=a.unwrap,k=a.wrap,l=new SideTable,m=new SideTable,n=window.HTMLTemplateElement;d.prototype=Object.create(e.prototype),g(d.prototype,{get content(){return n?k(this.impl.content):l.get(this)},get innerHTML(){return f(this.content)},set innerHTML(a){i(this.content,a)}}),n&&h(n,d),a.wrappers.HTMLTemplateElement=d}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){switch(a.localName){case"content":return new c(a);case"shadow":return new e(a);case"template":return new f(a)}d.call(this,a)}var c=a.wrappers.HTMLContentElement,d=a.wrappers.HTMLElement,e=a.wrappers.HTMLShadowElement,f=a.wrappers.HTMLTemplateElement;a.mixin;var g=a.registerWrapper,h=window.HTMLUnknownElement;b.prototype=Object.create(d.prototype),g(h,b),a.wrappers.HTMLUnknownElement=b}(this.ShadowDOMPolyfill),function(a){"use strict";var b=a.GetElementsByInterface,c=a.ParentNodeInterface,d=a.SelectorsInterface,e=a.mixin,f=a.registerObject,g=f(document.createDocumentFragment());e(g.prototype,c),e(g.prototype,d),e(g.prototype,b);var h=f(document.createTextNode("")),i=f(document.createComment(""));a.wrappers.Comment=i,a.wrappers.DocumentFragment=g,a.wrappers.Text=h}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){var b=i(a.impl.ownerDocument.createDocumentFragment());c.call(this,b),g(b,this);var d=a.shadowRoot;k.set(this,d),j.set(this,a)}var c=a.wrappers.DocumentFragment,d=a.elementFromPoint,e=a.getInnerHTML,f=a.mixin,g=a.rewrap,h=a.setInnerHTML,i=a.unwrap,j=new SideTable,k=new SideTable;b.prototype=Object.create(c.prototype),f(b.prototype,{get innerHTML(){return e(this)},set innerHTML(a){h(this,a),this.invalidateShadowRenderer()},get olderShadowRoot(){return k.get(this)||null},invalidateShadowRenderer:function(){return j.get(this).invalidateShadowRenderer()},elementFromPoint:function(a,b){return d(this,this.ownerDocument,a,b)},getElementById:function(a){return this.querySelector("#"+a)}}),a.wrappers.ShadowRoot=b,a.getHostForShadowRoot=function(a){return j.get(a)}}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){a.previousSibling_=a.previousSibling,a.nextSibling_=a.nextSibling,a.parentNode_=a.parentNode}function c(a){a.firstChild_=a.firstChild,a.lastChild_=a.lastChild}function d(a){F(a instanceof D);for(var d=a.firstChild;d;d=d.nextSibling)b(d);c(a)}function e(a){var b=J(a);d(a),b.firstChild&&(b.textContent="")}function f(a,c){var e=J(a),f=J(c);f.nodeType===D.DOCUMENT_FRAGMENT_NODE?d(c):(h(c),b(c)),a.lastChild_=a.lastChild,a.lastChild===a.firstChild&&(a.firstChild_=a.firstChild);var g=K(e.lastChild);g&&(g.nextSibling_=g.nextSibling),e.appendChild(f)}function g(a,c){var d=J(a),e=J(c);b(c),c.previousSibling&&(c.previousSibling.nextSibling_=c),c.nextSibling&&(c.nextSibling.previousSibling_=c),a.lastChild===c&&(a.lastChild_=c),a.firstChild===c&&(a.firstChild_=c),d.removeChild(e)}function h(a){var b=J(a),c=b.parentNode;c&&g(K(c),a)}function i(a,b){k(b).push(a),z(a,b);var c=M.get(a);c||M.set(a,c=[]),c.push(b)}function j(a){L.set(a,[])}function k(a){return L.get(a)}function l(a){for(var b=[],c=0,d=a.firstChild;d;d=d.nextSibling)b[c++]=d;return b}function m(a,b,c){for(var d=l(a),e=0;e<d.length;e++){var f=d[e];if(b(f)){if(c(f)===!1)return}else m(f,b,c)}}function n(a,b){var c=b.getAttribute("select");if(!c)return!0;if(c=c.trim(),!c)return!0;if(a.nodeType!==D.ELEMENT_NODE)return!1;if(!Q.test(c))return!1;if(":"===c[0]&&!R.test(c))return!1;try{return a.matches(c)}catch(d){return!1}}function o(){H=null,T.forEach(function(a){a.render()}),T=[]}function p(a){this.host=a,this.dirty=!1,this.invalidateAttributes(),this.associateNode(a)}function q(a){var b=O.get(a);return b||(b=new p(a),O.set(a,b)),b}function r(a){for(;a;a=a.parentNode)if(a instanceof E)return a;return null}function s(a){return q(G(a))}function t(a){return"content"===a.localName}function u(a){return"content"===a.localName}function v(a){return"shadow"===a.localName}function w(a){return"shadow"===a.localName}function x(a){return a.shadowRoot}function y(a){for(var b=[],c=a.shadowRoot;c;c=c.olderShadowRoot)b.push(c);return b}function z(a,b){N.set(a,b)}function A(a){new p(a).render()}var B=a.wrappers.HTMLContentElement,C=a.wrappers.HTMLShadowElement,D=a.wrappers.Node,E=a.wrappers.ShadowRoot,F=a.assert,G=a.getHostForShadowRoot;a.mixin;var H,I=a.oneOf,J=a.unwrap,K=a.wrap,L=new SideTable,M=new SideTable,N=new SideTable,O=new SideTable,P=new SideTable,Q=/^[*.:#[a-zA-Z_|]/,R=new RegExp("^:("+["link","visited","target","enabled","disabled","checked","indeterminate","nth-child","nth-last-child","nth-of-type","nth-last-of-type","first-child","last-child","first-of-type","last-of-type","only-of-type"].join("|")+")"),S=I(window,["requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","setTimeout"]),T=[];p.prototype={render:function(){if(this.dirty){this.invalidateAttributes(),this.treeComposition();var a=this.host,b=a.shadowRoot;this.removeAllChildNodes(this.host);var c=l(b);c.forEach(function(c){this.renderNode(a,b,c,!1)},this),this.dirty=!1}},invalidate:function(){if(!this.dirty){if(this.dirty=!0,T.push(this),H)return;H=window[S](o,0)}},renderNode:function(a,b,c,d){if(x(c)){this.appendChild(a,c);var e=q(c);e.dirty=!0,e.render()}else t(c)?this.renderInsertionPoint(a,b,c,d):v(c)?this.renderShadowInsertionPoint(a,b,c):this.renderAsAnyDomTree(a,b,c,d)},renderAsAnyDomTree:function(a,b,c,d){if(this.appendChild(a,c),x(c))A(c);else{var e=c,f=l(e);P.get(e)&&this.removeAllChildNodes(e),f.forEach(function(a){this.renderNode(e,b,a,d)},this)}},renderInsertionPoint:function(a,b,c,d){var e=k(c);e.length?(this.removeAllChildNodes(c),e.forEach(function(c){t(c)&&d?this.renderInsertionPoint(a,b,c,d):this.renderAsAnyDomTree(a,b,c,d)},this)):this.renderFallbackContent(a,c),this.remove(c)},renderShadowInsertionPoint:function(a,b,c){var d=b.olderShadowRoot;if(d){z(d,c),this.remove(c);var e=l(d);e.forEach(function(b){this.renderNode(a,d,b,!0)},this)}else this.renderFallbackContent(a,c)},renderFallbackContent:function(a,b){var c=l(b);this.associateNode(b),this.remove(b),c.forEach(function(b){this.appendChild(a,b)},this)},invalidateAttributes:function(){this.attributes=Object.create(null)},updateDependentAttributes:function(a){if(a){var b=this.attributes;/\.\w+/.test(a)&&(b["class"]=!0),/#\w+/.test(a)&&(b.id=!0),a.replace(/\[\s*([^\s=\|~\]]+)/g,function(a,c){b[c]=!0})}},dependsOnAttribute:function(a){return this.attributes[a]},distribute:function(a,b){var c=!1,d=this;return m(a,u,function(a){j(a),d.updateDependentAttributes(a.getAttribute("select"));for(var e=0;e<b.length;e++){var f=b[e];void 0!==f&&n(f,a)&&(i(f,a),b[e]=void 0,c=!0)}}),c?b.filter(function(a){return void 0!==a}):b},treeComposition:function(){var a=this.host,b=a.shadowRoot,c=[],d=l(a);d.forEach(function(a){if(t(a)){var b=k(a);b&&b.length||(b=l(a)),c.push.apply(c,b)}else c.push(a)});for(var e,f;b;){if(e=void 0,m(b,w,function(a){return e=a,!1}),f=e,c=this.distribute(b,c),f){var g=b.olderShadowRoot;if(g){b=g,z(b,f);continue}break}break}},appendChild:function(a,b){this.associateNode(a),f(a,b)},remove:function(a){this.associateNode(a.parentNode),h(a)},removeAllChildNodes:function(a){this.associateNode(a),e(a)},associateNode:function(a){P.set(a,this)}},D.prototype.invalidateShadowRenderer=function(){var a=P.get(this);return a?(a.invalidate(),!0):!1},B.prototype.getDistributedNodes=function(){var a=P.get(this);return a&&a.render(),k(this)},C.prototype.nodeWasAdded_=B.prototype.nodeWasAdded_=function(){this.invalidateShadowRenderer();var a,b=r(this);b&&(a=s(b)),P.set(this,a),a&&a.invalidate()},a.eventParentsTable=M,a.getRendererForHost=q,a.getShadowTrees=y,a.insertionParentTable=N,a.renderAllPending=o,a.visual={removeAllChildNodes:e,appendChild:f,removeChild:g}}(this.ShadowDOMPolyfill),function(a){"use strict";function b(b){if(window[b]){d(!a.wrappers[b]);var i=function(a){c.call(this,a)};i.prototype=Object.create(c.prototype),e(i.prototype,{get form(){return h(g(this).form)}}),f(window[b],i,document.createElement(b.slice(4,-7))),a.wrappers[b]=i}}var c=a.wrappers.HTMLElement,d=a.assert,e=a.mixin,f=a.registerWrapper,g=a.unwrap,h=a.wrap,i=["HTMLButtonElement","HTMLFieldSetElement","HTMLInputElement","HTMLKeygenElement","HTMLLabelElement","HTMLLegendElement","HTMLObjectElement","HTMLOptionElement","HTMLOutputElement","HTMLSelectElement","HTMLTextAreaElement"];i.forEach(b)}(this.ShadowDOMPolyfill),function(a){"use strict";
-function b(a){k.call(this,a)}function c(a){var c=document[a];b.prototype[a]=function(){return v(c.apply(this.impl,arguments))}}function d(a,b){y.call(b.impl,u(a)),e(a,b)}function e(a,b){a.shadowRoot&&b.adoptNode(a.shadowRoot),a instanceof n&&f(a,b);for(var c=a.firstChild;c;c=c.nextSibling)e(c,b)}function f(a,b){var c=a.olderShadowRoot;c&&b.adoptNode(c)}function g(a){this.impl=a}function h(a,b){var c=document.implementation[b];a.prototype[b]=function(){return v(c.apply(this.impl,arguments))}}function i(a,b){var c=document.implementation[b];a.prototype[b]=function(){return c.apply(this.impl,arguments)}}var j=a.GetElementsByInterface,k=a.wrappers.Node,l=a.ParentNodeInterface,m=a.SelectorsInterface,n=a.wrappers.ShadowRoot,o=a.defineWrapGetter,p=a.elementFromPoint,q=a.forwardMethodsToWrapper,r=a.matchesName,s=a.mixin,t=a.registerWrapper,u=a.unwrap,v=a.wrap,w=a.wrapEventTargetMethods;a.wrapNodeList;var x=new SideTable;b.prototype=Object.create(k.prototype),o(b,"documentElement"),o(b,"body"),o(b,"head"),["createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","getElementById"].forEach(c);var y=document.adoptNode;if(s(b.prototype,{adoptNode:function(a){return a.parentNode&&a.parentNode.removeChild(a),d(a,this),a},elementFromPoint:function(a,b){return p(this,this,a,b)}}),document.register){var z=document.register;b.prototype.register=function(b,c){function d(a){return a?(this.impl=a,void 0):document.createElement(b)}var e=c.prototype;if(a.nativePrototypeTable.get(e))throw new Error("NotSupportedError");for(var f,g=Object.getPrototypeOf(e),h=[];g&&!(f=a.nativePrototypeTable.get(g));)h.push(g),g=Object.getPrototypeOf(g);if(!f)throw new Error("NotSupportedError");for(var i=Object.create(f),j=h.length-1;j>=0;j--)i=Object.create(i);return["createdCallback","enteredDocumentCallback","leftDocumentCallback","attributeChangedCallback"].forEach(function(a){var b=e[a];b&&(i[a]=function(){b.apply(v(this),arguments)})}),z.call(u(this),b,{prototype:i}),d.prototype=e,d.prototype.constructor=d,a.constructorTable.set(i,d),a.nativePrototypeTable.set(e,i),d},q([window.HTMLDocument||window.Document],["register"])}q([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement,window.HTMLHtmlElement],["appendChild","compareDocumentPosition","contains","getElementsByClassName","getElementsByTagName","getElementsByTagNameNS","insertBefore","querySelector","querySelectorAll","removeChild","replaceChild",r]),q([window.HTMLDocument||window.Document],["adoptNode","contains","createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","elementFromPoint","getElementById"]),s(b.prototype,j),s(b.prototype,l),s(b.prototype,m),s(b.prototype,{get implementation(){var a=x.get(this);return a?a:(a=new g(u(this).implementation),x.set(this,a),a)}}),t(window.Document,b,document.implementation.createHTMLDocument("")),window.HTMLDocument&&t(window.HTMLDocument,b),w([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement]),h(g,"createDocumentType"),h(g,"createDocument"),h(g,"createHTMLDocument"),i(g,"hasFeature"),t(window.DOMImplementation,g),q([window.DOMImplementation],["createDocumentType","createDocument","createHTMLDocument","hasFeature"]),a.adoptNodeNoRemove=d,a.wrappers.DOMImplementation=g,a.wrappers.Document=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){c.call(this,a)}var c=a.wrappers.EventTarget,d=a.mixin,e=a.registerWrapper,f=a.unwrap,g=a.unwrapIfNeeded,h=a.wrap,i=window.Window;b.prototype=Object.create(c.prototype);var j=window.getComputedStyle;i.prototype.getComputedStyle=function(a,b){return j.call(this||window,g(a),b)},["addEventListener","removeEventListener","dispatchEvent"].forEach(function(a){i.prototype[a]=function(){var b=h(this||window);return b[a].apply(b,arguments)}}),d(b.prototype,{getComputedStyle:function(a,b){return j.call(f(this),g(a),b)}}),e(i,b),a.wrappers.Window=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){this.impl=a}function c(a){return new b(a)}function d(a){return a.map(c)}function e(a){var b=this;this.impl=new k(function(c){a.call(b,d(c),b)})}var f=a.defineGetter,g=a.defineWrapGetter,h=a.registerWrapper,i=a.unwrapIfNeeded,j=a.wrapNodeList;a.wrappers;var k=window.MutationObserver||window.WebKitMutationObserver;if(k){var l=window.MutationRecord;b.prototype={get addedNodes(){return j(this.impl.addedNodes)},get removedNodes(){return j(this.impl.removedNodes)}},["target","previousSibling","nextSibling"].forEach(function(a){g(b,a)}),["type","attributeName","attributeNamespace","oldValue"].forEach(function(a){f(b,a,function(){return this.impl[a]})}),l&&h(l,b),window.Node,e.prototype={observe:function(a,b){this.impl.observe(i(a),b)},disconnect:function(){this.impl.disconnect()},takeRecords:function(){return d(this.impl.takeRecords())}},a.wrappers.MutationObserver=e,a.wrappers.MutationRecord=b}}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){this.impl=a}var c=a.registerWrapper,d=a.unwrap,e=a.unwrapIfNeeded,f=a.wrap,g=window.Range;b.prototype={get startContainer(){return f(this.impl.startContainer)},get endContainer(){return f(this.impl.endContainer)},get commonAncestorContainer(){return f(this.impl.commonAncestorContainer)},setStart:function(a,b){this.impl.setStart(e(a),b)},setEnd:function(a,b){this.impl.setEnd(e(a),b)},setStartBefore:function(a){this.impl.setStartBefore(e(a))},setStartAfter:function(a){this.impl.setStartAfter(e(a))},setEndBefore:function(a){this.impl.setEndBefore(e(a))},setEndAfter:function(a){this.impl.setEndAfter(e(a))},selectNode:function(a){this.impl.selectNode(e(a))},selectNodeContents:function(a){this.impl.selectNodeContents(e(a))},compareBoundaryPoints:function(a,b){return this.impl.compareBoundaryPoints(a,d(b))},extractContents:function(){return f(this.impl.extractContents())},cloneContents:function(){return f(this.impl.cloneContents())},insertNode:function(a){this.impl.insertNode(e(a))},surroundContents:function(a){this.impl.surroundContents(e(a))},cloneRange:function(){return f(this.impl.cloneRange())},isPointInRange:function(a,b){return this.impl.isPointInRange(e(a),b)},comparePoint:function(a,b){return this.impl.comparePoint(e(a),b)},intersectsNode:function(a){return this.impl.intersectsNode(e(a))}},g.prototype.createContextualFragment&&(b.prototype.createContextualFragment=function(a){return f(this.impl.createContextualFragment(a))}),c(window.Range,b),a.wrappers.Range=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){var b=c[a],d=window[b];if(d){var e=document.createElement(a),f=e.constructor;window[b]=f}}a.isWrapperFor;var c={a:"HTMLAnchorElement",applet:"HTMLAppletElement",area:"HTMLAreaElement",audio:"HTMLAudioElement",br:"HTMLBRElement",base:"HTMLBaseElement",body:"HTMLBodyElement",button:"HTMLButtonElement",canvas:"HTMLCanvasElement",dl:"HTMLDListElement",datalist:"HTMLDataListElement",dir:"HTMLDirectoryElement",div:"HTMLDivElement",embed:"HTMLEmbedElement",fieldset:"HTMLFieldSetElement",font:"HTMLFontElement",form:"HTMLFormElement",frame:"HTMLFrameElement",frameset:"HTMLFrameSetElement",hr:"HTMLHRElement",head:"HTMLHeadElement",h1:"HTMLHeadingElement",html:"HTMLHtmlElement",iframe:"HTMLIFrameElement",input:"HTMLInputElement",li:"HTMLLIElement",label:"HTMLLabelElement",legend:"HTMLLegendElement",link:"HTMLLinkElement",map:"HTMLMapElement",menu:"HTMLMenuElement",menuitem:"HTMLMenuItemElement",meta:"HTMLMetaElement",meter:"HTMLMeterElement",del:"HTMLModElement",ol:"HTMLOListElement",object:"HTMLObjectElement",optgroup:"HTMLOptGroupElement",option:"HTMLOptionElement",output:"HTMLOutputElement",p:"HTMLParagraphElement",param:"HTMLParamElement",pre:"HTMLPreElement",progress:"HTMLProgressElement",q:"HTMLQuoteElement",script:"HTMLScriptElement",select:"HTMLSelectElement",source:"HTMLSourceElement",span:"HTMLSpanElement",style:"HTMLStyleElement",caption:"HTMLTableCaptionElement",col:"HTMLTableColElement",table:"HTMLTableElement",tr:"HTMLTableRowElement",thead:"HTMLTableSectionElement",tbody:"HTMLTableSectionElement",textarea:"HTMLTextAreaElement",title:"HTMLTitleElement",ul:"HTMLUListElement",video:"HTMLVideoElement"};Object.keys(c).forEach(b),Object.getOwnPropertyNames(a.wrappers).forEach(function(b){window[b]=a.wrappers[b]}),a.knownElements=c}(this.ShadowDOMPolyfill),function(){var a=window.ShadowDOMPolyfill;a.wrap,Object.defineProperties(HTMLElement.prototype,{webkitShadowRoot:{get:function(){return this.shadowRoot}}}),HTMLElement.prototype.webkitCreateShadowRoot=HTMLElement.prototype.createShadowRoot,window.dartExperimentalFixupGetTag=function(b){function c(a){if(a instanceof d)return"NodeList";if(a instanceof e)return"ShadowRoot";if(a instanceof MutationRecord)return"MutationRecord";if(a instanceof MutationObserver)return"MutationObserver";var c=f(a);if(a!==c){var g=a.constructor;if(g&&"GeneratedWrapper"==g.name){var h=g._ShadowDOMPolyfill$cacheTag_;return h||(h=Object.prototype.toString.call(c),h=h.substring(8,h.length-1),g._ShadowDOMPolyfill$cacheTag_=h),h}a=c}return b(a)}var d=a.wrappers.NodeList,e=a.wrappers.ShadowRoot,f=a.unwrapIfNeeded;return c}}();var Platform={};!function(a){function b(a,b){var c="";return Array.prototype.forEach.call(a,function(a){c+=a.textContent+"\n\n"}),b||(c=c.replace(m,"")),c}function c(a){var b=document.createElement("style");b.textContent=a,document.head.appendChild(b);var c=b.sheet.cssRules;return b.parentNode.removeChild(b),c}function d(a){for(var b=0,c=[];b<a.length;b++)c.push(a[b].cssText);return c.join("\n\n")}function e(a){a&&f().appendChild(document.createTextNode(a))}function f(){return g||(g=document.createElement("style"),g.setAttribute("ShadowCSSShim","")),g}var g,h={strictStyling:!1,registry:{},shimStyling:function(a,b,c){if(a){var d=this.registerDefinition(a,b,c);this.strictStyling&&this.applyScopeToContent(a,b),this.shimPolyfillDirectives(d.rootStyles,b),this.applyShimming(d.scopeStyles,b)}},shimShadowDOMStyling:function(a,b){this.shimPolyfillDirectives(a,b),this.applyShimming(a,b)},registerDefinition:function(a,b,c){var d=this.registry[b]={root:a,name:b,extendsName:c},e=a.querySelectorAll("style");e=e?Array.prototype.slice.call(e,0):[],d.rootStyles=e,d.scopeStyles=d.rootStyles;var f=this.registry[d.extendsName];return f&&(d.scopeStyles=d.scopeStyles.concat(f.scopeStyles)),d},applyScopeToContent:function(a,b){a&&(Array.prototype.forEach.call(a.querySelectorAll("*"),function(a){a.setAttribute(b,"")}),Array.prototype.forEach.call(a.querySelectorAll("template"),function(a){this.applyScopeToContent(a.content,b)},this))},shimPolyfillDirectives:function(a,b){a&&Array.prototype.forEach.call(a,function(a){a.textContent=this.convertPolyfillDirectives(a.textContent,b)},this)},convertPolyfillDirectives:function(a,b){for(var c,d,e="",f=0;c=n.exec(a);)e+=a.substring(f,c.index),d=c[1].slice(0,-2).replace(q,b),e+=this.scopeSelector(d,b)+"{",f=n.lastIndex;return e+=a.substring(f,a.length)},applyShimming:function(a,b){var c=this.shimAtHost(a,b);c+=this.shimScoping(a,b),e(c)},shimAtHost:function(a,b){return a?this.convertAtHostStyles(a,b):void 0},convertAtHostStyles:function(a,e){for(var f,g=b(a),h="",j=0;f=i.exec(g);)h+=g.substring(j,f.index),h+=this.scopeHostCss(f[1],e),j=i.lastIndex;h+=g.substring(j,g.length);var k=new RegExp("^"+e+p,"m"),g=d(this.findAtHostRules(c(h),k));return g},scopeHostCss:function(a,b){for(var c,d="";c=j.exec(a);)d+=this.scopeHostSelector(c[1],b)+" "+c[2]+"\n ";return d},scopeHostSelector:function(a,b){var c=[],d=a.split(","),e="[is="+b+"]";return d.forEach(function(a){a=a.trim(),a.match(k)?a=a.replace(k,b+"$1$3, "+e+"$1$3"):a.match(l)&&(a=b+a+", "+e+a),c.push(a)},this),c.join(", ")},findAtHostRules:function(a,b){return Array.prototype.filter.call(a,this.isHostRule.bind(this,b))},isHostRule:function(a,b){return b.selectorText&&b.selectorText.match(a)||b.cssRules&&this.findAtHostRules(b.cssRules,a).length||b.type==CSSRule.WEBKIT_KEYFRAMES_RULE},shimScoping:function(a,b){return a?this.convertScopedStyles(a,b):void 0},convertScopedStyles:function(a,d){Array.prototype.forEach.call(a,function(a){a.parentNode&&a.parentNode.removeChild(a)});var e=b(a).replace(i,"");e=this.convertPseudos(e);var f=c(e);return e=this.scopeRules(f,d)},convertPseudos:function(a){return a.replace(o," [pseudo=$1]")},scopeRules:function(a,b){var c="";return Array.prototype.forEach.call(a,function(a){a.selectorText&&a.style&&a.style.cssText?(c+=this.scopeSelector(a.selectorText,b,this.strictStyling)+" {\n ",c+=this.propertiesFromRule(a)+"\n}\n\n"):a.media?(c+="@media "+a.media.mediaText+" {\n",c+=this.scopeRules(a.cssRules,b),c+="\n}\n\n"):a.cssText&&(c+=a.cssText+"\n\n")},this),c},scopeSelector:function(a,b,c){var d=[],e=a.split(",");return e.forEach(function(a){a=a.trim(),this.selectorNeedsScoping(a,b)&&(a=c?this.applyStrictSelectorScope(a,b):this.applySimpleSelectorScope(a,b)),d.push(a)},this),d.join(", ")},selectorNeedsScoping:function(a,b){var c="("+b+"|\\[is="+b+"\\])",d=new RegExp("^"+c+p,"m");return!a.match(d)},applySimpleSelectorScope:function(a,b){return b+" "+a+", "+"[is="+b+"] "+a},applyStrictSelectorScope:function(a,b){var c=[" ",">","+","~"],d=a,e="["+b+"]";return c.forEach(function(a){var b=d.split(a);d=b.map(function(a){var b=a.trim();return b&&c.indexOf(b)<0&&b.indexOf(e)<0&&(a=b.replace(/([^:]*)(:*)(.*)/,"$1"+e+"$2$3")),a}).join(a)}),d},propertiesFromRule:function(a){var b=a.style.cssText;return a.style.content&&!a.style.content.match(/['"]+/)&&(b="content: '"+a.style.content+"';\n"+a.style.cssText.replace(/content:[^;]*;/g,"")),b}},i=/@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,j=/([^{]*)({[\s\S]*?})/gim,k=/(.*)((?:\*)|(?:\:scope))(.*)/,l=/^[.\[:]/,m=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,n=/\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim,o=/::(x-[^\s{,(]*)/gim,p="([>\\s~+[.,{:][\\s\\S]*)?$",q=/@host/gim;if(window.ShadowDOMPolyfill){e("style { display: none !important; }\n");var r=document.querySelector("head");r.insertBefore(f(),r.childNodes[0])}a.ShadowCSS=h}(window.Platform),function(a){function b(a,b){if(window.ShadowDOMPolyfill){for(var h,i=this.convertPolyfillDirectives(a,b),j="",k=0;h=e.exec(i);)j+=i.substring(k,h.index),j+=this.scopeHostCss(h[1],b),k=e.lastIndex;j+=i.substring(k,i.length);var l=new RegExp("^"+b+g,"m"),m=d(this.findAtHostRules(c(j),l));i=i.replace(f,""),i=this.convertPseudos(i);var n=c(i),o=this.scopeRules(n,b);return m+o}}function c(a){var b=document.createElement("style");b.textContent=a,document.head.appendChild(b);var c=b.sheet.cssRules;return b.parentNode.removeChild(b),c}function d(a){for(var b=0,c=[];b<a.length;b++)c.push(a[b].cssText);return c.join("\n\n")}var e=/@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,f=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,g="([>\\s~+[.,{:][\\s\\S]*)?$";a.ShadowCSS.shimShadowDOMStyling2=b}(window.Platform)}
\ No newline at end of file
+if(!HTMLElement.prototype.createShadowRoot&&!HTMLElement.prototype.webkitCreateShadowRoot||window.__forceShadowDomPolyfill){!function(){Element.prototype.webkitCreateShadowRoot&&(Element.prototype.webkitCreateShadowRoot=function(){return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot()})}(),function(a){"use strict";function b(){function a(a){"splice"===a[0].type&&"splice"===a[1].type&&(b=!0)}if("function"!=typeof Object.observe||"function"!=typeof Array.observe)return!1;var b=!1,c=[0];return Array.observe(c,a),c[1]=1,c.length=0,Object.deliverChangeRecords(a),b}function c(){if(a.document&&"securityPolicy"in a.document&&!a.document.securityPolicy.allowsEval)return!1;try{var b=new Function("","return true;");return b()}catch(c){return!1}}function d(a){return+a===a>>>0}function e(a){return+a}function f(a){return a===Object(a)}function g(a,b){return a===b?0!==a||1/a===1/b:H(a)&&H(b)?!0:a!==a&&b!==b}function h(a){return"string"!=typeof a?!1:(a=a.trim(),""==a?!0:"."==a[0]?!1:P.test(a))}function i(a,b){if(b!==Q)throw Error("Use Path.get to retrieve path objects");return""==a.trim()?this:d(a)?(this.push(a),this):(a.split(/\s*\.\s*/).filter(function(a){return a}).forEach(function(a){this.push(a)},this),G&&!F&&this.length&&(this.getValueFrom=this.compiledGetValueFromFn()),void 0)}function j(a){if(a instanceof i)return a;null==a&&(a=""),"string"!=typeof a&&(a=String(a));var b=R[a];if(b)return b;if(!h(a))return S;var b=new i(a,Q);return R[a]=b,b}function k(b){for(var c=0;T>c&&b.check();)b.report(),c++;a.testingExposeCycleCount&&(a.dirtyCheckCycleCount=c)}function l(a){for(var b in a)return!1;return!0}function m(a){return l(a.added)&&l(a.removed)&&l(a.changed)}function n(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function o(a,b){var c=b||(Array.isArray(a)?[]:{});for(var d in a)c[d]=a[d];return Array.isArray(a)&&(c.length=a.length),c}function p(a,b,c,d){if(this.closed=!1,this.object=a,this.callback=b,this.target=c,this.token=d,this.reporting=!0,F){var e=this;this.boundInternalCallback=function(a){e.internalCallback(a)}}q(this)}function q(a){V&&(U.push(a),p._allObserversCount++)}function r(a,b,c,d){p.call(this,a,b,c,d),this.connect(),this.sync(!0)}function s(a,b,c,d){if(!Array.isArray(a))throw Error("Provided object is not an Array");r.call(this,a,b,c,d)}function t(a){this.arr=[],this.callback=a,this.isObserved=!0}function u(a,b,c,d,e,g,h){var b=b instanceof i?b:j(b);return b&&b.length&&f(a)?(p.call(this,a,c,d,e),this.valueFn=g,this.setValueFn=h,this.path=b,this.connect(),this.sync(!0),void 0):(this.value_=b?b.getValueFrom(a):void 0,this.value=g?g(this.value_):this.value_,this.closed=!0,void 0)}function v(a,b,c,d){p.call(this,void 0,a,b,c),this.valueFn=d,this.observed=[],this.values=[],this.started=!1}function w(a,b){if("function"==typeof Object.observe){var c=Object.getNotifier(a);return function(d,e){var f={object:a,type:d,name:b};2===arguments.length&&(f.oldValue=e),c.notify(f)}}}function x(a,b,c){for(var d={},e={},f=0;f<b.length;f++){var g=b[f];$[g.type]?(g.name in c||(c[g.name]=g.oldValue),"updated"!=g.type&&("new"!=g.type?g.name in d?(delete d[g.name],delete c[g.name]):e[g.name]=!0:g.name in e?delete e[g.name]:d[g.name]=!0)):(console.error("Unknown changeRecord type: "+g.type),console.error(g))}for(var h in d)d[h]=a[h];for(var h in e)e[h]=void 0;var i={};for(var h in c)if(!(h in d||h in e)){var j=a[h];c[h]!==j&&(i[h]=j)}return{added:d,removed:e,changed:i}}function y(a,b,c){return{index:a,removed:b,addedCount:c}}function z(){}function A(a,b,c,d,e,f){return db.calcSplices(a,b,c,d,e,f)}function B(a,b,c,d){return c>b||a>d?-1:b==c||d==a?0:c>a?d>b?b-c:d-c:b>d?d-a:b-a}function C(a,b,c,d){for(var e=y(b,c,d),f=!1,g=0,h=0;h<a.length;h++){var i=a[h];if(i.index+=g,!f){var j=B(e.index,e.index+e.removed.length,i.index,i.index+i.addedCount);if(j>=0){a.splice(h,1),h--,g-=i.addedCount-i.removed.length,e.addedCount+=i.addedCount-j;var k=e.removed.length+i.removed.length-j;if(e.addedCount||k){var c=i.removed;if(e.index<i.index){var l=e.removed.slice(0,i.index-e.index);Array.prototype.push.apply(l,c),c=l}if(e.index+e.removed.length>i.index+i.addedCount){var m=e.removed.slice(i.index+i.addedCount-e.index);Array.prototype.push.apply(c,m)}e.removed=c,i.index<e.index&&(e.index=i.index)}else f=!0}else if(e.index<i.index){f=!0,a.splice(h,0,e),h++;var n=e.addedCount-e.removed.length;i.index+=n,g+=n}}}f||a.push(e)}function D(a,b){for(var c=[],f=0;f<b.length;f++){var g=b[f];switch(g.type){case"splice":C(c,g.index,g.removed.slice(),g.addedCount);break;case"new":case"updated":case"deleted":if(!d(g.name))continue;var h=e(g.name);if(0>h)continue;C(c,h,[g.oldValue],1);break;default:console.error("Unexpected record type: "+JSON.stringify(g))}}return c}function E(a,b){var c=[];return D(a,b).forEach(function(b){return 1==b.addedCount&&1==b.removed.length?(b.removed[0]!==a[b.index]&&c.push(b),void 0):(c=c.concat(A(a,b.index,b.index+b.addedCount,b.removed,0,b.removed.length)),void 0)}),c}var F=b(),G=c(),H=a.Number.isNaN||function(b){return"number"==typeof b&&a.isNaN(b)},I="__proto__"in{}?function(a){return a}:function(a){var b=a.__proto__;if(!b)return a;var c=Object.create(b);return Object.getOwnPropertyNames(a).forEach(function(b){Object.defineProperty(c,b,Object.getOwnPropertyDescriptor(a,b))}),c},J="[$_a-zA-Z]",K="[$_a-zA-Z0-9]",L=J+"+"+K+"*",M="(?:[0-9]|[1-9]+[0-9]+)",N="(?:"+L+"|"+M+")",O="(?:"+N+")(?:\\s*\\.\\s*"+N+")*",P=new RegExp("^"+O+"$"),Q={},R={};i.get=j,i.prototype=I({__proto__:[],valid:!0,toString:function(){return this.join(".")},getValueFrom:function(a,b){for(var c=0;c<this.length;c++){if(null==a)return;b&&b.observe(a),a=a[this[c]]}return a},compiledGetValueFromFn:function(){var a=this.map(function(a){return d(a)?'["'+a+'"]':"."+a}),b="",c="obj";b+="if (obj != null";for(var e=0;e<this.length-1;e++)this[e],c+=a[e],b+=" &&\n "+c+" != null";return b+=")\n",c+=a[e],b+=" return "+c+";\nelse\n return undefined;",new Function("obj",b)},setValueFrom:function(a,b){if(!this.length)return!1;for(var c=0;c<this.length-1;c++){if(!f(a))return!1;a=a[this[c]]}return f(a)?(a[this[c]]=b,!0):!1}});var S=new i("",Q);S.valid=!1,S.getValueFrom=S.setValueFrom=function(){};var T=1e3;p.prototype={internalCallback:function(a){this.closed||this.reporting&&this.check(a)&&(this.report(),this.testingResults&&(this.testingResults.anyChanged=!0))},close:function(){this.closed||(this.object&&"function"==typeof this.object.close&&this.object.close(),this.disconnect(),this.object=void 0,this.closed=!0)},deliver:function(a){this.closed||(F?(this.testingResults=a,Object.deliverChangeRecords(this.boundInternalCallback),this.testingResults=void 0):k(this))},report:function(){this.reporting&&(this.sync(!1),this.callback&&(this.reportArgs.push(this.token),this.invokeCallback(this.reportArgs)),this.reportArgs=void 0)},invokeCallback:function(a){try{this.callback.apply(this.target,a)}catch(b){p._errorThrownDuringCallback=!0,console.error("Exception caught during observer callback: "+(b.stack||b))}},reset:function(){this.closed||(F&&(this.reporting=!1,Object.deliverChangeRecords(this.boundInternalCallback),this.reporting=!0),this.sync(!0))}};var U,V=!F||a.forceCollectObservers;p._allObserversCount=0,V&&(U=[]);var W=!1,X="function"==typeof Object.deliverAllChangeRecords;a.Platform=a.Platform||{},a.Platform.performMicrotaskCheckpoint=function(){if(!W){if(X)return Object.deliverAllChangeRecords(),void 0;if(V){W=!0;var b=0,c={};do{b++;var d=U;U=[],c.anyChanged=!1;for(var e=0;e<d.length;e++){var f=d[e];f.closed||(F?f.deliver(c):f.check()&&(c.anyChanged=!0,f.report()),U.push(f))}}while(T>b&&c.anyChanged);a.testingExposeCycleCount&&(a.dirtyCheckCycleCount=b),p._allObserversCount=U.length,W=!1}}},V&&(a.Platform.clearObservers=function(){U=[]}),r.prototype=I({__proto__:p.prototype,connect:function(){F&&Object.observe(this.object,this.boundInternalCallback)},sync:function(){F||(this.oldObject=o(this.object))},check:function(a){var b,c;if(F){if(!a)return!1;c={},b=x(this.object,a,c)}else c=this.oldObject,b=n(this.object,this.oldObject);return m(b)?!1:(this.reportArgs=[b.added||{},b.removed||{},b.changed||{}],this.reportArgs.push(function(a){return c[a]}),!0)},disconnect:function(){F?this.object&&Object.unobserve(this.object,this.boundInternalCallback):this.oldObject=void 0}}),s.prototype=I({__proto__:r.prototype,connect:function(){F&&Array.observe(this.object,this.boundInternalCallback)},sync:function(){F||(this.oldObject=this.object.slice())},check:function(a){var b;if(F){if(!a)return!1;b=E(this.object,a)}else b=A(this.object,0,this.object.length,this.oldObject,0,this.oldObject.length);return b&&b.length?(this.reportArgs=[b],!0):!1}}),s.applySplices=function(a,b,c){c.forEach(function(c){for(var d=[c.index,c.removed.length],e=c.index;e<c.index+c.addedCount;)d.push(b[e]),e++;Array.prototype.splice.apply(a,d)})};var Y=Object.getPrototypeOf({}),Z=Object.getPrototypeOf([]);t.prototype={reset:function(){this.isObserved=!this.isObserved},observe:function(a){if(f(a)&&a!==Y&&a!==Z){var b=this.arr.indexOf(a);b>=0&&this.arr[b+1]===this.isObserved||(0>b&&(b=this.arr.length,this.arr[b]=a,Object.observe(a,this.callback)),this.arr[b+1]=this.isObserved,this.observe(Object.getPrototypeOf(a)))}},cleanup:function(){for(var a=0,b=0,c=this.isObserved;b<this.arr.length;){var d=this.arr[b];this.arr[b+1]==c?(b>a&&(this.arr[a]=d,this.arr[a+1]=c),a+=2):Object.unobserve(d,this.callback),b+=2}this.arr.length=a}},u.prototype=I({__proto__:p.prototype,connect:function(){F&&(this.observedSet=new t(this.boundInternalCallback))},disconnect:function(){this.value=void 0,this.value_=void 0,this.observedSet&&(this.observedSet.reset(),this.observedSet.cleanup(),this.observedSet=void 0)},check:function(){return this.observedSet&&this.observedSet.reset(),this.value_=this.path.getValueFrom(this.object,this.observedSet),this.observedSet&&this.observedSet.cleanup(),g(this.value_,this.oldValue_)?!1:(this.value=this.valueFn?this.valueFn(this.value_):this.value_,this.reportArgs=[this.value,this.oldValue],!0)},sync:function(a){a&&(this.observedSet&&this.observedSet.reset(),this.value_=this.path.getValueFrom(this.object,this.observedSet),this.value=this.valueFn?this.valueFn(this.value_):this.value_,this.observedSet&&this.observedSet.cleanup()),this.oldValue_=this.value_,this.oldValue=this.value},setValue:function(a){this.path&&("function"==typeof this.setValueFn&&(a=this.setValueFn(a)),this.path.setValueFrom(this.object,a))}}),v.prototype=I({__proto__:u.prototype,addPath:function(a,b){if(this.started)throw Error("Cannot add more paths once started.");var b=b instanceof i?b:j(b),c=b?b.getValueFrom(a):void 0;this.observed.push(a,b),this.values.push(c)},start:function(){this.connect(),this.sync(!0)},getValues:function(){this.observedSet&&this.observedSet.reset();for(var a=!1,b=0;b<this.observed.length;b+=2){var c=this.observed[b+1];if(c){var d=this.observed[b],e=c.getValueFrom(d,this.observedSet),f=this.values[b/2];g(e,f)||(this.values[b/2]=e,a=!0)}}return this.observedSet&&this.observedSet.cleanup(),a},check:function(){return this.getValues()?(this.value=this.valueFn(this.values),g(this.value,this.oldValue)?!1:(this.reportArgs=[this.value,this.oldValue],!0)):void 0},sync:function(a){a&&(this.getValues(),this.value=this.valueFn(this.values)),this.oldValue=this.value},close:function(){if(this.observed){for(var a=0;a<this.observed.length;a+=2){var b=this.observed[a];b&&"function"==typeof b.close&&b.close()}this.observed=void 0,this.values=void 0}p.prototype.close.call(this)}});var $={"new":!0,updated:!0,deleted:!0};u.defineProperty=function(a,b,c){var d=c.object,e=j(c.path),f=w(a,b),g=new u(d,c.path,function(a,b){f&&f("updated",b)});return Object.defineProperty(a,b,{get:function(){return e.getValueFrom(d)},set:function(a){e.setValueFrom(d,a)},configurable:!0}),{close:function(){var c=e.getValueFrom(d);f&&g.deliver(),g.close(),Object.defineProperty(a,b,{value:c,writable:!0,configurable:!0})}}};var _=0,ab=1,bb=2,cb=3;z.prototype={calcEditDistances:function(a,b,c,d,e,f){for(var g=f-e+1,h=c-b+1,i=new Array(g),j=0;g>j;j++)i[j]=new Array(h),i[j][0]=j;for(var k=0;h>k;k++)i[0][k]=k;for(var j=1;g>j;j++)for(var k=1;h>k;k++)if(this.equals(a[b+k-1],d[e+j-1]))i[j][k]=i[j-1][k-1];else{var l=i[j-1][k]+1,m=i[j][k-1]+1;i[j][k]=m>l?l:m}return i},spliceOperationsFromEditDistances:function(a){for(var b=a.length-1,c=a[0].length-1,d=a[b][c],e=[];b>0||c>0;)if(0!=b)if(0!=c){var f,g=a[b-1][c-1],h=a[b-1][c],i=a[b][c-1];f=i>h?g>h?h:g:g>i?i:g,f==g?(g==d?e.push(_):(e.push(ab),d=g),b--,c--):f==h?(e.push(cb),b--,d=h):(e.push(bb),c--,d=i)}else e.push(cb),b--;else e.push(bb),c--;return e.reverse(),e},calcSplices:function(a,b,c,d,e,f){var g=0,h=0,i=Math.min(c-b,f-e);if(0==b&&0==e&&(g=this.sharedPrefix(a,d,i)),c==a.length&&f==d.length&&(h=this.sharedSuffix(a,d,i-g)),b+=g,e+=g,c-=h,f-=h,0==c-b&&0==f-e)return[];if(b==c){for(var j=y(b,[],0);f>e;)j.removed.push(d[e++]);return[j]}if(e==f)return[y(b,[],c-b)];for(var k=this.spliceOperationsFromEditDistances(this.calcEditDistances(a,b,c,d,e,f)),j=void 0,l=[],m=b,n=e,o=0;o<k.length;o++)switch(k[o]){case _:j&&(l.push(j),j=void 0),m++,n++;break;case ab:j||(j=y(m,[],0)),j.addedCount++,m++,j.removed.push(d[n]),n++;break;case bb:j||(j=y(m,[],0)),j.addedCount++,m++;break;case cb:j||(j=y(m,[],0)),j.removed.push(d[n]),n++}return j&&l.push(j),l},sharedPrefix:function(a,b,c){for(var d=0;c>d;d++)if(!this.equals(a[d],b[d]))return d;return c},sharedSuffix:function(a,b,c){for(var d=a.length,e=b.length,f=0;c>f&&this.equals(a[--d],b[--e]);)f++;return f},calculateSplices:function(a,b){return this.calcSplices(a,0,a.length,b,0,b.length)},equals:function(a,b){return a===b}};var db=new z;a.Observer=p,a.Observer.hasObjectObserve=F,a.ArrayObserver=s,a.ArrayObserver.calculateSplices=function(a,b){return db.calculateSplices(a,b)},a.ArraySplice=z,a.ObjectObserver=r,a.PathObserver=u,a.CompoundPathObserver=v,a.Path=i}("undefined"!=typeof global&&global?global:this),("undefined"==typeof WeakMap||navigator.userAgent.indexOf("Firefox/")>-1)&&!function(){var a=Object.defineProperty,b=Date.now()%1e9,c=function(){this.name="__st"+(1e9*Math.random()>>>0)+(b++ +"__")};c.prototype={set:function(b,c){var d=b[this.name];d&&d[0]===b?d[1]=c:a(b,this.name,{value:[b,c],writable:!0})},get:function(a){var b;return(b=a[this.name])&&b[0]===a?b[1]:void 0},"delete":function(a){this.set(a,void 0)}},window.WeakMap=c}();var ShadowDOMPolyfill={};!function(a){"use strict";function b(a){if(!a)throw new Error("Assertion failed")}function c(a,b){return Object.getOwnPropertyNames(b).forEach(function(c){Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))}),a}function d(a,b){return Object.getOwnPropertyNames(b).forEach(function(c){switch(c){case"arguments":case"caller":case"length":case"name":case"prototype":case"toString":return}Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))}),a}function e(a,b){for(var c=0;c<b.length;c++)if(b[c]in a)return b[c]}function f(a){var b=a.__proto__||Object.getPrototypeOf(a),c=C.get(b);if(c)return c;var d=f(b),e=r(d);return o(b,e,a),e}function g(a,b){m(a,b,!0)}function h(a,b){m(b,a,!1)}function i(a){return/^on[a-z]+$/.test(a)}function j(a){return F?new Function("return this.impl."+a):function(){return this.impl[a]}}function k(a){return F?new Function("v","this.impl."+a+" = v"):function(b){this.impl[a]=b}}function l(a){return F?new Function("return this.impl."+a+".apply(this.impl, arguments)"):function(){return this.impl[a].apply(this.impl,arguments)}}function m(b,c,d){Object.getOwnPropertyNames(b).forEach(function(e){if(!(e in c)){I&&b.__lookupGetter__(e);var f;try{f=Object.getOwnPropertyDescriptor(b,e)}catch(g){f=J}var h,m;if(d&&"function"==typeof f.value)return c[e]=l(e),void 0;var n=i(e);h=n?a.getEventHandlerGetter(e):j(e),(f.writable||f.set)&&(m=n?a.getEventHandlerSetter(e):k(e)),Object.defineProperty(c,e,{get:h,set:m,configurable:f.configurable,enumerable:f.enumerable})}})}function n(a,b,c){var e=a.prototype;o(e,b,c),d(b,a)}function o(a,c,d){var e=c.prototype;b(void 0===C.get(a)),C.set(a,c),D.set(e,a),g(a,e),d&&h(e,d)}function p(a,b){return C.get(b.prototype)===a}function q(a){var b=Object.getPrototypeOf(a),c=f(b),d=r(c);return o(b,d,a),d}function r(a){function b(b){a.call(this,b)}return b.prototype=Object.create(a.prototype),b.prototype.constructor=b,b}function s(a){return a instanceof E.EventTarget||a instanceof E.Event||a instanceof E.Range||a instanceof E.DOMImplementation}function t(a){return a instanceof M||a instanceof L||a instanceof N||a instanceof O||a instanceof K}function u(a){return null===a?null:(b(t(a)),a.polymerWrapper_||(a.polymerWrapper_=new(f(a))(a)))}function v(a){return null===a?null:(b(s(a)),a.impl)}function w(a){return a&&s(a)?v(a):a}function x(a){return a&&!s(a)?u(a):a}function y(a,c){null!==c&&(b(t(a)),b(void 0===c||s(c)),a.polymerWrapper_=c)}function z(a,b,c){Object.defineProperty(a.prototype,b,{get:c,configurable:!0,enumerable:!0})}function A(a,b){z(a,b,function(){return u(this.impl[b])})}function B(a,b){a.forEach(function(a){b.forEach(function(b){a.prototype[b]=function(){var a=x(this);return a[b].apply(a,arguments)}})})}var C=new WeakMap,D=new WeakMap,E=Object.create(null),F=!("securityPolicy"in document)||document.securityPolicy.allowsEval;if(F)try{var G=new Function("","return true;");F=G()}catch(H){}Object.getOwnPropertyNames(window);var I=/Firefox/.test(navigator.userAgent),J={get:function(){},set:function(){},configurable:!0,enumerable:!0},K=DOMImplementation,L=Event,M=Node,N=Window,O=Range;a.assert=b,a.constructorTable=C,a.defineGetter=z,a.defineWrapGetter=A,a.forwardMethodsToWrapper=B,a.isWrapperFor=p,a.mixin=c,a.nativePrototypeTable=D,a.oneOf=e,a.registerObject=q,a.registerWrapper=n,a.rewrap=y,a.unwrap=v,a.unwrapIfNeeded=w,a.wrap=u,a.wrapIfNeeded=x,a.wrappers=E}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){return a instanceof N.ShadowRoot}function c(a){var b=a.localName;return"content"===b||"shadow"===b}function d(a){return!!a.shadowRoot}function e(a){var b;return a.parentNode||(b=a.defaultView)&&M(b)||null}function f(f,g,h){if(h.length)return h.shift();if(b(f))return j(f)||a.getHostForShadowRoot(f);var i=a.eventParentsTable.get(f);if(i){for(var k=1;k<i.length;k++)h[k-1]=i[k];return i[0]}if(g&&c(f)){var l=f.parentNode;if(l&&d(l))for(var m=a.getShadowTrees(l),n=j(g),k=0;k<m.length;k++)if(m[k].contains(n))return n}return e(f)}function g(a){for(var d=[],e=a,g=[],i=[];e;){var j=null;if(c(e)){j=h(d);var k=d[d.length-1]||e;d.push(k)}else d.length||d.push(e);var l=d[d.length-1];g.push({target:l,currentTarget:e}),b(e)&&d.pop(),e=f(e,j,i)}return g}function h(a){for(var b=a.length-1;b>=0;b--)if(!c(a[b]))return a[b];return null}function i(d,e){for(var g=[];d;){for(var i=[],j=e,l=void 0;j;){var n=null;if(i.length){if(c(j)&&(n=h(i),k(l))){var o=i[i.length-1];i.push(o)}}else i.push(j);if(m(j,d))return i[i.length-1];b(j)&&i.pop(),l=j,j=f(j,n,g)}d=b(d)?a.getHostForShadowRoot(d):d.parentNode}}function j(b){return a.insertionParentTable.get(b)}function k(a){return j(a)}function l(a){for(var b;b=a.parentNode;)a=b;return a}function m(a,b){return l(a)===l(b)}function n(b,c){if(b===c)return!0;if(b instanceof N.ShadowRoot){var d=a.getHostForShadowRoot(b);return n(l(d),c)}return!1}function o(a){switch(a){case"DOMAttrModified":case"DOMAttributeNameChanged":case"DOMCharacterDataModified":case"DOMElementNameChanged":case"DOMNodeInserted":case"DOMNodeInsertedIntoDocument":case"DOMNodeRemoved":case"DOMNodeRemovedFromDocument":case"DOMSubtreeModified":return!0}return!1}function p(b){if(!P.get(b)){P.set(b,!0),o(b.type)||a.renderAllPending();var c=M(b.target),d=M(b);return q(d,c)}}function q(a,b){var c=g(b);return"load"===a.type&&2===c.length&&c[0].target instanceof N.Document&&c.shift(),X.set(a,c),r(a,c)&&s(a,c)&&t(a,c),T.set(a,w.NONE),R.set(a,null),a.defaultPrevented}function r(a,b){for(var c,d=b.length-1;d>0;d--){var e=b[d].target,f=b[d].currentTarget;if(e!==f&&(c=w.CAPTURING_PHASE,!u(b[d],a,c)))return!1}return!0}function s(a,b){var c=w.AT_TARGET;return u(b[0],a,c)}function t(a,b){for(var c,d=a.bubbles,e=1;e<b.length;e++){var f=b[e].target,g=b[e].currentTarget;if(f===g)c=w.AT_TARGET;else{if(!d||V.get(a))continue;c=w.BUBBLING_PHASE}if(!u(b[e],a,c))return}}function u(a,b,c){var d=a.target,e=a.currentTarget,f=O.get(e);if(!f)return!0;if("relatedTarget"in b){var g=L(b),h=M(g.relatedTarget),j=i(e,h);if(j===d)return!0;S.set(b,j)}T.set(b,c);var k=b.type,l=!1;Q.set(b,d),R.set(b,e);for(var m=0;m<f.length;m++){var n=f[m];if(n.removed)l=!0;else if(!(n.type!==k||!n.capture&&c===w.CAPTURING_PHASE||n.capture&&c===w.BUBBLING_PHASE))try{if("function"==typeof n.handler?n.handler.call(e,b):n.handler.handleEvent(b),V.get(b))return!1}catch(o){window.onerror?window.onerror(o.message):console.error(o)}}if(l){var p=f.slice();f.length=0;for(var m=0;m<p.length;m++)p[m].removed||f.push(p[m])}return!U.get(b)}function v(a,b,c){this.type=a,this.handler=b,this.capture=Boolean(c)}function w(a,b){return a instanceof Y?(this.impl=a,void 0):M(A(Y,"Event",a,b))}function x(a){return a&&a.relatedTarget?Object.create(a,{relatedTarget:{value:L(a.relatedTarget)}}):a}function y(a,b,c){var d=window[a],e=function(b,c){return b instanceof d?(this.impl=b,void 0):M(A(d,a,b,c))};return e.prototype=Object.create(b.prototype),c&&J(e.prototype,c),d&&(d.prototype["init"+a]?K(d,e,document.createEvent(a)):K(d,e,new d("temp"))),e}function z(a,b){return function(){arguments[b]=L(arguments[b]);var c=L(this);c[a].apply(c,arguments)}}function A(a,b,c,d){if(gb)return new a(c,x(d));var e=L(document.createEvent(b)),f=fb[b],g=[c];return Object.keys(f).forEach(function(a){var b=null!=d&&a in d?d[a]:f[a];"relatedTarget"===a&&(b=L(b)),g.push(b)}),e["init"+b].apply(e,g),e}function B(a){return"function"==typeof a?!0:a&&a.handleEvent}function C(a){this.impl=a}function D(b){return b instanceof N.ShadowRoot&&(b=a.getHostForShadowRoot(b)),L(b)}function E(a){I(a,jb)}function F(b,c,d,e){a.renderAllPending();for(var f=M(kb.call(c.impl,d,e)),h=g(f,this),i=0;i<h.length;i++){var j=h[i];if(j.currentTarget===b)return j.target}return null}function G(a){return function(){var b=W.get(this);return b&&b[a]&&b[a].value||null}}function H(a){var b=a.slice(2);return function(c){var d=W.get(this);d||(d=Object.create(null),W.set(this,d));var e=d[a];if(e&&this.removeEventListener(b,e.wrapped,!1),"function"==typeof c){var f=function(b){var d=c.call(this,b);d===!1?b.preventDefault():"onbeforeunload"===a&&"string"==typeof d&&(b.returnValue=d)};this.addEventListener(b,f,!1),d[a]={value:c,wrapped:f}}}}var I=a.forwardMethodsToWrapper,J=a.mixin,K=a.registerWrapper,L=a.unwrap,M=a.wrap,N=a.wrappers;new WeakMap;var O=new WeakMap,P=new WeakMap,Q=new WeakMap,R=new WeakMap,S=new WeakMap,T=new WeakMap,U=new WeakMap,V=new WeakMap,W=new WeakMap,X=new WeakMap;v.prototype={equals:function(a){return this.handler===a.handler&&this.type===a.type&&this.capture===a.capture},get removed(){return null===this.handler},remove:function(){this.handler=null}};var Y=window.Event;w.prototype={get target(){return Q.get(this)},get currentTarget(){return R.get(this)},get eventPhase(){return T.get(this)},get path(){var a=new N.NodeList,b=X.get(this);if(b){for(var c=0,d=b.length-1,e=l(R.get(this)),f=0;d>=f;f++){var g=b[f].currentTarget,h=l(g);n(e,h)&&(f!==d||g instanceof N.Node)&&(a[c++]=g)}a.length=c}return a},stopPropagation:function(){U.set(this,!0)},stopImmediatePropagation:function(){U.set(this,!0),V.set(this,!0)}},K(Y,w,document.createEvent("Event"));var Z=y("UIEvent",w),$=y("CustomEvent",w),_={get relatedTarget(){return S.get(this)||M(L(this).relatedTarget)}},ab=J({initMouseEvent:z("initMouseEvent",14)},_),bb=J({initFocusEvent:z("initFocusEvent",5)},_),cb=y("MouseEvent",Z,ab),db=y("FocusEvent",Z,bb),eb=y("MutationEvent",w,{initMutationEvent:z("initMutationEvent",3),get relatedNode(){return M(this.impl.relatedNode)}}),fb=Object.create(null),gb=function(){try{new window.MouseEvent("click")}catch(a){return!1}return!0}();if(!gb){var hb=function(a,b,c){if(c){var d=fb[c];b=J(J({},d),b)}fb[a]=b};hb("Event",{bubbles:!1,cancelable:!1}),hb("CustomEvent",{detail:null},"Event"),hb("UIEvent",{view:null,detail:0},"Event"),hb("MouseEvent",{screenX:0,screenY:0,clientX:0,clientY:0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:null},"UIEvent"),hb("FocusEvent",{relatedTarget:null},"UIEvent")}var ib=window.EventTarget,jb=["addEventListener","removeEventListener","dispatchEvent"];[Node,Window].forEach(function(a){var b=a.prototype;jb.forEach(function(a){Object.defineProperty(b,a+"_",{value:b[a]})})}),C.prototype={addEventListener:function(a,b,c){if(B(b)){var d=new v(a,b,c),e=O.get(this);if(e){for(var f=0;f<e.length;f++)if(d.equals(e[f]))return}else e=[],O.set(this,e);e.push(d);var g=D(this);g.addEventListener_(a,p,!0)}},removeEventListener:function(a,b,c){c=Boolean(c);var d=O.get(this);if(d){for(var e=0,f=!1,g=0;g<d.length;g++)d[g].type===a&&d[g].capture===c&&(e++,d[g].handler===b&&(f=!0,d[g].remove()));if(f&&1===e){var h=D(this);h.removeEventListener_(a,p,!0)}}},dispatchEvent:function(a){var b=D(this);return b.dispatchEvent_(L(a))}},ib&&K(ib,C);var kb=document.elementFromPoint;a.adjustRelatedTarget=i,a.elementFromPoint=F,a.getEventHandlerGetter=G,a.getEventHandlerSetter=H,a.wrapEventTargetMethods=E,a.wrappers.CustomEvent=$,a.wrappers.Event=w,a.wrappers.EventTarget=C,a.wrappers.FocusEvent=db,a.wrappers.MouseEvent=cb,a.wrappers.MutationEvent=eb,a.wrappers.UIEvent=Z}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a,b){Object.defineProperty(a,b,{enumerable:!1})}function c(){this.length=0,b(this,"length")}function d(a){if(null==a)return a;for(var b=new c,d=0,e=a.length;e>d;d++)b[d]=f(a[d]);return b.length=e,b}function e(a,b){a.prototype[b]=function(){return d(this.impl[b].apply(this.impl,arguments))}}var f=a.wrap;c.prototype={item:function(a){return this[a]}},b(c.prototype,"item"),a.wrappers.NodeList=c,a.addWrapNodeListMethod=e,a.wrapNodeList=d}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){o(a instanceof k)}function c(a,b,c,d){if(!(a instanceof DocumentFragment))return a.parentNode&&a.parentNode.removeChild(a),a.parentNode_=b,a.previousSibling_=c,a.nextSibling_=d,c&&(c.nextSibling_=a),d&&(d.previousSibling_=a),[a];for(var e,f=[];e=a.firstChild;)a.removeChild(e),f.push(e),e.parentNode_=b;for(var g=0;g<f.length;g++)f[g].previousSibling_=f[g-1]||c,f[g].nextSibling_=f[g+1]||d;return c&&(c.nextSibling_=f[0]),d&&(d.previousSibling_=f[f.length-1]),f}function d(a){if(a instanceof DocumentFragment){for(var b=[],c=0,d=a.firstChild;d;d=d.nextSibling)b[c++]=d;return b}return[a]}function e(a){for(var b=0;b<a.length;b++)a[b].nodeWasAdded_()}function f(a,b){var c=a.nodeType===k.DOCUMENT_NODE?a:a.ownerDocument;c!==b.ownerDocument&&c.adoptNode(b)}function g(b,c){if(c.length){var d=b.ownerDocument;if(d!==c[0].ownerDocument)for(var e=0;e<c.length;e++)a.adoptNodeNoRemove(c[e],d)}}function h(a,b){g(a,b);var c=b.length;if(1===c)return r(b[0]);for(var d=r(a.ownerDocument.createDocumentFragment()),e=0;c>e;e++)d.appendChild(r(b[e]));return d}function i(a){if(a.invalidateShadowRenderer()){for(var b=a.firstChild;b;){o(b.parentNode===a);var c=b.nextSibling,d=r(b),e=d.parentNode;e&&y.call(e,d),b.previousSibling_=b.nextSibling_=b.parentNode_=null,b=c}a.firstChild_=a.lastChild_=null}else for(var c,f=r(a),g=f.firstChild;g;)c=g.nextSibling,y.call(f,g),g=c}function j(a){var b=a.parentNode;return b&&b.invalidateShadowRenderer()}function k(a){o(a instanceof u),l.call(this,a),this.parentNode_=void 0,this.firstChild_=void 0,this.lastChild_=void 0,this.nextSibling_=void 0,this.previousSibling_=void 0}var l=a.wrappers.EventTarget,m=a.wrappers.NodeList,n=a.defineWrapGetter,o=a.assert,p=a.mixin,q=a.registerWrapper,r=a.unwrap,s=a.wrap,t=a.wrapIfNeeded,u=window.Node,v=u.prototype.appendChild,w=u.prototype.insertBefore,x=u.prototype.replaceChild,y=u.prototype.removeChild,z=u.prototype.compareDocumentPosition;k.prototype=Object.create(l.prototype),p(k.prototype,{appendChild:function(a){b(a);var g;if(this.invalidateShadowRenderer()||j(a)){var i=this.lastChild,k=null;g=c(a,this,i,k),this.lastChild_=g[g.length-1],i||(this.firstChild_=g[0]),v.call(this.impl,h(this,g))}else g=d(a),f(this,a),v.call(this.impl,r(a));return e(g),a},insertBefore:function(a,i){if(!i)return this.appendChild(a);b(a),b(i),o(i.parentNode===this);var k;if(this.invalidateShadowRenderer()||j(a)){var l=i.previousSibling,m=i;k=c(a,this,l,m),this.firstChild===i&&(this.firstChild_=k[0]);var n=r(i),p=n.parentNode;p?w.call(p,h(this,k),n):g(this,k)}else k=d(a),f(this,a),w.call(this.impl,r(a),r(i));return e(k),a},removeChild:function(a){if(b(a),a.parentNode!==this)throw new Error("NotFoundError");var c=r(a);if(this.invalidateShadowRenderer()){var d=this.firstChild,e=this.lastChild,f=a.nextSibling,g=a.previousSibling,h=c.parentNode;h&&y.call(h,c),d===a&&(this.firstChild_=f),e===a&&(this.lastChild_=g),g&&(g.nextSibling_=f),f&&(f.previousSibling_=g),a.previousSibling_=a.nextSibling_=a.parentNode_=void 0}else y.call(this.impl,c);return a},replaceChild:function(a,g){if(b(a),b(g),g.parentNode!==this)throw new Error("NotFoundError");var i,k=r(g);if(this.invalidateShadowRenderer()||j(a)){var l=g.previousSibling,m=g.nextSibling;m===a&&(m=a.nextSibling),i=c(a,this,l,m),this.firstChild===g&&(this.firstChild_=i[0]),this.lastChild===g&&(this.lastChild_=i[i.length-1]),g.previousSibling_=g.nextSibling_=g.parentNode_=void 0,k.parentNode&&x.call(k.parentNode,h(this,i),k)}else i=d(a),f(this,a),x.call(this.impl,r(a),k);return e(i),g},nodeWasAdded_:function(){},hasChildNodes:function(){return null===this.firstChild},get parentNode(){return void 0!==this.parentNode_?this.parentNode_:s(this.impl.parentNode)},get firstChild(){return void 0!==this.firstChild_?this.firstChild_:s(this.impl.firstChild)},get lastChild(){return void 0!==this.lastChild_?this.lastChild_:s(this.impl.lastChild)},get nextSibling(){return void 0!==this.nextSibling_?this.nextSibling_:s(this.impl.nextSibling)},get previousSibling(){return void 0!==this.previousSibling_?this.previousSibling_:s(this.impl.previousSibling)},get parentElement(){for(var a=this.parentNode;a&&a.nodeType!==k.ELEMENT_NODE;)a=a.parentNode;return a},get textContent(){for(var a="",b=this.firstChild;b;b=b.nextSibling)a+=b.textContent;return a},set textContent(a){if(this.invalidateShadowRenderer()){if(i(this),""!==a){var b=this.impl.ownerDocument.createTextNode(a);this.appendChild(b)}}else this.impl.textContent=a},get childNodes(){for(var a=new m,b=0,c=this.firstChild;c;c=c.nextSibling)a[b++]=c;return a.length=b,a},cloneNode:function(a){if(!this.invalidateShadowRenderer())return s(this.impl.cloneNode(a));var b=s(this.impl.cloneNode(!1));if(a)for(var c=this.firstChild;c;c=c.nextSibling)b.appendChild(c.cloneNode(!0));return b},contains:function(a){if(!a)return!1;if(a=t(a),a===this)return!0;var b=a.parentNode;return b?this.contains(b):!1},compareDocumentPosition:function(a){return z.call(this.impl,r(a))}}),n(k,"ownerDocument"),q(u,k,document.createDocumentFragment()),delete k.prototype.querySelector,delete k.prototype.querySelectorAll,k.prototype=p(Object.create(l.prototype),k.prototype),a.wrappers.Node=k}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a,c){for(var d,e=a.firstElementChild;e;){if(e.matches(c))return e;if(d=b(e,c))return d;e=e.nextElementSibling}return null}function c(a,b,d){for(var e=a.firstElementChild;e;)e.matches(b)&&(d[d.length++]=e),c(e,b,d),e=e.nextElementSibling;return d}var d={querySelector:function(a){return b(this,a)},querySelectorAll:function(a){return c(this,a,new NodeList)}},e={getElementsByTagName:function(a){return this.querySelectorAll(a)},getElementsByClassName:function(a){return this.querySelectorAll("."+a)},getElementsByTagNameNS:function(a,b){if("*"===a)return this.getElementsByTagName(b);for(var c=new NodeList,d=this.getElementsByTagName(b),e=0,f=0;e<d.length;e++)d[e].namespaceURI===a&&(c[f++]=d[e]);
+return c.length=f,c}};a.GetElementsByInterface=e,a.SelectorsInterface=d}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){for(;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}function c(a){for(;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}var d=a.wrappers.NodeList,e={get firstElementChild(){return b(this.firstChild)},get lastElementChild(){return c(this.lastChild)},get childElementCount(){for(var a=0,b=this.firstElementChild;b;b=b.nextElementSibling)a++;return a},get children(){for(var a=new d,b=0,c=this.firstElementChild;c;c=c.nextElementSibling)a[b++]=c;return a.length=b,a}},f={get nextElementSibling(){return b(this.nextSibling)},get previousElementSibling(){return c(this.previousSibling)}};a.ChildNodeInterface=f,a.ParentNodeInterface=e}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){d.call(this,a)}var c=a.ChildNodeInterface,d=a.wrappers.Node,e=a.mixin,f=a.registerWrapper,g=window.CharacterData;b.prototype=Object.create(d.prototype),e(b.prototype,{get textContent(){return this.data},set textContent(a){this.data=a}}),e(b.prototype,c),f(g,b,document.createTextNode("")),a.wrappers.CharacterData=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(b,c){var d=b.parentNode;if(d&&d.shadowRoot){var e=a.getRendererForHost(d);e.dependsOnAttribute(c)&&e.invalidate()}}function c(a){g.call(this,a)}function d(a,c,d){var e=d||c;Object.defineProperty(a,c,{get:function(){return this.impl[c]},set:function(a){this.impl[c]=a,b(this,e)},configurable:!0,enumerable:!0})}var e=a.ChildNodeInterface,f=a.GetElementsByInterface,g=a.wrappers.Node,h=a.ParentNodeInterface,i=a.SelectorsInterface;a.addWrapNodeListMethod;var j=a.mixin,k=a.oneOf,l=a.registerWrapper,m=a.wrappers,n=new WeakMap,o=window.Element,p=k(o.prototype,["matches","mozMatchesSelector","msMatchesSelector","webkitMatchesSelector"]),q=o.prototype[p];c.prototype=Object.create(g.prototype),j(c.prototype,{createShadowRoot:function(){var b=new m.ShadowRoot(this);n.set(this,b);var c=a.getRendererForHost(this);return c.invalidate(),b},get shadowRoot(){return n.get(this)||null},setAttribute:function(a,c){this.impl.setAttribute(a,c),b(this,a)},removeAttribute:function(a){this.impl.removeAttribute(a),b(this,a)},matches:function(a){return q.call(this.impl,a)}}),c.prototype[p]=function(a){return this.matches(a)},o.prototype.webkitCreateShadowRoot&&(c.prototype.webkitCreateShadowRoot=c.prototype.createShadowRoot),d(c.prototype,"id"),d(c.prototype,"className","class"),j(c.prototype,e),j(c.prototype,f),j(c.prototype,h),j(c.prototype,i),l(o,c),a.matchesName=p,a.wrappers.Element=c}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){switch(a){case"&":return"&";case"<":return"<";case'"':return"""}}function c(a){return a.replace(p,b)}function d(a){switch(a.nodeType){case Node.ELEMENT_NODE:for(var b,d=a.tagName.toLowerCase(),f="<"+d,g=a.attributes,h=0;b=g[h];h++)f+=" "+b.name+'="'+c(b.value)+'"';return f+=">",q[d]?f:f+e(a)+"</"+d+">";case Node.TEXT_NODE:return c(a.nodeValue);case Node.COMMENT_NODE:return"<!--"+c(a.nodeValue)+"-->";default:throw console.error(a),new Error("not implemented")}}function e(a){for(var b="",c=a.firstChild;c;c=c.nextSibling)b+=d(c);return b}function f(a,b,c){var d=c||"div";a.textContent="";var e=n(a.ownerDocument.createElement(d));e.innerHTML=b;for(var f;f=e.firstChild;)a.appendChild(o(f))}function g(a){j.call(this,a)}function h(b){k(g,b,function(){return a.renderAllPending(),this.impl[b]})}function i(b){Object.defineProperty(g.prototype,b,{value:function(){return a.renderAllPending(),this.impl[b].apply(this.impl,arguments)},configurable:!0,enumerable:!0})}var j=a.wrappers.Element,k=a.defineGetter,l=a.mixin,m=a.registerWrapper,n=a.unwrap,o=a.wrap,p=/&|<|"/g,q={area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},r=window.HTMLElement;g.prototype=Object.create(j.prototype),l(g.prototype,{get innerHTML(){return e(this)},set innerHTML(a){this.invalidateShadowRenderer()?f(this,a,this.tagName):this.impl.innerHTML=a},get outerHTML(){return d(this)},set outerHTML(a){var b=this.parentNode;b&&(b.invalidateShadowRenderer(),this.impl.outerHTML=a)}}),["clientHeight","clientLeft","clientTop","clientWidth","offsetHeight","offsetLeft","offsetTop","offsetWidth","scrollHeight","scrollLeft","scrollTop","scrollWidth"].forEach(h),["getBoundingClientRect","getClientRects","scrollIntoView"].forEach(i),m(r,g,document.createElement("b")),a.wrappers.HTMLElement=g,a.getInnerHTML=e,a.setInnerHTML=f}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){c.call(this,a)}var c=a.wrappers.HTMLElement,d=a.mixin,e=a.registerWrapper,f=window.HTMLContentElement;b.prototype=Object.create(c.prototype),d(b.prototype,{get select(){return this.getAttribute("select")},set select(a){this.setAttribute("select",a)},setAttribute:function(a,b){c.prototype.setAttribute.call(this,a,b),"select"===String(a).toLowerCase()&&this.invalidateShadowRenderer(!0)}}),f&&e(f,b),a.wrappers.HTMLContentElement=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){c.call(this,a)}var c=a.wrappers.HTMLElement,d=a.mixin,e=a.registerWrapper,f=window.HTMLShadowElement;b.prototype=Object.create(c.prototype),d(b.prototype,{}),f&&e(f,b),a.wrappers.HTMLShadowElement=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){if(!a.defaultView)return a;var b=m.get(a);if(!b){for(b=a.implementation.createHTMLDocument("");b.lastChild;)b.removeChild(b.lastChild);m.set(a,b)}return b}function c(a){for(var c,d=b(a.ownerDocument),e=j(d.createDocumentFragment());c=a.firstChild;)e.appendChild(c);return e}function d(a){if(e.call(this,a),!n){var b=c(a);l.set(this,k(b))}}var e=a.wrappers.HTMLElement,f=a.getInnerHTML,g=a.mixin,h=a.registerWrapper,i=a.setInnerHTML,j=a.unwrap,k=a.wrap,l=new WeakMap,m=new WeakMap,n=window.HTMLTemplateElement;d.prototype=Object.create(e.prototype),g(d.prototype,{get content(){return n?k(this.impl.content):l.get(this)},get innerHTML(){return f(this.content)},set innerHTML(a){i(this.content,a)}}),n&&h(n,d),a.wrappers.HTMLTemplateElement=d}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){switch(a.localName){case"content":return new c(a);case"shadow":return new e(a);case"template":return new f(a)}d.call(this,a)}var c=a.wrappers.HTMLContentElement,d=a.wrappers.HTMLElement,e=a.wrappers.HTMLShadowElement,f=a.wrappers.HTMLTemplateElement;a.mixin;var g=a.registerWrapper,h=window.HTMLUnknownElement;b.prototype=Object.create(d.prototype),g(h,b),a.wrappers.HTMLUnknownElement=b}(this.ShadowDOMPolyfill),function(a){"use strict";var b=a.GetElementsByInterface,c=a.ParentNodeInterface,d=a.SelectorsInterface,e=a.mixin,f=a.registerObject,g=f(document.createDocumentFragment());e(g.prototype,c),e(g.prototype,d),e(g.prototype,b);var h=f(document.createTextNode("")),i=f(document.createComment(""));a.wrappers.Comment=i,a.wrappers.DocumentFragment=g,a.wrappers.Text=h}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){var b=i(a.impl.ownerDocument.createDocumentFragment());c.call(this,b),g(b,this);var d=a.shadowRoot;k.set(this,d),j.set(this,a)}var c=a.wrappers.DocumentFragment,d=a.elementFromPoint,e=a.getInnerHTML,f=a.mixin,g=a.rewrap,h=a.setInnerHTML,i=a.unwrap,j=new WeakMap,k=new WeakMap;b.prototype=Object.create(c.prototype),f(b.prototype,{get innerHTML(){return e(this)},set innerHTML(a){h(this,a),this.invalidateShadowRenderer()},get olderShadowRoot(){return k.get(this)||null},invalidateShadowRenderer:function(){return j.get(this).invalidateShadowRenderer()},elementFromPoint:function(a,b){return d(this,this.ownerDocument,a,b)},getElementById:function(a){return this.querySelector("#"+a)}}),a.wrappers.ShadowRoot=b,a.getHostForShadowRoot=function(a){return j.get(a)}}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){a.previousSibling_=a.previousSibling,a.nextSibling_=a.nextSibling,a.parentNode_=a.parentNode}function c(a,c,e){var f=G(a),g=G(c),h=e?G(e):null;if(d(c),b(c),e)a.firstChild===e&&(a.firstChild_=e),e.previousSibling_=e.previousSibling;else{a.lastChild_=a.lastChild,a.lastChild===a.firstChild&&(a.firstChild_=a.firstChild);var i=H(f.lastChild);i&&(i.nextSibling_=i.nextSibling)}f.insertBefore(g,h)}function d(a){var c=G(a),d=c.parentNode;if(d){var e=H(d);b(a),a.previousSibling&&(a.previousSibling.nextSibling_=a),a.nextSibling&&(a.nextSibling.previousSibling_=a),e.lastChild===a&&(e.lastChild_=a),e.firstChild===a&&(e.firstChild_=a),d.removeChild(c)}}function e(a,b){g(b).push(a),x(a,b);var c=J.get(a);c||J.set(a,c=[]),c.push(b)}function f(a){I.set(a,[])}function g(a){return I.get(a)}function h(a){for(var b=[],c=0,d=a.firstChild;d;d=d.nextSibling)b[c++]=d;return b}function i(a,b,c){for(var d=a.firstChild;d;d=d.nextSibling)if(b(d)){if(c(d)===!1)return}else i(d,b,c)}function j(a,b){var c=b.getAttribute("select");if(!c)return!0;if(c=c.trim(),!c)return!0;if(!(a instanceof y))return!1;if(!N.test(c))return!1;if(":"===c[0]&&!O.test(c))return!1;try{return a.matches(c)}catch(d){return!1}}function k(){for(var a=0;a<Q.length;a++)Q[a].render();Q=[]}function l(){E=null,k()}function m(a){var b=L.get(a);return b||(b=new q(a),L.set(a,b)),b}function n(a){for(;a;a=a.parentNode)if(a instanceof C)return a;return null}function o(a){return m(D(a))}function p(a){this.skip=!1,this.node=a,this.childNodes=[]}function q(a){this.host=a,this.dirty=!1,this.invalidateAttributes(),this.associateNode(a)}function r(a){return a instanceof z}function s(a){return a instanceof z}function t(a){return a instanceof A}function u(a){return a instanceof A}function v(a){return a.shadowRoot}function w(a){for(var b=[],c=a.shadowRoot;c;c=c.olderShadowRoot)b.push(c);return b}function x(a,b){K.set(a,b)}var y=a.wrappers.Element,z=a.wrappers.HTMLContentElement,A=a.wrappers.HTMLShadowElement,B=a.wrappers.Node,C=a.wrappers.ShadowRoot;a.assert;var D=a.getHostForShadowRoot;a.mixin;var E,F=a.oneOf,G=a.unwrap,H=a.wrap,I=new WeakMap,J=new WeakMap,K=new WeakMap,L=new WeakMap,M=new WeakMap,N=/^[*.:#[a-zA-Z_|]/,O=new RegExp("^:("+["link","visited","target","enabled","disabled","checked","indeterminate","nth-child","nth-last-child","nth-of-type","nth-last-of-type","first-child","last-child","first-of-type","last-of-type","only-of-type"].join("|")+")"),P=F(window,["requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","setTimeout"]),Q=[],R=new ArraySplice;R.equals=function(a,b){return G(a.node)===b},p.prototype={append:function(a){var b=new p(a);return this.childNodes.push(b),b},sync:function(a){if(!this.skip){for(var b=this.node,e=this.childNodes,f=h(G(b)),g=a||new WeakMap,i=R.calculateSplices(e,f),j=0,k=0,l=0,m=0;m<i.length;m++){for(var n=i[m];l<n.index;l++)k++,e[j++].sync(g);for(var o=n.removed.length,p=0;o>p;p++){var q=H(f[k++]);g.get(q)||d(q)}for(var r=n.addedCount,s=f[k]&&H(f[k]),p=0;r>p;p++){var t=e[j++],u=t.node;c(b,u,s),g.set(u,!0),t.sync(g)}l+=r}for(var m=l;m<e.length;m++)e[m++].sync(g)}}},q.prototype={render:function(a){if(this.dirty){this.invalidateAttributes(),this.treeComposition();var b=this.host,c=b.shadowRoot;this.associateNode(b);for(var d=!e,e=a||new p(b),f=c.firstChild;f;f=f.nextSibling)this.renderNode(c,e,f,!1);d&&e.sync(),this.dirty=!1}},invalidate:function(){if(!this.dirty){if(this.dirty=!0,Q.push(this),E)return;E=window[P](l,0)}},renderNode:function(a,b,c,d){if(v(c)){b=b.append(c);var e=m(c);e.dirty=!0,e.render(b)}else r(c)?this.renderInsertionPoint(a,b,c,d):t(c)?this.renderShadowInsertionPoint(a,b,c):this.renderAsAnyDomTree(a,b,c,d)},renderAsAnyDomTree:function(a,b,c,d){if(b=b.append(c),v(c)){var e=m(c);b.skip=!e.dirty,e.render(b)}else for(var f=c.firstChild;f;f=f.nextSibling)this.renderNode(a,b,f,d)},renderInsertionPoint:function(a,b,c,d){var e=g(c);if(e.length){this.associateNode(c);for(var f=0;f<e.length;f++){var h=e[f];r(h)&&d?this.renderInsertionPoint(a,b,h,d):this.renderAsAnyDomTree(a,b,h,d)}}else this.renderFallbackContent(a,b,c);this.associateNode(c.parentNode)},renderShadowInsertionPoint:function(a,b,c){var d=a.olderShadowRoot;if(d){x(d,c),this.associateNode(c.parentNode);for(var e=d.firstChild;e;e=e.nextSibling)this.renderNode(d,b,e,!0)}else this.renderFallbackContent(a,b,c)},renderFallbackContent:function(a,b,c){this.associateNode(c),this.associateNode(c.parentNode);for(var d=c.firstChild;d;d=d.nextSibling)this.renderAsAnyDomTree(a,b,d,!1)},invalidateAttributes:function(){this.attributes=Object.create(null)},updateDependentAttributes:function(a){if(a){var b=this.attributes;/\.\w+/.test(a)&&(b["class"]=!0),/#\w+/.test(a)&&(b.id=!0),a.replace(/\[\s*([^\s=\|~\]]+)/g,function(a,c){b[c]=!0})}},dependsOnAttribute:function(a){return this.attributes[a]},distribute:function(a,b){var c=this;i(a,s,function(a){f(a),c.updateDependentAttributes(a.getAttribute("select"));for(var d=0;d<b.length;d++){var g=b[d];void 0!==g&&j(g,a)&&(e(g,a),b[d]=void 0)}})},treeComposition:function(){for(var a=this.host,b=a.shadowRoot,c=[],d=a.firstChild;d;d=d.nextSibling)if(r(d)){var e=g(d);e&&e.length||(e=h(d)),c.push.apply(c,e)}else c.push(d);for(var f,j;b;){if(f=void 0,i(b,u,function(a){return f=a,!1}),j=f,this.distribute(b,c),j){var k=b.olderShadowRoot;if(k){b=k,x(b,j);continue}break}break}},associateNode:function(a){M.set(a,this)}},B.prototype.invalidateShadowRenderer=function(){var a=M.get(this);return a?(a.invalidate(),!0):!1},z.prototype.getDistributedNodes=function(){var a=M.get(this);return a&&a.render(),g(this)},A.prototype.nodeWasAdded_=z.prototype.nodeWasAdded_=function(){this.invalidateShadowRenderer();var a,b=n(this);b&&(a=o(b)),M.set(this,a),a&&a.invalidate()},a.eventParentsTable=J,a.getRendererForHost=m,a.getShadowTrees=w,a.insertionParentTable=K,a.renderAllPending=k,a.visual={insertBefore:c,remove:d}}(this.ShadowDOMPolyfill),function(a){"use strict";function b(b){if(window[b]){d(!a.wrappers[b]);var i=function(a){c.call(this,a)};i.prototype=Object.create(c.prototype),e(i.prototype,{get form(){return h(g(this).form)}}),f(window[b],i,document.createElement(b.slice(4,-7))),a.wrappers[b]=i}}var c=a.wrappers.HTMLElement,d=a.assert,e=a.mixin,f=a.registerWrapper,g=a.unwrap,h=a.wrap,i=["HTMLButtonElement","HTMLFieldSetElement","HTMLInputElement","HTMLKeygenElement","HTMLLabelElement","HTMLLegendElement","HTMLObjectElement","HTMLOptionElement","HTMLOutputElement","HTMLSelectElement","HTMLTextAreaElement"];i.forEach(b)}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){k.call(this,a)}function c(a){var c=document[a];b.prototype[a]=function(){return v(c.apply(this.impl,arguments))}}function d(a,b){y.call(b.impl,u(a)),e(a,b)}function e(a,b){a.shadowRoot&&b.adoptNode(a.shadowRoot),a instanceof n&&f(a,b);for(var c=a.firstChild;c;c=c.nextSibling)e(c,b)}function f(a,b){var c=a.olderShadowRoot;c&&b.adoptNode(c)}function g(a){this.impl=a}function h(a,b){var c=document.implementation[b];a.prototype[b]=function(){return v(c.apply(this.impl,arguments))}}function i(a,b){var c=document.implementation[b];a.prototype[b]=function(){return c.apply(this.impl,arguments)}}var j=a.GetElementsByInterface,k=a.wrappers.Node,l=a.ParentNodeInterface,m=a.SelectorsInterface,n=a.wrappers.ShadowRoot,o=a.defineWrapGetter,p=a.elementFromPoint,q=a.forwardMethodsToWrapper,r=a.matchesName,s=a.mixin,t=a.registerWrapper,u=a.unwrap,v=a.wrap,w=a.wrapEventTargetMethods;a.wrapNodeList;var x=new WeakMap;b.prototype=Object.create(k.prototype),o(b,"documentElement"),o(b,"body"),o(b,"head"),["createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","getElementById"].forEach(c);var y=document.adoptNode;if(s(b.prototype,{adoptNode:function(a){return a.parentNode&&a.parentNode.removeChild(a),d(a,this),a},elementFromPoint:function(a,b){return p(this,this,a,b)}}),document.register){var z=document.register;b.prototype.register=function(b,c){function d(a){return a?(this.impl=a,void 0):document.createElement(b)}var e=c.prototype;if(a.nativePrototypeTable.get(e))throw new Error("NotSupportedError");for(var f,g=Object.getPrototypeOf(e),h=[];g&&!(f=a.nativePrototypeTable.get(g));)h.push(g),g=Object.getPrototypeOf(g);if(!f)throw new Error("NotSupportedError");for(var i=Object.create(f),j=h.length-1;j>=0;j--)i=Object.create(i);return["createdCallback","enteredDocumentCallback","leftDocumentCallback","attributeChangedCallback"].forEach(function(a){var b=e[a];b&&(i[a]=function(){b.apply(v(this),arguments)})}),z.call(u(this),b,{prototype:i}),d.prototype=e,d.prototype.constructor=d,a.constructorTable.set(i,d),a.nativePrototypeTable.set(e,i),d},q([window.HTMLDocument||window.Document],["register"])}q([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement,window.HTMLHtmlElement],["appendChild","compareDocumentPosition","contains","getElementsByClassName","getElementsByTagName","getElementsByTagNameNS","insertBefore","querySelector","querySelectorAll","removeChild","replaceChild",r]),q([window.HTMLDocument||window.Document],["adoptNode","contains","createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","elementFromPoint","getElementById"]),s(b.prototype,j),s(b.prototype,l),s(b.prototype,m),s(b.prototype,{get implementation(){var a=x.get(this);return a?a:(a=new g(u(this).implementation),x.set(this,a),a)}}),t(window.Document,b,document.implementation.createHTMLDocument("")),window.HTMLDocument&&t(window.HTMLDocument,b),w([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement]),h(g,"createDocumentType"),h(g,"createDocument"),h(g,"createHTMLDocument"),i(g,"hasFeature"),t(window.DOMImplementation,g),q([window.DOMImplementation],["createDocumentType","createDocument","createHTMLDocument","hasFeature"]),a.adoptNodeNoRemove=d,a.wrappers.DOMImplementation=g,a.wrappers.Document=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){c.call(this,a)}var c=a.wrappers.EventTarget,d=a.mixin,e=a.registerWrapper,f=a.unwrap,g=a.unwrapIfNeeded,h=a.wrap,i=a.renderAllPending,j=window.Window;b.prototype=Object.create(c.prototype);var k=window.getComputedStyle;j.prototype.getComputedStyle=function(a,b){return i(),k.call(this||window,g(a),b)},["addEventListener","removeEventListener","dispatchEvent"].forEach(function(a){j.prototype[a]=function(){var b=h(this||window);return b[a].apply(b,arguments)}}),d(b.prototype,{getComputedStyle:function(a,b){return k.call(f(this),g(a),b)}}),e(j,b),a.wrappers.Window=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){this.impl=a}function c(a){return new b(a)}function d(a){return a.map(c)}function e(a){var b=this;this.impl=new k(function(c){a.call(b,d(c),b)})}var f=a.defineGetter,g=a.defineWrapGetter,h=a.registerWrapper,i=a.unwrapIfNeeded,j=a.wrapNodeList;a.wrappers;var k=window.MutationObserver||window.WebKitMutationObserver;if(k){var l=window.MutationRecord;b.prototype={get addedNodes(){return j(this.impl.addedNodes)},get removedNodes(){return j(this.impl.removedNodes)}},["target","previousSibling","nextSibling"].forEach(function(a){g(b,a)}),["type","attributeName","attributeNamespace","oldValue"].forEach(function(a){f(b,a,function(){return this.impl[a]})}),l&&h(l,b),window.Node,e.prototype={observe:function(a,b){this.impl.observe(i(a),b)},disconnect:function(){this.impl.disconnect()},takeRecords:function(){return d(this.impl.takeRecords())}},a.wrappers.MutationObserver=e,a.wrappers.MutationRecord=b}}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){this.impl=a}var c=a.registerWrapper,d=a.unwrap,e=a.unwrapIfNeeded,f=a.wrap,g=window.Range;b.prototype={get startContainer(){return f(this.impl.startContainer)},get endContainer(){return f(this.impl.endContainer)},get commonAncestorContainer(){return f(this.impl.commonAncestorContainer)},setStart:function(a,b){this.impl.setStart(e(a),b)},setEnd:function(a,b){this.impl.setEnd(e(a),b)},setStartBefore:function(a){this.impl.setStartBefore(e(a))},setStartAfter:function(a){this.impl.setStartAfter(e(a))},setEndBefore:function(a){this.impl.setEndBefore(e(a))},setEndAfter:function(a){this.impl.setEndAfter(e(a))},selectNode:function(a){this.impl.selectNode(e(a))},selectNodeContents:function(a){this.impl.selectNodeContents(e(a))},compareBoundaryPoints:function(a,b){return this.impl.compareBoundaryPoints(a,d(b))},extractContents:function(){return f(this.impl.extractContents())},cloneContents:function(){return f(this.impl.cloneContents())},insertNode:function(a){this.impl.insertNode(e(a))},surroundContents:function(a){this.impl.surroundContents(e(a))},cloneRange:function(){return f(this.impl.cloneRange())},isPointInRange:function(a,b){return this.impl.isPointInRange(e(a),b)},comparePoint:function(a,b){return this.impl.comparePoint(e(a),b)},intersectsNode:function(a){return this.impl.intersectsNode(e(a))}},g.prototype.createContextualFragment&&(b.prototype.createContextualFragment=function(a){return f(this.impl.createContextualFragment(a))}),c(window.Range,b),a.wrappers.Range=b}(this.ShadowDOMPolyfill),function(a){"use strict";function b(a){var b=c[a],d=window[b];if(d){var e=document.createElement(a),f=e.constructor;window[b]=f}}a.isWrapperFor;var c={a:"HTMLAnchorElement",applet:"HTMLAppletElement",area:"HTMLAreaElement",audio:"HTMLAudioElement",br:"HTMLBRElement",base:"HTMLBaseElement",body:"HTMLBodyElement",button:"HTMLButtonElement",canvas:"HTMLCanvasElement",dl:"HTMLDListElement",datalist:"HTMLDataListElement",dir:"HTMLDirectoryElement",div:"HTMLDivElement",embed:"HTMLEmbedElement",fieldset:"HTMLFieldSetElement",font:"HTMLFontElement",form:"HTMLFormElement",frame:"HTMLFrameElement",frameset:"HTMLFrameSetElement",hr:"HTMLHRElement",head:"HTMLHeadElement",h1:"HTMLHeadingElement",html:"HTMLHtmlElement",iframe:"HTMLIFrameElement",input:"HTMLInputElement",li:"HTMLLIElement",label:"HTMLLabelElement",legend:"HTMLLegendElement",link:"HTMLLinkElement",map:"HTMLMapElement",menu:"HTMLMenuElement",menuitem:"HTMLMenuItemElement",meta:"HTMLMetaElement",meter:"HTMLMeterElement",del:"HTMLModElement",ol:"HTMLOListElement",object:"HTMLObjectElement",optgroup:"HTMLOptGroupElement",option:"HTMLOptionElement",output:"HTMLOutputElement",p:"HTMLParagraphElement",param:"HTMLParamElement",pre:"HTMLPreElement",progress:"HTMLProgressElement",q:"HTMLQuoteElement",script:"HTMLScriptElement",select:"HTMLSelectElement",source:"HTMLSourceElement",span:"HTMLSpanElement",style:"HTMLStyleElement",caption:"HTMLTableCaptionElement",col:"HTMLTableColElement",table:"HTMLTableElement",tr:"HTMLTableRowElement",thead:"HTMLTableSectionElement",tbody:"HTMLTableSectionElement",textarea:"HTMLTextAreaElement",title:"HTMLTitleElement",ul:"HTMLUListElement",video:"HTMLVideoElement"};Object.keys(c).forEach(b),Object.getOwnPropertyNames(a.wrappers).forEach(function(b){window[b]=a.wrappers[b]}),a.knownElements=c}(this.ShadowDOMPolyfill),function(){var a=window.ShadowDOMPolyfill;a.wrap,Object.defineProperties(HTMLElement.prototype,{webkitShadowRoot:{get:function(){return this.shadowRoot}}}),HTMLElement.prototype.webkitCreateShadowRoot=HTMLElement.prototype.createShadowRoot,window.dartExperimentalFixupGetTag=function(b){function c(a){if(a instanceof d)return"NodeList";if(a instanceof e)return"ShadowRoot";if(a instanceof MutationRecord)return"MutationRecord";if(a instanceof MutationObserver)return"MutationObserver";var c=f(a);if(a!==c){var g=a.constructor;if(g===c.constructor){var h=g._ShadowDOMPolyfill$cacheTag_;return h||(h=Object.prototype.toString.call(c),h=h.substring(8,h.length-1),g._ShadowDOMPolyfill$cacheTag_=h),h}a=c}return b(a)}var d=a.wrappers.NodeList,e=a.wrappers.ShadowRoot,f=a.unwrapIfNeeded;return c}}();var Platform={};!function(a){function b(a,b){var c="";return Array.prototype.forEach.call(a,function(a){c+=a.textContent+"\n\n"}),b||(c=c.replace(m,"")),c}function c(a){var b=document.createElement("style");b.textContent=a,document.head.appendChild(b);var c=b.sheet.cssRules;return b.parentNode.removeChild(b),c}function d(a){for(var b=0,c=[];b<a.length;b++)c.push(a[b].cssText);return c.join("\n\n")}function e(a){a&&f().appendChild(document.createTextNode(a))}function f(){return g||(g=document.createElement("style"),g.setAttribute("ShadowCSSShim","")),g}var g,h={strictStyling:!1,registry:{},shimStyling:function(a,b,c){if(a){var d=this.registerDefinition(a,b,c);this.strictStyling&&this.applyScopeToContent(a,b),this.shimPolyfillDirectives(d.rootStyles,b),this.applyShimming(d.scopeStyles,b)}},shimShadowDOMStyling:function(a,b){this.shimPolyfillDirectives(a,b),this.applyShimming(a,b)},registerDefinition:function(a,b,c){var d=this.registry[b]={root:a,name:b,extendsName:c},e=a.querySelectorAll("style");e=e?Array.prototype.slice.call(e,0):[],d.rootStyles=e,d.scopeStyles=d.rootStyles;var f=this.registry[d.extendsName];return f&&(d.scopeStyles=d.scopeStyles.concat(f.scopeStyles)),d},applyScopeToContent:function(a,b){a&&(Array.prototype.forEach.call(a.querySelectorAll("*"),function(a){a.setAttribute(b,"")}),Array.prototype.forEach.call(a.querySelectorAll("template"),function(a){this.applyScopeToContent(a.content,b)},this))},shimPolyfillDirectives:function(a,b){a&&Array.prototype.forEach.call(a,function(a){a.textContent=this.convertPolyfillDirectives(a.textContent,b)},this)},convertPolyfillDirectives:function(a,b){for(var c,d,e="",f=0;c=n.exec(a);)e+=a.substring(f,c.index),d=c[1].slice(0,-2).replace(q,b),e+=this.scopeSelector(d,b)+"{",f=n.lastIndex;return e+=a.substring(f,a.length)},applyShimming:function(a,b){var c=this.shimAtHost(a,b);c+=this.shimScoping(a,b),e(c)},shimAtHost:function(a,b){return a?this.convertAtHostStyles(a,b):void 0},convertAtHostStyles:function(a,e){for(var f,g=b(a),h="",j=0;f=i.exec(g);)h+=g.substring(j,f.index),h+=this.scopeHostCss(f[1],e),j=i.lastIndex;h+=g.substring(j,g.length);var k=new RegExp("^"+e+p,"m"),g=d(this.findAtHostRules(c(h),k));return g},scopeHostCss:function(a,b){for(var c,d="";c=j.exec(a);)d+=this.scopeHostSelector(c[1],b)+" "+c[2]+"\n ";return d},scopeHostSelector:function(a,b){var c=[],d=a.split(","),e="[is="+b+"]";return d.forEach(function(a){a=a.trim(),a.match(k)?a=a.replace(k,b+"$1$3, "+e+"$1$3"):a.match(l)&&(a=b+a+", "+e+a),c.push(a)},this),c.join(", ")},findAtHostRules:function(a,b){return Array.prototype.filter.call(a,this.isHostRule.bind(this,b))},isHostRule:function(a,b){return b.selectorText&&b.selectorText.match(a)||b.cssRules&&this.findAtHostRules(b.cssRules,a).length||b.type==CSSRule.WEBKIT_KEYFRAMES_RULE},shimScoping:function(a,b){return a?this.convertScopedStyles(a,b):void 0},convertScopedStyles:function(a,d){Array.prototype.forEach.call(a,function(a){a.parentNode&&a.parentNode.removeChild(a)});var e=b(a).replace(i,"");e=this.convertPseudos(e);var f=c(e);return e=this.scopeRules(f,d)},convertPseudos:function(a){return a.replace(o," [pseudo=$1]")},scopeRules:function(a,b){var c="";return Array.prototype.forEach.call(a,function(a){a.selectorText&&a.style&&a.style.cssText?(c+=this.scopeSelector(a.selectorText,b,this.strictStyling)+" {\n ",c+=this.propertiesFromRule(a)+"\n}\n\n"):a.media?(c+="@media "+a.media.mediaText+" {\n",c+=this.scopeRules(a.cssRules,b),c+="\n}\n\n"):a.cssText&&(c+=a.cssText+"\n\n")},this),c},scopeSelector:function(a,b,c){var d=[],e=a.split(",");return e.forEach(function(a){a=a.trim(),this.selectorNeedsScoping(a,b)&&(a=c?this.applyStrictSelectorScope(a,b):this.applySimpleSelectorScope(a,b)),d.push(a)},this),d.join(", ")},selectorNeedsScoping:function(a,b){var c="("+b+"|\\[is="+b+"\\])",d=new RegExp("^"+c+p,"m");return!a.match(d)},applySimpleSelectorScope:function(a,b){return b+" "+a+", "+"[is="+b+"] "+a},applyStrictSelectorScope:function(a,b){var c=[" ",">","+","~"],d=a,e="["+b+"]";return c.forEach(function(a){var b=d.split(a);d=b.map(function(a){var b=a.trim();return b&&c.indexOf(b)<0&&b.indexOf(e)<0&&(a=b.replace(/([^:]*)(:*)(.*)/,"$1"+e+"$2$3")),a}).join(a)}),d},propertiesFromRule:function(a){var b=a.style.cssText;return a.style.content&&!a.style.content.match(/['"]+/)&&(b="content: '"+a.style.content+"';\n"+a.style.cssText.replace(/content:[^;]*;/g,"")),b}},i=/@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,j=/([^{]*)({[\s\S]*?})/gim,k=/(.*)((?:\*)|(?:\:scope))(.*)/,l=/^[.\[:]/,m=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,n=/\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim,o=/::(x-[^\s{,(]*)/gim,p="([>\\s~+[.,{:][\\s\\S]*)?$",q=/@host/gim;if(window.ShadowDOMPolyfill){e("style { display: none !important; }\n");var r=document.querySelector("head");r.insertBefore(f(),r.childNodes[0])}a.ShadowCSS=h}(window.Platform),function(a){function b(a,b){if(window.ShadowDOMPolyfill){for(var h,i=this.convertPolyfillDirectives(a,b),j="",k=0;h=e.exec(i);)j+=i.substring(k,h.index),j+=this.scopeHostCss(h[1],b),k=e.lastIndex;j+=i.substring(k,i.length);var l=new RegExp("^"+b+g,"m"),m=d(this.findAtHostRules(c(j),l));i=i.replace(f,""),i=this.convertPseudos(i);var n=c(i),o=this.scopeRules(n,b);return m+o}}function c(a){var b=document.createElement("style");b.textContent=a,document.head.appendChild(b);var c=b.sheet.cssRules;return b.parentNode.removeChild(b),c}function d(a){for(var b=0,c=[];b<a.length;b++)c.push(a[b].cssText);return c.join("\n\n")}var e=/@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,f=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,g="([>\\s~+[.,{:][\\s\\S]*)?$";a.ShadowCSS.shimShadowDOMStyling2=b}(window.Platform)}
\ No newline at end of file
diff --git a/pkg/shadow_dom/lib/src/platform/patches-shadowdom-polyfill.js b/pkg/shadow_dom/lib/src/platform/patches-shadowdom-polyfill.js
index 828a4fc..57c8c59 100644
--- a/pkg/shadow_dom/lib/src/platform/patches-shadowdom-polyfill.js
+++ b/pkg/shadow_dom/lib/src/platform/patches-shadowdom-polyfill.js
@@ -38,11 +38,9 @@
// Fix up class names for Firefox.
// For some of them (like HTMLFormElement and HTMLInputElement),
// the "constructor" property of the unwrapped nodes points at the
- // wrapper.
- // Note: it is safe to check for the GeneratedWrapper string because
- // we know it is some kind of Shadow DOM wrapper object.
- var ctor = obj.constructor;
- if (ctor && ctor.name == 'GeneratedWrapper') {
+ // same constructor as the wrapper.
+ var ctor = obj.constructor
+ if (ctor === unwrapped.constructor) {
var name = ctor._ShadowDOMPolyfill$cacheTag_;
if (!name) {
name = Object.prototype.toString.call(unwrapped);
diff --git a/pkg/shadow_dom/tool/build.sh b/pkg/shadow_dom/tool/build.sh
index 6e2adb7..b10ebe7 100755
--- a/pkg/shadow_dom/tool/build.sh
+++ b/pkg/shadow_dom/tool/build.sh
@@ -11,20 +11,24 @@
set -e
DIR=$( cd $( dirname "${BASH_SOURCE[0]}" ) && pwd )
-# Note: dartanalyzer and some tests needs to be run from the root directory
pushd $DIR > /dev/null
-SHADOWDOM_REMOTE=https://github.com/dart-lang/ShadowDOM.git
-SHADOWDOM_DIR=../../../third_party/polymer/ShadowDOM
+POLYMER_REMOTE=https://github.com/Polymer
+POLYMER_DIR=../../../third_party/polymer
-echo "*** Syncing $SHADOWDOM_DIR from $SHADOWDOM_REMOTE"
-if [ -d "$SHADOWDOM_DIR" ]; then
- pushd $SHADOWDOM_DIR > /dev/null
- git pull
- popd
-else
- git clone --branch shadowdom_patches $SHADOWDOM_REMOTE $SHADOWDOM_DIR
-fi
+for NAME in ShadowDOM observe-js WeakMap; do
+ GIT_REMOTE="$POLYMER_REMOTE/$NAME.git"
+ GIT_DIR="$POLYMER_DIR/$NAME"
+ echo "*** Syncing $GIT_DIR from $GIT_REMOTE"
+ if [ -d "$GIT_DIR" ]; then
+ pushd $GIT_DIR > /dev/null
+ git remote set-url origin $GIT_REMOTE
+ git pull
+ popd
+ else
+ git clone $GIT_REMOTE $GIT_DIR
+ fi
+done
echo '*** Installing NPM prerequisites'
npm install
diff --git a/pkg/stack_trace/lib/stack_trace.dart b/pkg/stack_trace/lib/stack_trace.dart
index f031f95..dba95e9 100644
--- a/pkg/stack_trace/lib/stack_trace.dart
+++ b/pkg/stack_trace/lib/stack_trace.dart
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
/**
+ * Stack trace generation and parsing.
+ *
* ## Installing ##
*
* Use [pub][] to install this package. Add the following to your `pubspec.yaml`
diff --git a/pkg/unittest/lib/matcher.dart b/pkg/unittest/lib/matcher.dart
index 09dd7eb..4830b8f 100644
--- a/pkg/unittest/lib/matcher.dart
+++ b/pkg/unittest/lib/matcher.dart
@@ -2,28 +2,21 @@
// 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.
/**
- * The matcher library provides a 3rd generation assertion mechanism, drawing
- * inspiration from [Hamcrest](http://code.google.com/p/hamcrest/).
+ * Support for specifying test expectations,
+ * such as for unit tests.
*
- * ## Installing ##
- *
- * Use [pub][] to install this package. Add the following to your `pubspec.yaml`
- * file.
- *
- * dependencies:
- * unittest: any
- *
- * Then run `pub install`.
- *
- * Import this into your Dart code with:
+ * This library is included in the
+ * [unittest package on pub.dartlang.org]
+ * (http://pub.dartlang.org/packages/unittest).
+ * Import this library into your Dart code with:
*
* import 'package:unittest/matcher.dart';
*
- * For more information, see the [unittest package on pub.dartlang.org].
- * (http://pub.dartlang.org/packages/unittest).
- *
- * [pub]: http://pub.dartlang.org
- * [pkg]: http://pub.dartlang.org/packages/matcher
+ * The matcher library provides a third-generation assertion mechanism, drawing
+ * inspiration from [Hamcrest](http://code.google.com/p/hamcrest/).
+ * For more information, see
+ * [Unit Testing with Dart]
+ * (http://www.dartlang.org/articles/dart-unit-tests/).
*/
library matcher;
diff --git a/pkg/unittest/lib/unittest.dart b/pkg/unittest/lib/unittest.dart
index e5923f3..4040870 100644
--- a/pkg/unittest/lib/unittest.dart
+++ b/pkg/unittest/lib/unittest.dart
@@ -3,38 +3,33 @@
// BSD-style license that can be found in the LICENSE file.
/**
- * A library for writing dart unit tests.
+ * Support for writing Dart unit tests.
*
- * ## Installing ##
+ * For information on installing and importing this library, see the
+ * [unittest package on pub.dartlang.org]
+ * (http://pub.dartlang.org/packages/unittest).
*
- * Use [pub][] to install this package. Add the following to your `pubspec.yaml`
- * file.
+ * **See also:**
+ * [Unit Testing with Dart]
+ * (http://www.dartlang.org/articles/dart-unit-tests/)
*
- * dependencies:
- * unittest: any
- *
- * Then run `pub install`.
- *
- * For more information, see the
- * [unittest package on pub.dartlang.org][pkg].
- *
- * See the [Getting Started](http://pub.dartlang.org/doc)
- * guide for more details.
- *
- * ##Concepts##
+ * ##Concepts
*
* * __Tests__: Tests are specified via the top-level function [test], they can be
* organized together using [group].
+ *
* * __Checks__: Test expectations can be specified via [expect]
+ *
* * __Matchers__: [expect] assertions are written declaratively using the
* [Matcher] class.
+ *
* * __Configuration__: The framework can be adapted by setting
* [unittestConfiguration] with a [Configuration]. See the other libraries
* in the `unittest` package for alternative implementations of
* [Configuration] including `compact_vm_config.dart`, `html_config.dart` and
* `html_enhanced_config.dart`.
*
- * ##Examples##
+ * ##Examples
*
* A trivial test:
*
@@ -131,15 +126,12 @@
* Timer.run()], as the Future exception handler may not capture exceptions
* in such code.
*
- * Note: due to some language limitations we have to use different functions
+ * Note: Due to some language limitations we have to use different functions
* depending on the number of positional arguments of the callback. In the
* future, we plan to expose a single `expectAsync` function that can be used
* regardless of the number of positional arguments. This requires new langauge
* features or fixes to the current spec (e.g. see
* [Issue 2706](http://dartbug.com/2706)).
- *
- * [pub]: http://pub.dartlang.org
- * [pkg]: http://pub.dartlang.org/packages/unittest
*/
library unittest;
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index a2fdf9d..94ca09e 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -918,7 +918,7 @@
}
-Dart_CObject* CObject::NewString(int length) {
+Dart_CObject* CObject::NewString(intptr_t length) {
Dart_CObject* cobject = New(Dart_CObject_kString, length + 1);
cobject->value.as_string = reinterpret_cast<char*>(cobject + 1);
return cobject;
@@ -933,7 +933,7 @@
}
-Dart_CObject* CObject::NewArray(int length) {
+Dart_CObject* CObject::NewArray(intptr_t length) {
Dart_CObject* cobject =
New(Dart_CObject_kArray, length * sizeof(Dart_CObject*)); // NOLINT
cobject->value.as_array.length = length;
@@ -943,7 +943,7 @@
}
-Dart_CObject* CObject::NewUint8Array(int length) {
+Dart_CObject* CObject::NewUint8Array(intptr_t length) {
Dart_CObject* cobject = New(Dart_CObject_kTypedData, length);
cobject->value.as_typed_data.type = Dart_TypedData_kUint8;
cobject->value.as_typed_data.length = length;
@@ -953,7 +953,7 @@
Dart_CObject* CObject::NewExternalUint8Array(
- int64_t length, uint8_t* data, void* peer,
+ intptr_t length, uint8_t* data, void* peer,
Dart_WeakPersistentHandleFinalizer callback) {
Dart_CObject* cobject = New(Dart_CObject_kExternalTypedData);
cobject->value.as_external_typed_data.type = Dart_TypedData_kUint8;
@@ -966,8 +966,16 @@
Dart_CObject* CObject::NewIOBuffer(int64_t length) {
+ // Make sure that we do not have an integer overflow here. Actual check
+ // against max elements will be done at the time of writing, as the constant
+ // is not part of the public API.
+ if (length > kIntptrMax) {
+ return NULL;
+ }
uint8_t* data = IOBuffer::Allocate(length);
- return NewExternalUint8Array(length, data, data, IOBuffer::Finalizer);
+ ASSERT(data != NULL);
+ return NewExternalUint8Array(
+ static_cast<intptr_t>(length), data, data, IOBuffer::Finalizer);
}
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index 85b5585..d4fc347 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -271,13 +271,14 @@
static Dart_CObject* NewIntptr(intptr_t value);
// TODO(sgjesse): Add support for kBigint.
static Dart_CObject* NewDouble(double value);
- static Dart_CObject* NewString(int length);
+ static Dart_CObject* NewString(intptr_t length);
static Dart_CObject* NewString(const char* str);
- static Dart_CObject* NewArray(int length);
- static Dart_CObject* NewUint8Array(int length);
+ static Dart_CObject* NewArray(intptr_t length);
+ static Dart_CObject* NewUint8Array(intptr_t length);
static Dart_CObject* NewExternalUint8Array(
- int64_t length, uint8_t* data, void* peer,
+ intptr_t length, uint8_t* data, void* peer,
Dart_WeakPersistentHandleFinalizer callback);
+
static Dart_CObject* NewIOBuffer(int64_t length);
static void FreeIOBufferData(Dart_CObject* object);
@@ -452,11 +453,11 @@
public:
DECLARE_COBJECT_CONSTRUCTORS(Array)
- int Length() const { return cobject_->value.as_array.length; }
- CObject* operator[](int index) const {
+ intptr_t Length() const { return cobject_->value.as_array.length; }
+ CObject* operator[](intptr_t index) const {
return new CObject(cobject_->value.as_array.values[index]);
}
- void SetAt(int index, CObject* value) {
+ void SetAt(intptr_t index, CObject* value) {
cobject_->value.as_array.values[index] = value->AsApiCObject();
}
@@ -480,7 +481,7 @@
Dart_TypedData_Type Type() const {
return cobject_->value.as_typed_data.type;
}
- int Length() const { return cobject_->value.as_typed_data.length; }
+ intptr_t Length() const { return cobject_->value.as_typed_data.length; }
uint8_t* Buffer() const { return cobject_->value.as_typed_data.values; }
private:
@@ -492,7 +493,7 @@
public:
DECLARE_COBJECT_TYPED_DATA_CONSTRUCTORS(Uint8)
- int Length() const { return cobject_->value.as_typed_data.length; }
+ intptr_t Length() const { return cobject_->value.as_typed_data.length; }
uint8_t* Buffer() const { return cobject_->value.as_typed_data.values; }
private:
@@ -504,8 +505,10 @@
public:
DECLARE_COBJECT_EXTERNAL_TYPED_DATA_CONSTRUCTORS(Uint8)
- int Length() const { return cobject_->value.as_external_typed_data.length; }
- void SetLength(uint64_t length) {
+ intptr_t Length() const {
+ return cobject_->value.as_external_typed_data.length;
+ }
+ void SetLength(intptr_t length) {
cobject_->value.as_external_typed_data.length = length;
}
uint8_t* Data() const { return cobject_->value.as_external_typed_data.data; }
diff --git a/runtime/bin/dbg_message.cc b/runtime/bin/dbg_message.cc
index 739e82c..e03a337 100644
--- a/runtime/bin/dbg_message.cc
+++ b/runtime/bin/dbg_message.cc
@@ -415,7 +415,7 @@
res = Dart_GetActivationFrame(trace, 0, &frame);
ASSERT_NOT_ERROR(res);
Dart_CodeLocation location;
- res = Dart_ActivationFrameGetLocation(frame, NULL, &location);
+ res = Dart_ActivationFrameGetLocation(frame, NULL, NULL, &location);
ASSERT_NOT_ERROR(res);
if (!Dart_IsNull(location.script_url)) {
ASSERT(Dart_IsString(location.script_url));
@@ -438,7 +438,7 @@
ASSERT_NOT_ERROR(res);
Dart_Handle func_name;
Dart_CodeLocation location;
- res = Dart_ActivationFrameGetLocation(frame, &func_name, &location);
+ res = Dart_ActivationFrameGetLocation(frame, &func_name, NULL, &location);
ASSERT_NOT_ERROR(res);
ASSERT(Dart_IsString(func_name));
msg->Printf("%s{\"functionName\":", (i > 0) ? "," : "");
diff --git a/runtime/bin/directory.cc b/runtime/bin/directory.cc
index 0f771ee..c7b499a 100644
--- a/runtime/bin/directory.cc
+++ b/runtime/bin/directory.cc
@@ -26,6 +26,10 @@
if (current != NULL) {
Dart_SetReturnValue(args, DartUtils::NewString(current));
free(current);
+ } else {
+ Dart_Handle err = DartUtils::NewDartOSError();
+ if (Dart_IsError(err)) Dart_PropagateError(err);
+ Dart_SetReturnValue(args, err);
}
}
diff --git a/runtime/bin/directory_patch.dart b/runtime/bin/directory_patch.dart
index 49eca55..314298d 100644
--- a/runtime/bin/directory_patch.dart
+++ b/runtime/bin/directory_patch.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
patch class _Directory {
- /* patch */ static String _current() native "Directory_Current";
+ /* patch */ static _current() native "Directory_Current";
/* patch */ static _setCurrent(path) native "Directory_SetCurrent";
/* patch */ static _createTemp(String template) native "Directory_CreateTemp";
/* patch */ static int _exists(String path) native "Directory_Exists";
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc
index 22c7a3a..f5f77bd 100644
--- a/runtime/bin/directory_win.cc
+++ b/runtime/bin/directory_win.cc
@@ -351,6 +351,7 @@
char* Directory::Current() {
int length = GetCurrentDirectoryW(0, NULL);
+ if (length == 0) return NULL;
wchar_t* current = new wchar_t[length + 1];
GetCurrentDirectoryW(length + 1, current);
char* result = StringUtils::WideToUtf8(current);
diff --git a/runtime/bin/eventhandler_android.cc b/runtime/bin/eventhandler_android.cc
index 975a6d1..9064be1 100644
--- a/runtime/bin/eventhandler_android.cc
+++ b/runtime/bin/eventhandler_android.cc
@@ -163,6 +163,7 @@
// WriteToBlocking will write up to 512 bytes atomically, and since our msg
// is smaller than 512, we don't need a thread lock.
// See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'.
+ ASSERT(kInterruptMessageSize < PIPE_BUF);
intptr_t result =
FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize);
if (result != kInterruptMessageSize) {
diff --git a/runtime/bin/eventhandler_linux.cc b/runtime/bin/eventhandler_linux.cc
index e95def6..a14f125 100644
--- a/runtime/bin/eventhandler_linux.cc
+++ b/runtime/bin/eventhandler_linux.cc
@@ -168,6 +168,7 @@
// WriteToBlocking will write up to 512 bytes atomically, and since our msg
// is smaller than 512, we don't need a thread lock.
// See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'.
+ ASSERT(kInterruptMessageSize < PIPE_BUF);
intptr_t result =
FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize);
if (result != kInterruptMessageSize) {
diff --git a/runtime/bin/eventhandler_macos.cc b/runtime/bin/eventhandler_macos.cc
index 20c6b61..8964e3b 100644
--- a/runtime/bin/eventhandler_macos.cc
+++ b/runtime/bin/eventhandler_macos.cc
@@ -186,7 +186,7 @@
msg.data = data;
// WriteToBlocking will write up to 512 bytes atomically, and since our msg
// is smaller than 512, we don't need a thread lock.
- // See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'.
+ ASSERT(kInterruptMessageSize < PIPE_BUF);
intptr_t result =
FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize);
if (result != kInterruptMessageSize) {
diff --git a/runtime/bin/file.cc b/runtime/bin/file.cc
index bb68071..35ea5fc 100644
--- a/runtime/bin/file.cc
+++ b/runtime/bin/file.cc
@@ -919,6 +919,7 @@
if (!file->IsClosed()) {
int64_t length = CObjectInt32OrInt64ToInt64(request[2]);
Dart_CObject* io_buffer = CObject::NewIOBuffer(length);
+ ASSERT(io_buffer != NULL);
uint8_t* data = io_buffer->value.as_external_typed_data.data;
int64_t bytes_read = file->Read(data, length);
if (bytes_read >= 0) {
@@ -950,6 +951,7 @@
if (!file->IsClosed()) {
int64_t length = CObjectInt32OrInt64ToInt64(request[2]);
Dart_CObject* io_buffer = CObject::NewIOBuffer(length);
+ ASSERT(io_buffer != NULL);
uint8_t* data = io_buffer->value.as_external_typed_data.data;
int64_t bytes_read = file->Read(data, length);
if (bytes_read >= 0) {
diff --git a/runtime/bin/platform_android.cc b/runtime/bin/platform_android.cc
index e069d5a..dfa2e27 100644
--- a/runtime/bin/platform_android.cc
+++ b/runtime/bin/platform_android.cc
@@ -26,14 +26,6 @@
perror("Setting signal handler failed");
return false;
}
- // Unblock SIGCHLD as waiting on spawned child process depends
- // on successful interception of this signal.
- sigset_t newset;
- sigemptyset(&newset);
- sigaddset(&newset, SIGCHLD);
- if (sigprocmask(SIG_UNBLOCK, &newset, NULL) != 0) {
- perror("Unblocking SIGCHLD signal failed");
- }
return true;
}
diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc
index 4a70ee5..af557af 100644
--- a/runtime/bin/platform_linux.cc
+++ b/runtime/bin/platform_linux.cc
@@ -26,14 +26,6 @@
perror("Setting signal handler failed");
return false;
}
- // Unblock SIGCHLD as waiting on spawned child process depends
- // on successful interception of this signal.
- sigset_t newset;
- sigemptyset(&newset);
- sigaddset(&newset, SIGCHLD);
- if (sigprocmask(SIG_UNBLOCK, &newset, NULL) != 0) {
- perror("Unblocking SIGCHLD signal failed");
- }
return true;
}
diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc
index e1b914b..0576c34 100644
--- a/runtime/bin/platform_macos.cc
+++ b/runtime/bin/platform_macos.cc
@@ -27,15 +27,6 @@
perror("Setting signal handler failed");
return false;
}
- // Unblock SIGCHLD as waiting on spawned child process depends
- // on successful interception of this signal.
- sigset_t newset;
- sigemptyset(&newset);
- sigaddset(&newset, SIGCHLD);
- if (sigprocmask(SIG_UNBLOCK, &newset, NULL) != 0) {
- perror("Unblocking SIGCHLD signal failed");
- }
-
return true;
}
diff --git a/runtime/bin/process_android.cc b/runtime/bin/process_android.cc
index fd97e7a..378b9ba 100644
--- a/runtime/bin/process_android.cc
+++ b/runtime/bin/process_android.cc
@@ -10,7 +10,6 @@
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <poll.h> // NOLINT
-#include <signal.h> // NOLINT
#include <stdio.h> // NOLINT
#include <stdlib.h> // NOLINT
#include <string.h> // NOLINT
@@ -111,161 +110,121 @@
dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex();
-// The exit code handler sets up a separate thread which is signalled
-// on SIGCHLD. That separate thread can then get the exit code from
+// The exit code handler sets up a separate thread which waits for child
+// processes to terminate. That separate thread can then get the exit code from
// processes that have exited and communicate it to Dart through the
// event loop.
class ExitCodeHandler {
public:
- // Ensure that the ExitCodeHandler has been initialized.
- static bool EnsureInitialized() {
+ // Notify the ExitCodeHandler that another process exists.
+ static void ProcessStarted() {
// Multiple isolates could be starting processes at the same
- // time. Make sure that only one of them initializes the
- // ExitCodeHandler.
- MutexLocker locker(mutex_);
- if (initialized_) {
- return true;
+ // time. Make sure that only one ExitCodeHandler thread exists.
+ MonitorLocker locker(monitor_);
+ process_count_++;
+
+ monitor_->Notify();
+
+ if (running_) {
+ return;
}
- // Allocate a pipe that the signal handler can write a byte to and
- // that the exit handler thread can poll.
- int result = TEMP_FAILURE_RETRY(pipe(sig_chld_fds_));
- if (result < 0) {
- return false;
- }
- FDUtils::SetCloseOnExec(sig_chld_fds_[0]);
- FDUtils::SetCloseOnExec(sig_chld_fds_[1]);
-
- // Start thread that polls the pipe and handles process exits when
- // data is received on the pipe.
- result = dart::Thread::Start(ExitCodeHandlerEntry, sig_chld_fds_[0]);
+ // Start thread that handles process exits when wait returns.
+ int result = dart::Thread::Start(ExitCodeHandlerEntry, 0);
if (result != 0) {
FATAL1("Failed to start exit code handler worker thread %d", result);
}
- // Mark write end non-blocking.
- FDUtils::SetNonBlocking(sig_chld_fds_[1]);
-
- // Thread started and the ExitCodeHandler is initialized.
- initialized_ = true;
- return true;
- }
-
- // Get the write end of the pipe.
- static int WakeUpFd() {
- return sig_chld_fds_[1];
+ running_ = true;
}
static void TerminateExitCodeThread() {
- MutexLocker locker(mutex_);
- if (!initialized_) {
+ MonitorLocker locker(monitor_);
+
+ if (!running_) {
return;
}
- uint8_t data = kThreadTerminateByte;
- ssize_t result =
- TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), &data, 1));
- if (result < 1) {
- perror("Failed to write to wake-up fd to terminate exit code thread");
+ // Set terminate_done_ to false, so we can use it as a guard for our
+ // monitor.
+ running_ = false;
+
+ // Fork to wake up waitpid.
+ if (TEMP_FAILURE_RETRY(fork()) == 0) {
+ exit(0);
}
- {
- MonitorLocker terminate_locker(thread_terminate_monitor_);
- while (!thread_terminated_) {
- terminate_locker.Wait();
- }
- }
- }
+ monitor_->Notify();
- static void ExitCodeThreadTerminated() {
- MonitorLocker locker(thread_terminate_monitor_);
- thread_terminated_ = true;
- locker.Notify();
+ while (!terminate_done_) {
+ monitor_->Wait(dart::Monitor::kNoTimeout);
+ }
}
private:
- static const uint8_t kThreadTerminateByte = 1;
-
- // GetProcessExitCodes is called on a separate thread when a SIGCHLD
- // signal is received to retrieve the exit codes and post them to
- // dart.
- static void GetProcessExitCodes() {
- pid_t pid = 0;
- int status = 0;
- while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) {
- int exit_code = 0;
- int negative = 0;
- if (WIFEXITED(status)) {
- exit_code = WEXITSTATUS(status);
- }
- if (WIFSIGNALED(status)) {
- exit_code = WTERMSIG(status);
- negative = 1;
- }
- intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
- if (exit_code_fd != 0) {
- int message[2] = { exit_code, negative };
- ssize_t result =
- FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
- // If the process has been closed, the read end of the exit
- // pipe has been closed. It is therefore not a problem that
- // write fails with a broken pipe error. Other errors should
- // not happen.
- if (result != -1 && result != sizeof(message)) {
- FATAL("Failed to write entire process exit message");
- } else if (result == -1 && errno != EPIPE) {
- FATAL1("Failed to write exit code: %d", errno);
- }
- ProcessInfoList::RemoveProcess(pid);
- }
- }
- }
-
-
// Entry point for the separate exit code handler thread started by
// the ExitCodeHandler.
static void ExitCodeHandlerEntry(uword param) {
- struct pollfd pollfds;
- pollfds.fd = param;
- pollfds.events = POLLIN;
+ pid_t pid = 0;
+ int status = 0;
while (true) {
- int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1));
- if (result == -1) {
- ASSERT(EAGAIN == EWOULDBLOCK);
- if (errno != EWOULDBLOCK) {
- perror("ExitCodeHandler poll failed");
+ {
+ MonitorLocker locker(monitor_);
+ while (running_ && process_count_ == 0) {
+ monitor_->Wait(dart::Monitor::kNoTimeout);
}
- } else {
- // Read the byte from the wake-up fd.
- ASSERT(result = 1);
- intptr_t data = 0;
- ssize_t read_bytes = FDUtils::ReadFromBlocking(pollfds.fd, &data, 1);
- if (read_bytes < 1) {
- perror("Failed to read from wake-up fd in exit-code handler");
- }
- if (data == ExitCodeHandler::kThreadTerminateByte) {
- ExitCodeThreadTerminated();
+ if (!running_) {
+ terminate_done_ = true;
+ monitor_->Notify();
return;
}
- // Get the exit code from all processes that have died.
- GetProcessExitCodes();
+ }
+
+ if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) {
+ int exit_code = 0;
+ int negative = 0;
+ if (WIFEXITED(status)) {
+ exit_code = WEXITSTATUS(status);
+ }
+ if (WIFSIGNALED(status)) {
+ exit_code = WTERMSIG(status);
+ negative = 1;
+ }
+ intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
+ if (exit_code_fd != 0) {
+ int message[2] = { exit_code, negative };
+ ssize_t result =
+ FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
+ // If the process has been closed, the read end of the exit
+ // pipe has been closed. It is therefore not a problem that
+ // write fails with a broken pipe error. Other errors should
+ // not happen.
+ if (result != -1 && result != sizeof(message)) {
+ FATAL("Failed to write entire process exit message");
+ } else if (result == -1 && errno != EPIPE) {
+ FATAL1("Failed to write exit code: %d", errno);
+ }
+ ProcessInfoList::RemoveProcess(pid);
+ {
+ MonitorLocker locker(monitor_);
+ process_count_--;
+ }
+ }
}
}
}
- static dart::Mutex* mutex_;
- static bool initialized_;
- static int sig_chld_fds_[2];
- static bool thread_terminated_;
- static dart::Monitor* thread_terminate_monitor_;
+ static bool terminate_done_;
+ static int process_count_;
+ static bool running_;
+ static dart::Monitor* monitor_;
};
-dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex();
-bool ExitCodeHandler::initialized_ = false;
-int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 };
-bool ExitCodeHandler::thread_terminated_ = false;
-dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor();
+bool ExitCodeHandler::running_ = false;
+int ExitCodeHandler::process_count_ = 0;
+bool ExitCodeHandler::terminate_done_ = false;
+dart::Monitor* ExitCodeHandler::monitor_ = new dart::Monitor();
static void SetChildOsErrorMessage(char** os_error_message) {
@@ -276,21 +235,6 @@
}
-static void SigChldHandler(int process_signal, siginfo_t* siginfo, void* tmp) {
- // Save errno so it can be restored at the end.
- int entry_errno = errno;
- // Signal the exit code handler where the actual processing takes
- // place.
- ssize_t result =
- TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), "", 1));
- if (result < 1) {
- perror("Failed to write to wake-up fd in SIGCHLD handler");
- }
- // Restore errno.
- errno = entry_errno;
-}
-
-
static void ReportChildError(int exec_control_fd) {
// In the case of failure in the child process write the errno and
// the OS error message to the exec control pipe and exit.
@@ -330,14 +274,6 @@
int exec_control[2]; // Pipe to get the result from exec.
int result;
- bool initialized = ExitCodeHandler::EnsureInitialized();
- if (!initialized) {
- SetChildOsErrorMessage(os_error_message);
- Log::PrintErr("Error initializing exit code handler: %s\n",
- *os_error_message);
- return errno;
- }
-
result = TEMP_FAILURE_RETRY(pipe(read_in));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
@@ -413,13 +349,6 @@
program_environment[environment_length] = NULL;
}
- struct sigaction act;
- bzero(&act, sizeof(act));
- act.sa_sigaction = SigChldHandler;
- act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
- if (sigaction(SIGCHLD, &act, 0) != 0) {
- perror("Process start: setting signal handler failed");
- }
pid = TEMP_FAILURE_RETRY(fork());
if (pid < 0) {
SetChildOsErrorMessage(os_error_message);
@@ -477,6 +406,9 @@
ReportChildError(exec_control[1]);
}
+ // Be sure to listen for exit-codes, now we have a child-process.
+ ExitCodeHandler::ProcessStarted();
+
// The arguments and environment for the spawned process are not needed
// any longer.
delete[] program_arguments;
diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc
index adf5cd4..95d11cd 100644
--- a/runtime/bin/process_linux.cc
+++ b/runtime/bin/process_linux.cc
@@ -10,7 +10,6 @@
#include <errno.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <poll.h> // NOLINT
-#include <signal.h> // NOLINT
#include <stdio.h> // NOLINT
#include <stdlib.h> // NOLINT
#include <string.h> // NOLINT
@@ -111,161 +110,121 @@
dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex();
-// The exit code handler sets up a separate thread which is signalled
-// on SIGCHLD. That separate thread can then get the exit code from
+// The exit code handler sets up a separate thread which waits for child
+// processes to terminate. That separate thread can then get the exit code from
// processes that have exited and communicate it to Dart through the
// event loop.
class ExitCodeHandler {
public:
- // Ensure that the ExitCodeHandler has been initialized.
- static bool EnsureInitialized() {
+ // Notify the ExitCodeHandler that another process exists.
+ static void ProcessStarted() {
// Multiple isolates could be starting processes at the same
- // time. Make sure that only one of them initializes the
- // ExitCodeHandler.
- MutexLocker locker(mutex_);
- if (initialized_) {
- return true;
+ // time. Make sure that only one ExitCodeHandler thread exists.
+ MonitorLocker locker(monitor_);
+ process_count_++;
+
+ monitor_->Notify();
+
+ if (running_) {
+ return;
}
- // Allocate a pipe that the signal handler can write a byte to and
- // that the exit handler thread can poll.
- int result = TEMP_FAILURE_RETRY(pipe(sig_chld_fds_));
- if (result < 0) {
- return false;
- }
- FDUtils::SetCloseOnExec(sig_chld_fds_[0]);
- FDUtils::SetCloseOnExec(sig_chld_fds_[1]);
-
- // Start thread that polls the pipe and handles process exits when
- // data is received on the pipe.
- result = dart::Thread::Start(ExitCodeHandlerEntry, sig_chld_fds_[0]);
+ // Start thread that handles process exits when wait returns.
+ int result = dart::Thread::Start(ExitCodeHandlerEntry, 0);
if (result != 0) {
FATAL1("Failed to start exit code handler worker thread %d", result);
}
- // Mark write end non-blocking.
- FDUtils::SetNonBlocking(sig_chld_fds_[1]);
-
- // Thread started and the ExitCodeHandler is initialized.
- initialized_ = true;
- return true;
- }
-
- // Get the write end of the pipe.
- static int WakeUpFd() {
- return sig_chld_fds_[1];
+ running_ = true;
}
static void TerminateExitCodeThread() {
- MutexLocker locker(mutex_);
- if (!initialized_) {
+ MonitorLocker locker(monitor_);
+
+ if (!running_) {
return;
}
- uint8_t data = kThreadTerminateByte;
- ssize_t result =
- TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), &data, 1));
- if (result < 1) {
- perror("Failed to write to wake-up fd to terminate exit code thread");
+ // Set terminate_done_ to false, so we can use it as a guard for our
+ // monitor.
+ running_ = false;
+
+ // Fork to wake up waitpid.
+ if (TEMP_FAILURE_RETRY(fork()) == 0) {
+ exit(0);
}
- {
- MonitorLocker terminate_locker(thread_terminate_monitor_);
- while (!thread_terminated_) {
- terminate_locker.Wait();
- }
- }
- }
+ monitor_->Notify();
- static void ExitCodeThreadTerminated() {
- MonitorLocker locker(thread_terminate_monitor_);
- thread_terminated_ = true;
- locker.Notify();
+ while (!terminate_done_) {
+ monitor_->Wait(dart::Monitor::kNoTimeout);
+ }
}
private:
- static const uint8_t kThreadTerminateByte = 1;
-
- // GetProcessExitCodes is called on a separate thread when a SIGCHLD
- // signal is received to retrieve the exit codes and post them to
- // dart.
- static void GetProcessExitCodes() {
- pid_t pid = 0;
- int status = 0;
- while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) {
- int exit_code = 0;
- int negative = 0;
- if (WIFEXITED(status)) {
- exit_code = WEXITSTATUS(status);
- }
- if (WIFSIGNALED(status)) {
- exit_code = WTERMSIG(status);
- negative = 1;
- }
- intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
- if (exit_code_fd != 0) {
- int message[2] = { exit_code, negative };
- ssize_t result =
- FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
- // If the process has been closed, the read end of the exit
- // pipe has been closed. It is therefore not a problem that
- // write fails with a broken pipe error. Other errors should
- // not happen.
- if (result != -1 && result != sizeof(message)) {
- FATAL("Failed to write entire process exit message");
- } else if (result == -1 && errno != EPIPE) {
- FATAL1("Failed to write exit code: %d", errno);
- }
- ProcessInfoList::RemoveProcess(pid);
- }
- }
- }
-
-
// Entry point for the separate exit code handler thread started by
// the ExitCodeHandler.
static void ExitCodeHandlerEntry(uword param) {
- struct pollfd pollfds;
- pollfds.fd = param;
- pollfds.events = POLLIN;
+ pid_t pid = 0;
+ int status = 0;
while (true) {
- int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1));
- if (result == -1) {
- ASSERT(EAGAIN == EWOULDBLOCK);
- if (errno != EWOULDBLOCK) {
- perror("ExitCodeHandler poll failed");
+ {
+ MonitorLocker locker(monitor_);
+ while (running_ && process_count_ == 0) {
+ monitor_->Wait(dart::Monitor::kNoTimeout);
}
- } else {
- // Read the byte from the wake-up fd.
- ASSERT(result = 1);
- intptr_t data = 0;
- ssize_t read_bytes = FDUtils::ReadFromBlocking(pollfds.fd, &data, 1);
- if (read_bytes < 1) {
- perror("Failed to read from wake-up fd in exit-code handler");
- }
- if (data == ExitCodeHandler::kThreadTerminateByte) {
- ExitCodeThreadTerminated();
+ if (!running_) {
+ terminate_done_ = true;
+ monitor_->Notify();
return;
}
- // Get the exit code from all processes that have died.
- GetProcessExitCodes();
+ }
+
+ if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) {
+ int exit_code = 0;
+ int negative = 0;
+ if (WIFEXITED(status)) {
+ exit_code = WEXITSTATUS(status);
+ }
+ if (WIFSIGNALED(status)) {
+ exit_code = WTERMSIG(status);
+ negative = 1;
+ }
+ intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
+ if (exit_code_fd != 0) {
+ int message[2] = { exit_code, negative };
+ ssize_t result =
+ FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
+ // If the process has been closed, the read end of the exit
+ // pipe has been closed. It is therefore not a problem that
+ // write fails with a broken pipe error. Other errors should
+ // not happen.
+ if (result != -1 && result != sizeof(message)) {
+ FATAL("Failed to write entire process exit message");
+ } else if (result == -1 && errno != EPIPE) {
+ FATAL1("Failed to write exit code: %d", errno);
+ }
+ ProcessInfoList::RemoveProcess(pid);
+ {
+ MonitorLocker locker(monitor_);
+ process_count_--;
+ }
+ }
}
}
}
- static dart::Mutex* mutex_;
- static bool initialized_;
- static int sig_chld_fds_[2];
- static bool thread_terminated_;
- static dart::Monitor* thread_terminate_monitor_;
+ static bool terminate_done_;
+ static int process_count_;
+ static bool running_;
+ static dart::Monitor* monitor_;
};
-dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex();
-bool ExitCodeHandler::initialized_ = false;
-int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 };
-bool ExitCodeHandler::thread_terminated_ = false;
-dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor();
+bool ExitCodeHandler::running_ = false;
+int ExitCodeHandler::process_count_ = 0;
+bool ExitCodeHandler::terminate_done_ = false;
+dart::Monitor* ExitCodeHandler::monitor_ = new dart::Monitor();
static void SetChildOsErrorMessage(char** os_error_message) {
@@ -275,21 +234,6 @@
}
-static void SigChldHandler(int process_signal, siginfo_t* siginfo, void* tmp) {
- // Save errno so it can be restored at the end.
- int entry_errno = errno;
- // Signal the exit code handler where the actual processing takes
- // place.
- ssize_t result =
- TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), "", 1));
- if (result < 1) {
- perror("Failed to write to wake-up fd in SIGCHLD handler");
- }
- // Restore errno.
- errno = entry_errno;
-}
-
-
static void ReportChildError(int exec_control_fd) {
// In the case of failure in the child process write the errno and
// the OS error message to the exec control pipe and exit.
@@ -329,14 +273,6 @@
int exec_control[2]; // Pipe to get the result from exec.
int result;
- bool initialized = ExitCodeHandler::EnsureInitialized();
- if (!initialized) {
- SetChildOsErrorMessage(os_error_message);
- Log::PrintErr("Error initializing exit code handler: %s\n",
- *os_error_message);
- return errno;
- }
-
result = TEMP_FAILURE_RETRY(pipe(read_in));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
@@ -412,13 +348,6 @@
program_environment[environment_length] = NULL;
}
- struct sigaction act;
- bzero(&act, sizeof(act));
- act.sa_sigaction = SigChldHandler;
- act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
- if (sigaction(SIGCHLD, &act, 0) != 0) {
- perror("Process start: setting signal handler failed");
- }
pid = TEMP_FAILURE_RETRY(fork());
if (pid < 0) {
SetChildOsErrorMessage(os_error_message);
@@ -476,6 +405,9 @@
ReportChildError(exec_control[1]);
}
+ // Be sure to listen for exit-codes, now we have a child-process.
+ ExitCodeHandler::ProcessStarted();
+
// The arguments and environment for the spawned process are not needed
// any longer.
delete[] program_arguments;
diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc
index 1a15a39..edb222a 100644
--- a/runtime/bin/process_macos.cc
+++ b/runtime/bin/process_macos.cc
@@ -109,161 +109,121 @@
dart::Mutex* ProcessInfoList::mutex_ = new dart::Mutex();
-// The exit code handler sets up a separate thread which is signalled
-// on SIGCHLD. That separate thread can then get the exit code from
+// The exit code handler sets up a separate thread which waits for child
+// processes to terminate. That separate thread can then get the exit code from
// processes that have exited and communicate it to Dart through the
// event loop.
class ExitCodeHandler {
public:
- // Ensure that the ExitCodeHandler has been initialized.
- static bool EnsureInitialized() {
+ // Notify the ExitCodeHandler that another process exists.
+ static void ProcessStarted() {
// Multiple isolates could be starting processes at the same
- // time. Make sure that only one of them initializes the
- // ExitCodeHandler.
- MutexLocker locker(mutex_);
- if (initialized_) {
- return true;
+ // time. Make sure that only one ExitCodeHandler thread exists.
+ MonitorLocker locker(monitor_);
+ process_count_++;
+
+ monitor_->Notify();
+
+ if (running_) {
+ return;
}
- // Allocate a pipe that the signal handler can write a byte to and
- // that the exit handler thread can poll.
- int result = TEMP_FAILURE_RETRY(pipe(sig_chld_fds_));
- if (result < 0) {
- return false;
- }
- FDUtils::SetCloseOnExec(sig_chld_fds_[0]);
- FDUtils::SetCloseOnExec(sig_chld_fds_[1]);
-
- // Start thread that polls the pipe and handles process exits when
- // data is received on the pipe.
- result = dart::Thread::Start(ExitCodeHandlerEntry, sig_chld_fds_[0]);
+ // Start thread that handles process exits when wait returns.
+ int result = dart::Thread::Start(ExitCodeHandlerEntry, 0);
if (result != 0) {
FATAL1("Failed to start exit code handler worker thread %d", result);
}
- // Mark write end non-blocking.
- FDUtils::SetNonBlocking(sig_chld_fds_[1]);
-
- // Thread started and the ExitCodeHandler is initialized.
- initialized_ = true;
- return true;
- }
-
- // Get the write end of the pipe.
- static int WakeUpFd() {
- return sig_chld_fds_[1];
+ running_ = true;
}
static void TerminateExitCodeThread() {
- MutexLocker locker(mutex_);
- if (!initialized_) {
+ MonitorLocker locker(monitor_);
+
+ if (!running_) {
return;
}
- uint8_t data = kThreadTerminateByte;
- ssize_t result =
- TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), &data, 1));
- if (result < 1) {
- perror("Failed to write to wake-up fd to terminate exit code thread");
+ // Set terminate_done_ to false, so we can use it as a guard for our
+ // monitor.
+ running_ = false;
+
+ // Fork to wake up waitpid.
+ if (TEMP_FAILURE_RETRY(fork()) == 0) {
+ exit(0);
}
- {
- MonitorLocker terminate_locker(thread_terminate_monitor_);
- while (!thread_terminated_) {
- terminate_locker.Wait();
- }
- }
- }
+ monitor_->Notify();
- static void ExitCodeThreadTerminated() {
- MonitorLocker locker(thread_terminate_monitor_);
- thread_terminated_ = true;
- locker.Notify();
+ while (!terminate_done_) {
+ monitor_->Wait(dart::Monitor::kNoTimeout);
+ }
}
private:
- static const uint8_t kThreadTerminateByte = 1;
-
- // GetProcessExitCodes is called on a separate thread when a SIGCHLD
- // signal is received to retrieve the exit codes and post them to
- // dart.
- static void GetProcessExitCodes() {
- pid_t pid = 0;
- int status = 0;
- while ((pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG))) > 0) {
- int exit_code = 0;
- int negative = 0;
- if (WIFEXITED(status)) {
- exit_code = WEXITSTATUS(status);
- }
- if (WIFSIGNALED(status)) {
- exit_code = WTERMSIG(status);
- negative = 1;
- }
- intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
- if (exit_code_fd != 0) {
- int message[2] = { exit_code, negative };
- ssize_t result =
- FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
- // If the process has been closed, the read end of the exit
- // pipe has been closed. It is therefore not a problem that
- // writennnj fails with a broken pipe error. Other errors should
- // not happen.
- if (result != -1 && result != sizeof(message)) {
- FATAL("Failed to write entire process exit message");
- } else if (result == -1 && errno != EPIPE) {
- FATAL1("Failed to write exit code: %d", errno);
- }
- ProcessInfoList::RemoveProcess(pid);
- }
- }
- }
-
-
// Entry point for the separate exit code handler thread started by
// the ExitCodeHandler.
static void ExitCodeHandlerEntry(uword param) {
- struct pollfd pollfds;
- pollfds.fd = param;
- pollfds.events = POLLIN;
+ pid_t pid = 0;
+ int status = 0;
while (true) {
- int result = TEMP_FAILURE_RETRY(poll(&pollfds, 1, -1));
- if (result == -1) {
- ASSERT(EAGAIN == EWOULDBLOCK);
- if (errno != EWOULDBLOCK) {
- perror("ExitCodeHandler poll failed");
+ {
+ MonitorLocker locker(monitor_);
+ while (running_ && process_count_ == 0) {
+ monitor_->Wait(dart::Monitor::kNoTimeout);
}
- } else {
- // Read the byte from the wake-up fd.
- ASSERT(result = 1);
- intptr_t data = 0;
- ssize_t read_bytes = FDUtils::ReadFromBlocking(pollfds.fd, &data, 1);
- if (read_bytes < 1) {
- perror("Failed to read from wake-up fd in exit-code handler");
- }
- if (data == ExitCodeHandler::kThreadTerminateByte) {
- ExitCodeThreadTerminated();
+ if (!running_) {
+ terminate_done_ = true;
+ monitor_->Notify();
return;
}
- // Get the exit code from all processes that have died.
- GetProcessExitCodes();
+ }
+
+ if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) {
+ int exit_code = 0;
+ int negative = 0;
+ if (WIFEXITED(status)) {
+ exit_code = WEXITSTATUS(status);
+ }
+ if (WIFSIGNALED(status)) {
+ exit_code = WTERMSIG(status);
+ negative = 1;
+ }
+ intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid);
+ if (exit_code_fd != 0) {
+ int message[2] = { exit_code, negative };
+ ssize_t result =
+ FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message));
+ // If the process has been closed, the read end of the exit
+ // pipe has been closed. It is therefore not a problem that
+ // write fails with a broken pipe error. Other errors should
+ // not happen.
+ if (result != -1 && result != sizeof(message)) {
+ FATAL("Failed to write entire process exit message");
+ } else if (result == -1 && errno != EPIPE) {
+ FATAL1("Failed to write exit code: %d", errno);
+ }
+ ProcessInfoList::RemoveProcess(pid);
+ {
+ MonitorLocker locker(monitor_);
+ process_count_--;
+ }
+ }
}
}
}
- static dart::Mutex* mutex_;
- static bool initialized_;
- static int sig_chld_fds_[2];
- static bool thread_terminated_;
- static dart::Monitor* thread_terminate_monitor_;
+ static bool terminate_done_;
+ static int process_count_;
+ static bool running_;
+ static dart::Monitor* monitor_;
};
-dart::Mutex* ExitCodeHandler::mutex_ = new dart::Mutex();
-bool ExitCodeHandler::initialized_ = false;
-int ExitCodeHandler::sig_chld_fds_[2] = { 0, 0 };
-bool ExitCodeHandler::thread_terminated_ = false;
-dart::Monitor* ExitCodeHandler::thread_terminate_monitor_ = new dart::Monitor();
+bool ExitCodeHandler::running_ = false;
+int ExitCodeHandler::process_count_ = 0;
+bool ExitCodeHandler::terminate_done_ = false;
+dart::Monitor* ExitCodeHandler::monitor_ = new dart::Monitor();
static void SetChildOsErrorMessage(char** os_error_message) {
@@ -274,21 +234,6 @@
}
-static void SigChldHandler(int process_signal, siginfo_t* siginfo, void* tmp) {
- // Save errno so it can be restored at the end.
- int entry_errno = errno;
- // Signal the exit code handler where the actual processing takes
- // place.
- ssize_t result =
- TEMP_FAILURE_RETRY(write(ExitCodeHandler::WakeUpFd(), "", 1));
- if (result < 1) {
- perror("Failed to write to wake-up fd in SIGCHLD handler");
- }
- // Restore errno.
- errno = entry_errno;
-}
-
-
static void ReportChildError(int exec_control_fd) {
// In the case of failure in the child process write the errno and
// the OS error message to the exec control pipe and exit.
@@ -328,14 +273,6 @@
int exec_control[2]; // Pipe to get the result from exec.
int result;
- bool initialized = ExitCodeHandler::EnsureInitialized();
- if (!initialized) {
- SetChildOsErrorMessage(os_error_message);
- Log::PrintErr("Error initializing exit code handler: %s\n",
- *os_error_message);
- return errno;
- }
-
result = TEMP_FAILURE_RETRY(pipe(read_in));
if (result < 0) {
SetChildOsErrorMessage(os_error_message);
@@ -411,13 +348,6 @@
program_environment[environment_length] = NULL;
}
- struct sigaction act;
- bzero(&act, sizeof(act));
- act.sa_sigaction = SigChldHandler;
- act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
- if (sigaction(SIGCHLD, &act, 0) != 0) {
- perror("Process start: setting signal handler failed");
- }
pid = TEMP_FAILURE_RETRY(fork());
if (pid < 0) {
SetChildOsErrorMessage(os_error_message);
@@ -475,6 +405,9 @@
ReportChildError(exec_control[1]);
}
+ // Be sure to listen for exit-codes, now we have a child-process.
+ ExitCodeHandler::ProcessStarted();
+
// The arguments and environment for the spawned process are not needed
// any longer.
delete[] program_arguments;
diff --git a/runtime/bin/process_patch.dart b/runtime/bin/process_patch.dart
index 441e75b..e0e8fc6 100644
--- a/runtime/bin/process_patch.dart
+++ b/runtime/bin/process_patch.dart
@@ -323,7 +323,7 @@
_exitHandler._nativeSocket);
getOutput(output, encoding) {
- if (stderrEncoding == null) return output;
+ if (encoding == null) return output;
return encoding.decode(output);
}
diff --git a/runtime/include/dart_debugger_api.h b/runtime/include/dart_debugger_api.h
index 50bf9f0..bd47064 100755
--- a/runtime/include/dart_debugger_api.h
+++ b/runtime/include/dart_debugger_api.h
@@ -390,7 +390,7 @@
/**
- * DEPRECATED -- Use Dart_ActivationframeGetLocation instead.
+ * DEPRECATED -- Use Dart_ActivationFrameGetLocation instead.
*
* Returns information about the given activation frame.
* \function_name receives a string handle with the qualified
@@ -420,6 +420,7 @@
*
* \function_name receives a string handle with the qualified
* function name.
+ * \function receives a handle to the function.
* \location.script_url receives a string handle with the url of
* the source script that contains the frame's function.
* Receives a null handle if there is no textual location
@@ -440,6 +441,7 @@
DART_EXPORT Dart_Handle Dart_ActivationFrameGetLocation(
Dart_ActivationFrame activation_frame,
Dart_Handle* function_name,
+ Dart_Handle* function,
Dart_CodeLocation* location);
diff --git a/runtime/include/dart_native_api.h b/runtime/include/dart_native_api.h
index 7691d5c..cdfc5df 100644
--- a/runtime/include/dart_native_api.h
+++ b/runtime/include/dart_native_api.h
@@ -53,17 +53,17 @@
char* as_string;
char* as_bigint;
struct {
- int length;
+ intptr_t length;
struct _Dart_CObject** values;
} as_array;
struct {
Dart_TypedData_Type type;
- int length;
+ intptr_t length;
uint8_t* values;
} as_typed_data;
struct {
Dart_TypedData_Type type;
- int length;
+ intptr_t length;
uint8_t* data;
void* peer;
Dart_WeakPersistentHandleFinalizer callback;
diff --git a/runtime/lib/collection_patch.dart b/runtime/lib/collection_patch.dart
index 3b3bcc0..3716cca 100644
--- a/runtime/lib/collection_patch.dart
+++ b/runtime/lib/collection_patch.dart
@@ -4,27 +4,39 @@
patch class HashMap<K, V> {
/* patch */ factory HashMap({ bool equals(K key1, K key2),
- int hashCode(K key) }) {
- if (hashCode == null) {
+ int hashCode(K key),
+ bool isValidKey(potentialKey) }) {
+ if (isValidKey == null) {
+ if (hashCode == null) {
+ if (equals == null) {
+ return new _HashMap<K, V>();
+ }
+ if (identical(identical, equals)) {
+ return new _IdentityHashMap<K, V>();
+ }
+ hashCode = _defaultHashCode;
+ } else if (equals == null) {
+ equals = _defaultEquals;
+ }
+ } else {
+ if (hashCode == null) {
+ hashCode = _defaultHashCode;
+ }
if (equals == null) {
- return new _HashMapImpl<K, V>();
+ equals = _defaultEquals;
}
- if (identical(identical, equals)) {
- return new _IdentityHashMap<K, V>();
- }
- hashCode = _defaultHashCode;
- } else if (equals == null) {
- equals = _defaultEquals;
}
- return new _CustomHashMap<K, V>(equals, hashCode);
+ return new _CustomHashMap<K, V>(equals, hashCode, isValidKey);
}
}
const int _MODIFICATION_COUNT_MASK = 0x3fffffff;
-class _HashMapImpl<K, V> implements HashMap<K, V> {
+class _HashMap<K, V> implements HashMap<K, V> {
static const int _INITIAL_CAPACITY = 8;
+ Type get runtimeType => HashMap;
+
int _elementCount = 0;
List<_HashMapEntry> _buckets = new List(_INITIAL_CAPACITY);
int _modificationCount = 0;
@@ -144,11 +156,7 @@
while (entry != null) {
_HashMapEntry next = entry.next;
if (hashCode == entry.hashCode && entry.key == key) {
- if (previous == null) {
- buckets[index] = next;
- } else {
- previous.next = next;
- }
+ _removeEntry(entry, previous, index);
_elementCount--;
_modificationCount =
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
@@ -166,6 +174,16 @@
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
}
+ void _removeEntry(_HashMapEntry entry,
+ _HashMapEntry previousInBucket,
+ int bucketIndex) {
+ if (previousInBucket == null) {
+ _buckets[bucketIndex] = entry.next;
+ } else {
+ previousInBucket.next = entry.next;
+ }
+ }
+
void _addEntry(List buckets, int index, int length,
K key, V value, int hashCode) {
_HashMapEntry entry =
@@ -201,12 +219,17 @@
String toString() => Maps.mapToString(this);
}
-class _CustomHashMap<K, V> extends _HashMapImpl<K, V> {
+class _CustomHashMap<K, V> extends _HashMap<K, V> {
final _Equality<K> _equals;
final _Hasher<K> _hashCode;
- _CustomHashMap(this._equals, this._hashCode);
+ final _Predicate _validKey;
+ _CustomHashMap(this._equals, this._hashCode, validKey)
+ : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test;
+
+ Type get runtimeType => HashMap;
bool containsKey(Object key) {
+ if (!_validKey(key)) return false;
int hashCode = _hashCode(key);
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
@@ -219,6 +242,7 @@
}
V operator[](Object key) {
+ if (!_validKey(key)) return null;
int hashCode = _hashCode(key);
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
@@ -271,6 +295,7 @@
}
V remove(Object key) {
+ if (!_validKey(key)) return null;
int hashCode = _hashCode(key);
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
@@ -279,11 +304,7 @@
while (entry != null) {
_HashMapEntry next = entry.next;
if (hashCode == entry.hashCode && _equals(entry.key, key)) {
- if (previous == null) {
- buckets[index] = next;
- } else {
- previous.next = next;
- }
+ _removeEntry(entry, previous, index);
_elementCount--;
_modificationCount =
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
@@ -298,7 +319,9 @@
String toString() => Maps.mapToString(this);
}
-class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> {
+class _IdentityHashMap<K, V> extends _HashMap<K, V> {
+ Type get runtimeType => HashMap;
+
bool containsKey(Object key) {
int hashCode = key.hashCode;
List buckets = _buckets;
@@ -372,11 +395,7 @@
while (entry != null) {
_HashMapEntry next = entry.next;
if (hashCode == entry.hashCode && identical(entry.key, key)) {
- if (previous == null) {
- buckets[index] = next;
- } else {
- previous.next = next;
- }
+ _removeEntry(entry, previous, index);
_elementCount--;
_modificationCount =
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
@@ -596,11 +615,11 @@
}
abstract class _LinkedHashMapIterator<T> implements Iterator<T> {
- final _LinkedHashMap _map;
+ final LinkedHashMap _map;
var _next;
T _current;
int _modificationCount;
- _LinkedHashMapIterator(_LinkedHashMap map)
+ _LinkedHashMapIterator(LinkedHashMap map)
: _map = map,
_current = null,
_next = map._nextEntry,
@@ -626,12 +645,12 @@
}
class _LinkedHashMapKeyIterator<K> extends _LinkedHashMapIterator<K> {
- _LinkedHashMapKeyIterator(_LinkedHashMap map) : super(map);
+ _LinkedHashMapKeyIterator(LinkedHashMap map) : super(map);
K _getValue(_LinkedHashMapEntry entry) => entry.key;
}
class _LinkedHashMapValueIterator<V> extends _LinkedHashMapIterator<V> {
- _LinkedHashMapValueIterator(_LinkedHashMap map) : super(map);
+ _LinkedHashMapValueIterator(LinkedHashMap map) : super(map);
V _getValue(_LinkedHashMapEntry entry) => entry.value;
}
@@ -640,153 +659,74 @@
* A hash-based map that iterates keys and values in key insertion order.
*/
patch class LinkedHashMap<K, V> {
- static const int _INITIAL_CAPACITY = 8;
- static const int _MODIFICATION_COUNT_MASK = 0x3fffffff;
-
- int _elementCount = 0;
- List<_HashMapEntry> _buckets = new List(_INITIAL_CAPACITY);
- int _modificationCount = 0;
-
var _nextEntry;
var _previousEntry;
- /* patch */ LinkedHashMap() {
- _nextEntry = _previousEntry = this;
- }
-
- /* patch */ int get length => _elementCount;
- /* patch */ bool get isEmpty => _elementCount == 0;
- /* patch */ bool get isNotEmpty => _elementCount != 0;
-
- /* patch */ Iterable<K> get keys => new _LinkedHashMapKeyIterable<K>(this);
- /* patch */ Iterable<V> get values => new _LinkedHashMapValueIterable<V>(this);
-
- /* patch */ bool containsKey(Object key) {
- int hashCode = key.hashCode;
- List buckets = _buckets;
- int index = hashCode & (buckets.length - 1);
- _HashMapEntry entry = buckets[index];
- while (entry != null) {
- if (hashCode == entry.hashCode && entry.key == key) return true;
- entry = entry.next;
+ /* patch */ factory LinkedHashMap({ bool equals(K key1, K key2),
+ int hashCode(K key),
+ bool isValidKey(potentialKey) }) {
+ if (isValidKey == null) {
+ if (hashCode == null) {
+ if (equals == null) {
+ return new _LinkedHashMap<K, V>();
+ }
+ if (identical(identical, equals)) {
+ return new _LinkedIdentityHashMap<K, V>();
+ }
+ hashCode = _defaultHashCode;
+ } else if (equals == null) {
+ equals = _defaultEquals;
+ }
+ } else {
+ if (hashCode == null) {
+ hashCode = _defaultHashCode;
+ }
+ if (equals == null) {
+ equals = _defaultEquals;
+ }
}
- return false;
+ return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey);
}
+}
- /* patch */ bool containsValue(Object value) {
- var cursor = _nextEntry;
+// Methods that are exactly the same in all three linked hash map variants.
+abstract class _LinkedHashMapMixin<K, V> implements LinkedHashMap<K, V> {
+ var _nextEntry;
+ var _previousEntry;
+
+ Type get runtimeType => LinkedHashMap;
+
+ bool containsValue(Object value) {
int modificationCount = _modificationCount;
+ var cursor = _nextEntry;
while (!identical(cursor, this)) {
_HashMapEntry entry = cursor;
if (entry.value == value) return true;
+ if (modificationCount != _modificationCount) {
+ throw new ConcurrentModificationError(this);
+ }
cursor = cursor._nextEntry;
}
return false;
}
- /* patch */ V operator[](Object key) {
- int hashCode = key.hashCode;
- List buckets = _buckets;
- int index = hashCode & (buckets.length - 1);
- _HashMapEntry entry = buckets[index];
- while (entry != null) {
- if (hashCode == entry.hashCode && entry.key == key) {
- return entry.value;
- }
- entry = entry.next;
- }
- return null;
- }
-
- /* patch */ void operator []=(K key, V value) {
- int hashCode = key.hashCode;
- List buckets = _buckets;
- int length = buckets.length;
- int index = hashCode & (length - 1);
- _HashMapEntry entry = buckets[index];
- while (entry != null) {
- if (hashCode == entry.hashCode && entry.key == key) {
- entry.value = value;
- return;
- }
- entry = entry.next;
- }
- _addEntry(buckets, index, length, key, value, hashCode);
- }
-
- /* patch */ V putIfAbsent(K key, V ifAbsent()) {
- int hashCode = key.hashCode;
- List buckets = _buckets;
- int length = buckets.length;
- int index = hashCode & (length - 1);
- _HashMapEntry entry = buckets[index];
- while (entry != null) {
- if (hashCode == entry.hashCode && entry.key == key) {
- return entry.value;
- }
- entry = entry.next;
- }
- int stamp = _modificationCount;
- V value = ifAbsent();
- if (stamp == _modificationCount) {
- _addEntry(buckets, index, length, key, value, hashCode);
- } else {
- this[key] = value;
- }
- return value;
- }
-
- /* patch */ void addAll(Map<K, V> other) {
- other.forEach((K key, V value) {
- this[key] = value;
- });
- }
-
- /* patch */ void forEach(void action(K key, V value)) {
- int stamp = _modificationCount;
+ void forEach(void action(K key, V value)) {
+ int modificationCount = _modificationCount;
var cursor = _nextEntry;
while (!identical(cursor, this)) {
_HashMapEntry entry = cursor;
action(entry.key, entry.value);
- if (stamp != _modificationCount) {
+ if (modificationCount != _modificationCount) {
throw new ConcurrentModificationError(this);
}
cursor = cursor._nextEntry;
}
}
- /* patch */ V remove(Object key) {
- int hashCode = key.hashCode;
- List buckets = _buckets;
- int index = hashCode & (buckets.length - 1);
- _LinkedHashMapEntry entry = buckets[index];
- _HashMapEntry previous = null;
- while (entry != null) {
- _HashMapEntry next = entry.next;
- if (hashCode == entry.hashCode && entry.key == key) {
- if (previous == null) {
- buckets[index] = next;
- } else {
- previous.next = next;
- }
- entry._previousEntry._nextEntry = entry._nextEntry;
- entry._nextEntry._previousEntry = entry._previousEntry;
- entry._nextEntry = entry._previousEntry = null;
- _elementCount--;
- _modificationCount =
- (_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
- return entry.value;
- }
- previous = entry;
- entry = next;
- }
- return null;
- }
-
- /* patch */ void clear() {
- _elementCount = 0;
+ void clear() {
_nextEntry = _previousEntry = this;
- _buckets = new List(_INITIAL_CAPACITY);
+ _elementCount = 0;
+ _buckets = new List(_HashMap._INITIAL_CAPACITY);
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
}
@@ -804,26 +744,50 @@
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
}
- void _resize() {
- List oldBuckets = _buckets;
- int oldLength = oldBuckets.length;
- int newLength = oldLength << 1;
- List newBuckets = new List(newLength);
- for (int i = 0; i < oldLength; i++) {
- _HashMapEntry entry = oldBuckets[i];
- while (entry != null) {
- _HashMapEntry next = entry.next;
- int hashCode = entry.hashCode;
- int index = hashCode & (newLength - 1);
- entry.next = newBuckets[index];
- newBuckets[index] = entry;
- entry = next;
- }
+ void _removeEntry(_LinkedHashMapEntry entry,
+ _HashMapEntry previousInBucket,
+ int bucketIndex) {
+ var previousInChain = entry._previousEntry;
+ var nextInChain = entry._nextEntry;
+ previousInChain._nextEntry = nextInChain;
+ nextInChain._previousEntry = previousInChain;
+ if (previousInBucket == null) {
+ _buckets[bucketIndex] = entry.next;
+ } else {
+ previousInBucket.next = entry.next;
}
- _buckets = newBuckets;
+ }
+
+
+ Iterable<K> get keys => new _LinkedHashMapKeyIterable<K>(this);
+ Iterable<V> get values => new _LinkedHashMapValueIterable<V>(this);
+}
+
+class _LinkedHashMap<K, V> extends _HashMap<K, V>
+ with _LinkedHashMapMixin<K, V> {
+ _LinkedHashMap() {
+ _nextEntry = _previousEntry = this;
}
}
+class _LinkedIdentityHashMap<K, V> extends _IdentityHashMap<K, V>
+ with _LinkedHashMapMixin<K, V> {
+ _LinkedIdentityHashMap() {
+ _nextEntry = _previousEntry = this;
+ }
+}
+
+class _LinkedCustomHashMap<K, V> extends _CustomHashMap<K, V>
+ with _LinkedHashMapMixin<K, V> {
+ _LinkedCustomHashMap(bool equals(K key1, K key2),
+ int hashCode(K key),
+ bool isValidKey(potentialKey))
+ : super(equals, hashCode, isValidKey) {
+ _nextEntry = _previousEntry = this;
+ }
+}
+
+
patch class LinkedHashSet<E> extends _HashSetBase<E> {
static const int _INITIAL_CAPACITY = 8;
_LinkedHashTable<E> _table;
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index 5be23af..84dc9d6 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -35,6 +35,12 @@
}
+static int BitLengthInt64(int64_t value) {
+ value ^= value >> (8 * sizeof(value) - 1); // flip bits if negative.
+ return value == 0 ? 0 : Utils::HighestBit(value) + 1;
+}
+
+
DEFINE_NATIVE_ENTRY(Integer_bitAndFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
@@ -327,6 +333,18 @@
}
+DEFINE_NATIVE_ENTRY(Smi_bitLength, 1) {
+ const Smi& operand = Smi::CheckedHandle(arguments->NativeArgAt(0));
+ if (FLAG_trace_intrinsified_natives) {
+ OS::Print("Smi_bitLength: %s\n", operand.ToCString());
+ }
+ int64_t value = operand.AsInt64Value();
+ intptr_t result = BitLengthInt64(value);
+ ASSERT(Smi::IsValid(result));
+ return Smi::New(result);
+}
+
+
// Mint natives.
DEFINE_NATIVE_ENTRY(Mint_bitNegate, 1) {
@@ -340,6 +358,19 @@
}
+DEFINE_NATIVE_ENTRY(Mint_bitLength, 1) {
+ const Mint& operand = Mint::CheckedHandle(arguments->NativeArgAt(0));
+ ASSERT(CheckInteger(operand));
+ if (FLAG_trace_intrinsified_natives) {
+ OS::Print("Mint_bitLength: %s\n", operand.ToCString());
+ }
+ int64_t value = operand.AsInt64Value();
+ intptr_t result = BitLengthInt64(value);
+ ASSERT(Smi::IsValid(result));
+ return Smi::New(result);
+}
+
+
DEFINE_NATIVE_ENTRY(Mint_shlFromInt, 2) {
// Use the preallocated out of memory exception to avoid calling
// into dart code or allocating any code.
@@ -362,6 +393,12 @@
}
+DEFINE_NATIVE_ENTRY(Bigint_bitLength, 1) {
+ const Bigint& value = Bigint::CheckedHandle(arguments->NativeArgAt(0));
+ return Integer::New(BigintOperations::BitLength(value));
+}
+
+
DEFINE_NATIVE_ENTRY(Bigint_shlFromInt, 2) {
// Use the preallocated out of memory exception to avoid calling
// into dart code or allocating any code.
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index dca7ef8..7c242ef 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -95,6 +95,20 @@
bool get isNegative => this < 0;
bool get isInfinite => false;
+ int toUnsigned(int width) {
+ return this & ((1 << width) - 1);
+ }
+
+ int toSigned(int width) {
+ // The value of binary number weights each bit by a power of two. The
+ // twos-complement value weights the sign bit negatively. We compute the
+ // value of the negative weighting by isolating the sign bit with the
+ // correct power of two weighting and subtracting it from the value of the
+ // lower bits.
+ int signMask = 1 << (width - 1);
+ return (this & (signMask - 1)) - (this & signMask);
+ }
+
int compareTo(num other) {
final int EQUAL = 0, LESS = -1, GREATER = 1;
if (other is double) {
@@ -212,6 +226,8 @@
return this;
}
int operator ~() native "Smi_bitNegate";
+ int get bitLength native "Smi_bitLength";
+
int _shrFromInt(int other) native "Smi_shrFromInt";
int _shlFromInt(int other) native "Smi_shlFromInt";
@@ -252,6 +268,7 @@
return this;
}
int operator ~() native "Mint_bitNegate";
+ int get bitLength native "Mint_bitLength";
// Shift by mint exceeds range that can be handled by the VM.
int _shrFromInt(int other) {
@@ -275,6 +292,7 @@
return this;
}
int operator ~() native "Bigint_bitNegate";
+ int get bitLength native "Bigint_bitLength";
// Shift by bigint exceeds range that can be handled by the VM.
int _shrFromInt(int other) {
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 22b5634..8880d2306 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -61,8 +61,7 @@
function_name,
kNumArguments,
Object::empty_array(),
- Resolver::kIsQualified,
- NULL); // No ambiguity error expected.
+ Resolver::kIsQualified);
ASSERT(!func.IsNull());
isolate->object_store()->set_receive_port_create_function(func);
}
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index 3f70c58..8bed356 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -29,15 +29,6 @@
}
-// Note a "raw type" is not the same as a RawType.
-static RawAbstractType* RawTypeOfClass(const Class& cls) {
- Type& type = Type::Handle(Type::New(cls,
- Object::null_abstract_type_arguments(),
- Scanner::kDummyTokenIndex));
- return ClassFinalizer::FinalizeType(cls, type, ClassFinalizer::kCanonicalize);
-}
-
-
static void ThrowMirroredCompilationError(const String& message) {
Array& args = Array::Handle(Array::New(1));
args.SetAt(0, message);
@@ -294,6 +285,7 @@
static RawInstance* CreateClassMirror(const Class& cls,
const AbstractType& type,
+ const Bool& is_declaration,
const Instance& owner_mirror) {
ASSERT(!cls.IsDynamicClass() && !cls.IsVoidClass());
@@ -307,16 +299,12 @@
}
}
+ ASSERT(!type.IsNull());
+
const Bool& is_generic = Bool::Get(cls.NumTypeParameters() != 0);
const Bool& is_mixin_typedef = Bool::Get(cls.is_mixin_typedef());
- // If the class is not generic, the mirror must have a non-null runtime type.
- // If the class is generic, the mirror will have a null runtime type if it
- // represents the declaration or a non-null runtime type if it represents an
- // instantiation.
- ASSERT(!(cls.NumTypeParameters() == 0) || !type.IsNull());
-
- const Array& args = Array::Handle(Array::New(5));
+ const Array& args = Array::Handle(Array::New(6));
args.SetAt(0, MirrorReference::Handle(MirrorReference::New(cls)));
args.SetAt(1, type);
// We do not set the names of anonymous mixin applications because the mirrors
@@ -327,6 +315,7 @@
}
args.SetAt(3, is_generic);
args.SetAt(4, is_mixin_typedef);
+ args.SetAt(5, cls.NumTypeParameters() == 0 ? Bool::False() : is_declaration);
return CreateMirror(Symbols::_LocalClassMirrorImpl(), args);
}
@@ -359,7 +348,7 @@
// TODO(mlippautz): Create once in the VM isolate and retrieve from there.
return CreateMirror(Symbols::_SpecialTypeMirrorImpl(), args);
}
- return CreateClassMirror(cls, type, Object::null_instance());
+ return CreateClassMirror(cls, type, Bool::False(), Object::null_instance());
} else if (type.IsTypeParameter()) {
return CreateTypeVariableMirror(TypeParameter::Cast(type),
Object::null_instance());
@@ -424,16 +413,11 @@
Exceptions::ThrowByType(Exceptions::kArgument, args);
UNREACHABLE();
}
- // Strip the type for generics only.
- if (cls.NumTypeParameters() == 0) {
- return CreateClassMirror(cls,
- type,
- Object::null_instance());
- } else {
- return CreateClassMirror(cls,
- AbstractType::Handle(),
- Object::null_instance());
- }
+ const Type& stripped_type = Type::Handle(cls.RareType());
+ return CreateClassMirror(cls,
+ stripped_type,
+ Bool::True(), // is_declaration
+ Object::null_instance());
}
@@ -523,13 +507,6 @@
}
-DEFINE_NATIVE_ENTRY(ClassMirror_name, 1) {
- GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
- const Class& klass = Class::Handle(ref.GetClassReferent());
- return klass.UserVisibleName();
-}
-
-
DEFINE_NATIVE_ENTRY(ClassMirror_library, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
const Class& klass = Class::Handle(ref.GetClassReferent());
@@ -668,13 +645,11 @@
if (!klass.IsCanonicalSignatureClass() &&
!klass.IsDynamicClass() &&
!RawObject::IsImplementationClassId(klass.id())) {
- if (klass.NumTypeParameters() == 0) {
- // Include runtime type for non-generics only.
- type = RawTypeOfClass(klass);
- } else {
- type = AbstractType::null();
- }
- member_mirror = CreateClassMirror(klass, type, owner_mirror);
+ type = klass.RareType();
+ member_mirror = CreateClassMirror(klass,
+ type,
+ Bool::True(), // is_declaration
+ owner_mirror);
member_mirrors.Add(member_mirror);
}
} else if (entry.IsField()) {
@@ -745,11 +720,10 @@
DEFINE_NATIVE_ENTRY(TypeVariableMirror_owner, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(TypeParameter, param, arguments->NativeArgAt(0));
const Class& owner = Class::Handle(param.parameterized_class());
- // The owner of a type variable must be a generic class: pass a null runtime
- // type to get a mirror on the declaration.
- ASSERT(owner.NumTypeParameters() != 0);
+ const Type& type = Type::Handle(owner.RareType());
return CreateClassMirror(owner,
- AbstractType::Handle(),
+ type,
+ Bool::True(), // is_declaration
Instance::null_instance());
}
@@ -974,7 +948,7 @@
if (function.IsNull() ||
!function.AreValidArguments(args_descriptor, NULL) ||
!function.is_visible()) {
- ThrowNoSuchMethod(AbstractType::Handle(RawTypeOfClass(klass)),
+ ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
function_name,
function,
InvocationMirror::kStatic,
@@ -1009,7 +983,7 @@
klass.LookupStaticFunctionAllowPrivate(internal_getter_name));
if (getter.IsNull() || !getter.is_visible()) {
- ThrowNoSuchMethod(AbstractType::Handle(RawTypeOfClass(klass)),
+ ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
getter_name,
getter,
InvocationMirror::kStatic,
@@ -1048,7 +1022,7 @@
klass.LookupStaticFunctionAllowPrivate(internal_setter_name));
if (setter.IsNull() || !setter.is_visible()) {
- ThrowNoSuchMethod(AbstractType::Handle(RawTypeOfClass(klass)),
+ ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
setter_name,
setter,
InvocationMirror::kStatic,
@@ -1084,13 +1058,14 @@
}
-DEFINE_NATIVE_ENTRY(ClassMirror_invokeConstructor, 4) {
+DEFINE_NATIVE_ENTRY(ClassMirror_invokeConstructor, 5) {
GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
const Class& klass = Class::Handle(ref.GetClassReferent());
+ GET_NATIVE_ARGUMENT(Type, type, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(
- String, constructor_name, arguments->NativeArgAt(1));
- GET_NON_NULL_NATIVE_ARGUMENT(Array, explicit_args, arguments->NativeArgAt(2));
- GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(3));
+ String, constructor_name, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Array, explicit_args, arguments->NativeArgAt(3));
+ GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));
// By convention, the static function implementing a named constructor 'C'
// for class 'A' is labeled 'A.C', and the static function implementing the
@@ -1113,7 +1088,7 @@
// Pretend we didn't find the constructor at all when the arity is wrong
// so as to produce the same NoSuchMethodError as the non-reflective case.
lookup_constructor = Function::null();
- ThrowNoSuchMethod(AbstractType::Handle(RawTypeOfClass(klass)),
+ ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
internal_constructor_name,
lookup_constructor,
InvocationMirror::kConstructor,
@@ -1154,7 +1129,7 @@
// Pretend we didn't find the constructor at all when the arity is wrong
// so as to produce the same NoSuchMethodError as the non-reflective case.
redirected_constructor = Function::null();
- ThrowNoSuchMethod(AbstractType::Handle(RawTypeOfClass(klass)),
+ ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
internal_constructor_name,
redirected_constructor,
InvocationMirror::kConstructor,
@@ -1162,19 +1137,27 @@
UNREACHABLE();
}
+ ASSERT(!type.IsNull());
+ const AbstractTypeArguments& type_arguments =
+ AbstractTypeArguments::Handle(type.arguments());
+
Instance& new_object = Instance::Handle();
if (redirected_constructor.IsConstructor()) {
// Constructors get the uninitialized object and a constructor phase. Note
// we have delayed allocation until after the function type and argument
// matching checks.
new_object = Instance::New(redirected_klass);
+ if (!type_arguments.IsNull()) {
+ // The type arguments will be null if the class has no type parameters, in
+ // which case the following call would fail because there is no slot
+ // reserved in the object for the type vector.
+ new_object.SetTypeArguments(type_arguments);
+ }
args.SetAt(0, new_object);
args.SetAt(1, Smi::Handle(Smi::New(Function::kCtorPhaseAll)));
} else {
// Factories get type arguments.
- // TODO(12921): Should we allow the user to specify type arguments? Use type
- // arguments from the mirror?
- args.SetAt(0, Object::null_abstract_type_arguments());
+ args.SetAt(0, type_arguments);
}
// Invoke the constructor and return the new object.
@@ -1183,7 +1166,8 @@
args,
args_descriptor_array));
if (result.IsError()) {
- return result.raw();
+ ThrowInvokeError(Error::Cast(result));
+ UNREACHABLE();
}
// Factories may return null.
@@ -1211,14 +1195,8 @@
const Array& args_descriptor_array =
Array::Handle(ArgumentsDescriptor::New(args.Length(), arg_names));
- String& ambiguity_error_msg = String::Handle(isolate);
const Function& function = Function::Handle(
- library.LookupFunctionAllowPrivate(function_name, &ambiguity_error_msg));
-
- if (function.IsNull() && !ambiguity_error_msg.IsNull()) {
- ThrowMirroredCompilationError(ambiguity_error_msg);
- UNREACHABLE();
- }
+ library.LookupFunctionAllowPrivate(function_name));
ArgumentsDescriptor args_descriptor(args_descriptor_array);
if (function.IsNull() ||
@@ -1253,16 +1231,14 @@
// To access a top-level we may need to use the Field or the
// getter Function. The getter function may either be in the
// library or in the field's owner class, depending.
- String& ambiguity_error_msg = String::Handle(isolate);
const Field& field = Field::Handle(
- library.LookupFieldAllowPrivate(getter_name, &ambiguity_error_msg));
+ library.LookupFieldAllowPrivate(getter_name));
Function& getter = Function::Handle();
- if (field.IsNull() && ambiguity_error_msg.IsNull()) {
+ if (field.IsNull()) {
// No field found and no ambiguity error. Check for a getter in the lib.
const String& internal_getter_name =
String::Handle(Field::GetterName(getter_name));
- getter = library.LookupFunctionAllowPrivate(internal_getter_name,
- &ambiguity_error_msg);
+ getter = library.LookupFunctionAllowPrivate(internal_getter_name);
} else if (!field.IsNull() && FieldIsUninitialized(field)) {
// A field was found. Check for a getter in the field's owner classs.
const Class& klass = Class::Handle(field.owner());
@@ -1284,15 +1260,11 @@
if (!field.IsNull()) {
return field.value();
}
- if (ambiguity_error_msg.IsNull()) {
- ThrowNoSuchMethod(Instance::null_instance(),
- getter_name,
- getter,
- InvocationMirror::kTopLevel,
- InvocationMirror::kGetter);
- } else {
- ThrowMirroredCompilationError(ambiguity_error_msg);
- }
+ ThrowNoSuchMethod(Instance::null_instance(),
+ getter_name,
+ getter,
+ InvocationMirror::kTopLevel,
+ InvocationMirror::kGetter);
UNREACHABLE();
return Instance::null();
}
@@ -1310,26 +1282,20 @@
// To access a top-level we may need to use the Field or the
// setter Function. The setter function may either be in the
// library or in the field's owner class, depending.
- String& ambiguity_error_msg = String::Handle(isolate);
const Field& field = Field::Handle(
- library.LookupFieldAllowPrivate(setter_name, &ambiguity_error_msg));
+ library.LookupFieldAllowPrivate(setter_name));
- if (field.IsNull() && ambiguity_error_msg.IsNull()) {
+ if (field.IsNull()) {
const String& internal_setter_name =
String::Handle(Field::SetterName(setter_name));
const Function& setter = Function::Handle(
- library.LookupFunctionAllowPrivate(internal_setter_name,
- &ambiguity_error_msg));
+ library.LookupFunctionAllowPrivate(internal_setter_name));
if (setter.IsNull() || !setter.is_visible()) {
- if (ambiguity_error_msg.IsNull()) {
- ThrowNoSuchMethod(Instance::null_instance(),
- setter_name,
- setter,
- InvocationMirror::kTopLevel,
- InvocationMirror::kSetter);
- } else {
- ThrowMirroredCompilationError(ambiguity_error_msg);
- }
+ ThrowNoSuchMethod(Instance::null_instance(),
+ setter_name,
+ setter,
+ InvocationMirror::kTopLevel,
+ InvocationMirror::kSetter);
UNREACHABLE();
}
@@ -1372,12 +1338,8 @@
return CreateLibraryMirror(Library::Handle(owner.library()));
}
- AbstractType& type = AbstractType::Handle();
- if (owner.NumTypeParameters() == 0) {
- // Include runtime type for non-generics only.
- type = RawTypeOfClass(owner);
- }
- return CreateClassMirror(owner, type, Object::null_instance());
+ Type& type = Type::Handle(owner.RareType());
+ return CreateClassMirror(owner, type, Bool::True(), Object::null_instance());
}
diff --git a/runtime/lib/mirrors_impl.dart b/runtime/lib/mirrors_impl.dart
index 844b6a8..534ded4 100644
--- a/runtime/lib/mirrors_impl.dart
+++ b/runtime/lib/mirrors_impl.dart
@@ -21,11 +21,8 @@
return new_map;
}
-Map _makeMemberMap(List mirrors) {
- Map result = new Map<Symbol, dynamic>();
- mirrors.forEach((mirror) => result[mirror.simpleName] = mirror);
- return result;
-}
+Map _makeMemberMap(List mirrors) => new Map<Symbol, dynamic>.fromIterable(
+ mirrors, key: (e) => e.simpleName);
String _n(Symbol symbol) => _symbol_dev.Symbol.getName(symbol);
@@ -39,13 +36,6 @@
return _s('${_n(owner.qualifiedName)}.${_n(simpleName)}');
}
-Map<Symbol, dynamic> _convertStringToSymbolMap(Map<String, dynamic> map) {
- if (map == null) return null;
- Map<Symbol, dynamic> result = new Map<Symbol, dynamic>();
- map.forEach((name, value) => result[_s(name)] = value);
- return result;
-}
-
String _makeSignatureString(TypeMirror returnType,
List<ParameterMirror> parameters) {
StringBuffer buf = new StringBuffer();
@@ -83,18 +73,12 @@
return buf.toString();
}
-Map<Uri, LibraryMirror> _createLibrariesMap(List<LibraryMirror> list) {
- var map = new Map<Uri, LibraryMirror>();
- list.forEach((LibraryMirror mirror) => map[mirror.uri] = mirror);
- return map;
-}
-
List _metadata(reflectee)
native 'DeclarationMirror_metadata';
// This will verify the argument types, unwrap them, and ensure we have a fixed
// array.
-List _unwarpAsyncPositionals(wrappedArgs) {
+List _unwrapAsyncPositionals(wrappedArgs) {
List unwrappedArgs = new List(wrappedArgs.length);
for(int i = 0; i < wrappedArgs.length; i++){
var wrappedArg = wrappedArgs[i];
@@ -109,7 +93,8 @@
}
return unwrappedArgs;
}
-Map _unwarpAsyncNamed(wrappedArgs) {
+
+Map _unwrapAsyncNamed(wrappedArgs) {
if (wrappedArgs==null) return null;
Map unwrappedArgs = new Map();
wrappedArgs.forEach((name, wrappedArg){
@@ -128,13 +113,13 @@
class _LocalMirrorSystemImpl extends MirrorSystem {
// Change parameter back to "this.libraries" when native code is changed.
_LocalMirrorSystemImpl(List<LibraryMirror> libraries, this.isolate)
- : this.libraries = _createLibrariesMap(libraries);
+ : this.libraries = new Map<Uri, LibraryMirror>.fromIterable(
+ libraries, key: (e) => e.uri);
final Map<Uri, LibraryMirror> libraries;
final IsolateMirror isolate;
TypeMirror _dynamicType = null;
-
TypeMirror get dynamicType {
if (_dynamicType == null) {
_dynamicType = new _SpecialTypeMirrorImpl('dynamic');
@@ -143,7 +128,6 @@
}
TypeMirror _voidType = null;
-
TypeMirror get voidType {
if (_voidType == null) {
_voidType = new _SpecialTypeMirrorImpl('void');
@@ -166,7 +150,7 @@
class _LocalIsolateMirrorImpl extends _LocalMirrorImpl
implements IsolateMirror {
- _LocalIsolateMirrorImpl(this.debugName, this.rootLibrary) {}
+ _LocalIsolateMirrorImpl(this.debugName, this.rootLibrary);
final String debugName;
final bool isCurrent = true;
@@ -223,8 +207,8 @@
[Map<Symbol, dynamic> namedArguments]) {
return new Future(() {
return this.invoke(memberName,
- _unwarpAsyncPositionals(positionalArguments),
- _unwarpAsyncNamed(namedArguments));
+ _unwrapAsyncPositionals(positionalArguments),
+ _unwrapAsyncNamed(namedArguments));
});
}
@@ -385,11 +369,6 @@
return _function;
}
- String get source {
- throw new UnimplementedError(
- 'ClosureMirror.source is not implemented');
- }
-
InstanceMirror apply(List<Object> positionalArguments,
[Map<Symbol, Object> namedArguments]) {
// TODO(iposva): When closures get an ordinary call method, this can be
@@ -421,8 +400,8 @@
Future<InstanceMirror> applyAsync(List positionalArguments,
[Map<Symbol, dynamic> namedArguments]) {
return new Future(() {
- return this.apply(_unwarpAsyncPositionals(positionalArguments),
- _unwarpAsyncNamed(namedArguments));
+ return this.apply(_unwrapAsyncPositionals(positionalArguments),
+ _unwrapAsyncNamed(namedArguments));
});
}
@@ -446,15 +425,17 @@
this._reflectedType,
String simpleName,
this._isGeneric,
- this._isMixinTypedef)
+ this._isMixinTypedef,
+ this._isGenericDeclaration)
: this._simpleName = _s(simpleName),
super(reflectee);
final Type _reflectedType;
final bool _isGeneric;
final bool _isMixinTypedef;
+ final bool _isGenericDeclaration;
- bool get hasReflectedType => _reflectedType != null;
+ bool get hasReflectedType => !_isGenericDeclaration;
Type get reflectedType {
if (!hasReflectedType) {
throw new UnsupportedError(
@@ -465,15 +446,9 @@
Symbol _simpleName;
Symbol get simpleName {
- // dynamic, void and the function types have their names set eagerly in the
- // constructor.
+ // All but anonymous mixin applications have their name set at construction.
if(_simpleName == null) {
- var simpleString = _name(_reflectee);
- if (simpleString.contains('&')) {
- _simpleName = this._mixinApplicationName;
- } else {
- _simpleName = _s(simpleString);
- }
+ _simpleName = this._mixinApplicationName;
}
return _simpleName;
}
@@ -499,8 +474,7 @@
final bool isTopLevel = true;
SourceLocation get location {
- throw new UnimplementedError(
- 'ClassMirror.location is not implemented');
+ throw new UnimplementedError('ClassMirror.location is not implemented');
}
// TODO(rmacnak): Remove these left-overs from the days of separate interfaces
@@ -565,7 +539,6 @@
}
Map<Symbol, Mirror> _members;
-
Map<Symbol, Mirror> get members {
if (_members == null) {
var whoseMembers = _isMixinTypedef ? _trueSuperclass : this;
@@ -574,11 +547,7 @@
return _members;
}
- Map<Symbol, MethodMirror> _methods = null;
- Map<Symbol, MethodMirror> _getters = null;
- Map<Symbol, MethodMirror> _setters = null;
- Map<Symbol, VariableMirror> _variables = null;
-
+ Map<Symbol, MethodMirror> _methods;
Map<Symbol, MethodMirror> get methods {
if (_methods == null) {
_methods = _filterMap(
@@ -588,6 +557,7 @@
return _methods;
}
+ Map<Symbol, MethodMirror> _getters;
Map<Symbol, MethodMirror> get getters {
if (_getters == null) {
_getters = _filterMap(
@@ -597,6 +567,7 @@
return _getters;
}
+ Map<Symbol, MethodMirror> _setters;
Map<Symbol, MethodMirror> get setters {
if (_setters == null) {
_setters = _filterMap(
@@ -606,6 +577,7 @@
return _setters;
}
+ Map<Symbol, VariableMirror> _variables;
Map<Symbol, VariableMirror> get variables {
if (_variables == null) {
_variables = _filterMap(
@@ -616,7 +588,6 @@
}
Map<Symbol, MethodMirror> _constructors;
-
Map<Symbol, MethodMirror> get constructors {
if (_constructors == null) {
var constructorsList = _computeConstructors(_reflectee);
@@ -628,7 +599,6 @@
}
Map<Symbol, TypeVariableMirror> _typeVariables = null;
-
Map<Symbol, TypeVariableMirror> get typeVariables {
if (_typeVariables == null) {
List params = _ClassMirror_type_variables(_reflectee);
@@ -646,20 +616,18 @@
Map<Symbol, TypeMirror> _typeArguments = null;
Map<Symbol, TypeMirror> get typeArguments {
if(_typeArguments == null) {
- if(_reflectedType == null) {
+ if(_isGenericDeclaration) {
_typeArguments = new LinkedHashMap<Symbol, TypeMirror>();
} else {
_typeArguments =
- new LinkedHashMap<Symbol, TypeMirror>.fromIterables(typeVariables.keys,
- _computeTypeArguments(_reflectedType));
+ new LinkedHashMap<Symbol, TypeMirror>.fromIterables(
+ typeVariables.keys, _computeTypeArguments(_reflectedType));
}
}
return _typeArguments;
}
- bool get isOriginalDeclaration {
- return !_isGeneric || _reflectedType == null;
- }
+ bool get isOriginalDeclaration => !_isGeneric || _isGenericDeclaration;
ClassMirror get originalDeclaration {
if (isOriginalDeclaration) {
@@ -669,9 +637,7 @@
}
}
- String toString() {
- return "ClassMirror on '${_n(simpleName)}'";
- }
+ String toString() => "ClassMirror on '${_n(simpleName)}'";
InstanceMirror newInstance(Symbol constructorName,
List positionalArguments,
@@ -694,6 +660,7 @@
}
return reflect(_invokeConstructor(_reflectee,
+ _reflectedType,
_n(constructorName),
arguments,
names));
@@ -704,8 +671,8 @@
[Map<Symbol, dynamic> namedArguments]) {
return new Future(() {
return this.newInstance(constructorName,
- _unwarpAsyncPositionals(positionalArguments),
- _unwarpAsyncNamed(namedArguments));
+ _unwrapAsyncPositionals(positionalArguments),
+ _unwrapAsyncNamed(namedArguments));
});
}
@@ -718,14 +685,12 @@
bool operator ==(other) {
return this.runtimeType == other.runtimeType &&
this._reflectee == other._reflectee &&
- this._reflectedType == other._reflectedType;
+ this._reflectedType == other._reflectedType &&
+ this._isGenericDeclaration == other._isGenericDeclaration;
}
int get hashCode => simpleName.hashCode;
- static _name(reflectee)
- native "ClassMirror_name";
-
static _library(reflectee)
native "ClassMirror_library";
@@ -753,7 +718,7 @@
_invokeSetter(reflectee, setterName, value)
native 'ClassMirror_invokeSetter';
- static _invokeConstructor(reflectee, constructorName, arguments, argumentNames)
+ static _invokeConstructor(reflectee, type, constructorName, arguments, argumentNames)
native 'ClassMirror_invokeConstructor';
static _ClassMirror_type_variables(reflectee)
@@ -766,7 +731,7 @@
class _LocalFunctionTypeMirrorImpl extends _LocalClassMirrorImpl
implements FunctionTypeMirror {
_LocalFunctionTypeMirrorImpl(reflectee, reflectedType)
- : super(reflectee, reflectedType, null, false, false);
+ : super(reflectee, reflectedType, null, false, false, false);
// FunctionTypeMirrors have a simpleName generated from their signature.
Symbol _simpleName = null;
@@ -907,9 +872,13 @@
this._owner)
: super(reflectee, _s(simpleName));
+ final bool isTopLevel = true;
+
// TODO(12282): Deal with generic typedefs.
bool get _isGeneric => false;
+ bool get isPrivate => false;
+
DeclarationMirror _owner;
DeclarationMirror get owner {
if (_owner == null) {
@@ -918,13 +887,8 @@
return _owner;
}
- bool get isPrivate => false;
-
- final bool isTopLevel = true;
-
SourceLocation get location {
- throw new UnimplementedError(
- 'TypedefMirror.location is not implemented');
+ throw new UnimplementedError('TypedefMirror.location is not implemented');
}
TypeMirror _referent = null;
@@ -967,14 +931,12 @@
final bool isTopLevel = false;
SourceLocation get location {
- throw new UnimplementedError(
- 'LibraryMirror.location is not implemented');
+ throw new UnimplementedError('LibraryMirror.location is not implemented');
}
final Uri uri;
Map<Symbol, Mirror> _members;
-
Map<Symbol, Mirror> get members {
if (_members == null) {
_members = _makeMemberMap(_computeMembers(_reflectee));
@@ -982,12 +944,7 @@
return _members;
}
- Map<Symbol, ClassMirror> _classes = null;
- Map<Symbol, MethodMirror> _functions = null;
- Map<Symbol, MethodMirror> _getters = null;
- Map<Symbol, MethodMirror> _setters = null;
- Map<Symbol, VariableMirror> _variables = null;
-
+ Map<Symbol, ClassMirror> _classes;
Map<Symbol, ClassMirror> get classes {
if (_classes == null) {
_classes = _filterMap(members,
@@ -996,30 +953,31 @@
return _classes;
}
+ Map<Symbol, MethodMirror> _functions;
Map<Symbol, MethodMirror> get functions {
if (_functions == null) {
- _functions = _filterMap(members,
- (key, value) => (value is MethodMirror));
+ _functions = _filterMap(members, (key, value) => (value is MethodMirror));
}
return _functions;
}
+ Map<Symbol, MethodMirror> _getters;
Map<Symbol, MethodMirror> get getters {
if (_getters == null) {
- _getters = _filterMap(functions,
- (key, value) => (value.isGetter));
+ _getters = _filterMap(functions, (key, value) => (value.isGetter));
}
return _getters;
}
+ Map<Symbol, MethodMirror> _setters;
Map<Symbol, MethodMirror> get setters {
if (_setters == null) {
- _setters = _filterMap(functions,
- (key, value) => (value.isSetter));
+ _setters = _filterMap(functions, (key, value) => (value.isSetter));
}
return _setters;
}
+ Map<Symbol, VariableMirror> _variables;
Map<Symbol, VariableMirror> get variables {
if (_variables == null) {
_variables = _filterMap(members,
@@ -1034,8 +992,6 @@
return _metadata(_reflectee).map(reflect).toList(growable:false);
}
- String toString() => "LibraryMirror on '${_n(simpleName)}'";
-
bool operator ==(other) {
return this.runtimeType == other.runtimeType &&
this._reflectee == other._reflectee;
@@ -1043,6 +999,8 @@
int get hashCode => simpleName.hashCode;
+ String toString() => "LibraryMirror on '${_n(simpleName)}'";
+
_invoke(reflectee, memberName, arguments, argumentNames)
native 'LibraryMirror_invoke';
@@ -1097,16 +1055,13 @@
return _owner;
}
- bool get isPrivate {
- return _n(simpleName).startsWith('_') ||
- _n(constructorName).startsWith('_');
- }
+ bool get isPrivate => _n(simpleName).startsWith('_') ||
+ _n(constructorName).startsWith('_');
bool get isTopLevel => owner is LibraryMirror;
SourceLocation get location {
- throw new UnimplementedError(
- 'MethodMirror.location is not implemented');
+ throw new UnimplementedError('MethodMirror.location is not implemented');
}
TypeMirror _returnType = null;
@@ -1197,18 +1152,15 @@
: super(reflectee, _s(simpleName));
final DeclarationMirror owner;
+ final bool isStatic;
+ final bool isFinal;
- bool get isPrivate {
- return _n(simpleName).startsWith('_');
- }
+ bool get isPrivate => _n(simpleName).startsWith('_');
- bool get isTopLevel {
- return owner is LibraryMirror;
- }
+ bool get isTopLevel => owner is LibraryMirror;
SourceLocation get location {
- throw new UnimplementedError(
- 'VariableMirror.location is not implemented');
+ throw new UnimplementedError('VariableMirror.location is not implemented');
}
TypeMirror _type;
@@ -1219,9 +1171,6 @@
return _type;
}
- final bool isStatic;
- final bool isFinal;
-
String toString() => "VariableMirror on '${_n(simpleName)}'";
static _VariableMirror_type(reflectee)
@@ -1290,24 +1239,17 @@
_SpecialTypeMirrorImpl(String name) : simpleName = _s(name);
final bool isPrivate = false;
+ final DeclarationMirror owner = null;
+ final Symbol simpleName;
final bool isTopLevel = true;
-
// Fixed length 0, therefore immutable.
final List<InstanceMirror> metadata = new List(0);
- final DeclarationMirror owner = null;
- final Symbol simpleName;
-
SourceLocation get location {
- throw new UnimplementedError(
- 'TypeMirror.location is not implemented');
+ throw new UnimplementedError('TypeMirror.location is not implemented');
}
- Symbol get qualifiedName {
- return simpleName;
- }
-
- String toString() => "TypeMirror on '${_n(simpleName)}'";
+ Symbol get qualifiedName => simpleName;
// TODO(11955): Remove once dynamicType and voidType are canonical objects in
// the object store.
@@ -1319,6 +1261,8 @@
}
int get hashCode => simpleName.hashCode;
+
+ String toString() => "TypeMirror on '${_n(simpleName)}'";
}
class _Mirrors {
diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart
index b9ec3c9..e4f959e 100644
--- a/runtime/lib/string_patch.dart
+++ b/runtime/lib/string_patch.dart
@@ -74,8 +74,6 @@
String operator +(String other) native "String_concat";
- String concat(String other) => this + other;
-
String toString() {
return this;
}
diff --git a/runtime/lib/typed_data.dart b/runtime/lib/typed_data.dart
index 0a8b0ef..1007c49 100644
--- a/runtime/lib/typed_data.dart
+++ b/runtime/lib/typed_data.dart
@@ -2325,6 +2325,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Int16List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Int16List.BYTES_PER_ELEMENT);
}
@@ -2376,6 +2377,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Uint16List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Uint16List.BYTES_PER_ELEMENT);
}
@@ -2427,6 +2429,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Int32List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Int32List.BYTES_PER_ELEMENT);
}
@@ -2478,6 +2481,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Uint32List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Uint32List.BYTES_PER_ELEMENT);
}
@@ -2529,6 +2533,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Int64List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Int64List.BYTES_PER_ELEMENT);
}
@@ -2580,6 +2585,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Uint64List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Uint64List.BYTES_PER_ELEMENT);
}
@@ -2631,6 +2637,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Float32List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Float32List.BYTES_PER_ELEMENT);
}
@@ -2682,6 +2689,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Float64List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Float64List.BYTES_PER_ELEMENT);
}
@@ -2733,6 +2741,7 @@
_rangeCheck(buffer.lengthInBytes,
offsetInBytes,
length * Float32x4List.BYTES_PER_ELEMENT);
+ _offsetAlignmentCheck(_offsetInBytes, Float32x4List.BYTES_PER_ELEMENT);
}
@@ -3125,6 +3134,14 @@
}
+void _offsetAlignmentCheck(int offset, int alignment) {
+ if ((offset % alignment) != 0) {
+ throw new RangeError('Offset ($offset) must be a multiple of '
+ 'BYTES_PER_ELEMENT ($alignment)');
+ }
+}
+
+
int _defaultIfNull(object, value) {
if (object == null) {
return value;
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 6add296..1ed67c6 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -48,6 +48,9 @@
# Skip until we stabilize language tests.
*: Skip
+[ $arch == x64 && $mode == debug ]
+cc/FindCodeObject: Pass, Timeout # Issue 13144
+
[ $arch == simarm ]
dart/isolate_mirror_local_test: Skip
diff --git a/runtime/vm/assembler_ia32.cc b/runtime/vm/assembler_ia32.cc
index 3b9757c..0ee5ee7 100644
--- a/runtime/vm/assembler_ia32.cc
+++ b/runtime/vm/assembler_ia32.cc
@@ -1679,6 +1679,14 @@
}
+void Assembler::bsrl(Register dst, Register src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0xBD);
+ EmitRegisterOperand(dst, src);
+}
+
+
void Assembler::enter(const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xC8);
diff --git a/runtime/vm/assembler_ia32.h b/runtime/vm/assembler_ia32.h
index 4ddd90e..be28c74 100644
--- a/runtime/vm/assembler_ia32.h
+++ b/runtime/vm/assembler_ia32.h
@@ -595,6 +595,8 @@
void negl(Register reg);
void notl(Register reg);
+ void bsrl(Register dst, Register src);
+
void enter(const Immediate& imm);
void leave();
diff --git a/runtime/vm/assembler_ia32_test.cc b/runtime/vm/assembler_ia32_test.cc
index 04c2915..5628494 100644
--- a/runtime/vm/assembler_ia32_test.cc
+++ b/runtime/vm/assembler_ia32_test.cc
@@ -294,6 +294,27 @@
}
+ASSEMBLER_TEST_GENERATE(BitScanReverse, assembler) {
+ __ movl(ECX, Address(ESP, kWordSize));
+ __ movl(EAX, Immediate(666)); // Marker for conditional write.
+ __ bsrl(EAX, ECX);
+ __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BitScanReverse, test) {
+ typedef int (*Bsr)(int input);
+ Bsr call = reinterpret_cast<Bsr>(test->entry());
+ EXPECT_EQ(666, call(0));
+ EXPECT_EQ(0, call(1));
+ EXPECT_EQ(1, call(2));
+ EXPECT_EQ(1, call(3));
+ EXPECT_EQ(2, call(4));
+ EXPECT_EQ(5, call(42));
+ EXPECT_EQ(31, call(-1));
+}
+
+
ASSEMBLER_TEST_GENERATE(MoveExtend, assembler) {
__ pushl(EBX); // preserve EBX.
__ movl(EDX, Immediate(0x1234ffff));
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 15c02d8..e54e09c 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -859,9 +859,13 @@
}
void BranchEqual(Register rd, int32_t value, Label* l) {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- beq(rd, CMPRES2, l);
+ if (value == 0) {
+ beq(rd, ZR, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ beq(rd, CMPRES2, l);
+ }
}
void BranchEqual(Register rd, const Object& object, Label* l) {
@@ -871,9 +875,13 @@
}
void BranchNotEqual(Register rd, int32_t value, Label* l) {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- bne(rd, CMPRES2, l);
+ if (value == 0) {
+ bne(rd, ZR, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ bne(rd, CMPRES2, l);
+ }
}
void BranchNotEqual(Register rd, const Object& object, Label* l) {
@@ -888,9 +896,13 @@
}
void BranchSignedGreater(Register rd, int32_t value, Label* l) {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- BranchSignedGreater(rd, CMPRES2, l);
+ if (value == 0) {
+ bgtz(rd, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ BranchSignedGreater(rd, CMPRES2, l);
+ }
}
void BranchUnsignedGreater(Register rd, Register rs, Label* l) {
@@ -899,9 +911,13 @@
}
void BranchUnsignedGreater(Register rd, int32_t value, Label* l) {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- BranchUnsignedGreater(rd, CMPRES2, l);
+ if (value == 0) {
+ BranchNotEqual(rd, 0, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ BranchUnsignedGreater(rd, CMPRES2, l);
+ }
}
void BranchSignedGreaterEqual(Register rd, Register rs, Label* l) {
@@ -910,13 +926,17 @@
}
void BranchSignedGreaterEqual(Register rd, int32_t value, Label* l) {
- if (Utils::IsInt(kImmBits, value)) {
- slti(CMPRES2, rd, Immediate(value));
- beq(CMPRES2, ZR, l);
+ if (value == 0) {
+ bgez(rd, l);
} else {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- BranchSignedGreaterEqual(rd, CMPRES2, l);
+ if (Utils::IsInt(kImmBits, value)) {
+ slti(CMPRES2, rd, Immediate(value));
+ beq(CMPRES2, ZR, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ BranchSignedGreaterEqual(rd, CMPRES2, l);
+ }
}
}
@@ -926,13 +946,17 @@
}
void BranchUnsignedGreaterEqual(Register rd, int32_t value, Label* l) {
- if (Utils::IsUint(kImmBits, value)) {
- sltiu(CMPRES2, rd, Immediate(value));
- beq(CMPRES2, ZR, l);
+ if (value == 0) {
+ b(l);
} else {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- BranchUnsignedGreaterEqual(rd, CMPRES2, l);
+ if (Utils::IsUint(kImmBits, value)) {
+ sltiu(CMPRES2, rd, Immediate(value));
+ beq(CMPRES2, ZR, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ BranchUnsignedGreaterEqual(rd, CMPRES2, l);
+ }
}
}
@@ -941,13 +965,17 @@
}
void BranchSignedLess(Register rd, int32_t value, Label* l) {
- if (Utils::IsInt(kImmBits, value)) {
- slti(CMPRES2, rd, Immediate(value));
- bne(CMPRES2, ZR, l);
+ if (value == 0) {
+ bltz(rd, l);
} else {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- BranchSignedGreater(CMPRES2, rd, l);
+ if (Utils::IsInt(kImmBits, value)) {
+ slti(CMPRES2, rd, Immediate(value));
+ bne(CMPRES2, ZR, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ BranchSignedGreater(CMPRES2, rd, l);
+ }
}
}
@@ -956,6 +984,7 @@
}
void BranchUnsignedLess(Register rd, int32_t value, Label* l) {
+ ASSERT(value != 0);
if (Utils::IsUint(kImmBits, value)) {
sltiu(CMPRES2, rd, Immediate(value));
bne(CMPRES2, ZR, l);
@@ -971,9 +1000,13 @@
}
void BranchSignedLessEqual(Register rd, int32_t value, Label* l) {
- ASSERT(rd != CMPRES2);
- LoadImmediate(CMPRES2, value);
- BranchSignedGreaterEqual(CMPRES2, rd, l);
+ if (value == 0) {
+ blez(rd, l);
+ } else {
+ ASSERT(rd != CMPRES2);
+ LoadImmediate(CMPRES2, value);
+ BranchSignedGreaterEqual(CMPRES2, rd, l);
+ }
}
void BranchUnsignedLessEqual(Register rd, Register rs, Label* l) {
diff --git a/runtime/vm/assembler_x64.cc b/runtime/vm/assembler_x64.cc
index 7b1315d..efdb624 100644
--- a/runtime/vm/assembler_x64.cc
+++ b/runtime/vm/assembler_x64.cc
@@ -65,6 +65,47 @@
#undef __
+Assembler::Assembler(bool use_far_branches)
+ : buffer_(),
+ object_pool_(GrowableObjectArray::Handle()),
+ patchable_pool_entries_(),
+ prologue_offset_(-1),
+ comments_() {
+ // Far branching mode is only needed and implemented for MIPS and ARM.
+ ASSERT(!use_far_branches);
+ if (Isolate::Current() != Dart::vm_isolate()) {
+ object_pool_ = GrowableObjectArray::New(Heap::kOld);
+
+ // These objects and labels need to be accessible through every pool-pointer
+ // at the same index.
+ object_pool_.Add(Object::Handle(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+
+ object_pool_.Add(Bool::True(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+
+ object_pool_.Add(Bool::False(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+
+ if (StubCode::UpdateStoreBuffer_entry() != NULL) {
+ FindExternalLabel(&StubCode::UpdateStoreBufferLabel(), kNotPatchable);
+ patchable_pool_entries_.Add(kNotPatchable);
+ } else {
+ object_pool_.Add(Object::Handle(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+ }
+
+ if (StubCode::CallToRuntime_entry() != NULL) {
+ FindExternalLabel(&StubCode::CallToRuntimeLabel(), kNotPatchable);
+ patchable_pool_entries_.Add(kNotPatchable);
+ } else {
+ object_pool_.Add(Object::Handle(), Heap::kOld);
+ patchable_pool_entries_.Add(kNotPatchable);
+ }
+ }
+}
+
+
void Assembler::InitializeMemoryWithBreakpoints(uword data, int length) {
memset(reinterpret_cast<void*>(data), Instr::kBreakPointInstruction, length);
}
@@ -95,25 +136,45 @@
}
+void Assembler::LoadExternalLabel(Register dst,
+ const ExternalLabel* label,
+ Patchability patchable,
+ Register pp) {
+ const int32_t offset =
+ Array::element_offset(FindExternalLabel(label, patchable));
+ LoadWordFromPoolOffset(dst, pp, offset - kHeapObjectTag);
+}
+
+
void Assembler::call(const ExternalLabel* label) {
- AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ { // Encode movq(TMP, Immediate(label->address())), but always as imm64.
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRegisterREX(TMP, REX_W);
+ EmitUint8(0xB8 | (TMP & 7));
+ EmitInt64(label->address());
+ }
+ call(TMP);
+}
+
+
+void Assembler::CallPatchable(const ExternalLabel* label) {
intptr_t call_start = buffer_.GetPosition();
-
- // Encode movq(TMP, Immediate(label->address())), but always as imm64.
- EmitRegisterREX(TMP, REX_W);
- EmitUint8(0xB8 | (TMP & 7));
- EmitInt64(label->address());
-
- // Encode call(TMP).
- Operand operand(TMP);
- EmitOperandREX(2, operand, REX_NONE);
- EmitUint8(0xFF);
- EmitOperand(2, operand);
-
+ LoadExternalLabel(TMP, label, kPatchable, PP);
+ call(TMP);
ASSERT((buffer_.GetPosition() - call_start) == kCallExternalLabelSize);
}
+void Assembler::Call(const ExternalLabel* label, Register pp) {
+ if (Isolate::Current() == Dart::vm_isolate()) {
+ call(label);
+ } else {
+ LoadExternalLabel(TMP, label, kNotPatchable, pp);
+ call(TMP);
+ }
+}
+
+
void Assembler::pushq(Register reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitRegisterREX(reg, REX_NONE);
@@ -1960,6 +2021,15 @@
}
+void Assembler::J(Condition condition, const ExternalLabel* label,
+ Register pp) {
+ Label no_jump;
+ j(static_cast<Condition>(condition ^ 1), &no_jump); // Negate condition.
+ Jmp(label, pp);
+ Bind(&no_jump);
+}
+
+
void Assembler::jmp(Register reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
Operand operand(reg);
@@ -1994,24 +2064,30 @@
void Assembler::jmp(const ExternalLabel* label) {
- AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ { // Encode movq(TMP, Immediate(label->address())), but always as imm64.
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRegisterREX(TMP, REX_W);
+ EmitUint8(0xB8 | (TMP & 7));
+ EmitInt64(label->address());
+ }
+ jmp(TMP);
+}
+
+
+void Assembler::JmpPatchable(const ExternalLabel* label, Register pp) {
intptr_t call_start = buffer_.GetPosition();
-
- // Encode movq(TMP, Immediate(label->address())), but always as imm64.
- EmitRegisterREX(TMP, REX_W);
- EmitUint8(0xB8 | (TMP & 7));
- EmitInt64(label->address());
-
- // Encode jmp(TMP).
- Operand operand(TMP);
- EmitOperandREX(4, operand, REX_NONE);
- EmitUint8(0xFF);
- EmitOperand(4, operand);
-
+ LoadExternalLabel(TMP, label, kPatchable, pp);
+ jmp(TMP);
ASSERT((buffer_.GetPosition() - call_start) == kCallExternalLabelSize);
}
+void Assembler::Jmp(const ExternalLabel* label, Register pp) {
+ LoadExternalLabel(TMP, label, kNotPatchable, pp);
+ jmp(TMP);
+}
+
+
void Assembler::lock() {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF0);
@@ -2091,49 +2167,111 @@
}
-void Assembler::LoadObject(Register dst, const Object& object) {
- if (object.IsSmi() || object.InVMHeap()) {
- movq(dst, Immediate(reinterpret_cast<int64_t>(object.raw())));
+intptr_t Assembler::FindObject(const Object& obj, Patchability patchable) {
+ // The object pool cannot be used in the vm isolate.
+ ASSERT(Isolate::Current() != Dart::vm_isolate());
+ ASSERT(!object_pool_.IsNull());
+
+ // TODO(zra): This can be slow. Add a hash map from obj.raw() to
+ // object pool indexes to speed lookup.
+ for (int i = 0; i < object_pool_.Length(); i++) {
+ if ((object_pool_.At(i) == obj.raw()) &&
+ (patchable_pool_entries_[i] != kPatchable)) {
+ return i;
+ }
+ }
+ object_pool_.Add(obj, Heap::kOld);
+ patchable_pool_entries_.Add(patchable);
+ return object_pool_.Length() - 1;
+}
+
+
+intptr_t Assembler::FindExternalLabel(const ExternalLabel* label,
+ Patchability patchable) {
+ // The object pool cannot be used in the vm isolate.
+ ASSERT(Isolate::Current() != Dart::vm_isolate());
+ ASSERT(!object_pool_.IsNull());
+ const uword address = label->address();
+ ASSERT(Utils::IsAligned(address, 4));
+ // The address is stored in the object array as a RawSmi.
+ const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(address));
+ if (patchable == kNotPatchable) {
+ return FindObject(smi, kNotPatchable);
+ }
+ // If the call is patchable, do not reuse an existing entry since each
+ // reference may be patched independently.
+ object_pool_.Add(smi, Heap::kOld);
+ patchable_pool_entries_.Add(patchable);
+ return object_pool_.Length() - 1;
+}
+
+
+bool Assembler::CanLoadFromObjectPool(const Object& object) {
+ // TODO(zra, kmillikin): Move the use of large Smis into the constant pool.
+ if (object.IsSmi()) {
+ return false;
+ }
+ ASSERT(object.IsNotTemporaryScopedHandle());
+ ASSERT(object.IsOld());
+ return (Isolate::Current() != Dart::vm_isolate()) &&
+ // Not in the VMHeap, OR is one of the VMHeap objects we put in every
+ // object pool.
+ // TODO(zra): Evaluate putting all VM heap objects into the pool.
+ (!object.InVMHeap() || (object.raw() == Object::null()) ||
+ (object.raw() == Bool::True().raw()) ||
+ (object.raw() == Bool::False().raw()));
+}
+
+
+void Assembler::LoadWordFromPoolOffset(Register dst, Register pp,
+ int32_t offset) {
+ // This sequence must be of fixed size. AddressBaseImm32
+ // forces the address operand to use a fixed-size imm32 encoding.
+ movq(dst, Address::AddressBaseImm32(pp, offset));
+}
+
+
+void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
+ if (CanLoadFromObjectPool(object)) {
+ const int32_t offset =
+ Array::element_offset(FindObject(object, kNotPatchable));
+ LoadWordFromPoolOffset(dst, pp, offset - kHeapObjectTag);
} else {
- ASSERT(object.IsNotTemporaryScopedHandle());
- ASSERT(object.IsOld());
- AssemblerBuffer::EnsureCapacity ensured(&buffer_);
- EmitRegisterREX(dst, REX_W);
- EmitUint8(0xB8 | (dst & 7));
- buffer_.EmitObject(object);
+ ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
+ object.IsSmi() ||
+ object.InVMHeap());
+ movq(dst, Immediate(reinterpret_cast<int64_t>(object.raw())));
}
}
void Assembler::StoreObject(const Address& dst, const Object& object) {
- if (object.IsSmi() || object.InVMHeap()) {
- movq(dst, Immediate(reinterpret_cast<int64_t>(object.raw())));
- } else {
- ASSERT(object.IsNotTemporaryScopedHandle());
- ASSERT(object.IsOld());
- LoadObject(TMP, object);
+ if (CanLoadFromObjectPool(object)) {
+ LoadObject(TMP, object, PP);
movq(dst, TMP);
+ } else {
+ movq(dst, Immediate(reinterpret_cast<int64_t>(object.raw())));
}
}
void Assembler::PushObject(const Object& object) {
- if (object.IsSmi() || object.InVMHeap()) {
- pushq(Immediate(reinterpret_cast<int64_t>(object.raw())));
- } else {
- LoadObject(TMP, object);
+ if (CanLoadFromObjectPool(object)) {
+ LoadObject(TMP, object, PP);
pushq(TMP);
+ } else {
+ pushq(Immediate(reinterpret_cast<int64_t>(object.raw())));
}
}
void Assembler::CompareObject(Register reg, const Object& object) {
- if (object.IsSmi() || object.InVMHeap()) {
- cmpq(reg, Immediate(reinterpret_cast<int64_t>(object.raw())));
- } else {
+ if (CanLoadFromObjectPool(object)) {
ASSERT(reg != TMP);
- LoadObject(TMP, object);
+ LoadObject(TMP, object, PP);
cmpq(reg, TMP);
+ } else {
+ cmpq(reg, Immediate(reinterpret_cast<int64_t>(object.raw())));
}
}
@@ -2196,7 +2334,7 @@
if (object != RAX) {
movq(RAX, object);
}
- call(&StubCode::UpdateStoreBufferLabel());
+ Call(&StubCode::UpdateStoreBufferLabel(), PP);
if (value != RAX) popq(RAX);
Bind(&done);
}
@@ -2298,6 +2436,23 @@
}
+void Assembler::LeaveFrameWithPP() {
+ movq(PP, Address(RBP, -2 * kWordSize));
+ LeaveFrame();
+}
+
+
+void Assembler::ReturnPatchable() {
+ // This sequence must have a fixed size so that it can be patched by the
+ // debugger.
+ intptr_t start = buffer_.GetPosition();
+ LeaveFrameWithPP();
+ ret();
+ nop(4);
+ ASSERT((buffer_.GetPosition() - start) == 13);
+}
+
+
void Assembler::ReserveAlignedFrameSpace(intptr_t frame_space) {
// Reserve space for arguments and align frame before entering
// the C++ world.
@@ -2378,18 +2533,59 @@
}
+void Assembler::LoadPoolPointer(Register pp) {
+ Label next;
+ call(&next);
+ Bind(&next);
+
+ // Load new pool pointer.
+ const intptr_t object_pool_pc_dist =
+ Instructions::HeaderSize() - Instructions::object_pool_offset() +
+ CodeSize();
+ popq(pp);
+ movq(pp, Address(pp, -object_pool_pc_dist));
+}
+
+
void Assembler::EnterDartFrame(intptr_t frame_size) {
EnterFrame(0);
+
Label dart_entry;
call(&dart_entry);
Bind(&dart_entry);
// The runtime system assumes that the code marker address is
// kEntryPointToPcMarkerOffset bytes from the entry. If there is any code
// generated before entering the frame, the address needs to be adjusted.
+ const intptr_t object_pool_pc_dist =
+ Instructions::HeaderSize() - Instructions::object_pool_offset() +
+ CodeSize();
const intptr_t offset = kEntryPointToPcMarkerOffset - CodeSize();
if (offset != 0) {
addq(Address(RSP, 0), Immediate(offset));
}
+ // Save caller's pool pointer
+ pushq(PP);
+
+ // Load callee's pool pointer.
+ movq(PP, Address(RSP, 1 * kWordSize));
+ movq(PP, Address(PP, -object_pool_pc_dist - offset));
+
+ if (frame_size != 0) {
+ subq(RSP, Immediate(frame_size));
+ }
+}
+
+
+void Assembler::EnterDartFrameWithInfo(intptr_t frame_size,
+ Register new_pp, Register new_pc) {
+ if (new_pc == kNoRegister) {
+ EnterDartFrame(0);
+ } else {
+ EnterFrame(0);
+ pushq(new_pc);
+ pushq(PP);
+ movq(PP, new_pp);
+ }
if (frame_size != 0) {
subq(RSP, Immediate(frame_size));
}
@@ -2401,18 +2597,32 @@
// pointer is already set up. The PC marker is not correct for the
// optimized function and there may be extra space for spill slots to
// allocate.
-void Assembler::EnterOsrFrame(intptr_t extra_size) {
- Label dart_entry;
- call(&dart_entry);
- Bind(&dart_entry);
- // The runtime system assumes that the code marker address is
- // kEntryPointToPcMarkerOffset bytes from the entry. Since there is no
- // code to set up the frame pointer, the address needs to be adjusted.
- const intptr_t offset = kEntryPointToPcMarkerOffset - CodeSize();
- if (offset != 0) {
- addq(Address(RSP, 0), Immediate(offset));
+void Assembler::EnterOsrFrame(intptr_t extra_size,
+ Register new_pp, Register new_pc) {
+ if (new_pc == kNoRegister) {
+ Label dart_entry;
+ call(&dart_entry);
+ Bind(&dart_entry);
+ // The runtime system assumes that the code marker address is
+ // kEntryPointToPcMarkerOffset bytes from the entry. Since there is no
+ // code to set up the frame pointer, the address needs to be adjusted.
+ const intptr_t object_pool_pc_dist =
+ Instructions::HeaderSize() - Instructions::object_pool_offset() +
+ CodeSize();
+ const intptr_t offset = kEntryPointToPcMarkerOffset - CodeSize();
+ if (offset != 0) {
+ addq(Address(RSP, 0), Immediate(offset));
+ }
+
+ // Load callee's pool pointer.
+ movq(PP, Address(RSP, 0));
+ movq(PP, Address(PP, -object_pool_pc_dist - offset));
+
+ popq(Address(RBP, kPcMarkerSlotFromFp * kWordSize));
+ } else {
+ movq(Address(RBP, kPcMarkerSlotFromFp * kWordSize), new_pc);
+ movq(PP, new_pp);
}
- popq(Address(RBP, kPcMarkerSlotFromFp * kWordSize));
if (extra_size != 0) {
subq(RSP, Immediate(extra_size));
}
@@ -2425,6 +2635,14 @@
}
+void Assembler::EnterStubFrameWithPP() {
+ EnterFrame(0);
+ pushq(Immediate(0)); // Push 0 in the saved PC area for stub frames.
+ pushq(PP); // Save caller's pool pointer
+ LoadPoolPointer(PP);
+}
+
+
void Assembler::TryAllocate(const Class& cls,
Label* failure,
bool near_jump,
@@ -2647,7 +2865,6 @@
return xmm_reg_names[reg];
}
-
} // namespace dart
#endif // defined TARGET_ARCH_X64
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index f4c945f..96921ee 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -212,6 +212,20 @@
Operand::operator=(other);
return *this;
}
+
+ static Address AddressBaseImm32(Register base, int32_t disp) {
+ return Address(base, disp, true);
+ }
+
+ private:
+ Address(Register base, int32_t disp, bool fixed) {
+ ASSERT(fixed);
+ SetModRM(2, base);
+ if ((base & 7) == RSP) {
+ SetSIB(TIMES_1, RSP, base);
+ }
+ SetDisp32(disp);
+ }
};
@@ -321,14 +335,8 @@
class Assembler : public ValueObject {
public:
- explicit Assembler(bool use_far_branches = false)
- : buffer_(),
- object_pool_(GrowableObjectArray::Handle()),
- prologue_offset_(-1),
- comments_() {
- // This mode is only needed and implemented for MIPS and ARM.
- ASSERT(!use_far_branches);
- }
+ explicit Assembler(bool use_far_branches = false);
+
~Assembler() { }
static const bool kNearJump = true;
@@ -342,7 +350,7 @@
void call(Label* label);
void call(const ExternalLabel* label);
- static const intptr_t kCallExternalLabelSize = 13;
+ static const intptr_t kCallExternalLabelSize = 10;
void pushq(Register reg);
void pushq(const Address& address);
@@ -652,7 +660,17 @@
void Drop(intptr_t stack_elements);
- void LoadObject(Register dst, const Object& object);
+ enum Patchability {
+ kPatchable,
+ kNotPatchable,
+ };
+
+ void LoadObject(Register dst, const Object& obj, Register pp);
+ void JmpPatchable(const ExternalLabel* label, Register pp);
+ void Jmp(const ExternalLabel* label, Register pp);
+ void J(Condition condition, const ExternalLabel* label, Register pp);
+ void CallPatchable(const ExternalLabel* label);
+ void Call(const ExternalLabel* label, Register pp);
void StoreObject(const Address& dst, const Object& obj);
void PushObject(const Object& object);
void CompareObject(Register reg, const Object& object);
@@ -680,6 +698,8 @@
void EnterFrame(intptr_t frame_space);
void LeaveFrame();
+ void LeaveFrameWithPP();
+ void ReturnPatchable();
void ReserveAlignedFrameSpace(intptr_t frame_space);
// Create a frame for calling into runtime that preserves all volatile
@@ -731,6 +751,8 @@
buffer_.FinalizeInstructions(region);
}
+ void LoadPoolPointer(Register pp);
+
// Set up a Dart frame on entry with a frame pointer and PC information to
// enable easy access to the RawInstruction object of code corresponding
// to this frame.
@@ -739,6 +761,7 @@
// ret PC
// saved RBP <=== RBP
// pc (used to derive the RawInstruction Object of the dart code)
+ // saved PP
// locals space <=== RSP
// .....
// This code sets this up with the sequence:
@@ -746,13 +769,17 @@
// movq rbp, rsp
// call L
// L: <code to adjust saved pc if there is any intrinsification code>
+ // ...
+ // pushq r15
// .....
void EnterDartFrame(intptr_t frame_size);
+ void EnterDartFrameWithInfo(intptr_t frame_size,
+ Register new_pp, Register new_pc);
// Set up a Dart frame for a function compiled for on-stack replacement.
// The frame layout is a normal Dart frame, but the frame is partially set
// up on entry (it is the frame of the unoptimized code).
- void EnterOsrFrame(intptr_t extra_size);
+ void EnterOsrFrame(intptr_t extra_size, Register new_pp, Register new_pc);
// Set up a stub frame so that the stack traversal code can easily identify
// a stub frame.
@@ -768,8 +795,9 @@
// pushq immediate(0)
// .....
void EnterStubFrame();
+ void EnterStubFrameWithPP();
- // Instruction pattern from entrypoint is used in dart frame prologs
+ // Instruction pattern from entrypoint is used in dart frame prologues
// to set up the frame and save a PC which can be used to figure out the
// RawInstruction object corresponding to the code running in the frame.
// entrypoint:
@@ -802,7 +830,13 @@
private:
AssemblerBuffer buffer_;
- GrowableObjectArray& object_pool_; // Object pool is not used on x64.
+
+ // Objects and jump targets.
+ GrowableObjectArray& object_pool_;
+
+ // Patchability of pool entries.
+ GrowableArray<Patchability> patchable_pool_entries_;
+
int prologue_offset_;
class CodeComment : public ZoneAllocated {
@@ -822,6 +856,16 @@
GrowableArray<CodeComment*> comments_;
+ intptr_t FindObject(const Object& obj, Patchability patchable);
+ intptr_t FindExternalLabel(const ExternalLabel* label,
+ Patchability patchable);
+ void LoadExternalLabel(Register dst,
+ const ExternalLabel* label,
+ Patchability patchable,
+ Register pp);
+ bool CanLoadFromObjectPool(const Object& object);
+ void LoadWordFromPoolOffset(Register dst, Register pp, int32_t offset);
+
inline void EmitUint8(uint8_t value);
inline void EmitInt32(int32_t value);
inline void EmitInt64(int64_t value);
diff --git a/runtime/vm/assembler_x64_test.cc b/runtime/vm/assembler_x64_test.cc
index cbad36f..2d148cd 100644
--- a/runtime/vm/assembler_x64_test.cc
+++ b/runtime/vm/assembler_x64_test.cc
@@ -200,6 +200,28 @@
__ movq(RAX, Address(R13, R10, TIMES_2, 256 * kWordSize));
__ movq(RAX, Address(R13, R12, TIMES_2, 256 * kWordSize));
__ movq(RAX, Address(R13, R13, TIMES_2, 256 * kWordSize));
+
+ __ movq(RAX, Address::AddressBaseImm32(RSP, 0));
+ __ movq(RAX, Address::AddressBaseImm32(RBP, 0));
+ __ movq(RAX, Address::AddressBaseImm32(RAX, 0));
+ __ movq(RAX, Address::AddressBaseImm32(R10, 0));
+ __ movq(RAX, Address::AddressBaseImm32(R12, 0));
+ __ movq(RAX, Address::AddressBaseImm32(R13, 0));
+ __ movq(R10, Address::AddressBaseImm32(RAX, 0));
+
+ __ movq(RAX, Address::AddressBaseImm32(RSP, kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(RBP, kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(RAX, kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(R10, kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(R12, kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(R13, kWordSize));
+
+ __ movq(RAX, Address::AddressBaseImm32(RSP, -kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(RBP, -kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(RAX, -kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(R10, -kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(R12, -kWordSize));
+ __ movq(RAX, Address::AddressBaseImm32(R13, -kWordSize));
}
@@ -2159,14 +2181,15 @@
ObjectStore* object_store = Isolate::Current()->object_store();
const Object& obj = Object::ZoneHandle(object_store->smi_class());
Label fail;
- __ LoadObject(RAX, obj);
+ __ EnterDartFrame(0);
+ __ LoadObject(RAX, obj, PP);
__ CompareObject(RAX, obj);
__ j(NOT_EQUAL, &fail);
- __ LoadObject(RCX, obj);
+ __ LoadObject(RCX, obj, PP);
__ CompareObject(RCX, obj);
__ j(NOT_EQUAL, &fail);
const Smi& smi = Smi::ZoneHandle(Smi::New(15));
- __ LoadObject(RCX, smi);
+ __ LoadObject(RCX, smi, PP);
__ CompareObject(RCX, smi);
__ j(NOT_EQUAL, &fail);
__ pushq(RAX);
@@ -2180,9 +2203,11 @@
__ CompareObject(RCX, smi);
__ j(NOT_EQUAL, &fail);
__ movl(RAX, Immediate(1)); // OK
+ __ LeaveFrameWithPP();
__ ret();
__ Bind(&fail);
__ movl(RAX, Immediate(0)); // Fail.
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -2393,12 +2418,14 @@
// Called from assembler_test.cc.
ASSEMBLER_TEST_GENERATE(StoreIntoObject, assembler) {
+ __ EnterDartFrame(0);
__ pushq(CTX);
__ movq(CTX, RDI);
__ StoreIntoObject(RDX,
FieldAddress(RDX, GrowableObjectArray::data_offset()),
RSI);
__ popq(CTX);
+ __ LeaveFrameWithPP();
__ ret();
}
diff --git a/runtime/vm/ast.cc b/runtime/vm/ast.cc
index 467de8c..2c911eb 100644
--- a/runtime/vm/ast.cc
+++ b/runtime/vm/ast.cc
@@ -12,19 +12,21 @@
namespace dart {
-#define DEFINE_VISIT_FUNCTION(type, name) \
- void type::Visit(AstNodeVisitor* visitor) { \
- visitor->Visit##type(this); \
- }
-NODE_LIST(DEFINE_VISIT_FUNCTION)
+#define DEFINE_VISIT_FUNCTION(BaseName) \
+void BaseName##Node::Visit(AstNodeVisitor* visitor) { \
+ visitor->Visit##BaseName##Node(this); \
+}
+
+FOR_EACH_NODE(DEFINE_VISIT_FUNCTION)
#undef DEFINE_VISIT_FUNCTION
-#define DEFINE_NAME_FUNCTION(type, name) \
- const char* type::ShortName() const { \
- return name; \
- }
-NODE_LIST(DEFINE_NAME_FUNCTION)
+#define DEFINE_NAME_FUNCTION(BaseName) \
+const char* BaseName##Node::PrettyName() const { \
+ return #BaseName; \
+}
+
+FOR_EACH_NODE(DEFINE_NAME_FUNCTION)
#undef DEFINE_NAME_FUNCTION
@@ -35,12 +37,13 @@
explicit AstNodeCollector(GrowableArray<AstNode*>* nodes)
: nodes_(nodes) { }
-#define DEFINE_VISITOR_FUNCTION(type, name) \
- virtual void Visit##type(type* node) { \
+#define DEFINE_VISITOR_FUNCTION(BaseName) \
+ virtual void Visit##BaseName##Node(BaseName##Node* node) { \
nodes_->Add(node); \
node->VisitChildren(this); \
}
-NODE_LIST(DEFINE_VISITOR_FUNCTION)
+
+FOR_EACH_NODE(DEFINE_VISITOR_FUNCTION)
#undef DEFINE_VISITOR_FUNCTION
private:
@@ -137,7 +140,7 @@
}
-const char* TypeNode::Name() const {
+const char* TypeNode::TypeName() const {
return String::Handle(type().UserVisibleName()).ToCString();
}
@@ -150,8 +153,8 @@
}
-const char* ComparisonNode::Name() const {
- return Token::Str(kind_);
+const char* ComparisonNode::TokenName() const {
+ return (kind_ == Token::kAS) ? "as" : Token::Str(kind_);
}
@@ -239,12 +242,12 @@
}
-const char* BinaryOpNode::Name() const {
+const char* BinaryOpNode::TokenName() const {
return Token::Str(kind_);
}
-const char* BinaryOpWithMask32Node::Name() const {
+const char* BinaryOpWithMask32Node::TokenName() const {
return Token::Str(kind());
}
@@ -405,21 +408,16 @@
}
-const char* UnaryOpNode::Name() const {
+const char* UnaryOpNode::TokenName() const {
return Token::Str(kind_);
}
-const char* JumpNode::Name() const {
+const char* JumpNode::TokenName() const {
return Token::Str(kind_);
}
-const char* LoadLocalNode::Name() const {
- return local().name().ToCString();
-}
-
-
bool LoadLocalNode::IsPotentiallyConst() const {
// Parameters of const constructors are implicitly final and can be
// used in initializer expressions.
diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h
index 4b40760..2f7af98 100644
--- a/runtime/vm/ast.h
+++ b/runtime/vm/ast.h
@@ -15,56 +15,56 @@
namespace dart {
-#define NODE_LIST(V) \
- V(ReturnNode, "return") \
- V(LiteralNode, "literal") \
- V(TypeNode, "type") \
- V(AssignableNode, "assignable") \
- V(BinaryOpNode, "binop") \
- V(BinaryOpWithMask32Node, "binop with mask 32") \
- V(ComparisonNode, "compare") \
- V(UnaryOpNode, "unaryop") \
- V(ConditionalExprNode, "?:") \
- V(IfNode, "if") \
- V(SwitchNode, "switch") \
- V(CaseNode, "case") \
- V(WhileNode, "while") \
- V(DoWhileNode, "dowhile") \
- V(ForNode, "for") \
- V(JumpNode, "jump") \
- V(ArgumentListNode, "args") \
- V(ArrayNode, "array") \
- V(ClosureNode, "closure") \
- V(InstanceCallNode, "instance call") \
- V(StaticCallNode, "static call") \
- V(ClosureCallNode, "closure call") \
- V(CloneContextNode, "clone context") \
- V(ConstructorCallNode, "constructor call") \
- V(InstanceGetterNode, "instance getter call") \
- V(InstanceSetterNode, "instance setter call") \
- V(StaticGetterNode, "static getter") \
- V(StaticSetterNode, "static setter") \
- V(NativeBodyNode, "native body") \
- V(PrimaryNode, "primary") \
- V(LoadLocalNode, "load local") \
- V(StoreLocalNode, "store local") \
- V(LoadInstanceFieldNode, "load field") \
- V(StoreInstanceFieldNode, "store field") \
- V(LoadStaticFieldNode, "load static field") \
- V(StoreStaticFieldNode, "store static field") \
- V(LoadIndexedNode, "load indexed") \
- V(StoreIndexedNode, "store indexed") \
- V(SequenceNode, "seq") \
- V(LetNode, "let") \
- V(CatchClauseNode, "catch clause block") \
- V(TryCatchNode, "try catch block") \
- V(ThrowNode, "throw") \
- V(InlinedFinallyNode, "inlined finally") \
+#define FOR_EACH_NODE(V) \
+ V(Return) \
+ V(Literal) \
+ V(Type) \
+ V(Assignable) \
+ V(BinaryOp) \
+ V(BinaryOpWithMask32) \
+ V(Comparison) \
+ V(UnaryOp) \
+ V(ConditionalExpr) \
+ V(If) \
+ V(Switch) \
+ V(Case) \
+ V(While) \
+ V(DoWhile) \
+ V(For) \
+ V(Jump) \
+ V(ArgumentList) \
+ V(Array) \
+ V(Closure) \
+ V(InstanceCall) \
+ V(StaticCall) \
+ V(ClosureCall) \
+ V(CloneContext) \
+ V(ConstructorCall) \
+ V(InstanceGetter) \
+ V(InstanceSetter) \
+ V(StaticGetter) \
+ V(StaticSetter) \
+ V(NativeBody) \
+ V(Primary) \
+ V(LoadLocal) \
+ V(StoreLocal) \
+ V(LoadInstanceField) \
+ V(StoreInstanceField) \
+ V(LoadStaticField) \
+ V(StoreStaticField) \
+ V(LoadIndexed) \
+ V(StoreIndexed) \
+ V(Sequence) \
+ V(Let) \
+ V(CatchClause) \
+ V(TryCatch) \
+ V(Throw) \
+ V(InlinedFinally) \
-#define DEFINE_FORWARD_DECLARATION(type, name) class type;
-NODE_LIST(DEFINE_FORWARD_DECLARATION)
-#undef DEFINE_FORWARD_DECLARATION
+#define FORWARD_DECLARATION(BaseName) class BaseName##Node;
+FOR_EACH_NODE(FORWARD_DECLARATION)
+#undef FORWARD_DECLARATION
// Abstract class to implement an AST node visitor. An example is AstPrinter.
@@ -73,9 +73,10 @@
AstNodeVisitor() {}
virtual ~AstNodeVisitor() {}
-#define DEFINE_VISITOR_FUNCTION(type, name) \
- virtual void Visit##type(type* node) { }
-NODE_LIST(DEFINE_VISITOR_FUNCTION)
+#define DEFINE_VISITOR_FUNCTION(BaseName) \
+ virtual void Visit##BaseName##Node(BaseName##Node* node) { }
+
+ FOR_EACH_NODE(DEFINE_VISITOR_FUNCTION)
#undef DEFINE_VISITOR_FUNCTION
private:
@@ -85,7 +86,7 @@
#define DECLARE_COMMON_NODE_FUNCTIONS(type) \
virtual void Visit(AstNodeVisitor* visitor); \
- virtual const char* ShortName() const; \
+ virtual const char* PrettyName() const; \
virtual bool Is##type() const { return true; } \
virtual type* As##type() { return this; }
@@ -99,22 +100,16 @@
intptr_t token_pos() const { return token_pos_; }
-#define AST_TYPE_CHECK(type, name) \
- virtual bool Is##type() const { return false; } \
- virtual type* As##type() { return NULL; }
-NODE_LIST(AST_TYPE_CHECK)
+#define AST_TYPE_CHECK(BaseName) \
+ virtual bool Is##BaseName##Node() const { return false; } \
+ virtual BaseName##Node* As##BaseName##Node() { return NULL; }
+
+ FOR_EACH_NODE(AST_TYPE_CHECK)
#undef AST_TYPE_CHECK
virtual void Visit(AstNodeVisitor* visitor) = 0;
virtual void VisitChildren(AstNodeVisitor* visitor) const = 0;
- virtual const char* ShortName() const = 0;
-
- // 'ShortName' is predefined for each AstNode and is the default
- // implementation of "Name()". Each AST node can override the function
- // "Name" to do more complex name composition.
- virtual const char* Name() const {
- return ShortName();
- }
+ virtual const char* PrettyName() const = 0;
// Convert the node into an assignment node using the rhs which is passed in,
// this is typically used for converting nodes like LoadLocalNode,
@@ -358,7 +353,7 @@
const AbstractType& type() const { return type_; }
- virtual const char* Name() const;
+ const char* TypeName() const;
virtual const Instance* EvalConstExpr() const {
// TODO(regis): What if the type is malbounded?
@@ -547,7 +542,7 @@
right()->Visit(visitor);
}
- virtual const char* Name() const;
+ const char* TokenName() const;
virtual bool IsPotentiallyConst() const;
virtual const Instance* EvalConstExpr() const;
@@ -591,7 +586,7 @@
right()->Visit(visitor);
}
- virtual const char* Name() const;
+ const char* TokenName() const;
virtual bool IsPotentiallyConst() const;
virtual const Instance* EvalConstExpr() const;
@@ -627,7 +622,7 @@
return mask32_;
}
- virtual const char* Name() const;
+ const char* TokenName() const;
DECLARE_COMMON_NODE_FUNCTIONS(BinaryOpWithMask32Node);
private:
@@ -660,7 +655,7 @@
operand()->Visit(visitor);
}
- virtual const char* Name() const;
+ const char* TokenName() const;
virtual bool IsPotentiallyConst() const;
virtual const Instance* EvalConstExpr() const;
@@ -900,7 +895,7 @@
};
-// initializer, condition, increment expressions can be NULL.
+// The condition can be NULL.
class ForNode : public AstNode {
public:
ForNode(intptr_t token_pos,
@@ -978,7 +973,7 @@
inlined_finally_list_.Add(finally_node);
}
- virtual const char* Name() const;
+ const char* TokenName() const;
virtual void VisitChildren(AstNodeVisitor* visitor) const { }
@@ -1003,7 +998,6 @@
virtual void VisitChildren(AstNodeVisitor* visitor) const { }
- virtual const char* Name() const;
virtual const Instance* EvalConstExpr() const;
virtual bool IsPotentiallyConst() const;
virtual AstNode* MakeAssignmentNode(AstNode* rhs);
diff --git a/runtime/vm/ast_printer.cc b/runtime/vm/ast_printer.cc
index 21ff734..2847085 100644
--- a/runtime/vm/ast_printer.cc
+++ b/runtime/vm/ast_printer.cc
@@ -18,23 +18,22 @@
void AstPrinter::VisitGenericAstNode(AstNode* node) {
- OS::Print("(%s ", node->Name());
+ OS::Print("(%s ", node->PrettyName());
node->VisitChildren(this);
OS::Print(")");
}
-void AstPrinter::VisitSequenceNode(SequenceNode* node_sequence) {
+void AstPrinter::VisitSequenceNode(SequenceNode* node) {
// TODO(regis): Make the output more readable by indenting the nested
// sequences. This could be achieved using a AstPrinterContext similar to the
// CodeGeneratorContext.
- ASSERT(node_sequence != NULL);
- for (int i = 0; i < node_sequence->length(); i++) {
- OS::Print("scope %p: ",
- node_sequence->scope());
- node_sequence->NodeAt(i)->Visit(this);
+ OS::Print("(%s (scope \"%p\")", node->PrettyName(), node->scope());
+ for (int i = 0; i < node->length(); ++i) {
OS::Print("\n");
+ node->NodeAt(i)->Visit(this);
}
+ OS::Print(")");
}
@@ -55,17 +54,19 @@
void AstPrinter::VisitGenericLocalNode(AstNode* node,
const LocalVariable& var) {
- OS::Print("(%s %s%s '%s'",
- node->Name(),
+ OS::Print("(%s %s%s \"%s\"",
+ node->PrettyName(),
var.is_final() ? "final " : "",
String::Handle(var.type().Name()).ToCString(),
var.name().ToCString());
if (var.HasIndex()) {
- OS::Print(" @%d", var.index());
if (var.is_captured()) {
- OS::Print(" ctx %d", var.owner()->context_level());
+ OS::Print(" (context %d %d)", var.owner()->context_level(), var.index());
+ } else {
+ OS::Print(" (stack %d)", var.index());
}
}
+ OS::Print(" ");
node->VisitChildren(this);
OS::Print(")");
}
@@ -82,8 +83,8 @@
void AstPrinter::VisitGenericFieldNode(AstNode* node, const Field& field) {
- OS::Print("(%s %s%s '%s' ",
- node->Name(),
+ OS::Print("(%s %s%s \"%s\" ",
+ node->PrettyName(),
field.is_final() ? "final " : "",
String::Handle(AbstractType::Handle(field.type()).Name()).
ToCString(),
@@ -125,59 +126,63 @@
void AstPrinter::VisitLiteralNode(LiteralNode* node) {
const Instance& literal = node->literal();
- OS::Print("'%s'", literal.ToCString());
+ OS::Print("(%s \"%s\")", node->PrettyName(), literal.ToCString());
}
void AstPrinter::VisitTypeNode(TypeNode* node) {
const AbstractType& type = node->type();
- OS::Print("'%s'", String::Handle(type.Name()).ToCString());
+ OS::Print("(%s \"%s\")",
+ node->PrettyName(),
+ String::Handle(type.Name()).ToCString());
}
void AstPrinter::VisitAssignableNode(AssignableNode* node) {
- OS::Print("(assignable ");
- node->expr()->Visit(this);
const AbstractType& type = node->type();
const String& dst_name = node->dst_name();
- OS::Print(" to type '%s' of '%s')",
+ OS::Print("(%s (type \"%s\") (of \"%s\") ",
+ node->PrettyName(),
String::Handle(type.Name()).ToCString(),
dst_name.ToCString());
-}
-
-
-void AstPrinter::VisitPrimaryNode(PrimaryNode* node) {
- OS::Print("***** PRIMARY NODE IN AST ***** (%s '%s')",
- node->Name(), node->primary().ToCString());
-}
-
-
-void AstPrinter::VisitComparisonNode(ComparisonNode* node) {
- if (node->kind() == Token::kAS) {
- OS::Print("(as ");
- node->VisitChildren(this);
- OS::Print(")");
- } else {
- VisitGenericAstNode(node);
- }
-}
-
-
-void AstPrinter::VisitBinaryOpNode(BinaryOpNode* node) {
- VisitGenericAstNode(node);
-}
-
-
-void AstPrinter::VisitBinaryOpWithMask32Node(BinaryOpWithMask32Node* node) {
- OS::Print("(%s ", node->Name());
node->VisitChildren(this);
- OS::Print(" & 0x%" Px64 "", node->mask32());
OS::Print(")");
}
+void AstPrinter::VisitPrimaryNode(PrimaryNode* node) {
+ OS::Print("*****%s***** \"%s\")",
+ node->PrettyName(),
+ node->primary().ToCString());
+}
+
+
+void AstPrinter::VisitComparisonNode(ComparisonNode* node) {
+ OS::Print("(%s %s ", node->PrettyName(), node->TokenName());
+ node->VisitChildren(this);
+ OS::Print(")");
+}
+
+
+void AstPrinter::VisitBinaryOpNode(BinaryOpNode* node) {
+ OS::Print("(%s %s ", node->PrettyName(), node->TokenName());
+ node->VisitChildren(this);
+ OS::Print(")");
+}
+
+
+void AstPrinter::VisitBinaryOpWithMask32Node(BinaryOpWithMask32Node* node) {
+ OS::Print("(%s %s ", node->PrettyName(), node->TokenName());
+ node->VisitChildren(this);
+ OS::Print(" & \"0x%" Px64 "", node->mask32());
+ OS::Print("\")");
+}
+
+
void AstPrinter::VisitUnaryOpNode(UnaryOpNode* node) {
- VisitGenericAstNode(node);
+ OS::Print("(%s %s ", node->PrettyName(), node->TokenName());
+ node->VisitChildren(this);
+ OS::Print(")");
}
@@ -187,25 +192,17 @@
void AstPrinter::VisitIfNode(IfNode* node) {
- OS::Print("(if ");
- node->condition()->Visit(this);
- OS::Print(" then ");
- node->true_branch()->Visit(this);
- OS::Print(" else ");
- if (node->false_branch() != NULL) {
- node->false_branch()->Visit(this);
- }
- OS::Print(")");
+ VisitGenericAstNode(node);
}
void AstPrinter::VisitCaseNode(CaseNode* node) {
- OS::Print("(case (");
+ OS::Print("(%s (", node->PrettyName());
for (int i = 0; i < node->case_expressions()->length(); i++) {
node->case_expressions()->NodeAt(i)->Visit(this);
}
if (node->contains_default()) {
- OS::Print(" default ");
+ OS::Print(" default");
}
OS::Print(")");
node->statements()->Visit(this);
@@ -214,71 +211,66 @@
void AstPrinter::VisitSwitchNode(SwitchNode* node) {
- OS::Print("(switch ");
- node->body()->Visit(this);
- OS::Print(")");
+ VisitGenericAstNode(node);
}
void AstPrinter::VisitWhileNode(WhileNode* node) {
- OS::Print("(while ");
- node->condition()->Visit(this);
- OS::Print(" do ");
- node->body()->Visit(this);
- OS::Print(")");
+ VisitGenericAstNode(node);
}
void AstPrinter::VisitForNode(ForNode* node) {
- OS::Print("(for (init: ");
+ // Complicated because the condition is optional and so we clearly want to
+ // indicate the subparts.
+ OS::Print("(%s (init ", node->PrettyName());
node->initializer()->Visit(this);
- OS::Print("; cond:");
if (node->condition() != NULL) {
+ OS::Print(") (cond ");
node->condition()->Visit(this);
}
- OS::Print("; incr:");
+ OS::Print(") (update ");
node->increment()->Visit(this);
- OS::Print(") body:");
+ OS::Print(") ");
node->body()->Visit(this);
- OS::Print("endfor)");
-}
-
-
-void AstPrinter::VisitDoWhileNode(DoWhileNode* node) {
- OS::Print("(do ");
- node->body()->Visit(this);
- OS::Print(" while ");
- node->condition()->Visit(this);
OS::Print(")");
}
+void AstPrinter::VisitDoWhileNode(DoWhileNode* node) {
+ VisitGenericAstNode(node);
+}
+
+
void AstPrinter::VisitJumpNode(JumpNode* node) {
- OS::Print("(%s %s in scope %p)",
- node->Name(),
+ OS::Print("(%s %s %s (scope \"%p\"))",
+ node->PrettyName(),
+ node->TokenName(),
node->label()->name().ToCString(),
node->label()->owner());
}
void AstPrinter::VisitInstanceCallNode(InstanceCallNode* node) {
- OS::Print("(%s '%s'(", node->Name(), node->function_name().ToCString());
+ OS::Print("(%s \"%s\" ",
+ node->PrettyName(),
+ node->function_name().ToCString());
node->VisitChildren(this);
- OS::Print("))");
+ OS::Print(")");
}
void AstPrinter::VisitStaticCallNode(StaticCallNode* node) {
const char* function_fullname = node->function().ToFullyQualifiedCString();
- OS::Print("(%s '%s'(", node->Name(), function_fullname);
+ OS::Print("(%s \"%s\" ", node->PrettyName(), function_fullname);
node->VisitChildren(this);
- OS::Print("))");
+ OS::Print(")");
}
void AstPrinter::VisitClosureNode(ClosureNode* node) {
const char* function_fullname = node->function().ToFullyQualifiedCString();
- OS::Print("(%s '%s')", node->Name(), function_fullname);
+ OS::Print("(%s \"%s\")", node->PrettyName(), function_fullname);
}
@@ -290,93 +282,88 @@
void AstPrinter::VisitConstructorCallNode(ConstructorCallNode* node) {
const char* kind = node->constructor().IsFactory() ? "factory " : "";
const char* constructor_name = node->constructor().ToFullyQualifiedCString();
- OS::Print("(%s %s'%s' ((this)", node->Name(), kind, constructor_name);
+ OS::Print("(%s %s \"%s\" ", node->PrettyName(), kind, constructor_name);
node->VisitChildren(this);
- OS::Print("))");
+ OS::Print(")");
}
void AstPrinter::VisitInstanceGetterNode(InstanceGetterNode* node) {
- OS::Print("(%s 'get %s'(", node->Name(), node->field_name().ToCString());
+ OS::Print("(%s \"%s\" ",
+ node->PrettyName(),
+ node->field_name().ToCString());
node->VisitChildren(this);
- OS::Print("))");
+ OS::Print(")");
}
void AstPrinter::VisitInstanceSetterNode(InstanceSetterNode* node) {
- OS::Print("(%s 'set %s'(", node->Name(), node->field_name().ToCString());
+ OS::Print("(%s \"%s\" ",
+ node->PrettyName(),
+ node->field_name().ToCString());
node->VisitChildren(this);
- OS::Print("))");
+ OS::Print(")");
}
void AstPrinter::VisitStaticGetterNode(StaticGetterNode* node) {
String& class_name = String::Handle(node->cls().Name());
- OS::Print("(%s '%s.%s'(",
- node->Name(),
+ OS::Print("(%s \"%s.%s\")",
+ node->PrettyName(),
class_name.ToCString(),
node->field_name().ToCString());
- OS::Print("))");
}
void AstPrinter::VisitStaticSetterNode(StaticSetterNode* node) {
String& class_name = String::Handle(node->cls().Name());
- OS::Print("(%s '%s.%s'(",
- node->Name(), class_name.ToCString(), node->field_name().ToCString());
+ OS::Print("(%s \"%s.%s\" ",
+ node->PrettyName(),
+ class_name.ToCString(),
+ node->field_name().ToCString());
node->VisitChildren(this);
- OS::Print("))");
+ OS::Print(")");
}
void AstPrinter::VisitLoadIndexedNode(LoadIndexedNode* node) {
- OS::Print("(%s%s ", node->Name(), node->IsSuperLoad() ? " super" : "");
+ OS::Print("(%s%s ", node->PrettyName(), node->IsSuperLoad() ? " super" : "");
node->VisitChildren(this);
OS::Print(")");
}
void AstPrinter::VisitStoreIndexedNode(StoreIndexedNode* node) {
- OS::Print("(%s%s ", node->Name(), node->IsSuperStore() ? " super" : "");
+ OS::Print("(%s%s ", node->PrettyName(), node->IsSuperStore() ? " super" : "");
node->VisitChildren(this);
OS::Print(")");
}
void AstPrinter::VisitNativeBodyNode(NativeBodyNode* node) {
- OS::Print("(native_c call '%s'(%d args))",
+ OS::Print("(%s \"%s\" (%d args))",
+ node->PrettyName(),
node->native_c_function_name().ToCString(),
NativeArguments::ParameterCountForResolution(node->function()));
}
void AstPrinter::VisitCatchClauseNode(CatchClauseNode* node) {
- node->VisitChildren(this);
+ VisitGenericAstNode(node);
}
void AstPrinter::VisitTryCatchNode(TryCatchNode* node) {
- OS::Print("(");
-
- // First visit the try block.
- OS::Print("(try block (");
+ OS::Print("(%s ", node->PrettyName());
node->try_block()->Visit(this);
- OS::Print("))");
-
- // Now visit the catch block if it exists.
if (node->catch_block() != NULL) {
- OS::Print("(catch block () (");
node->catch_block()->Visit(this);
- OS::Print("))");
}
-
- // Now visit the finally block if it exists.
if (node->finally_block() != NULL) {
- OS::Print("(finally block () (");
+ OS::Print("(finally ");
node->finally_block()->Visit(this);
- OS::Print("))");
+ OS::Print(")");
}
-
OS::Print(")");
}
diff --git a/runtime/vm/ast_printer.h b/runtime/vm/ast_printer.h
index d61a802b..f64d979 100644
--- a/runtime/vm/ast_printer.h
+++ b/runtime/vm/ast_printer.h
@@ -21,10 +21,11 @@
static void PrintLocalScope(const LocalScope* scope, int variable_index);
-#define DEFINE_VISITOR_FUNCTION(type, name) \
- virtual void Visit##type(type* node);
-NODE_LIST(DEFINE_VISITOR_FUNCTION)
-#undef DEFINE_VISITOR_FUNCTION
+#define DECLARE_VISITOR_FUNCTION(BaseName) \
+ virtual void Visit##BaseName##Node(BaseName##Node* node);
+
+ FOR_EACH_NODE(DECLARE_VISITOR_FUNCTION)
+#undef DECLARE_VISITOR_FUNCTION
private:
AstPrinter();
diff --git a/runtime/vm/bigint_operations.cc b/runtime/vm/bigint_operations.cc
index 7fba196e..b7c7a69 100644
--- a/runtime/vm/bigint_operations.cc
+++ b/runtime/vm/bigint_operations.cc
@@ -1291,6 +1291,34 @@
}
+int64_t BigintOperations::BitLength(const Bigint& bigint) {
+ ASSERT(IsClamped(bigint));
+ intptr_t length = bigint.Length();
+ if (length == 0) return 0;
+ intptr_t last = length - 1;
+
+ Chunk high_chunk = bigint.GetChunkAt(last);
+ ASSERT(high_chunk != 0);
+ int64_t bit_length =
+ static_cast<int64_t>(kDigitBitSize) * last + CountBits(high_chunk);
+
+ if (bigint.IsNegative()) {
+ // We are calculating the 2's complement bitlength but we have a sign and
+ // magnitude representation. The length is the same except when the
+ // magnitude is an exact power of two, 2^k. In 2's complement format,
+ // -(2^k) takes one fewer bit than (2^k).
+
+ if ((high_chunk & (high_chunk - 1)) == 0) { // Single bit set?
+ for (intptr_t i = 0; i < last; i++) {
+ if (bigint.GetChunkAt(i) != 0) return bit_length;
+ }
+ bit_length -= 1;
+ }
+ }
+ return bit_length;
+}
+
+
int BigintOperations::Compare(const Bigint& a, const Bigint& b) {
bool a_is_negative = a.IsNegative();
bool b_is_negative = b.IsNegative();
diff --git a/runtime/vm/bigint_operations.h b/runtime/vm/bigint_operations.h
index 892ee6c..b38bc41 100644
--- a/runtime/vm/bigint_operations.h
+++ b/runtime/vm/bigint_operations.h
@@ -92,6 +92,7 @@
static RawBigint* BitOr(const Bigint& a, const Bigint& b);
static RawBigint* BitXor(const Bigint& a, const Bigint& b);
static RawBigint* BitNot(const Bigint& bigint);
+ static int64_t BitLength(const Bigint& bigint);
static int Compare(const Bigint& a, const Bigint& b);
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index 8d807f4..826222b 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -45,9 +45,12 @@
V(Smi_shlFromInt, 2) \
V(Smi_shrFromInt, 2) \
V(Smi_bitNegate, 1) \
+ V(Smi_bitLength, 1) \
V(Mint_bitNegate, 1) \
+ V(Mint_bitLength, 1) \
V(Mint_shlFromInt, 2) \
V(Bigint_bitNegate, 1) \
+ V(Bigint_bitLength, 1) \
V(Bigint_shlFromInt, 2) \
V(Double_getIsNegative, 1) \
V(Double_getIsInfinite, 1) \
@@ -259,7 +262,6 @@
V(InstanceMirror_invokeSetter, 4) \
V(ClosureMirror_function, 1) \
V(ClosureMirror_apply, 3) \
- V(ClassMirror_name, 1) \
V(ClassMirror_library, 1) \
V(ClassMirror_supertype, 1) \
V(ClassMirror_interfaces, 1) \
@@ -270,7 +272,7 @@
V(ClassMirror_invoke, 5) \
V(ClassMirror_invokeGetter, 3) \
V(ClassMirror_invokeSetter, 4) \
- V(ClassMirror_invokeConstructor, 4) \
+ V(ClassMirror_invokeConstructor, 5) \
V(ClassMirror_type_variables, 1) \
V(ClassMirror_type_arguments, 1) \
V(LibraryMirror_invoke, 5) \
diff --git a/runtime/vm/cha_test.cc b/runtime/vm/cha_test.cc
index d63bf6a..e0bfc6c 100644
--- a/runtime/vm/cha_test.cc
+++ b/runtime/vm/cha_test.cc
@@ -33,24 +33,23 @@
const Library& lib = Library::Handle(Library::LookupLibrary(name));
EXPECT(!lib.IsNull());
- String& ambiguity_error_msg = String::Handle();
const Class& class_a = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("A")), &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New("A"))));
EXPECT(!class_a.IsNull());
const intptr_t class_a_id = class_a.id();
const Class& class_b = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("B")), &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New("B"))));
EXPECT(!class_b.IsNull());
const intptr_t class_b_id = class_b.id();
const Class& class_c = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("C")), &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New("C"))));
EXPECT(!class_c.IsNull());
const intptr_t class_c_id = class_c.id();
const Class& class_d = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("D")), &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New("D"))));
EXPECT(!class_d.IsNull());
const intptr_t class_d_id = class_d.id();
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 7b216f2..2214422 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -253,28 +253,21 @@
// Resolve unresolved_class in the library of cls, or return null.
-RawClass* ClassFinalizer::ResolveClass(const Class& cls,
- const UnresolvedClass& unresolved_class,
- Error* ambiguity_error) {
+RawClass* ClassFinalizer::ResolveClass(
+ const Class& cls,
+ const UnresolvedClass& unresolved_class) {
const String& class_name = String::Handle(unresolved_class.ident());
Library& lib = Library::Handle();
Class& resolved_class = Class::Handle();
- String& ambiguity_error_msg = String::Handle();
if (unresolved_class.library_prefix() == LibraryPrefix::null()) {
lib = cls.library();
ASSERT(!lib.IsNull());
- resolved_class = lib.LookupClass(class_name, &ambiguity_error_msg);
+ resolved_class = lib.LookupClass(class_name);
} else {
LibraryPrefix& lib_prefix = LibraryPrefix::Handle();
lib_prefix = unresolved_class.library_prefix();
ASSERT(!lib_prefix.IsNull());
- resolved_class = lib_prefix.LookupClass(class_name, &ambiguity_error_msg);
- }
- if (resolved_class.IsNull() && !ambiguity_error_msg.IsNull()) {
- const Script& script = Script::Handle(cls.script());
- *ambiguity_error = Parser::FormatErrorMsg(
- script, unresolved_class.token_pos(), "Error",
- "%s", ambiguity_error_msg.ToCString());
+ resolved_class = lib_prefix.LookupClass(class_name);
}
return resolved_class.raw();
}
@@ -467,9 +460,8 @@
// Lookup the type class.
const UnresolvedClass& unresolved_class =
UnresolvedClass::Handle(type.unresolved_class());
- Error& ambiguous_error = Error::Handle();
const Class& type_class =
- Class::Handle(ResolveClass(cls, unresolved_class, &ambiguous_error));
+ Class::Handle(ResolveClass(cls, unresolved_class));
// Replace unresolved class with resolved type class.
const Type& parameterized_type = Type::Cast(type);
@@ -478,7 +470,7 @@
FLAG_error_on_bad_type) {
// The type class could not be resolved. The type is malformed.
FinalizeMalformedType(
- ambiguous_error, // May be null.
+ Error::Handle(), // No previous error.
cls,
parameterized_type,
"cannot resolve class '%s' from '%s'",
@@ -1861,25 +1853,27 @@
type_args.IsNull() ? 0 : type_args.Length();
AbstractType& arg = AbstractType::Handle();
if (num_type_arguments > 0) {
- if (num_type_arguments != num_type_parameters) {
+ if (num_type_arguments == num_type_parameters) {
+ for (int i = 0; i < num_type_arguments; i++) {
+ arg = type_args.TypeAt(i);
+ collected_args.Add(arg);
+ }
+ return;
+ }
+ if (FLAG_error_on_bad_type) {
const Script& script = Script::Handle(cls.script());
const String& type_class_name = String::Handle(type_class.Name());
- // TODO(regis): This should not be a compile time error anymore.
ReportError(Error::Handle(), // No previous error.
script, type.token_pos(),
"wrong number of type arguments for class '%s'",
type_class_name.ToCString());
}
- for (int i = 0; i < num_type_arguments; i++) {
- arg = type_args.TypeAt(i);
- collected_args.Add(arg);
- }
- } else {
- // Fill arguments with type dynamic.
- for (int i = 0; i < num_type_parameters; i++) {
- arg = Type::DynamicType();
- collected_args.Add(arg);
- }
+ // Discard provided type arguments and treat type as raw.
+ }
+ // Fill arguments with type dynamic.
+ for (int i = 0; i < num_type_parameters; i++) {
+ arg = Type::DynamicType();
+ collected_args.Add(arg);
}
}
@@ -1889,18 +1883,16 @@
// Resolve super type and all mixin types.
const GrowableObjectArray& type_args =
GrowableObjectArray::Handle(GrowableObjectArray::New());
- AbstractType& type = AbstractType::Handle(mixin_app.super_type());
+ AbstractType& type = AbstractType::Handle(mixin_app.SuperType());
ResolveType(cls, type, kCanonicalizeWellFormed);
ASSERT(type.HasResolvedTypeClass());
// TODO(hausner): May need to handle BoundedType here.
ASSERT(type.IsType());
CollectTypeArguments(cls, Type::Cast(type), type_args);
- const Array& mixins = Array::Handle(mixin_app.mixin_types());
Class& mixin_app_class = Class::Handle();
- for (int i = 0; i < mixins.Length(); i++) {
- type ^= mixins.At(i);
- ASSERT(type.HasResolvedTypeClass()); // Newly created class in parser.
- mixin_app_class ^= type.type_class();
+ const intptr_t depth = mixin_app.Depth();
+ for (int i = 0; i < depth; i++) {
+ mixin_app_class = mixin_app.MixinAppAt(i);
type = mixin_app_class.mixin();
ASSERT(!type.IsNull());
ResolveType(cls, type, kCanonicalizeWellFormed);
@@ -1918,16 +1910,13 @@
OS::Print("ResolveMixinAppType: mixin appl type args: %s\n",
mixin_app_args.ToCString());
}
- // The last element in the mixins array is the lowest mixin application
- // type in the mixin chain. Build a new super type with its type class
- // and the collected type arguments from the super type and all
- // mixin types. This super type replaces the MixinAppType object
- // in the class that extends the mixin application.
- type ^= mixins.At(mixins.Length() - 1);
- mixin_app_class ^= type.type_class();
- return Type::New(mixin_app_class,
- mixin_app_args,
- mixin_app.token_pos());
+ // The mixin application class at depth k is a subclass of mixin application
+ // class at depth k - 1. Build a new super type with the class at the highest
+ // depth (the last one processed by the loop above) as the type class and the
+ // collected type arguments from the super type and all mixin types.
+ // This super type replaces the MixinAppType object in the class that extends
+ // the mixin application.
+ return Type::New(mixin_app_class, mixin_app_args, mixin_app.token_pos());
}
diff --git a/runtime/vm/class_finalizer.h b/runtime/vm/class_finalizer.h
index 5221a2a..2d632e8 100644
--- a/runtime/vm/class_finalizer.h
+++ b/runtime/vm/class_finalizer.h
@@ -105,8 +105,7 @@
GrowableArray<intptr_t>* visited);
static void CheckForLegalConstClass(const Class& cls);
static RawClass* ResolveClass(const Class& cls,
- const UnresolvedClass& unresolved_class,
- Error* ambiguity_error);
+ const UnresolvedClass& unresolved_class);
static void ResolveRedirectingFactoryTarget(
const Class& cls,
const Function& factory,
diff --git a/runtime/vm/code_descriptors_test.cc b/runtime/vm/code_descriptors_test.cc
index f1c41db..8d33ebd 100644
--- a/runtime/vm/code_descriptors_test.cc
+++ b/runtime/vm/code_descriptors_test.cc
@@ -223,10 +223,9 @@
const String& name = String::Handle(String::New(TestCase::url()));
const Library& lib = Library::Handle(Library::LookupLibrary(name));
EXPECT(!lib.IsNull());
- String& ambiguity_error_msg = String::Handle();
Class& cls = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("A")), &ambiguity_error_msg));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ lib.LookupClass(String::Handle(Symbols::New("A"))));
+ EXPECT(!cls.IsNull());
// Now compile the two functions 'A.foo' and 'A.moo'
String& function_moo_name = String::Handle(String::New("moo"));
diff --git a/runtime/vm/code_generator_test.cc b/runtime/vm/code_generator_test.cc
index cac140a..34ac993 100644
--- a/runtime/vm/code_generator_test.cc
+++ b/runtime/vm/code_generator_test.cc
@@ -275,8 +275,7 @@
static RawClass* LookupClass(const Library& lib, const char* name) {
const String& cls_name = String::ZoneHandle(Symbols::New(name));
- String& ambiguity_error_msg = String::Handle();
- return lib.LookupClass(cls_name, &ambiguity_error_msg);
+ return lib.LookupClass(cls_name);
}
@@ -296,7 +295,7 @@
EXPECT(CompilerTest::TestCompileScript(lib, script));
EXPECT(ClassFinalizer::FinalizePendingClasses());
Class& cls = Class::Handle(LookupClass(lib, "A"));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ EXPECT(!cls.IsNull());
// 'bar' will not be compiled.
String& function_bar_name = String::Handle(String::New("bar"));
@@ -343,7 +342,7 @@
EXPECT(CompilerTest::TestCompileScript(lib, script));
EXPECT(ClassFinalizer::FinalizePendingClasses());
Class& cls = Class::ZoneHandle(LookupClass(lib, "A"));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ EXPECT(!cls.IsNull());
String& constructor_name = String::Handle(String::New("A."));
Function& constructor =
@@ -535,7 +534,7 @@
EXPECT(CompilerTest::TestCompileScript(lib, script));
EXPECT(ClassFinalizer::FinalizePendingClasses());
Class& cls = Class::ZoneHandle(LookupClass(lib, "A"));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ EXPECT(!cls.IsNull());
String& constructor_name = String::Handle(String::New("A."));
Function& constructor =
@@ -561,11 +560,9 @@
Library& app_lib = Library::Handle();
app_lib ^= libs.At(num_libs - 1);
ASSERT(!app_lib.IsNull());
- String& ambiguity_error_msg = String::Handle();
const Class& cls = Class::Handle(
- app_lib.LookupClass(String::Handle(Symbols::New("A")),
- &ambiguity_error_msg));
- EXPECT_EQ(cls.raw(), result.clazz()); // No ambiguity error expected.
+ app_lib.LookupClass(String::Handle(Symbols::New("A"))));
+ EXPECT_EQ(cls.raw(), result.clazz());
}
} // namespace dart
diff --git a/runtime/vm/code_patcher.cc b/runtime/vm/code_patcher.cc
index 986ade4..6fda035 100644
--- a/runtime/vm/code_patcher.cc
+++ b/runtime/vm/code_patcher.cc
@@ -29,11 +29,11 @@
const uword patch_addr = code.GetPcForDeoptId(Isolate::kNoDeoptId,
PcDescriptors::kEntryPatch);
ASSERT(patch_addr != 0);
- JumpPattern jmp_entry(patch_addr);
+ JumpPattern jmp_entry(patch_addr, code);
ASSERT(!jmp_entry.IsValid());
const uword patch_buffer = code.GetPatchCodePc();
ASSERT(patch_buffer != 0);
- JumpPattern jmp_patch(patch_buffer);
+ JumpPattern jmp_patch(patch_buffer, code);
ASSERT(jmp_patch.IsValid());
const uword jump_target = jmp_patch.TargetAddress();
SwapCode(jmp_patch.pattern_length_in_bytes(),
@@ -49,13 +49,13 @@
const uword patch_addr = code.GetPcForDeoptId(Isolate::kNoDeoptId,
PcDescriptors::kEntryPatch);
ASSERT(patch_addr != 0);
- JumpPattern jmp_entry(patch_addr);
+ JumpPattern jmp_entry(patch_addr, code);
ASSERT(jmp_entry.IsValid());
const uword jump_target = jmp_entry.TargetAddress();
const uword patch_buffer = code.GetPatchCodePc();
ASSERT(patch_buffer != 0);
// 'patch_buffer' contains original entry code.
- JumpPattern jmp_patch(patch_buffer);
+ JumpPattern jmp_patch(patch_buffer, code);
ASSERT(!jmp_patch.IsValid());
SwapCode(jmp_patch.pattern_length_in_bytes(),
reinterpret_cast<char*>(patch_addr),
@@ -72,7 +72,7 @@
if (patch_addr == 0) {
return true;
}
- JumpPattern jmp_entry(patch_addr);
+ JumpPattern jmp_entry(patch_addr, code);
if (code.Size() < (jmp_entry.pattern_length_in_bytes() * 2)) {
return false;
}
diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc
index ae307d57..5782527 100644
--- a/runtime/vm/code_patcher_x64.cc
+++ b/runtime/vm/code_patcher_x64.cc
@@ -16,53 +16,60 @@
namespace dart {
// The expected pattern of a Dart unoptimized call (static and instance):
-// 00: 48 bb imm64 mov RBX, ic-data
-// 10: 49 bb imm64 mov R11, target_address
-// 20: 41 ff d3 call R11
-// 23 <- return address
+// 00: 49 8b 9f imm32 mov RBX, [PP + off]
+// 07: 4d 8b 9f imm32 mov R11, [PP + off]
+// 14: 41 ff d3 call R11
+// 17 <- return address
class UnoptimizedCall : public ValueObject {
public:
- explicit UnoptimizedCall(uword return_address)
- : start_(return_address - kCallPatternSize) {
+ UnoptimizedCall(uword return_address, const Code& code)
+ : start_(return_address - kCallPatternSize),
+ object_pool_(Array::Handle(code.ObjectPool())) {
ASSERT(IsValid(return_address));
- ASSERT((kCallPatternSize - 10) == Assembler::kCallExternalLabelSize);
+ ASSERT((kCallPatternSize - 7) == Assembler::kCallExternalLabelSize);
}
- static const int kCallPatternSize = 23;
+ static const int kCallPatternSize = 17;
static bool IsValid(uword return_address) {
uint8_t* code_bytes =
reinterpret_cast<uint8_t*>(return_address - kCallPatternSize);
- return (code_bytes[00] == 0x48) && (code_bytes[01] == 0xBB) &&
- (code_bytes[10] == 0x49) && (code_bytes[11] == 0xBB) &&
- (code_bytes[20] == 0x41) && (code_bytes[21] == 0xFF) &&
- (code_bytes[22] == 0xD3);
+ return (code_bytes[0] == 0x49) && (code_bytes[1] == 0x8B) &&
+ (code_bytes[2] == 0x9F) &&
+ (code_bytes[7] == 0x4D) && (code_bytes[8] == 0x8B) &&
+ (code_bytes[9] == 0x9F) &&
+ (code_bytes[14] == 0x41) && (code_bytes[15] == 0xFF) &&
+ (code_bytes[16] == 0xD3);
}
RawObject* ic_data() const {
- return *reinterpret_cast<RawObject**>(start_ + 0 + 2);
+ int index = InstructionPattern::IndexFromPPLoad(start_ + 3);
+ return object_pool_.At(index);
}
uword target() const {
- return *reinterpret_cast<uword*>(start_ + 10 + 2);
+ int index = InstructionPattern::IndexFromPPLoad(start_ + 10);
+ return reinterpret_cast<uword>(object_pool_.At(index));
}
void set_target(uword target) const {
- uword* target_addr = reinterpret_cast<uword*>(start_ + 10 + 2);
- *target_addr = target;
- CPU::FlushICache(start_ + 10, 2 + 8);
+ int index = InstructionPattern::IndexFromPPLoad(start_ + 10);
+ const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(target));
+ object_pool_.SetAt(index, smi);
+ // No need to flush the instruction cache, since the code is not modified.
}
private:
uword start_;
+ const Array& object_pool_;
DISALLOW_IMPLICIT_CONSTRUCTORS(UnoptimizedCall);
};
class InstanceCall : public UnoptimizedCall {
public:
- explicit InstanceCall(uword return_address)
- : UnoptimizedCall(return_address) {
+ InstanceCall(uword return_address, const Code& code)
+ : UnoptimizedCall(return_address, code) {
#if defined(DEBUG)
ICData& test_ic_data = ICData::Handle();
test_ic_data ^= ic_data();
@@ -77,8 +84,8 @@
class UnoptimizedStaticCall : public UnoptimizedCall {
public:
- explicit UnoptimizedStaticCall(uword return_address)
- : UnoptimizedCall(return_address) {
+ UnoptimizedStaticCall(uword return_address, const Code& code)
+ : UnoptimizedCall(return_address, code) {
#if defined(DEBUG)
ICData& test_ic_data = ICData::Handle();
test_ic_data ^= ic_data();
@@ -92,50 +99,54 @@
// The expected pattern of a dart static call:
-// mov R10, arguments_descriptor_array (10 bytes) (optional in polym. calls)
-// mov R11, target_address (10 bytes)
-// call R11 (3 bytes)
+// 00 mov R10, arguments_descriptor_array (10 bytes) (optional in polym. calls)
+// 11: 4d 8b 9f imm32 mov R11, [PP + off]
+// 16: call R11 (3 bytes)
// <- return address
class StaticCall : public ValueObject {
public:
- explicit StaticCall(uword return_address)
- : start_(return_address - kCallPatternSize) {
+ explicit StaticCall(uword return_address, const Code& code)
+ : start_(return_address - kCallPatternSize),
+ object_pool_(Array::Handle(code.ObjectPool())) {
ASSERT(IsValid(return_address));
ASSERT(kCallPatternSize == Assembler::kCallExternalLabelSize);
}
- static const int kCallPatternSize = 13;
+ static const int kCallPatternSize = 10;
static bool IsValid(uword return_address) {
uint8_t* code_bytes =
reinterpret_cast<uint8_t*>(return_address - kCallPatternSize);
- return (code_bytes[00] == 0x49) && (code_bytes[01] == 0xBB) &&
- (code_bytes[10] == 0x41) && (code_bytes[11] == 0xFF) &&
- (code_bytes[12] == 0xD3);
+ return (code_bytes[0] == 0x4D) && (code_bytes[1] == 0x8B) &&
+ (code_bytes[2] == 0x9F) &&
+ (code_bytes[7] == 0x41) && (code_bytes[8] == 0xFF) &&
+ (code_bytes[9] == 0xD3);
}
uword target() const {
- return *reinterpret_cast<uword*>(start_ + 2);
+ int index = InstructionPattern::IndexFromPPLoad(start_ + 3);
+ return reinterpret_cast<uword>(object_pool_.At(index));
}
void set_target(uword target) const {
- uword* target_addr = reinterpret_cast<uword*>(start_ + 2);
- *target_addr = target;
- CPU::FlushICache(start_, 2 + 8);
+ int index = InstructionPattern::IndexFromPPLoad(start_ + 3);
+ const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(target));
+ object_pool_.SetAt(index, smi);
+ // No need to flush the instruction cache, since the code is not modified.
}
private:
uword start_;
-
+ const Array& object_pool_;
DISALLOW_IMPLICIT_CONSTRUCTORS(StaticCall);
};
// The expected code pattern of a dart closure call:
-// 00: 49 ba imm64 mov R10, immediate 2 ; 10 bytes
-// 10: 49 bb imm64 mov R11, target_address ; 10 bytes
-// 20: 41 ff d3 call R11 ; 3 bytes
-// 23: <- return_address
+// 00: 49 ba imm64 mov R10, immediate 2 ; 10 bytes
+// 10: 4d 8b 9f imm32 mov R11, [PP + off]
+// 17: 41 ff d3 call R11 ; 3 bytes
+// 20: <- return_address
class ClosureCall : public ValueObject {
public:
explicit ClosureCall(uword return_address)
@@ -147,9 +158,10 @@
uint8_t* code_bytes =
reinterpret_cast<uint8_t*>(return_address - kCallPatternSize);
return (code_bytes[00] == 0x49) && (code_bytes[01] == 0xBA) &&
- (code_bytes[10] == 0x49) && (code_bytes[11] == 0xBB) &&
- (code_bytes[20] == 0x41) && (code_bytes[21] == 0xFF) &&
- (code_bytes[22] == 0xD3);
+ (code_bytes[10] == 0x4D) && (code_bytes[11] == 0x8B) &&
+ (code_bytes[12] == 0x9F) &&
+ (code_bytes[17] == 0x41) && (code_bytes[18] == 0xFF) &&
+ (code_bytes[19] == 0xD3);
}
RawArray* arguments_descriptor() const {
@@ -157,7 +169,7 @@
}
private:
- static const int kCallPatternSize = 10 + 10 + 3;
+ static const int kCallPatternSize = 10 + 7 + 3;
uword start_;
DISALLOW_IMPLICIT_CONSTRUCTORS(ClosureCall);
};
@@ -174,7 +186,7 @@
uword CodePatcher::GetStaticCallTargetAt(uword return_address,
const Code& code) {
ASSERT(code.ContainsInstructionAt(return_address));
- StaticCall call(return_address);
+ StaticCall call(return_address, code);
return call.target();
}
@@ -183,7 +195,7 @@
const Code& code,
uword new_target) {
ASSERT(code.ContainsInstructionAt(return_address));
- StaticCall call(return_address);
+ StaticCall call(return_address, code);
call.set_target(new_target);
}
@@ -192,7 +204,7 @@
const Code& code,
uword new_target) {
ASSERT(code.ContainsInstructionAt(return_address));
- InstanceCall call(return_address);
+ InstanceCall call(return_address, code);
call.set_target(new_target);
}
@@ -201,7 +213,7 @@
const Code& code,
ICData* ic_data) {
ASSERT(code.ContainsInstructionAt(return_address));
- InstanceCall call(return_address);
+ InstanceCall call(return_address, code);
if (ic_data != NULL) {
*ic_data ^= call.ic_data();
}
@@ -227,7 +239,7 @@
RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
uword return_address, const Code& code, ICData* ic_data_result) {
ASSERT(code.ContainsInstructionAt(return_address));
- UnoptimizedStaticCall static_call(return_address);
+ UnoptimizedStaticCall static_call(return_address, code);
ICData& ic_data = ICData::Handle();
ic_data ^= static_call.ic_data();
if (ic_data_result != NULL) {
diff --git a/runtime/vm/code_patcher_x64_test.cc b/runtime/vm/code_patcher_x64_test.cc
index 202c27a..67561db 100644
--- a/runtime/vm/code_patcher_x64_test.cc
+++ b/runtime/vm/code_patcher_x64_test.cc
@@ -39,10 +39,10 @@
15,
1));
- __ LoadObject(RBX, ic_data);
+ __ LoadObject(RBX, ic_data, PP);
ExternalLabel target_label(
"InlineCache", StubCode::OneArgCheckInlineCacheEntryPoint());
- __ call(&target_label);
+ __ CallPatchable(&target_label);
__ ret();
}
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 1e6ce14..1f2d1c2 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -46,7 +46,7 @@
"Do loop invariant code motion.");
DEFINE_FLAG(bool, propagate_types, true, "Do static type propagation.");
DEFINE_FLAG(bool, allocation_sinking, true,
- "attempt to sink temporary allocations to side exits");
+ "Attempt to sink temporary allocations to side exits");
DEFINE_FLAG(int, deoptimization_counter_threshold, 16,
"How many times we allow deoptimization before we disallow optimization.");
DEFINE_FLAG(int, deoptimization_counter_licm_threshold, 8,
diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc
index e962155..fcee41e 100644
--- a/runtime/vm/compiler_test.cc
+++ b/runtime/vm/compiler_test.cc
@@ -43,10 +43,9 @@
Library& lib = Library::Handle(Library::CoreLibrary());
EXPECT(CompilerTest::TestCompileScript(lib, script));
EXPECT(ClassFinalizer::FinalizePendingClasses());
- String& ambiguity_error_msg = String::Handle();
Class& cls = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("A")), &ambiguity_error_msg));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ lib.LookupClass(String::Handle(Symbols::New("A"))));
+ EXPECT(!cls.IsNull());
String& function_foo_name = String::Handle(String::New("foo"));
Function& function_foo =
Function::Handle(cls.LookupStaticFunction(function_foo_name));
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index 3fa9526..6839b67 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -85,8 +85,9 @@
// Register aliases.
const Register TMP = R11; // Used as scratch register by the assembler.
-const Register CTX = R15; // Caches current context in generated code.
-const Register PP = kNoRegister; // No object pool pointer.
+const Register CTX = R14; // Caches current context in generated code.
+// Caches object pool pointer in generated code.
+const Register PP = R15;
const Register SPREG = RSP; // Stack pointer register.
const Register FPREG = RBP; // Frame pointer register.
const Register ICREG = RBX; // IC data register.
diff --git a/runtime/vm/coverage.cc b/runtime/vm/coverage.cc
new file mode 100644
index 0000000..be99d26
--- /dev/null
+++ b/runtime/vm/coverage.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2013, 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.
+
+#include "vm/coverage.h"
+
+#include "vm/isolate.h"
+#include "vm/json_stream.h"
+#include "vm/object.h"
+#include "vm/object_store.h"
+
+namespace dart {
+
+DEFINE_FLAG(bool, print_coverage, false, "Print code coverage.");
+
+void CodeCoverage::PrintClass(const Class& cls, const JSONArray& jsarr) {
+ const Array& functions = Array::Handle(cls.functions());
+ ASSERT(!functions.IsNull());
+ Function& function = Function::Handle();
+ Code& code = Code::Handle();
+ Script& script = Script::Handle();
+ String& url = String::Handle();
+ String& name = String::Handle();
+ PcDescriptors& descriptors = PcDescriptors::Handle();
+ Array& ic_array = Array::Handle();
+ ICData& ic_data = ICData::Handle();
+ for (int i = 0; i < functions.Length(); i++) {
+ function ^= functions.At(i);
+ if (function.HasCode()) {
+ JSONObject jsobj(&jsarr);
+
+ script = function.script();
+ url = script.url();
+ name = function.QualifiedUserVisibleName();
+ jsobj.AddProperty("source", url.ToCString());
+ jsobj.AddProperty("function", name.ToCString());
+
+ code = function.unoptimized_code();
+ ic_array = code.ExtractTypeFeedbackArray();
+ descriptors = code.pc_descriptors();
+
+ JSONArray jsarr(&jsobj, "hits");
+ for (int j = 0; j < descriptors.Length(); j++) {
+ PcDescriptors::Kind kind = descriptors.DescriptorKind(j);
+ // Only IC based calls have counting.
+ if ((kind == PcDescriptors::kIcCall) ||
+ (kind == PcDescriptors::kUnoptStaticCall)) {
+ intptr_t deopt_id = descriptors.DeoptId(j);
+ ic_data ^= ic_array.At(deopt_id);
+ if (!ic_data.IsNull() && (ic_data.AggregateCount() > 0)) {
+ intptr_t token_pos = descriptors.TokenPos(j);
+ intptr_t line = -1;
+ intptr_t col = -1;
+ script.GetTokenLocation(token_pos, &line, &col);
+ JSONObject ic_info(&jsarr);
+ ic_info.AddProperty("line", line);
+ ic_info.AddProperty("col", col);
+ ic_info.AddProperty("count", ic_data.AggregateCount());
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void CodeCoverage::Print(Isolate* isolate) {
+ JSONStream stream;
+
+ {
+ const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+ isolate, isolate->object_store()->libraries());
+ Library& lib = Library::Handle();
+ Class& cls = Class::Handle();
+ JSONArray jsarr(&stream);
+ for (int i = 0; i < libs.Length(); i++) {
+ lib ^= libs.At(i);
+ ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
+ while (it.HasNext()) {
+ cls = it.GetNextClass();
+ if (cls.is_finalized()) {
+ // Only classes that have been finalized do have a meaningful list of
+ // functions.
+ PrintClass(cls, jsarr);
+ }
+ }
+ }
+ }
+
+ OS::Print("### COVERAGE DATA ###\n"
+ "%s\n"
+ "### END ###\n", stream.ToCString());
+}
+
+} // namespace dart
diff --git a/runtime/vm/coverage.h b/runtime/vm/coverage.h
new file mode 100644
index 0000000..9819105
--- /dev/null
+++ b/runtime/vm/coverage.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013, 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.
+
+#ifndef VM_COVERAGE_H_
+#define VM_COVERAGE_H_
+
+#include "vm/allocation.h"
+#include "vm/flags.h"
+
+namespace dart {
+
+DECLARE_FLAG(bool, print_coverage);
+
+// Forward declarations.
+class Class;
+class Isolate;
+class JSONArray;
+
+class CodeCoverage : public AllStatic {
+ public:
+ static void Print(Isolate* isolate);
+
+ private:
+ static void PrintClass(const Class& cls, const JSONArray& arr);
+};
+
+} // namespace dart
+
+#endif // VM_COVERAGE_H_
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 350efa0..7a673f9 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -60,7 +60,7 @@
if (obj.IsInstance()) {
const Library& core_lib = Library::Handle(Library::CoreLibrary());
const Class& list_class =
- Class::Handle(core_lib.LookupClass(Symbols::List(), NULL));
+ Class::Handle(core_lib.LookupClass(Symbols::List()));
ASSERT(!list_class.IsNull());
const Instance& instance = Instance::Cast(obj);
const Class& obj_class = Class::Handle(isolate, obj.clazz());
@@ -1073,8 +1073,7 @@
function_name,
kNumArguments,
Object::empty_array(),
- Resolver::kIsQualified,
- NULL)); // No ambiguity error expected.
+ Resolver::kIsQualified));
ASSERT(!function.IsNull());
const Array& args = Array::Handle(isolate, Array::New(kNumArguments));
args.SetAt(0, Integer::Handle(isolate, Integer::New(port_id)));
@@ -2102,7 +2101,7 @@
return ApiError::New(message);
}
const Class& cls = Class::Handle(
- isolate, lib.LookupClassAllowPrivate(class_name, NULL));
+ isolate, lib.LookupClassAllowPrivate(class_name));
ASSERT(!cls.IsNull());
Object& result = Object::Handle(isolate);
String& dot_name = String::Handle(String::New("."));
@@ -2488,7 +2487,7 @@
Library::Handle(isolate->object_store()->typed_data_library());
ASSERT(!lib.IsNull());
const Class& cls = Class::Handle(
- isolate, lib.LookupClassAllowPrivate(Symbols::ByteData(), NULL));
+ isolate, lib.LookupClassAllowPrivate(Symbols::ByteData()));
ASSERT(!cls.IsNull());
return ResolveConstructor(CURRENT_FUNC,
cls,
@@ -2847,13 +2846,18 @@
RETURN_TYPE_ERROR(isolate, type, Type);
}
Class& cls = Class::Handle(isolate);
+ AbstractTypeArguments& type_arguments =
+ AbstractTypeArguments::Handle(isolate);
+
if (result.IsType()) {
cls = Type::Cast(result).type_class();
+ type_arguments = Type::Cast(result).arguments();
} else if (result.IsClass()) {
// For backwards compatibility we allow class objects to be passed in
// for now. This needs to be removed once all code that uses class
// objects to invoke Dart_New is removed.
cls ^= result.raw();
+ type_arguments = Type::Handle(cls.RareType()).arguments();
} else {
RETURN_TYPE_ERROR(isolate, type, Type);
}
@@ -2914,12 +2918,18 @@
Array::Handle(isolate, Array::New(number_of_arguments + extra_args));
if (constructor.IsConstructor()) {
// Constructors get the uninitialized object and a constructor phase.
+ if (!type_arguments.IsNull()) {
+ // The type arguments will be null if the class has no type parameters, in
+ // which case the following call would fail because there is no slot
+ // reserved in the object for the type vector.
+ new_object.SetTypeArguments(type_arguments);
+ }
args.SetAt(arg_index++, new_object);
args.SetAt(arg_index++,
Smi::Handle(isolate, Smi::New(Function::kCtorPhaseAll)));
} else {
// Factories get type arguments.
- args.SetAt(arg_index++, TypeArguments::Handle(isolate));
+ args.SetAt(arg_index++, type_arguments);
}
Object& argument = Object::Handle(isolate);
for (int i = 0; i < number_of_arguments; i++) {
@@ -3082,14 +3092,9 @@
return state;
}
- String& ambiguity_error_msg = String::Handle(isolate);
Function& function = Function::Handle(isolate);
- function = lib.LookupFunctionAllowPrivate(function_name,
- &ambiguity_error_msg);
+ function = lib.LookupFunctionAllowPrivate(function_name);
if (function.IsNull()) {
- if (!ambiguity_error_msg.IsNull()) {
- return Api::NewError("%s.", ambiguity_error_msg.ToCString());
- }
return Api::NewError("%s: did not find top-level function '%s'.",
CURRENT_FUNC,
function_name.ToCString());
@@ -3248,15 +3253,13 @@
// To access a top-level we may need to use the Field or the
// getter Function. The getter function may either be in the
// library or in the field's owner class, depending.
- String& ambiguity_error_msg = String::Handle(isolate);
const Library& lib = Library::Cast(obj);
- field = lib.LookupFieldAllowPrivate(field_name, &ambiguity_error_msg);
- if (field.IsNull() && ambiguity_error_msg.IsNull()) {
+ field = lib.LookupFieldAllowPrivate(field_name);
+ if (field.IsNull()) {
// No field found and no ambiguity error. Check for a getter in the lib.
const String& getter_name =
String::Handle(isolate, Field::GetterName(field_name));
- getter = lib.LookupFunctionAllowPrivate(getter_name,
- &ambiguity_error_msg);
+ getter = lib.LookupFunctionAllowPrivate(getter_name);
} else if (!field.IsNull() && FieldIsUninitialized(isolate, field)) {
// A field was found. Check for a getter in the field's owner classs.
const Class& cls = Class::Handle(isolate, field.owner());
@@ -3273,9 +3276,6 @@
if (!field.IsNull()) {
return Api::NewHandle(isolate, field.value());
}
- if (!ambiguity_error_msg.IsNull()) {
- return Api::NewError("%s.", ambiguity_error_msg.ToCString());
- }
return Api::NewError("%s: did not find top-level variable '%s'.",
CURRENT_FUNC, field_name.ToCString());
@@ -3405,14 +3405,12 @@
// To access a top-level we may need to use the Field or the
// setter Function. The setter function may either be in the
// library or in the field's owner class, depending.
- String& ambiguity_error_msg = String::Handle(isolate);
const Library& lib = Library::Cast(obj);
- field = lib.LookupFieldAllowPrivate(field_name, &ambiguity_error_msg);
- if (field.IsNull() && ambiguity_error_msg.IsNull()) {
+ field = lib.LookupFieldAllowPrivate(field_name);
+ if (field.IsNull()) {
const String& setter_name =
String::Handle(isolate, Field::SetterName(field_name));
- setter ^= lib.LookupFunctionAllowPrivate(setter_name,
- &ambiguity_error_msg);
+ setter ^= lib.LookupFunctionAllowPrivate(setter_name);
}
if (!setter.IsNull()) {
@@ -3435,9 +3433,6 @@
field.set_value(value_instance);
return Api::Success();
}
- if (!ambiguity_error_msg.IsNull()) {
- return Api::NewError("%s.", ambiguity_error_msg.ToCString());
- }
return Api::NewError("%s: did not find top-level variable '%s'.",
CURRENT_FUNC, field_name.ToCString());
@@ -4031,13 +4026,9 @@
if (cls_name.IsNull()) {
RETURN_TYPE_ERROR(isolate, class_name, String);
}
- String& ambiguity_error_msg = String::Handle(isolate);
const Class& cls = Class::Handle(
- isolate, lib.LookupClassAllowPrivate(cls_name, &ambiguity_error_msg));
+ isolate, lib.LookupClassAllowPrivate(cls_name));
if (cls.IsNull()) {
- if (!ambiguity_error_msg.IsNull()) {
- return Api::NewError("%s.", ambiguity_error_msg.ToCString());
- }
// TODO(turnidge): Return null or error in this case?
const String& lib_name = String::Handle(isolate, lib.name());
return Api::NewError("Class '%s' not found in library '%s'.",
@@ -4068,14 +4059,9 @@
if (::Dart_IsError(state)) {
return state;
}
- String& ambiguity_error_msg = String::Handle(isolate);
const Class& cls =
- Class::Handle(isolate, lib.LookupClassAllowPrivate(name_str,
- &ambiguity_error_msg));
+ Class::Handle(isolate, lib.LookupClassAllowPrivate(name_str));
if (cls.IsNull()) {
- if (!ambiguity_error_msg.IsNull()) {
- return Api::NewError("%s.", ambiguity_error_msg.ToCString());
- }
const String& lib_name = String::Handle(isolate, lib.name());
return Api::NewError("Type '%s' not found in library '%s'.",
name_str.ToCString(), lib_name.ToCString());
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index a9def3f..dda6809 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -5296,8 +5296,13 @@
" external B.named(x);\n"
" external B(v);\n"
"}\n"
+ "class C {\n"
+ " external int method();\n"
+ "}\n"
+ "\n"
"external int unpatched();\n"
"external int topLevel(var value);\n"
+ "external int topLevel2(var value);\n"
"external int get topLevelGetter;\n"
"external void set topLevelSetter(int value);\n";
@@ -5322,7 +5327,19 @@
"patch int set topLevelSetter(value) { _topLevelValue = value; }\n"
"patch int get topLevelGetter => 2 * _topLevelValue;\n"
// Allow top level methods named patch.
- "patch(x) => x*3;\n";
+ "patch(x) => x*3;\n"
+ ; // NOLINT
+
+ const char* kPatchClassOnlyChars =
+ "patch class C {\n"
+ " /*patch*/ int method() {\n"
+ " return 42;\n"
+ " }\n"
+ "}\n"
+ ; // NOLINT
+
+ const char* kPatchNoClassChars =
+ "patch topLevel2(x) => x * 2;\n";
const char* kScriptChars =
"import 'theLibrary';\n"
@@ -5356,17 +5373,27 @@
result = Dart_LoadLibrary(url, source);
EXPECT_VALID(result);
- const String& patch_url = String::Handle(String::New("theLibrary patch"));
- const String& patch_source = String::Handle(String::New(kPatchChars));
- const Script& patch_script = Script::Handle(Script::New(
- patch_url, patch_source, RawScript::kPatchTag));
-
+ const char* patchNames[] = { "main library patch",
+ "patch class only",
+ "patch no class" };
+ const char* patches[] = { kPatchChars,
+ kPatchClassOnlyChars,
+ kPatchNoClassChars };
const String& lib_url = String::Handle(String::New("theLibrary"));
+
const Library& lib = Library::Handle(Library::LookupLibrary(lib_url));
- const Error& err = Error::Handle(lib.Patch(patch_script));
- if (!err.IsNull()) {
- OS::Print("Patching error: %s\n", err.ToErrorCString());
- EXPECT(false);
+
+ for (int i = 0; i < 3; i++) {
+ const String& patch_url = String::Handle(String::New(patchNames[i]));
+ const String& patch_source = String::Handle(String::New(patches[i]));
+ const Script& patch_script = Script::Handle(Script::New(
+ patch_url, patch_source, RawScript::kPatchTag));
+
+ const Error& err = Error::Handle(lib.Patch(patch_script));
+ if (!err.IsNull()) {
+ OS::Print("Patching error: %s\n", err.ToErrorCString());
+ EXPECT(false);
+ }
}
result = Dart_SetNativeResolver(result, &PatchNativeResolver);
EXPECT_VALID(result);
@@ -5421,6 +5448,10 @@
EXPECT(Dart_IsInteger(result));
EXPECT_VALID(Dart_IntegerToInt64(result, &value));
EXPECT_EQ(8, value);
+
+ // Make sure all source files show up in the patched library.
+ const Array& lib_scripts = Array::Handle(lib.LoadedScripts());
+ EXPECT_EQ(4, lib_scripts.Length());
}
@@ -5597,7 +5628,7 @@
result = Dart_Invoke(result, NewString("main"), 0, NULL);
EXPECT(Dart_IsError(result));
- EXPECT_SUBSTRING("ambiguous reference: 'foo'", Dart_GetError(result));
+ EXPECT_SUBSTRING("NoSuchMethodError", Dart_GetError(result));
}
@@ -6889,9 +6920,9 @@
TEST_CASE(MakeExternalString) {
- int peer8 = 40;
- int peer16 = 41;
- int canonical_str_peer = 42;
+ static int peer8 = 40;
+ static int peer16 = 41;
+ static int canonical_str_peer = 42;
intptr_t length = 0;
intptr_t expected_length = 0;
{
diff --git a/runtime/vm/dart_api_message.cc b/runtime/vm/dart_api_message.cc
index 390e9fb..c8484f9 100644
--- a/runtime/vm/dart_api_message.cc
+++ b/runtime/vm/dart_api_message.cc
@@ -2,6 +2,7 @@
// 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.
+#include "vm/bigint_operations.h"
#include "vm/dart_api_message.h"
#include "vm/object.h"
#include "vm/snapshot_ids.h"
@@ -887,17 +888,23 @@
Dart_CObject_Type type = object->type;
if (type == Dart_CObject_kArray) {
+ const intptr_t array_length = object->value.as_array.length;
+ if (array_length < 0 ||
+ array_length > Array::kMaxElements) {
+ return false;
+ }
+
// Write out the serialization header value for this object.
WriteInlinedHeader(object);
// Write out the class and tags information.
WriteIndexedObject(kArrayCid);
WriteIntptrValue(0);
-
- WriteSmi(object->value.as_array.length);
+ // Write out the length information.
+ WriteSmi(array_length);
// Write out the type arguments.
WriteNullObject();
// Write out array elements.
- for (int i = 0; i < object->value.as_array.length; i++) {
+ for (int i = 0; i < array_length; i++) {
bool success = WriteCObjectRef(object->value.as_array.values[i]);
if (!success) return false;
}
@@ -916,12 +923,17 @@
Dart_CObject_Type type = object->type;
if (type == Dart_CObject_kArray) {
+ const intptr_t array_length = object->value.as_array.length;
+ if (array_length < 0 ||
+ array_length > Array::kMaxElements) {
+ return false;
+ }
// Write out the serialization header value for this object.
WriteInlinedHeader(object);
// Write out the class information.
WriteIndexedObject(kArrayCid);
// Write out the length information.
- WriteSmi(object->value.as_array.length);
+ WriteSmi(array_length);
// Add object to forward list so that this object is serialized later.
AddToForwardList(object);
return true;
@@ -935,6 +947,11 @@
Dart_CObject_Type type =
static_cast<Dart_CObject_Type>(object->type & kDartCObjectTypeMask);
ASSERT(type == Dart_CObject_kArray);
+ const intptr_t array_length = object->value.as_array.length;
+ if (array_length < 0 ||
+ array_length > Array::kMaxElements) {
+ return false;
+ }
// Write out the serialization header value for this object.
intptr_t object_id = GetMarkedCObjectMark(object);
@@ -942,12 +959,12 @@
// Write out the class and tags information.
WriteIndexedObject(kArrayCid);
WriteIntptrValue(0);
-
- WriteSmi(object->value.as_array.length);
+ // Write out the length information.
+ WriteSmi(array_length);
// Write out the type arguments.
WriteNullObject();
// Write out array elements.
- for (int i = 0; i < object->value.as_array.length; i++) {
+ for (int i = 0; i < array_length; i++) {
bool success = WriteCObjectRef(object->value.as_array.values[i]);
if (!success) return false;
}
@@ -975,13 +992,19 @@
WriteInt64(object);
break;
case Dart_CObject_kBigint: {
+ char* hex_string = object->value.as_bigint;
+ const intptr_t chunk_len =
+ BigintOperations::ComputeChunkLength(hex_string);
+ if (chunk_len < 0 ||
+ chunk_len > Bigint::kMaxElements) {
+ return false;
+ }
// Write out the serialization header value for this object.
WriteInlinedHeader(object);
// Write out the class and tags information.
WriteIndexedObject(kBigintCid);
WriteIntptrValue(0);
// Write hex string length and content
- char* hex_string = object->value.as_bigint;
intptr_t len = strlen(hex_string);
WriteIntptrValue(len);
for (intptr_t i = 0; i < len; i++) {
@@ -1008,6 +1031,10 @@
Utf8::Type type;
intptr_t len = Utf8::CodeUnitCount(utf8_str, utf8_len, &type);
+ ASSERT(len > 0);
+ if (len > String::kMaxElements) {
+ return false;
+ }
// Write out the serialization header value for this object.
WriteInlinedHeader(object);
@@ -1059,11 +1086,16 @@
UNIMPLEMENTED();
}
+ intptr_t len = object->value.as_typed_data.length;
+ if (len < 0 ||
+ len > TypedData::MaxElements(class_id)) {
+ return false;
+ }
+
WriteIndexedObject(class_id);
WriteIntptrValue(RawObject::ClassIdTag::update(class_id, 0));
- uint8_t* bytes = object->value.as_typed_data.values;
- intptr_t len = object->value.as_typed_data.length;
WriteSmi(len);
+ uint8_t* bytes = object->value.as_typed_data.values;
for (intptr_t i = 0; i < len; i++) {
Write<uint8_t>(bytes[i]);
}
@@ -1081,7 +1113,12 @@
WriteIndexedObject(kExternalTypedDataUint8ArrayCid);
WriteIntptrValue(RawObject::ClassIdTag::update(
kExternalTypedDataUint8ArrayCid, 0));
- int length = object->value.as_external_typed_data.length;
+ intptr_t length = object->value.as_external_typed_data.length;
+ if (length < 0 ||
+ length > ExternalTypedData::MaxElements(
+ kExternalTypedDataUint8ArrayCid)) {
+ return false;
+ }
uint8_t* data = object->value.as_external_typed_data.data;
void* peer = object->value.as_external_typed_data.peer;
Dart_WeakPersistentHandleFinalizer callback =
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 2c018c8..cb40979 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -118,8 +118,7 @@
Class& invocation_mirror_class = Class::Handle(
core_lib.LookupClass(
- String::Handle(core_lib.PrivateName(Symbols::InvocationMirror())),
- NULL)); // No ambiguity error expected.
+ String::Handle(core_lib.PrivateName(Symbols::InvocationMirror()))));
ASSERT(!invocation_mirror_class.IsNull());
const String& function_name =
String::Handle(core_lib.PrivateName(Symbols::AllocateInvocationMirror()));
@@ -302,8 +301,7 @@
const String& class_name,
const String& constructor_name,
const Array& arguments) {
- const Class& cls = Class::Handle(
- lib.LookupClassAllowPrivate(class_name, NULL)); // No ambiguity expected.
+ const Class& cls = Class::Handle(lib.LookupClassAllowPrivate(class_name));
ASSERT(!cls.IsNull());
// For now, we only support a non-parameterized or raw type.
const int kNumExtraArgs = 2; // implicit rcvr and construction phase args.
@@ -392,8 +390,7 @@
function_name,
kNumArguments,
Object::empty_array(),
- Resolver::kIsQualified,
- NULL); // No ambiguity error expected.
+ Resolver::kIsQualified);
ASSERT(!function.IsNull());
isolate->object_store()->set_lookup_receive_port_function(function);
}
@@ -425,8 +422,7 @@
function_name,
kNumArguments,
Object::empty_array(),
- Resolver::kIsQualified,
- NULL); // No ambiguity error expected.
+ Resolver::kIsQualified);
ASSERT(!function.IsNull());
isolate->object_store()->set_handle_message_function(function);
}
@@ -461,8 +457,7 @@
function_name,
kNumArguments,
Object::empty_array(),
- Resolver::kIsQualified,
- NULL)); // No ambiguity error expected.
+ Resolver::kIsQualified));
ASSERT(!function.IsNull());
const Array& args = Array::Handle(Array::New(kNumArguments));
args.SetAt(0, Integer::Handle(Integer::New(port_id)));
diff --git a/runtime/vm/dart_entry_test.cc b/runtime/vm/dart_entry_test.cc
index 0983efb..fa8cc81 100644
--- a/runtime/vm/dart_entry_test.cc
+++ b/runtime/vm/dart_entry_test.cc
@@ -27,9 +27,8 @@
Library& lib = Library::Handle(Library::CoreLibrary());
EXPECT_EQ(true, CompilerTest::TestCompileScript(lib, script));
EXPECT(ClassFinalizer::FinalizePendingClasses());
- String& ambiguity_error_msg = String::Handle();
Class& cls = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("A")), &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New("A"))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
String& name = String::Handle(String::New("foo"));
Function& function = Function::Handle(cls.LookupStaticFunction(name));
@@ -56,9 +55,8 @@
Library& lib = Library::Handle(Library::CoreLibrary());
EXPECT_EQ(true, CompilerTest::TestCompileScript(lib, script));
EXPECT(ClassFinalizer::FinalizePendingClasses());
- String& ambiguity_error_msg = String::Handle();
Class& cls = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("A")), &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New("A"))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
String& name = String::Handle(String::New("foo"));
Function& function = Function::Handle(cls.LookupStaticFunction(name));
@@ -84,9 +82,8 @@
Library& lib = Library::Handle(Library::CoreLibrary());
EXPECT_EQ(true, CompilerTest::TestCompileScript(lib, script));
EXPECT(ClassFinalizer::FinalizePendingClasses());
- String& ambiguity_error_msg = String::Handle();
Class& cls = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("A")), &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New("A"))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
// Invoke the constructor.
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index fae125c..9e02f77 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -829,9 +829,7 @@
const Library& library,
const String& fname) {
ASSERT(!library.IsNull());
- String& ambiguity_error_msg = String::Handle();
- const Object& object = Object::Handle(
- library.LookupObject(fname, &ambiguity_error_msg));
+ const Object& object = Object::Handle(library.LookupObject(fname));
if (!object.IsNull() && object.IsFunction()) {
return Function::Cast(object).raw();
}
@@ -862,9 +860,7 @@
if (class_name.Length() == 0) {
return ResolveLibraryFunction(library, function_name);
}
- String& ambiguity_error_msg = String::Handle();
- const Class& cls = Class::Handle(
- library.LookupClass(class_name, &ambiguity_error_msg));
+ const Class& cls = Class::Handle(library.LookupClass(class_name));
Function& function = Function::Handle();
if (!cls.IsNull()) {
function = cls.LookupStaticFunction(function_name);
diff --git a/runtime/vm/debugger_api_impl.cc b/runtime/vm/debugger_api_impl.cc
index f9bbe1c..09e24c0 100644
--- a/runtime/vm/debugger_api_impl.cc
+++ b/runtime/vm/debugger_api_impl.cc
@@ -255,6 +255,7 @@
DART_EXPORT Dart_Handle Dart_ActivationFrameGetLocation(
Dart_ActivationFrame activation_frame,
Dart_Handle* function_name,
+ Dart_Handle* function,
Dart_CodeLocation* location) {
// TODO(hausner): Implement a way to recognize when there
// is no source code for the code in the frame.
@@ -264,6 +265,9 @@
if (function_name != NULL) {
*function_name = Api::NewHandle(isolate, frame->QualifiedFunctionName());
}
+ if (function != NULL) {
+ *function = Api::NewHandle(isolate, frame->function().raw());
+ }
if (location != NULL) {
location->script_url = Api::NewHandle(isolate, frame->SourceUrl());
diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc
index b56bc15..2426b3f 100644
--- a/runtime/vm/debugger_api_impl_test.cc
+++ b/runtime/vm/debugger_api_impl_test.cc
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
#include "include/dart_debugger_api.h"
+#include "include/dart_mirrors_api.h"
#include "platform/assert.h"
#include "vm/dart_api_impl.h"
#include "vm/thread.h"
@@ -235,8 +236,9 @@
Dart_Handle expected_locals,
bool skip_null_expects) {
Dart_Handle func_name;
+ Dart_Handle func;
Dart_Handle res;
- res = Dart_ActivationFrameInfo(frame, &func_name, NULL, NULL, NULL);
+ res = Dart_ActivationFrameGetLocation(frame, &func_name, &func, NULL);
EXPECT_TRUE(res);
EXPECT(Dart_IsString(func_name));
const char* func_name_chars;
@@ -244,6 +246,11 @@
if (expected_name != NULL) {
EXPECT_SUBSTRING(expected_name, func_name_chars);
}
+ EXPECT(Dart_IsFunction(func));
+ const char* func_name_chars_from_func_handle;
+ Dart_StringToCString(Dart_FunctionName(func),
+ &func_name_chars_from_func_handle);
+ EXPECT_STREQ(func_name_chars, func_name_chars_from_func_handle);
if (!Dart_IsNull(expected_locals)) {
Dart_Handle locals = Dart_GetLocalVariables(frame);
@@ -1558,6 +1565,19 @@
EXPECT(Dart_IsDouble(r));
EXPECT_EQ(50.0, ToDouble(r));
+ Dart_Handle closure =
+ Dart_EvaluateExpr(point, NewString("() => _factor * sqrt(x*x + y*y))"));
+ EXPECT_VALID(closure);
+ EXPECT(Dart_IsClosure(closure));
+ r = Dart_InvokeClosure(closure, 0, NULL);
+ EXPECT_EQ(50.0, ToDouble(r));
+
+ r = Dart_EvaluateExpr(point,
+ NewString("(() => _factor * sqrt(x*x + y*y))()"));
+ EXPECT_VALID(r);
+ EXPECT(Dart_IsDouble(r));
+ EXPECT_EQ(50.0, ToDouble(r));
+
Dart_Handle len = Dart_EvaluateExpr(point, NewString("l.length"));
EXPECT_VALID(len);
EXPECT(Dart_IsNumber(len));
@@ -1583,6 +1603,20 @@
EXPECT_VALID(len);
EXPECT(Dart_IsNumber(len));
EXPECT_EQ(6, ToInt64(len));
+
+ closure = Dart_EvaluateExpr(script_lib, NewString("(x) => l.length + x"));
+ EXPECT_VALID(closure);
+ EXPECT(Dart_IsClosure(closure));
+ Dart_Handle args[1] = { Dart_NewInteger(1) };
+ len = Dart_InvokeClosure(closure, 1, args);
+ EXPECT_VALID(len);
+ EXPECT(Dart_IsNumber(len));
+ EXPECT_EQ(6, ToInt64(len));
+
+ len = Dart_EvaluateExpr(script_lib, NewString("((x) => l.length + x)(1)"));
+ EXPECT_VALID(len);
+ EXPECT(Dart_IsNumber(len));
+ EXPECT_EQ(6, ToInt64(len));
}
diff --git a/runtime/vm/debugger_x64.cc b/runtime/vm/debugger_x64.cc
index 29d3b33..f9b31c7 100644
--- a/runtime/vm/debugger_x64.cc
+++ b/runtime/vm/debugger_x64.cc
@@ -7,6 +7,7 @@
#include "vm/debugger.h"
+#include "vm/assembler.h"
#include "vm/cpu.h"
#include "vm/stub_code.h"
@@ -42,16 +43,15 @@
void CodeBreakpoint::PatchFunctionReturn() {
uint8_t* code = reinterpret_cast<uint8_t*>(pc_ - 13);
- // movq %rbp,%rsp
- ASSERT((code[0] == 0x48) && (code[1] == 0x89) && (code[2] == 0xec));
- ASSERT(code[3] == 0x5d); // popq %rbp
- ASSERT(code[4] == 0xc3); // ret
- // Next 8 bytes are nop instructions
- ASSERT((code[5] == 0x90) && (code[6] == 0x90) &&
- (code[7] == 0x90) && (code[8] == 0x90) &&
- (code[9] == 0x90) && (code[10] == 0x90) &&
- (code[11] == 0x90) && (code[12] == 0x90));
- // Smash code with call instruction and relative target address.
+ ASSERT((code[0] == 0x4c) && (code[1] == 0x8b) && (code[2] == 0x7d) &&
+ (code[3] == 0xf0)); // movq r15,[rbp-0x10]
+ ASSERT((code[4] == 0x48) && (code[5] == 0x89) &&
+ (code[6] == 0xec)); // mov rsp, rbp
+ ASSERT(code[7] == 0x5d); // pop rbp
+ ASSERT(code[8] == 0xc3); // ret
+ ASSERT((code[9] == 0x0F) && (code[10] == 0x1F) && (code[11] == 0x40) &&
+ (code[12] == 0x00)); // nops
+ // Smash code with call instruction and relative target address.
uword stub_addr = StubCode::BreakpointReturnEntryPoint();
code[0] = 0x49;
code[1] = 0xbb;
@@ -66,19 +66,13 @@
void CodeBreakpoint::RestoreFunctionReturn() {
uint8_t* code = reinterpret_cast<uint8_t*>(pc_ - 13);
ASSERT((code[0] == 0x49) && (code[1] == 0xbb));
- code[0] = 0x48; // movq %rbp,%rsp
- code[1] = 0x89;
- code[2] = 0xec;
- code[3] = 0x5d; // popq %rbp
- code[4] = 0xc3; // ret
- code[5] = 0x90; // nop
- code[6] = 0x90; // nop
- code[7] = 0x90; // nop
- code[8] = 0x90; // nop
- code[9] = 0x90; // nop
- code[10] = 0x90; // nop
- code[11] = 0x90; // nop
- code[12] = 0x90; // nop
+
+ MemoryRegion code_region(reinterpret_cast<void*>(pc_ - 13), 13);
+ Assembler assembler;
+
+ assembler.ReturnPatchable();
+ assembler.FinalizeInstructions(code_region);
+
CPU::FlushICache(pc_ - 13, 13);
}
diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc
index 1db657e..54fbb01 100644
--- a/runtime/vm/deopt_instructions.cc
+++ b/runtime/vm/deopt_instructions.cc
@@ -449,7 +449,7 @@
: reg_(static_cast<FpuRegister>(reg_as_int)) {}
virtual intptr_t from_index() const { return static_cast<intptr_t>(reg_); }
- virtual DeoptInstr::Kind kind() const { return kFloat32x4FpuRegister; }
+ virtual DeoptInstr::Kind kind() const { return kUint32x4FpuRegister; }
virtual const char* ToCString() const {
return Isolate::Current()->current_zone()->PrintToString(
diff --git a/runtime/vm/disassembler.cc b/runtime/vm/disassembler.cc
index 627f0de..121b01e 100644
--- a/runtime/vm/disassembler.cc
+++ b/runtime/vm/disassembler.cc
@@ -44,12 +44,11 @@
intptr_t human_size,
uword pc) {
uint8_t* pc_ptr = reinterpret_cast<uint8_t*>(pc);
- stream_->OpenObject();
- stream_->PrintProperty("type", "DisassembledInstruction");
- stream_->PrintfProperty("pc", "%p", pc_ptr);
- stream_->PrintProperty("hex", hex_buffer);
- stream_->PrintProperty("human", human_buffer);
- stream_->CloseObject();
+ JSONObject jsobj(&jsarr_);
+ jsobj.AddProperty("type", "DisassembledInstruction");
+ jsobj.AddPropertyF("pc", "%p", pc_ptr);
+ jsobj.AddProperty("hex", hex_buffer);
+ jsobj.AddProperty("human", human_buffer);
}
@@ -68,10 +67,9 @@
p[i] = ' ';
}
}
- stream_->OpenObject();
- stream_->PrintProperty("type", "DisassembledInstructionComment");
- stream_->PrintProperty("comment", p);
- stream_->CloseObject();
+ JSONObject jsobj(&jsarr_);
+ jsobj.AddProperty("type", "DisassembledInstructionComment");
+ jsobj.AddProperty("comment", p);
free(p);
}
diff --git a/runtime/vm/disassembler.h b/runtime/vm/disassembler.h
index 893f0e8..c7772a5 100644
--- a/runtime/vm/disassembler.h
+++ b/runtime/vm/disassembler.h
@@ -13,7 +13,7 @@
// Froward declaration.
class MemoryRegion;
-class JSONStream;
+class JSONArray;
// Disassembly formatter interface, which consumes the
// disassembled instructions in any desired form.
@@ -58,8 +58,8 @@
// Disassemble into a JSONStream.
class DisassembleToJSONStream : public DisassemblyFormatter {
public:
- explicit DisassembleToJSONStream(JSONStream* stream) : DisassemblyFormatter(),
- stream_(stream) { }
+ explicit DisassembleToJSONStream(const JSONArray& jsarr)
+ : DisassemblyFormatter(), jsarr_(jsarr) { }
~DisassembleToJSONStream() { }
virtual void ConsumeInstruction(char* hex_buffer,
@@ -71,7 +71,7 @@
virtual void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
private:
- JSONStream* stream_;
+ const JSONArray& jsarr_;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(DisassembleToJSONStream);
};
diff --git a/runtime/vm/disassembler_x64.cc b/runtime/vm/disassembler_x64.cc
index 7aefeb5..0055f6f 100644
--- a/runtime/vm/disassembler_x64.cc
+++ b/runtime/vm/disassembler_x64.cc
@@ -1442,8 +1442,8 @@
opcode == 0x16 || opcode == 0x51 || opcode == 0x52 ||
opcode == 0x53 || opcode == 0x54 || opcode == 0x56 ||
opcode == 0x57 || opcode == 0x58 || opcode == 0x59 ||
- opcode == 0x5C || opcode == 0x5D || opcode == 0x5E ||
- opcode == 0x5F) {
+ opcode == 0x5A || opcode == 0x5C || opcode == 0x5D ||
+ opcode == 0x5E || opcode == 0x5F) {
const char* mnemonic = NULL;
switch (opcode) {
case 0x12: mnemonic = "movhlps"; break;
@@ -1458,6 +1458,7 @@
case 0x57: mnemonic = "xorps"; break;
case 0x58: mnemonic = "addps"; break;
case 0x59: mnemonic = "mulps"; break;
+ case 0x5A: mnemonic = "cvtsd2ss"; break;
case 0x5C: mnemonic = "subps"; break;
case 0x5D: mnemonic = "minps"; break;
case 0x5E: mnemonic = "divps"; break;
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index 68c2654..04cf4f5 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -323,7 +323,7 @@
isolate->object_store()->error_class());
if (error_class.IsNull()) {
const Library& core_lib = Library::Handle(isolate, Library::CoreLibrary());
- error_class = core_lib.LookupClass(Symbols::Error(), NULL);
+ error_class = core_lib.LookupClass(Symbols::Error());
ASSERT(!error_class.IsNull());
isolate->object_store()->set_error_class(error_class);
}
@@ -483,7 +483,7 @@
const String& cls_name = String::Handle(Symbols::New(class_name));
const Library& core_lib = Library::Handle(Library::CoreLibrary());
// No ambiguity error expected: passing NULL.
- Class& cls = Class::Handle(core_lib.LookupClass(cls_name, NULL));
+ Class& cls = Class::Handle(core_lib.LookupClass(cls_name));
ASSERT(!cls.IsNull());
// There are no parameterized error types, so no need to set type arguments.
return Instance::New(cls);
diff --git a/runtime/vm/find_code_object_test.cc b/runtime/vm/find_code_object_test.cc
index ea82a810..cd390dc 100644
--- a/runtime/vm/find_code_object_test.cc
+++ b/runtime/vm/find_code_object_test.cc
@@ -14,7 +14,7 @@
namespace dart {
TEST_CASE(FindCodeObject) {
-#if defined(TARGET_ARCH_IA32)
+#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
const int kLoopCount = 50000;
#else
const int kLoopCount = 25000;
@@ -53,9 +53,7 @@
source = String::New(scriptChars);
script = Script::New(url, source, RawScript::kScriptTag);
EXPECT(CompilerTest::TestCompileScript(lib, script));
- String& ambiguity_error_msg = String::Handle();
- clsA = lib.LookupClass(String::Handle(Symbols::New("A")),
- &ambiguity_error_msg);
+ clsA = lib.LookupClass(String::Handle(Symbols::New("A")));
EXPECT(!clsA.IsNull());
ClassFinalizer::FinalizePendingClasses();
for (int i = 0; i < kNumFunctions; i++) {
@@ -103,8 +101,7 @@
source = String::New(scriptChars);
script = Script::New(url, source, RawScript::kScriptTag);
EXPECT(CompilerTest::TestCompileScript(lib, script));
- clsB = lib.LookupClass(String::Handle(Symbols::New("B")),
- &ambiguity_error_msg);
+ clsB = lib.LookupClass(String::Handle(Symbols::New("B")));
EXPECT(!clsB.IsNull());
ClassFinalizer::FinalizePendingClasses();
for (int i = 0; i < kNumFunctions; i++) {
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc
index 36c148d..e7f4293 100644
--- a/runtime/vm/flow_graph.cc
+++ b/runtime/vm/flow_graph.cc
@@ -12,6 +12,7 @@
namespace dart {
+DECLARE_FLAG(bool, reorder_basic_blocks);
DECLARE_FLAG(bool, trace_optimization);
DECLARE_FLAG(bool, verify_compiler);
DEFINE_FLAG(bool, optimize_try_catch, true, "Optimization of try-catch");
@@ -41,6 +42,14 @@
}
+GrowableArray<BlockEntryInstr*>* FlowGraph::codegen_block_order(
+ bool is_optimized) {
+ return (is_optimized && FLAG_reorder_basic_blocks)
+ ? &optimized_block_order_
+ : &reverse_postorder_;
+}
+
+
ConstantInstr* FlowGraph::GetConstant(const Object& object) {
// Check if the constant is already in the pool.
GrowableArray<Definition*>* pool = graph_entry_->initial_definitions();
diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h
index 4a84196..34112c6 100644
--- a/runtime/vm/flow_graph.h
+++ b/runtime/vm/flow_graph.h
@@ -78,9 +78,7 @@
const GrowableArray<BlockEntryInstr*>& reverse_postorder() const {
return reverse_postorder_;
}
- GrowableArray<BlockEntryInstr*>* codegen_block_order(bool is_optimized) {
- return is_optimized ? &optimized_block_order_ : &reverse_postorder_;
- }
+ GrowableArray<BlockEntryInstr*>* codegen_block_order(bool is_optimized);
// Iterators.
BlockIterator reverse_postorder_iterator() const {
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 46f98b3..78efd46 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -993,7 +993,7 @@
new ZoneGrowableArray<PushArgumentInstr*>(2);
arguments->Add(push_left);
arguments->Add(push_right);
- const String& name = String::ZoneHandle(Symbols::New(node->Name()));
+ const String& name = String::ZoneHandle(Symbols::New(node->TokenName()));
const intptr_t kNumArgsChecked = 2;
InstanceCallInstr* call = new InstanceCallInstr(node->token_pos(),
name,
@@ -1438,7 +1438,7 @@
ASSERT(Token::IsRelationalOperator(node->kind()));
InstanceCallInstr* comp =
new InstanceCallInstr(node->token_pos(),
- String::ZoneHandle(Symbols::New(node->Name())),
+ String::ZoneHandle(Symbols::New(node->TokenName())),
node->kind(),
arguments,
Object::null_array(),
@@ -1473,7 +1473,7 @@
arguments->Add(push_value);
InstanceCallInstr* call =
new InstanceCallInstr(node->token_pos(),
- String::ZoneHandle(Symbols::New(node->Name())),
+ String::ZoneHandle(Symbols::New(node->TokenName())),
node->kind(),
arguments,
Object::null_array(),
@@ -3061,12 +3061,6 @@
dst_name);
}
- if (!node->field().is_final()) {
- // For now, disable list length guarding on non-final fields by specifying
- // that the field doesn't have a length. See issue #12485.
- node->field().set_guarded_list_length(Field::kNoFixedLength);
- }
-
store_value = Bind(BuildStoreExprTemp(store_value));
GuardFieldInstr* guard =
new GuardFieldInstr(store_value,
@@ -3680,7 +3674,7 @@
// Resolve and call NoSuchMethodError._throwNew.
const Library& core_lib = Library::Handle(Library::CoreLibrary());
const Class& cls = Class::Handle(
- core_lib.LookupClass(Symbols::NoSuchMethodError(), NULL));
+ core_lib.LookupClass(Symbols::NoSuchMethodError()));
ASSERT(!cls.IsNull());
const Function& func = Function::ZoneHandle(
Resolver::ResolveStatic(cls,
diff --git a/runtime/vm/flow_graph_builder.h b/runtime/vm/flow_graph_builder.h
index 4a8457b..e3d01c3 100644
--- a/runtime/vm/flow_graph_builder.h
+++ b/runtime/vm/flow_graph_builder.h
@@ -208,9 +208,11 @@
entry_(NULL),
exit_(NULL) { }
-#define DEFINE_VISIT(type, name) virtual void Visit##type(type* node);
- NODE_LIST(DEFINE_VISIT)
-#undef DEFINE_VISIT
+#define DECLARE_VISIT(BaseName) \
+ virtual void Visit##BaseName##Node(BaseName##Node* node);
+
+ FOR_EACH_NODE(DECLARE_VISIT)
+#undef DECLARE_VISIT
FlowGraphBuilder* owner() const { return owner_; }
intptr_t temp_index() const { return temp_index_; }
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index 834d0ef..fbe3484 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -89,7 +89,7 @@
Isolate::Current()->object_store()->uint32x4_class())),
list_class_(Class::ZoneHandle(
Library::Handle(Library::CoreLibrary()).
- LookupClass(Symbols::List(), NULL))),
+ LookupClass(Symbols::List()))),
parallel_move_resolver_(this),
pending_deoptimization_env_(NULL) {
ASSERT(assembler != NULL);
@@ -180,12 +180,15 @@
void FlowGraphCompiler::CompactBlock(BlockEntryInstr* block) {
BlockInfo* block_info = block_info_[block->postorder_number()];
+ // Break out of cycles in the control flow graph.
if (block_info->is_marked()) {
return;
}
block_info->mark();
if (IsEmptyBlock(block)) {
+ // For empty blocks, record a corresponding nonempty target as their
+ // jump label.
BlockEntryInstr* target = block->next()->AsGoto()->successor();
CompactBlock(target);
block_info->set_jump_label(GetJumpLabel(target));
@@ -194,7 +197,10 @@
void FlowGraphCompiler::CompactBlocks() {
- Label* fallthrough_label = NULL;
+ // This algorithm does not garbage collect blocks in place, but merely
+ // records forwarding label information. In this way it avoids having to
+ // change join and target entries.
+ Label* nonempty_label = NULL;
for (intptr_t i = block_order().length() - 1; i >= 1; --i) {
BlockEntryInstr* block = block_order()[i];
@@ -203,16 +209,19 @@
CompactBlock(block);
}
+ // For nonempty blocks, record the next nonempty block in the block
+ // order. Since no code is emitted for empty blocks, control flow is
+ // eligible to fall through to the next nonempty one.
if (!WasCompacted(block)) {
BlockInfo* block_info = block_info_[block->postorder_number()];
- block_info->set_fallthrough_label(fallthrough_label);
- fallthrough_label = GetJumpLabel(block);
+ block_info->set_next_nonempty_label(nonempty_label);
+ nonempty_label = GetJumpLabel(block);
}
}
ASSERT(block_order()[0]->IsGraphEntry());
BlockInfo* block_info = block_info_[block_order()[0]->postorder_number()];
- block_info->set_fallthrough_label(fallthrough_label);
+ block_info->set_next_nonempty_label(nonempty_label);
}
@@ -289,8 +298,8 @@
bool FlowGraphCompiler::CanFallThroughTo(BlockEntryInstr* block_entry) const {
const intptr_t current_index = current_block()->postorder_number();
- Label* fallthrough_label = block_info_[current_index]->fallthrough_label();
- return fallthrough_label == GetJumpLabel(block_entry);
+ Label* next_nonempty = block_info_[current_index]->next_nonempty_label();
+ return next_nonempty == GetJumpLabel(block_entry);
}
@@ -633,48 +642,6 @@
}
-void FlowGraphCompiler::EmitUnoptimizedStaticCall(
- const Function& target_function,
- const Array& arguments_descriptor,
- intptr_t argument_count,
- intptr_t deopt_id,
- intptr_t token_pos,
- LocationSummary* locs) {
- // TODO(srdjan): Improve performance of function recognition.
- MethodRecognizer::Kind recognized_kind =
- MethodRecognizer::RecognizeKind(target_function);
- int num_args_checked = 0;
- if ((recognized_kind == MethodRecognizer::kMathMin) ||
- (recognized_kind == MethodRecognizer::kMathMax)) {
- num_args_checked = 2;
- }
- const ICData& ic_data = ICData::ZoneHandle(
- ICData::New(parsed_function().function(), // Caller function.
- String::Handle(target_function.name()),
- arguments_descriptor,
- deopt_id,
- num_args_checked)); // No arguments checked.
- ic_data.AddTarget(target_function);
- uword label_address = 0;
- if (ic_data.num_args_tested() == 0) {
- label_address = StubCode::ZeroArgsUnoptimizedStaticCallEntryPoint();
- } else if (ic_data.num_args_tested() == 2) {
- label_address = StubCode::TwoArgsUnoptimizedStaticCallEntryPoint();
- } else {
- UNIMPLEMENTED();
- }
- ExternalLabel target_label("StaticCallICStub", label_address);
- assembler()->LoadObject(ICREG, ic_data);
- GenerateDartCall(deopt_id,
- token_pos,
- &target_label,
- PcDescriptors::kUnoptStaticCall,
- locs);
- assembler()->Drop(argument_count);
-}
-
-
-
void FlowGraphCompiler::GenerateNumberTypeCheck(Register kClassIdReg,
const AbstractType& type,
Label* is_instance_lbl,
diff --git a/runtime/vm/flow_graph_compiler.h b/runtime/vm/flow_graph_compiler.h
index a5369ec..3d174c2 100644
--- a/runtime/vm/flow_graph_compiler.h
+++ b/runtime/vm/flow_graph_compiler.h
@@ -202,33 +202,36 @@
class BlockInfo : public ZoneAllocated {
public:
BlockInfo()
- : jump_label_(&block_label_),
- block_label_(),
- fallthrough_label_(NULL),
+ : block_label_(),
+ jump_label_(&block_label_),
+ next_nonempty_label_(NULL),
is_marked_(false) {}
+ // The label to jump to when control is transferred to this block. For
+ // nonempty blocks it is the label of the block itself. For empty
+ // blocks it is the label of the first nonempty successor block.
Label* jump_label() const { return jump_label_; }
void set_jump_label(Label* label) { jump_label_ = label; }
- // Label of the block that will follow this block in the generated code.
- // Can be NULL if the block is the last block.
- Label* fallthrough_label() const { return fallthrough_label_; }
- void set_fallthrough_label(Label* fallthrough_label) {
- fallthrough_label_ = fallthrough_label;
- }
+ // The label of the first nonempty block after this one in the block
+ // order, or NULL if there is no nonempty block following this one.
+ Label* next_nonempty_label() const { return next_nonempty_label_; }
+ void set_next_nonempty_label(Label* label) { next_nonempty_label_ = label; }
bool WasCompacted() const {
return jump_label_ != &block_label_;
}
+ // Block compaction is recursive. Block info for already-compacted
+ // blocks is marked so as to avoid cycles in the graph.
bool is_marked() const { return is_marked_; }
void mark() { is_marked_ = true; }
private:
- Label* jump_label_;
Label block_label_;
- Label* fallthrough_label_;
+ Label* jump_label_;
+ Label* next_nonempty_label_;
bool is_marked_;
};
@@ -241,7 +244,6 @@
~FlowGraphCompiler();
static bool SupportsUnboxedMints();
- static bool SupportsInlinedTrigonometrics();
// Accessors.
Assembler* assembler() const { return assembler_; }
@@ -383,9 +385,6 @@
Register right,
bool needs_number_check,
intptr_t token_pos);
- // Implement equality: if any of the arguments is null do identity check.
- // Fallthrough calls super equality.
- void EmitSuperEqualityCallPrologue(Register result, Label* skip_call);
void EmitTrySync(Instruction* instr, intptr_t try_index);
diff --git a/runtime/vm/flow_graph_compiler_arm.cc b/runtime/vm/flow_graph_compiler_arm.cc
index b65b83b..0854649 100644
--- a/runtime/vm/flow_graph_compiler_arm.cc
+++ b/runtime/vm/flow_graph_compiler_arm.cc
@@ -43,12 +43,6 @@
}
-// TODO(srdjan): Enable by calling C-functions.
-bool FlowGraphCompiler::SupportsInlinedTrigonometrics() {
- return false;
-}
-
-
RawDeoptInfo* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
DeoptInfoBuilder* builder) {
if (deopt_env_ == NULL) return DeoptInfo::null();
@@ -1391,6 +1385,47 @@
}
+void FlowGraphCompiler::EmitUnoptimizedStaticCall(
+ const Function& target_function,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs) {
+ // TODO(srdjan): Improve performance of function recognition.
+ MethodRecognizer::Kind recognized_kind =
+ MethodRecognizer::RecognizeKind(target_function);
+ int num_args_checked = 0;
+ if ((recognized_kind == MethodRecognizer::kMathMin) ||
+ (recognized_kind == MethodRecognizer::kMathMax)) {
+ num_args_checked = 2;
+ }
+ const ICData& ic_data = ICData::ZoneHandle(
+ ICData::New(parsed_function().function(), // Caller function.
+ String::Handle(target_function.name()),
+ arguments_descriptor,
+ deopt_id,
+ num_args_checked)); // No arguments checked.
+ ic_data.AddTarget(target_function);
+ uword label_address = 0;
+ if (ic_data.num_args_tested() == 0) {
+ label_address = StubCode::ZeroArgsUnoptimizedStaticCallEntryPoint();
+ } else if (ic_data.num_args_tested() == 2) {
+ label_address = StubCode::TwoArgsUnoptimizedStaticCallEntryPoint();
+ } else {
+ UNIMPLEMENTED();
+ }
+ ExternalLabel target_label("StaticCallICStub", label_address);
+ __ LoadObject(R5, ic_data);
+ GenerateDartCall(deopt_id,
+ token_pos,
+ &target_label,
+ PcDescriptors::kUnoptStaticCall,
+ locs);
+ __ Drop(argument_count);
+}
+
+
void FlowGraphCompiler::EmitOptimizedStaticCall(
const Function& function,
const Array& arguments_descriptor,
@@ -1460,31 +1495,6 @@
}
-// Implement equality spec: if any of the arguments is null do identity check.
-// Fallthrough calls super equality.
-void FlowGraphCompiler::EmitSuperEqualityCallPrologue(Register result,
- Label* skip_call) {
- Label check_identity, fall_through;
- __ LoadImmediate(IP, reinterpret_cast<intptr_t>(Object::null()));
- __ ldr(result, Address(SP, 0 * kWordSize)); // Load right operand.
- __ cmp(result, ShifterOperand(IP)); // Is right null?
- __ ldr(result, Address(SP, 1 * kWordSize)); // Load left operand.
- __ b(&check_identity, EQ); // Branch if right (IP) is null; left in result.
- __ cmp(result, ShifterOperand(IP)); // Right is non-null; is left null?
- __ b(&fall_through, NE);
- // Right is non-null, left is null. We could return false, but we save code
- // by falling through with an IP different than null.
- __ mov(IP, ShifterOperand(0));
- __ Bind(&check_identity);
- __ cmp(result, ShifterOperand(IP));
- __ LoadObject(result, Bool::True(), EQ);
- __ LoadObject(result, Bool::False(), NE);
- __ Drop(2);
- __ b(skip_call);
- __ Bind(&fall_through);
-}
-
-
void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) {
// TODO(vegorov): consider saving only caller save (volatile) registers.
const intptr_t fpu_regs_count = locs->live_registers()->fpu_regs_count();
diff --git a/runtime/vm/flow_graph_compiler_ia32.cc b/runtime/vm/flow_graph_compiler_ia32.cc
index 9ca6baa..151ca9f 100644
--- a/runtime/vm/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/flow_graph_compiler_ia32.cc
@@ -47,11 +47,6 @@
}
-bool FlowGraphCompiler::SupportsInlinedTrigonometrics() {
- return true;
-}
-
-
RawDeoptInfo* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
DeoptInfoBuilder* builder) {
if (deopt_env_ == NULL) return DeoptInfo::null();
@@ -1285,6 +1280,47 @@
}
+void FlowGraphCompiler::EmitUnoptimizedStaticCall(
+ const Function& target_function,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs) {
+ // TODO(srdjan): Improve performance of function recognition.
+ MethodRecognizer::Kind recognized_kind =
+ MethodRecognizer::RecognizeKind(target_function);
+ int num_args_checked = 0;
+ if ((recognized_kind == MethodRecognizer::kMathMin) ||
+ (recognized_kind == MethodRecognizer::kMathMax)) {
+ num_args_checked = 2;
+ }
+ const ICData& ic_data = ICData::ZoneHandle(
+ ICData::New(parsed_function().function(), // Caller function.
+ String::Handle(target_function.name()),
+ arguments_descriptor,
+ deopt_id,
+ num_args_checked)); // No arguments checked.
+ ic_data.AddTarget(target_function);
+ uword label_address = 0;
+ if (ic_data.num_args_tested() == 0) {
+ label_address = StubCode::ZeroArgsUnoptimizedStaticCallEntryPoint();
+ } else if (ic_data.num_args_tested() == 2) {
+ label_address = StubCode::TwoArgsUnoptimizedStaticCallEntryPoint();
+ } else {
+ UNIMPLEMENTED();
+ }
+ ExternalLabel target_label("StaticCallICStub", label_address);
+ __ LoadObject(ECX, ic_data);
+ GenerateDartCall(deopt_id,
+ token_pos,
+ &target_label,
+ PcDescriptors::kUnoptStaticCall,
+ locs);
+ __ Drop(argument_count);
+}
+
+
void FlowGraphCompiler::EmitOptimizedInstanceCall(
ExternalLabel* target_label,
const ICData& ic_data,
@@ -1474,34 +1510,6 @@
}
-// Implement equality spec: if any of the arguments is null do identity check.
-// Fallthrough calls super equality.
-void FlowGraphCompiler::EmitSuperEqualityCallPrologue(Register result,
- Label* skip_call) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- Label check_identity, fall_through;
- __ cmpl(Address(ESP, 0 * kWordSize), raw_null);
- __ j(EQUAL, &check_identity, Assembler::kNearJump);
- __ cmpl(Address(ESP, 1 * kWordSize), raw_null);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
-
- __ Bind(&check_identity);
- __ popl(result);
- __ cmpl(result, Address(ESP, 0 * kWordSize));
- Label is_false;
- __ j(NOT_EQUAL, &is_false, Assembler::kNearJump);
- __ LoadObject(result, Bool::True());
- __ Drop(1);
- __ jmp(skip_call);
- __ Bind(&is_false);
- __ LoadObject(result, Bool::False());
- __ Drop(1);
- __ jmp(skip_call);
- __ Bind(&fall_through);
-}
-
-
// This function must be in sync with FlowGraphCompiler::RecordSafepoint.
void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) {
// TODO(vegorov): consider saving only caller save (volatile) registers.
diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc
index f3bbf2f..8e51051 100644
--- a/runtime/vm/flow_graph_compiler_mips.cc
+++ b/runtime/vm/flow_graph_compiler_mips.cc
@@ -43,12 +43,6 @@
}
-// TODO(srdjan): Enable by calling C-functions.
-bool FlowGraphCompiler::SupportsInlinedTrigonometrics() {
- return false;
-}
-
-
RawDeoptInfo* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
DeoptInfoBuilder* builder) {
if (deopt_env_ == NULL) return DeoptInfo::null();
@@ -1438,6 +1432,47 @@
}
+void FlowGraphCompiler::EmitUnoptimizedStaticCall(
+ const Function& target_function,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs) {
+ // TODO(srdjan): Improve performance of function recognition.
+ MethodRecognizer::Kind recognized_kind =
+ MethodRecognizer::RecognizeKind(target_function);
+ int num_args_checked = 0;
+ if ((recognized_kind == MethodRecognizer::kMathMin) ||
+ (recognized_kind == MethodRecognizer::kMathMax)) {
+ num_args_checked = 2;
+ }
+ const ICData& ic_data = ICData::ZoneHandle(
+ ICData::New(parsed_function().function(), // Caller function.
+ String::Handle(target_function.name()),
+ arguments_descriptor,
+ deopt_id,
+ num_args_checked)); // No arguments checked.
+ ic_data.AddTarget(target_function);
+ uword label_address = 0;
+ if (ic_data.num_args_tested() == 0) {
+ label_address = StubCode::ZeroArgsUnoptimizedStaticCallEntryPoint();
+ } else if (ic_data.num_args_tested() == 2) {
+ label_address = StubCode::TwoArgsUnoptimizedStaticCallEntryPoint();
+ } else {
+ UNIMPLEMENTED();
+ }
+ ExternalLabel target_label("StaticCallICStub", label_address);
+ __ LoadObject(S5, ic_data);
+ GenerateDartCall(deopt_id,
+ token_pos,
+ &target_label,
+ PcDescriptors::kUnoptStaticCall,
+ locs);
+ __ Drop(argument_count);
+}
+
+
void FlowGraphCompiler::EmitOptimizedStaticCall(
const Function& function,
const Array& arguments_descriptor,
@@ -1517,32 +1552,6 @@
}
-// Implement equality spec: if any of the arguments is null do identity check.
-// Fallthrough calls super equality.
-void FlowGraphCompiler::EmitSuperEqualityCallPrologue(Register result,
- Label* skip_call) {
- Label check_identity, is_false, fall_through;
- __ TraceSimMsg("SuperEqualityCallPrologue");
- __ lw(result, Address(SP, 0 * kWordSize)); // Load right operand.
- __ lw(CMPRES1, Address(SP, 1 * kWordSize)); // Load left operand.
- __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
- __ beq(result, TMP, &check_identity); // Is right null?
- __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
- __ bne(TMP, CMPRES1, &fall_through); // If right is non-null, check left.
-
- __ Bind(&check_identity);
- __ bne(result, CMPRES1, &is_false);
- __ LoadObject(result, Bool::True());
- __ Drop(2);
- __ b(skip_call);
- __ Bind(&is_false);
- __ LoadObject(result, Bool::False());
- __ Drop(2);
- __ b(skip_call);
- __ Bind(&fall_through);
-}
-
-
void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) {
__ TraceSimMsg("SaveLiveRegisters");
// TODO(vegorov): consider saving only caller save (volatile) registers.
diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
index 3d9b852d..e08d1ab 100644
--- a/runtime/vm/flow_graph_compiler_x64.cc
+++ b/runtime/vm/flow_graph_compiler_x64.cc
@@ -44,11 +44,6 @@
}
-bool FlowGraphCompiler::SupportsInlinedTrigonometrics() {
- return true;
-}
-
-
RawDeoptInfo* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
DeoptInfoBuilder* builder) {
if (deopt_env_ == NULL) return DeoptInfo::null();
@@ -66,10 +61,9 @@
// The real frame starts here.
builder->MarkFrameStart();
- // Callee's PC marker is not used anymore. Pass Function::null() to set to 0.
+ // Current PP, FP, and PC.
+ builder->AddPp(current->function(), slot_ix++);
builder->AddPcMarker(Function::Handle(), slot_ix++);
-
- // Current FP and PC.
builder->AddCallerFp(slot_ix++);
builder->AddReturnAddress(current->function(), deopt_id(), slot_ix++);
@@ -85,13 +79,14 @@
builder->AddCopy(current->ValueAt(i), current->LocationAt(i), slot_ix++);
}
- // Current PC marker and caller FP.
- builder->AddPcMarker(current->function(), slot_ix++);
- builder->AddCallerFp(slot_ix++);
-
Environment* previous = current;
current = current->outer();
while (current != NULL) {
+ // PP, FP, and PC.
+ builder->AddPp(current->function(), slot_ix++);
+ builder->AddPcMarker(previous->function(), slot_ix++);
+ builder->AddCallerFp(slot_ix++);
+
// For any outer environment the deopt id is that of the call instruction
// which is recorded in the outer environment.
builder->AddReturnAddress(current->function(),
@@ -115,10 +110,6 @@
slot_ix++);
}
- // PC marker and caller FP.
- builder->AddPcMarker(current->function(), slot_ix++);
- builder->AddCallerFp(slot_ix++);
-
// Iterate on the outer environment.
previous = current;
current = current->outer();
@@ -126,7 +117,11 @@
// The previous pointer is now the outermost environment.
ASSERT(previous != NULL);
- // For the outermost environment, set caller PC.
+ // For the outermost environment, set caller PC, caller PP, and caller FP.
+ builder->AddCallerPp(slot_ix++);
+ // PC marker.
+ builder->AddPcMarker(previous->function(), slot_ix++);
+ builder->AddCallerFp(slot_ix++);
builder->AddCallerPc(slot_ix++);
// For the outermost environment, set the incoming arguments.
@@ -151,7 +146,7 @@
ASSERT(deopt_env() != NULL);
- __ call(&StubCode::DeoptimizeLabel());
+ __ Call(&StubCode::DeoptimizeLabel(), PP);
set_pc_offset(assem->CodeSize());
__ int3();
#undef __
@@ -165,10 +160,8 @@
void FlowGraphCompiler::GenerateBoolToJump(Register bool_register,
Label* is_true,
Label* is_false) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label fall_through;
- __ cmpq(bool_register, raw_null);
+ __ CompareObject(bool_register, Object::null_object());
__ j(EQUAL, &fall_through, Assembler::kNearJump);
__ CompareObject(bool_register, Bool::True());
__ j(EQUAL, is_true);
@@ -187,22 +180,20 @@
Label* is_not_instance_lbl) {
const SubtypeTestCache& type_test_cache =
SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ LoadObject(temp_reg, type_test_cache);
+ __ LoadObject(temp_reg, type_test_cache, PP);
__ pushq(temp_reg); // Subtype test cache.
__ pushq(instance_reg); // Instance.
if (test_kind == kTestTypeOneArg) {
ASSERT(type_arguments_reg == kNoRegister);
- __ pushq(raw_null);
- __ call(&StubCode::Subtype1TestCacheLabel());
+ __ PushObject(Object::null_object());
+ __ Call(&StubCode::Subtype1TestCacheLabel(), PP);
} else if (test_kind == kTestTypeTwoArgs) {
ASSERT(type_arguments_reg == kNoRegister);
- __ pushq(raw_null);
- __ call(&StubCode::Subtype2TestCacheLabel());
+ __ PushObject(Object::null_object());
+ __ Call(&StubCode::Subtype2TestCacheLabel(), PP);
} else if (test_kind == kTestTypeThreeArgs) {
__ pushq(type_arguments_reg);
- __ call(&StubCode::Subtype3TestCacheLabel());
+ __ Call(&StubCode::Subtype3TestCacheLabel(), PP);
} else {
UNREACHABLE();
}
@@ -347,11 +338,9 @@
}
if (type.IsFunctionType()) {
// Check if instance is a closure.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
__ LoadClassById(R13, kClassIdReg);
__ movq(R13, FieldAddress(R13, Class::signature_function_offset()));
- __ cmpq(R13, raw_null);
+ __ CompareObject(R13, Object::null_object());
__ j(NOT_EQUAL, is_instance_lbl);
}
// Custom checking for numbers (Smi, Mint, Bigint and Double).
@@ -414,15 +403,13 @@
__ Comment("UninstantiatedTypeTest");
ASSERT(!type.IsInstantiated());
// Skip check if destination is a dynamic type.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
if (type.IsTypeParameter()) {
const TypeParameter& type_param = TypeParameter::Cast(type);
// Load instantiator (or null) and instantiator type arguments on stack.
__ movq(RDX, Address(RSP, 0)); // Get instantiator type arguments.
// RDX: instantiator type arguments.
// Check if type argument is dynamic.
- __ cmpq(RDX, raw_null);
+ __ CompareObject(RDX, Object::null_object());
__ j(EQUAL, is_instance_lbl);
// Can handle only type arguments that are instances of TypeArguments.
// (runtime checks canonicalize type arguments).
@@ -435,7 +422,7 @@
// Check if type argument is dynamic.
__ CompareObject(RDI, Type::ZoneHandle(Type::DynamicType()));
__ j(EQUAL, is_instance_lbl);
- __ cmpq(RDI, raw_null);
+ __ CompareObject(RDI, Object::null_object());
__ j(EQUAL, is_instance_lbl);
const Type& object_type = Type::ZoneHandle(Type::ObjectType());
__ CompareObject(RDI, object_type);
@@ -575,8 +562,6 @@
LocationSummary* locs) {
ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded());
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label is_instance, is_not_instance;
__ pushq(RCX); // Store instantiator on stack.
__ pushq(RDX); // Store instantiator type arguments.
@@ -590,7 +575,7 @@
// We can only inline this null check if the type is instantiated at compile
// time, since an uninstantiated type at compile time could be Object or
// dynamic at run time.
- __ cmpq(RAX, raw_null);
+ __ CompareObject(RAX, Object::null_object());
__ j(EQUAL, &is_not_instance);
}
@@ -610,7 +595,7 @@
__ PushObject(type); // Push the type.
__ pushq(RCX); // TODO(srdjan): Pass instantiator instead of null.
__ pushq(RDX); // Instantiator type arguments.
- __ LoadObject(RAX, test_cache);
+ __ LoadObject(RAX, test_cache, PP);
__ pushq(RAX);
GenerateCallRuntime(token_pos,
deopt_id,
@@ -622,21 +607,21 @@
__ Drop(5);
if (negate_result) {
__ popq(RDX);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ cmpq(RDX, RAX);
__ j(NOT_EQUAL, &done, Assembler::kNearJump);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
} else {
__ popq(RAX);
}
__ jmp(&done, Assembler::kNearJump);
}
__ Bind(&is_not_instance);
- __ LoadObject(RAX, Bool::Get(negate_result));
+ __ LoadObject(RAX, Bool::Get(negate_result), PP);
__ jmp(&done, Assembler::kNearJump);
__ Bind(&is_instance);
- __ LoadObject(RAX, Bool::Get(!negate_result));
+ __ LoadObject(RAX, Bool::Get(!negate_result), PP);
__ Bind(&done);
__ popq(RDX); // Remove pushed instantiator type arguments.
__ popq(RCX); // Remove pushed instantiator.
@@ -669,10 +654,8 @@
__ pushq(RCX); // Store instantiator.
__ pushq(RDX); // Store instantiator type arguments.
// A null object is always assignable and is returned as result.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label is_assignable, runtime_call;
- __ cmpq(RAX, raw_null);
+ __ CompareObject(RAX, Object::null_object());
__ j(EQUAL, &is_assignable);
if (!FLAG_eliminate_type_checks || dst_type.IsMalformed()) {
@@ -725,7 +708,7 @@
__ pushq(RCX); // Instantiator.
__ pushq(RDX); // Instantiator type arguments.
__ PushObject(dst_name); // Push the name of the destination.
- __ LoadObject(RAX, test_cache);
+ __ LoadObject(RAX, test_cache, PP);
__ pushq(RAX);
GenerateCallRuntime(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
// Pop the parameters supplied to the runtime entry. The result of the
@@ -770,7 +753,7 @@
__ pushq(RAX);
*push_emitted = true;
}
- __ LoadObject(RAX, loc.constant());
+ __ LoadObject(RAX, loc.constant(), PP);
__ movq(dest, RAX);
} else if (loc.IsRegister()) {
if (*push_emitted && loc.reg() == RAX) {
@@ -900,8 +883,6 @@
__ j(POSITIVE, &loop, Assembler::kNearJump);
// Copy or initialize optional named arguments.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label all_arguments_processed;
#ifdef DEBUG
const bool check_correct_named_args = true;
@@ -963,7 +944,7 @@
const Object& value = Object::ZoneHandle(
parsed_function().default_parameter_values().At(
param_pos - num_fixed_params));
- __ LoadObject(RAX, value);
+ __ LoadObject(RAX, value, PP);
__ Bind(&assign_optional_parameter);
// Assign RAX to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
@@ -978,7 +959,8 @@
if (check_correct_named_args) {
// Check that RDI now points to the null terminator in the arguments
// descriptor.
- __ cmpq(Address(RDI, 0), raw_null);
+ __ LoadObject(TMP, Object::null_object(), PP);
+ __ cmpq(Address(RDI, 0), TMP);
__ j(EQUAL, &all_arguments_processed, Assembler::kNearJump);
}
} else {
@@ -997,7 +979,7 @@
// Load RAX with default argument.
const Object& value = Object::ZoneHandle(
parsed_function().default_parameter_values().At(i));
- __ LoadObject(RAX, value);
+ __ LoadObject(RAX, value, PP);
// Assign RAX to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
@@ -1023,8 +1005,8 @@
const ICData& ic_data = ICData::ZoneHandle(
ICData::New(function, Symbols::Call(), Object::empty_array(),
Isolate::kNoDeoptId, kNumArgsChecked));
- __ LoadObject(RBX, ic_data);
- __ LeaveFrame(); // The arguments are still on the stack.
+ __ LoadObject(RBX, ic_data, PP);
+ __ LeaveFrameWithPP(); // The arguments are still on the stack.
__ jmp(&StubCode::CallNoSuchMethodFunctionLabel());
// The noSuchMethod call may return to the caller, but not here.
__ int3();
@@ -1042,12 +1024,13 @@
// R10 : arguments descriptor array.
__ movq(RCX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
__ SmiUntag(RCX);
+ __ LoadObject(R12, Object::null_object(), PP);
Label null_args_loop, null_args_loop_condition;
__ jmp(&null_args_loop_condition, Assembler::kNearJump);
const Address original_argument_addr(
RBP, RCX, TIMES_8, (kParamEndSlotFromFp + 1) * kWordSize);
__ Bind(&null_args_loop);
- __ movq(original_argument_addr, raw_null);
+ __ movq(original_argument_addr, R12);
__ Bind(&null_args_loop_condition);
__ decq(RCX);
__ j(POSITIVE, &null_args_loop, Assembler::kNearJump);
@@ -1072,20 +1055,43 @@
__ movq(RAX, Address(RSP, 2 * kWordSize)); // Receiver.
__ movq(RBX, Address(RSP, 1 * kWordSize)); // Value.
__ StoreIntoObject(RAX, FieldAddress(RAX, offset), RBX);
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ movq(RAX, raw_null);
+ __ LoadObject(RAX, Object::null_object(), PP);
__ ret();
}
void FlowGraphCompiler::EmitFrameEntry() {
const Function& function = parsed_function().function();
+ Register new_pp = kNoRegister;
+ Register new_pc = kNoRegister;
if (CanOptimizeFunction() &&
function.is_optimizable() &&
(!is_optimizing() || may_reoptimize())) {
const Register function_reg = RDI;
- __ LoadObject(function_reg, function);
+ new_pp = R13;
+ new_pc = R12;
+
+ Label next;
+ __ nop(4); // Need a fixed size sequence on frame entry.
+ __ call(&next);
+ __ Bind(&next);
+
+ const intptr_t object_pool_pc_dist =
+ Instructions::HeaderSize() - Instructions::object_pool_offset() +
+ __ CodeSize();
+ const intptr_t offset =
+ Assembler::kEntryPointToPcMarkerOffset - __ CodeSize();
+ __ popq(new_pc);
+ if (offset != 0) {
+ __ addq(new_pc, Immediate(offset));
+ }
+
+ // Load callee's pool pointer.
+ __ movq(new_pp, Address(new_pc, -object_pool_pc_dist - offset));
+
+ // Load function object using the callee's pool pointer.
+ __ LoadObject(function_reg, function, new_pp);
+
// Patch point is after the eventually inlined function object.
AddCurrentDescriptor(PcDescriptors::kEntryPatch,
Isolate::kNoDeoptId,
@@ -1101,8 +1107,30 @@
Immediate(FLAG_optimization_counter_threshold));
}
ASSERT(function_reg == RDI);
- __ j(GREATER_EQUAL, &StubCode::OptimizeFunctionLabel());
+ __ J(GREATER_EQUAL, &StubCode::OptimizeFunctionLabel(), R13);
} else if (!flow_graph().IsCompiledForOsr()) {
+ // We have to load the PP here too because a load of an external label
+ // may be patched at the AddCurrentDescriptor below.
+ new_pp = R13;
+ new_pc = R12;
+
+ Label next;
+ __ nop(4); // Need a fixed size sequence on frame entry.
+ __ call(&next);
+ __ Bind(&next);
+
+ const intptr_t object_pool_pc_dist =
+ Instructions::HeaderSize() - Instructions::object_pool_offset() +
+ __ CodeSize();
+ const intptr_t offset =
+ Assembler::kEntryPointToPcMarkerOffset - __ CodeSize();
+ __ popq(new_pc);
+ if (offset != 0) {
+ __ addq(new_pc, Immediate(offset));
+ }
+
+ // Load callee's pool pointer.
+ __ movq(new_pp, Address(new_pc, -object_pool_pc_dist - offset));
AddCurrentDescriptor(PcDescriptors::kEntryPatch,
Isolate::kNoDeoptId,
0); // No token position.
@@ -1113,10 +1141,10 @@
- flow_graph().num_stack_locals()
- flow_graph().num_copied_params();
ASSERT(extra_slots >= 0);
- __ EnterOsrFrame(extra_slots * kWordSize);
+ __ EnterOsrFrame(extra_slots * kWordSize, new_pp, new_pc);
} else {
ASSERT(StackSize() >= 0);
- __ EnterDartFrame(StackSize() * kWordSize);
+ __ EnterDartFrameWithInfo(StackSize() * kWordSize, new_pp, new_pc);
}
}
@@ -1170,8 +1198,8 @@
const ICData& ic_data = ICData::ZoneHandle(
ICData::New(function, name, Object::empty_array(),
Isolate::kNoDeoptId, kNumArgsChecked));
- __ LoadObject(RBX, ic_data);
- __ LeaveFrame(); // The arguments are still on the stack.
+ __ LoadObject(RBX, ic_data, PP);
+ __ LeaveFrameWithPP(); // The arguments are still on the stack.
__ jmp(&StubCode::CallNoSuchMethodFunctionLabel());
// The noSuchMethod call may return to the caller, but not here.
__ int3();
@@ -1189,9 +1217,7 @@
if (!is_optimizing() && (num_locals > 0)) {
__ Comment("Initialize spill slots");
const intptr_t slot_base = parsed_function().first_stack_local_index();
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ movq(RAX, raw_null);
+ __ LoadObject(RAX, Object::null_object(), PP);
for (intptr_t i = 0; i < num_locals; ++i) {
// Subtract index i (locals lie at lower addresses than RBP).
__ movq(Address(RBP, (slot_base - i) * kWordSize), RAX);
@@ -1218,11 +1244,15 @@
AddCurrentDescriptor(PcDescriptors::kPatchCode,
Isolate::kNoDeoptId,
0); // No token position.
- __ jmp(&StubCode::FixCallersTargetLabel());
+ // This is patched up to a point in FrameEntry where the PP for the
+ // current function is in R13 instead of PP.
+ __ JmpPatchable(&StubCode::FixCallersTargetLabel(), R13);
+
+ // TOOD(zra): Is this descriptor used?
AddCurrentDescriptor(PcDescriptors::kLazyDeoptJump,
Isolate::kNoDeoptId,
0); // No token position.
- __ jmp(&StubCode::DeoptimizeLazyLabel());
+ __ Jmp(&StubCode::DeoptimizeLazyLabel(), PP);
}
@@ -1230,7 +1260,7 @@
const ExternalLabel* label,
PcDescriptors::Kind kind,
LocationSummary* locs) {
- __ call(label);
+ __ Call(label, PP);
AddCurrentDescriptor(kind, Isolate::kNoDeoptId, token_pos);
RecordSafepoint(locs);
}
@@ -1241,7 +1271,7 @@
const ExternalLabel* label,
PcDescriptors::Kind kind,
LocationSummary* locs) {
- __ call(label);
+ __ CallPatchable(label);
AddCurrentDescriptor(kind, deopt_id, token_pos);
RecordSafepoint(locs);
// Marks either the continuation point in unoptimized code or the
@@ -1280,6 +1310,47 @@
}
+void FlowGraphCompiler::EmitUnoptimizedStaticCall(
+ const Function& target_function,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs) {
+ // TODO(srdjan): Improve performance of function recognition.
+ MethodRecognizer::Kind recognized_kind =
+ MethodRecognizer::RecognizeKind(target_function);
+ int num_args_checked = 0;
+ if ((recognized_kind == MethodRecognizer::kMathMin) ||
+ (recognized_kind == MethodRecognizer::kMathMax)) {
+ num_args_checked = 2;
+ }
+ const ICData& ic_data = ICData::ZoneHandle(
+ ICData::New(parsed_function().function(), // Caller function.
+ String::Handle(target_function.name()),
+ arguments_descriptor,
+ deopt_id,
+ num_args_checked)); // No arguments checked.
+ ic_data.AddTarget(target_function);
+ uword label_address = 0;
+ if (ic_data.num_args_tested() == 0) {
+ label_address = StubCode::ZeroArgsUnoptimizedStaticCallEntryPoint();
+ } else if (ic_data.num_args_tested() == 2) {
+ label_address = StubCode::TwoArgsUnoptimizedStaticCallEntryPoint();
+ } else {
+ UNIMPLEMENTED();
+ }
+ ExternalLabel target_label("StaticCallICStub", label_address);
+ __ LoadObject(RBX, ic_data, PP);
+ GenerateDartCall(deopt_id,
+ token_pos,
+ &target_label,
+ PcDescriptors::kUnoptStaticCall,
+ locs);
+ __ Drop(argument_count);
+}
+
+
void FlowGraphCompiler::EmitOptimizedInstanceCall(
ExternalLabel* target_label,
const ICData& ic_data,
@@ -1293,8 +1364,8 @@
// top-level function (parsed_function().function()) which could be
// reoptimized and which counter needs to be incremented.
// Pass the function explicitly, it is used in IC stub.
- __ LoadObject(RDI, parsed_function().function());
- __ LoadObject(RBX, ic_data);
+ __ LoadObject(RDI, parsed_function().function(), PP);
+ __ LoadObject(RBX, ic_data, PP);
GenerateDartCall(deopt_id,
token_pos,
target_label,
@@ -1310,7 +1381,7 @@
intptr_t deopt_id,
intptr_t token_pos,
LocationSummary* locs) {
- __ LoadObject(RBX, ic_data);
+ __ LoadObject(RBX, ic_data, PP);
GenerateDartCall(deopt_id,
token_pos,
target_label,
@@ -1346,7 +1417,7 @@
// RAX: class ID of the receiver (smi).
__ Bind(&load_cache);
- __ LoadObject(RBX, cache);
+ __ LoadObject(RBX, cache, PP);
__ movq(RDI, FieldAddress(RBX, MegamorphicCache::buckets_offset()));
__ movq(RBX, FieldAddress(RBX, MegamorphicCache::mask_offset()));
// RDI: cache buckets array.
@@ -1378,8 +1449,8 @@
__ movq(RAX, FieldAddress(RDI, RCX, TIMES_8, base + kWordSize));
__ movq(RAX, FieldAddress(RAX, Function::code_offset()));
__ movq(RAX, FieldAddress(RAX, Code::instructions_offset()));
- __ LoadObject(RBX, ic_data);
- __ LoadObject(R10, arguments_descriptor);
+ __ LoadObject(RBX, ic_data, PP);
+ __ LoadObject(R10, arguments_descriptor, PP);
__ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
__ call(RAX);
AddCurrentDescriptor(PcDescriptors::kOther, Isolate::kNoDeoptId, token_pos);
@@ -1396,7 +1467,7 @@
intptr_t deopt_id,
intptr_t token_pos,
LocationSummary* locs) {
- __ LoadObject(R10, arguments_descriptor);
+ __ LoadObject(R10, arguments_descriptor, PP);
// Do not use the code from the function, but let the code be patched so that
// we can record the outgoing edges to other code.
GenerateDartCall(deopt_id,
@@ -1429,9 +1500,9 @@
__ pushq(reg);
__ PushObject(obj);
if (is_optimizing()) {
- __ call(&StubCode::OptimizedIdenticalWithNumberCheckLabel());
+ __ CallPatchable(&StubCode::OptimizedIdenticalWithNumberCheckLabel());
} else {
- __ call(&StubCode::UnoptimizedIdenticalWithNumberCheckLabel());
+ __ CallPatchable(&StubCode::UnoptimizedIdenticalWithNumberCheckLabel());
}
AddCurrentDescriptor(PcDescriptors::kRuntimeCall,
Isolate::kNoDeoptId,
@@ -1453,9 +1524,9 @@
__ pushq(left);
__ pushq(right);
if (is_optimizing()) {
- __ call(&StubCode::OptimizedIdenticalWithNumberCheckLabel());
+ __ CallPatchable(&StubCode::OptimizedIdenticalWithNumberCheckLabel());
} else {
- __ call(&StubCode::UnoptimizedIdenticalWithNumberCheckLabel());
+ __ CallPatchable(&StubCode::UnoptimizedIdenticalWithNumberCheckLabel());
}
AddCurrentDescriptor(PcDescriptors::kRuntimeCall,
Isolate::kNoDeoptId,
@@ -1469,34 +1540,6 @@
}
-// Implement equality spec: if any of the arguments is null do identity check.
-// Fallthrough calls super equality.
-void FlowGraphCompiler::EmitSuperEqualityCallPrologue(Register result,
- Label* skip_call) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- Label check_identity, fall_through;
- __ cmpq(Address(RSP, 0 * kWordSize), raw_null);
- __ j(EQUAL, &check_identity, Assembler::kNearJump);
- __ cmpq(Address(RSP, 1 * kWordSize), raw_null);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
-
- __ Bind(&check_identity);
- __ popq(result);
- __ cmpq(result, Address(RSP, 0 * kWordSize));
- Label is_false;
- __ j(NOT_EQUAL, &is_false, Assembler::kNearJump);
- __ LoadObject(result, Bool::True());
- __ Drop(1);
- __ jmp(skip_call);
- __ Bind(&is_false);
- __ LoadObject(result, Bool::False());
- __ Drop(1);
- __ jmp(skip_call);
- __ Bind(&fall_through);
-}
-
-
// This function must be in sync with FlowGraphCompiler::RecordSafepoint.
void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) {
// TODO(vegorov): consider saving only caller save (volatile) registers.
@@ -1573,7 +1616,7 @@
const Array& arguments_descriptor =
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names));
- __ LoadObject(R10, arguments_descriptor);
+ __ LoadObject(R10, arguments_descriptor, PP);
for (intptr_t i = 0; i < len; i++) {
const bool is_last_check = (i == (len - 1));
Label next_test;
@@ -1615,7 +1658,6 @@
}
-
void FlowGraphCompiler::EmitDoubleCompareBool(Condition true_condition,
FpuRegister left,
FpuRegister right,
@@ -1625,10 +1667,10 @@
assembler()->j(PARITY_EVEN, &is_false, Assembler::kNearJump); // NaN false;
assembler()->j(true_condition, &is_true, Assembler::kNearJump);
assembler()->Bind(&is_false);
- assembler()->LoadObject(result, Bool::False());
+ assembler()->LoadObject(result, Bool::False(), PP);
assembler()->jmp(&done);
assembler()->Bind(&is_true);
- assembler()->LoadObject(result, Bool::True());
+ assembler()->LoadObject(result, Bool::True(), PP);
assembler()->Bind(&done);
}
@@ -1749,7 +1791,7 @@
if (constant.IsSmi() && (Smi::Cast(constant).Value() == 0)) {
__ xorq(destination.reg(), destination.reg());
} else {
- __ LoadObject(destination.reg(), constant);
+ __ LoadObject(destination.reg(), constant, PP);
}
} else {
ASSERT(destination.IsStackSlot());
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index 6ecb193..692c0f8 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -77,9 +77,12 @@
class ChildrenVisitor : public AstNodeVisitor {
public:
ChildrenVisitor() { }
-#define DEFINE_VISIT(type, name) \
- virtual void Visit##type(type* node) { node->VisitChildren(this); }
- NODE_LIST(DEFINE_VISIT);
+#define DEFINE_VISIT(BaseName) \
+ virtual void Visit##BaseName##Node(BaseName##Node* node) { \
+ node->VisitChildren(this); \
+ }
+
+ FOR_EACH_NODE(DEFINE_VISIT);
#undef DEFINE_VISIT
};
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 41385e4..53f4450 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -770,51 +770,24 @@
call->env(),
Definition::kEffect);
- bool emit_bounds_check = true;
- // Get the field for the array.
- const Field* field = NULL;
- if ((*array)->IsLoadField()) {
- LoadFieldInstr* load_field_instr = (*array)->AsLoadField();
- field = load_field_instr->field();
- }
- // Extract the guarded array length.
- intptr_t guarded_array_length = -1;
- if (field != NULL) {
- if (field->guarded_list_length() >= 0) {
- guarded_array_length = field->guarded_list_length();
- }
- }
- Definition* i = *index;
- // Check if we can skip emitting the bounds check.
- if (i->IsConstant() && guarded_array_length >= 0) {
- ConstantInstr* constant = i->AsConstant();
- ASSERT(constant != NULL);
- intptr_t ci = Smi::Cast(constant->value()).Value();
- if (ci < guarded_array_length) {
- emit_bounds_check = false;
- }
- }
-
- if (emit_bounds_check) {
- // Insert array length load and bounds check.
- const bool is_immutable =
- CheckArrayBoundInstr::IsFixedLengthArrayType(class_id);
- LoadFieldInstr* length =
- new LoadFieldInstr(new Value(*array),
- CheckArrayBoundInstr::LengthOffsetFor(class_id),
- Type::ZoneHandle(Type::SmiType()),
- is_immutable);
- length->set_result_cid(kSmiCid);
- length->set_recognized_kind(
- LoadFieldInstr::RecognizedKindFromArrayCid(class_id));
- InsertBefore(call, length, NULL, Definition::kValue);
- InsertBefore(call,
- new CheckArrayBoundInstr(new Value(length),
- new Value(*index),
- call->deopt_id()),
- call->env(),
- Definition::kEffect);
- }
+ // Insert array length load and bounds check.
+ const bool is_immutable =
+ CheckArrayBoundInstr::IsFixedLengthArrayType(class_id);
+ LoadFieldInstr* length =
+ new LoadFieldInstr(new Value(*array),
+ CheckArrayBoundInstr::LengthOffsetFor(class_id),
+ Type::ZoneHandle(Type::SmiType()),
+ is_immutable);
+ length->set_result_cid(kSmiCid);
+ length->set_recognized_kind(
+ LoadFieldInstr::RecognizedKindFromArrayCid(class_id));
+ InsertBefore(call, length, NULL, Definition::kValue);
+ InsertBefore(call,
+ new CheckArrayBoundInstr(new Value(length),
+ new Value(*index),
+ call->deopt_id()),
+ call->env(),
+ Definition::kEffect);
if (class_id == kGrowableObjectArrayCid) {
@@ -1732,8 +1705,8 @@
void FlowGraphOptimizer::ReplaceWithMathCFunction(
- InstanceCallInstr* call,
- MethodRecognizer::Kind recognized_kind) {
+ InstanceCallInstr* call,
+ MethodRecognizer::Kind recognized_kind) {
AddReceiverCheck(call);
ZoneGrowableArray<Value*>* args =
new ZoneGrowableArray<Value*>(call->ArgumentCount());
@@ -2735,14 +2708,11 @@
if ((recognized_kind == MethodRecognizer::kMathSqrt) ||
(recognized_kind == MethodRecognizer::kMathSin) ||
(recognized_kind == MethodRecognizer::kMathCos)) {
- if ((recognized_kind == MethodRecognizer::kMathSqrt) ||
- FlowGraphCompiler::SupportsInlinedTrigonometrics()) {
- MathUnaryInstr* math_unary =
- new MathUnaryInstr(recognized_kind,
- new Value(call->ArgumentAt(0)),
- call->deopt_id());
- ReplaceCall(call, math_unary);
- }
+ MathUnaryInstr* math_unary =
+ new MathUnaryInstr(recognized_kind,
+ new Value(call->ArgumentAt(0)),
+ call->deopt_id());
+ ReplaceCall(call, math_unary);
} else if ((recognized_kind == MethodRecognizer::kFloat32x4Zero) ||
(recognized_kind == MethodRecognizer::kFloat32x4Splat) ||
(recognized_kind == MethodRecognizer::kFloat32x4Constructor)) {
diff --git a/runtime/vm/guard_field_test.cc b/runtime/vm/guard_field_test.cc
new file mode 100644
index 0000000..193d4b7
--- /dev/null
+++ b/runtime/vm/guard_field_test.cc
@@ -0,0 +1,272 @@
+// Copyright (c) 2013, 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.
+
+#include "vm/dart_api_impl.h"
+#include "vm/dart_api_state.h"
+#include "vm/intermediate_language.h"
+#include "vm/object.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+RawField* LookupField(Dart_Handle library, const char* class_name,
+ const char* field_name) {
+ RawLibrary* raw_library = Library::RawCast(Api::UnwrapHandle(library));
+ Library& lib = Library::ZoneHandle(raw_library);
+ const String& classname = String::Handle(Symbols::New(class_name));
+ Class& cls = Class::Handle(lib.LookupClass(classname));
+ EXPECT(!cls.IsNull()); // No ambiguity error expected.
+
+ String& fieldname = String::Handle(String::New(field_name));
+ Field& field = Field::ZoneHandle(cls.LookupInstanceField(fieldname));
+ EXPECT(!field.IsNull());
+ return field.raw();
+}
+
+
+TEST_CASE(GuardFieldSimpleTest) {
+ const char* script_chars =
+ "class A {\n"
+ " var f1 = 3.0;\n"
+ " var f2 = 3;\n"
+ " var f3 = new List(4);\n"
+ " foo() {\n"
+ " f1 = f1 + f1;\n"
+ " }\n"
+ " bar() {\n"
+ " f2 = null;\n"
+ " f2 = 3.0;\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runFoo() {\n"
+ " var a = new A();\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " a.foo();\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runBar() {\n"
+ " var a = new A();\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " a.bar();\n"
+ " }\n"
+ "}\n"
+ "main() {\n"
+ " runFoo();\n"
+ " runBar();\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(script_chars, NULL);
+ Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
+ EXPECT_VALID(result);
+ Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1"));
+ Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2"));
+ Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3"));
+ const intptr_t no_length = Field::kNoFixedLength;
+ EXPECT_EQ(no_length, f1.guarded_list_length());
+ EXPECT_EQ(kDoubleCid, f1.guarded_cid());
+ EXPECT_EQ(false, f1.is_nullable());
+ EXPECT_EQ(no_length, f2.guarded_list_length());
+ EXPECT_EQ(kDynamicCid, f2.guarded_cid());
+ EXPECT_EQ(true, f2.is_nullable());
+ EXPECT_EQ(no_length, f3.guarded_list_length());
+}
+
+
+TEST_CASE(GuardFieldFinalListTest) {
+ const char* script_chars =
+ "class A {\n"
+ " var f1 = 3.0;\n"
+ " var f2 = 3;\n"
+ " final f3 = new List(4);\n"
+ " foo() {\n"
+ " f1 = f1 + f1;\n"
+ " }\n"
+ " bar() {\n"
+ " f2 = null;\n"
+ " f2 = 3.0;\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runFoo() {\n"
+ " var a = new A();\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " a.foo();\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runBar() {\n"
+ " var a = new A();\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " a.bar();\n"
+ " }\n"
+ "}\n"
+ "main() {\n"
+ " runFoo();\n"
+ " runBar();\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(script_chars, NULL);
+ Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
+ EXPECT_VALID(result);
+ Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1"));
+ Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2"));
+ Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3"));
+ const intptr_t no_length = Field::kNoFixedLength;
+ EXPECT_EQ(no_length, f1.guarded_list_length());
+ EXPECT_EQ(kDoubleCid, f1.guarded_cid());
+ EXPECT_EQ(false, f1.is_nullable());
+ EXPECT_EQ(no_length, f2.guarded_list_length());
+ EXPECT_EQ(kDynamicCid, f2.guarded_cid());
+ EXPECT_EQ(true, f2.is_nullable());
+ EXPECT_EQ(4, f3.guarded_list_length());
+ EXPECT_EQ(kArrayCid, f3.guarded_cid());
+ EXPECT_EQ(false, f3.is_nullable());
+}
+
+
+TEST_CASE(GuardFieldFinalVariableLengthListTest) {
+ const char* script_chars =
+ "class A {\n"
+ " var f1 = 3.0;\n"
+ " var f2 = 3;\n"
+ " final f3 = new List();\n"
+ " foo() {\n"
+ " f1 = f1 + f1;\n"
+ " }\n"
+ " bar() {\n"
+ " f2 = null;\n"
+ " f2 = 3.0;\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runFoo() {\n"
+ " var a = new A();\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " a.foo();\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runBar() {\n"
+ " var a = new A();\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " a.bar();\n"
+ " }\n"
+ "}\n"
+ "main() {\n"
+ " runFoo();\n"
+ " runBar();\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(script_chars, NULL);
+ Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
+ EXPECT_VALID(result);
+ Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1"));
+ Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2"));
+ Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3"));
+ const intptr_t no_length = Field::kNoFixedLength;
+ EXPECT_EQ(no_length, f1.guarded_list_length());
+ EXPECT_EQ(kDoubleCid, f1.guarded_cid());
+ EXPECT_EQ(false, f1.is_nullable());
+ EXPECT_EQ(no_length, f2.guarded_list_length());
+ EXPECT_EQ(kDynamicCid, f2.guarded_cid());
+ EXPECT_EQ(true, f2.is_nullable());
+ EXPECT_EQ(no_length, f3.guarded_list_length());
+ EXPECT_EQ(kGrowableObjectArrayCid, f3.guarded_cid());
+ EXPECT_EQ(false, f3.is_nullable());
+}
+
+
+TEST_CASE(GuardFieldConstructorTest) {
+ const char* script_chars =
+ "import 'dart:typed_data';\n"
+ "class A {\n"
+ " var f1 = 3.0;\n"
+ " var f2 = 3;\n"
+ " final f3;\n"
+ " A(x) : f3 = x;\n"
+ " foo() {\n"
+ " f1 = f1 + f1;\n"
+ " }\n"
+ " bar() {\n"
+ " f2 = null;\n"
+ " f2 = 3.0;\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runFoo() {\n"
+ " var l = new Float32List(5);\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " var a = new A(l);\n"
+ " a.foo();\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runBar() {\n"
+ " var l = new Float32List(5);\n"
+ " var a = new A(l);\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " a.bar();\n"
+ " }\n"
+ "}\n"
+ "main() {\n"
+ " runFoo();\n"
+ " runBar();\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(script_chars, NULL);
+ Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
+ EXPECT_VALID(result);
+ Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1"));
+ Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2"));
+ Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3"));
+ const intptr_t no_length = Field::kNoFixedLength;
+ EXPECT_EQ(no_length, f1.guarded_list_length());
+ EXPECT_EQ(kDoubleCid, f1.guarded_cid());
+ EXPECT_EQ(false, f1.is_nullable());
+ EXPECT_EQ(no_length, f2.guarded_list_length());
+ EXPECT_EQ(kDynamicCid, f2.guarded_cid());
+ EXPECT_EQ(true, f2.is_nullable());
+ const intptr_t length = 5;
+ EXPECT_EQ(length, f3.guarded_list_length());
+ EXPECT_EQ(kTypedDataFloat32ArrayCid, f3.guarded_cid());
+ EXPECT_EQ(false, f3.is_nullable());
+}
+
+
+TEST_CASE(GuardFieldConstructor2Test) {
+ const char* script_chars =
+ "import 'dart:typed_data';\n"
+ "class A {\n"
+ " final f3;\n"
+ " A(x) : f3 = x;\n"
+ " foo() {\n"
+ " }\n"
+ " bar() {\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runFoo() {\n"
+ " var l = new Float32List(5);\n"
+ " for (int i = 0; i < 2000; i++) {\n"
+ " var a = new A(l);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "runBar() {\n"
+ " var l = new Float32List(99);\n"
+ " var a = new A(l);\n"
+ "}\n"
+ "main() {\n"
+ " runFoo();\n"
+ " runBar();\n"
+ "}\n";
+ Dart_Handle lib = TestCase::LoadTestScript(script_chars, NULL);
+ Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
+ EXPECT_VALID(result);
+ Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3"));
+ const intptr_t no_length = Field::kNoFixedLength;
+ EXPECT_EQ(no_length, f3.guarded_list_length());
+ EXPECT_EQ(kTypedDataFloat32ArrayCid, f3.guarded_cid());
+ EXPECT_EQ(false, f3.is_nullable());
+}
+
+} // namespace dart
diff --git a/runtime/vm/heap.h b/runtime/vm/heap.h
index 6dd4419..2502674 100644
--- a/runtime/vm/heap.h
+++ b/runtime/vm/heap.h
@@ -55,7 +55,8 @@
};
// Default allocation sizes in MB for the old gen and code heaps.
- static const intptr_t kHeapSizeInMB = 512;
+ static const intptr_t kHeapSizeInMWords = 128;
+ static const intptr_t kHeapSizeInMB = kHeapSizeInMWords * kWordSize;
static const intptr_t kCodeHeapSizeInMB = 18;
~Heap();
diff --git a/runtime/vm/heap_histogram.cc b/runtime/vm/heap_histogram.cc
index 0ea4139..8f6123d 100644
--- a/runtime/vm/heap_histogram.cc
+++ b/runtime/vm/heap_histogram.cc
@@ -158,44 +158,47 @@
intptr_t size_sum = 0;
intptr_t count_sum = 0;
- stream->OpenObject();
- stream->PrintProperty("type", "ObjectHistogram");
- stream->OpenArray("properties");
- stream->PrintValue("size");
- stream->PrintValue("count");
- stream->CloseArray();
- stream->OpenArray("members");
- for (intptr_t pos = 0; pos < length; pos++) {
- Element* e = array[pos];
- if (e->count_ > 0) {
- cls = isolate_->class_table()->At(e->class_id_);
- str = cls.Name();
- lib = cls.library();
- stream->OpenObject();
- stream->PrintProperty("type", "ObjectHistogramEntry");
- // It should not be possible to overflow here because the total
- // size of the heap is bounded and we are dividing the value
- // by the number of major gcs that have occurred.
- size_sum += (e->size_ / major_gc_count_);
- count_sum += (e->count_ / major_gc_count_);
- stream->PrintProperty("size", e->size_ / major_gc_count_);
- stream->PrintProperty("count", e->count_ / major_gc_count_);
- stream->PrintProperty("name", str.ToCString());
- if (lib.IsNull()) {
- stream->PrintProperty("category", "");
- } else {
- str = lib.url();
- stream->PrintProperty("category", str.ToCString());
+ JSONObject jsobj(stream);
+ jsobj.AddProperty("type", "ObjectHistogram");
+ { // TODO(johnmccutchan): Why is this empty array needed here?
+ JSONArray jsarr(&jsobj, "properties");
+ jsarr.AddValue("size");
+ jsarr.AddValue("count");
+ }
+ {
+ JSONArray jsarr(&jsobj, "members");
+ for (intptr_t pos = 0; pos < length; pos++) {
+ Element* e = array[pos];
+ if (e->count_ > 0) {
+ cls = isolate_->class_table()->At(e->class_id_);
+ str = cls.Name();
+ lib = cls.library();
+ JSONObject jsobj(&jsarr);
+ jsobj.AddProperty("type", "ObjectHistogramEntry");
+ // It should not be possible to overflow here because the total
+ // size of the heap is bounded and we are dividing the value
+ // by the number of major gcs that have occurred.
+ size_sum += (e->size_ / major_gc_count_);
+ count_sum += (e->count_ / major_gc_count_);
+ jsobj.AddProperty("size", e->size_ / major_gc_count_);
+ jsobj.AddProperty("count", e->count_ / major_gc_count_);
+ jsobj.AddProperty("class", cls, true);
+ jsobj.AddProperty("lib", lib, true);
+ // TODO(johnmccutchan): Update the UI to use the class and lib object
+ // properties above instead of name and category strings.
+ jsobj.AddProperty("name", str.ToCString());
+ if (lib.IsNull()) {
+ jsobj.AddProperty("category", "");
+ } else {
+ str = lib.url();
+ jsobj.AddProperty("category", str.ToCString());
+ }
}
- stream->CloseObject();
}
}
- stream->CloseArray();
- stream->OpenObject("sums");
- stream->PrintProperty("size", size_sum);
- stream->PrintProperty("count", count_sum);
- stream->CloseObject();
- stream->CloseObject();
+ JSONObject sums(&jsobj, "sums");
+ sums.AddProperty("size", size_sum);
+ sums.AddProperty("count", count_sum);
// Deallocate the array for sorting.
free(array);
diff --git a/runtime/vm/instructions_arm.cc b/runtime/vm/instructions_arm.cc
index c896e11..4ae5936 100644
--- a/runtime/vm/instructions_arm.cc
+++ b/runtime/vm/instructions_arm.cc
@@ -163,7 +163,7 @@
}
-JumpPattern::JumpPattern(uword pc) : pc_(pc) { }
+JumpPattern::JumpPattern(uword pc, const Code& code) : pc_(pc) { }
bool JumpPattern::IsValid() const {
diff --git a/runtime/vm/instructions_arm.h b/runtime/vm/instructions_arm.h
index 98230ae..59cb259 100644
--- a/runtime/vm/instructions_arm.h
+++ b/runtime/vm/instructions_arm.h
@@ -50,7 +50,7 @@
class JumpPattern : public ValueObject {
public:
- explicit JumpPattern(uword pc);
+ JumpPattern(uword pc, const Code& code);
static const int kLengthInBytes = 3 * Instr::kInstrSize;
diff --git a/runtime/vm/instructions_arm_test.cc b/runtime/vm/instructions_arm_test.cc
index 0b23884..dec458d 100644
--- a/runtime/vm/instructions_arm_test.cc
+++ b/runtime/vm/instructions_arm_test.cc
@@ -38,10 +38,11 @@
ASSEMBLER_TEST_RUN(Jump, test) {
- JumpPattern jump1(test->entry());
+ JumpPattern jump1(test->entry(), test->code());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes(),
+ test->code());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/instructions_ia32.h b/runtime/vm/instructions_ia32.h
index 6f8af00..365ac26 100644
--- a/runtime/vm/instructions_ia32.h
+++ b/runtime/vm/instructions_ia32.h
@@ -11,6 +11,7 @@
#endif
#include "vm/allocation.h"
+#include "vm/object.h"
namespace dart {
@@ -85,7 +86,7 @@
class JumpPattern : public CallOrJumpPattern {
public:
- explicit JumpPattern(uword pc) : CallOrJumpPattern(pc) {}
+ JumpPattern(uword pc, const Code& code) : CallOrJumpPattern(pc) {}
private:
virtual const int* pattern() const;
diff --git a/runtime/vm/instructions_ia32_test.cc b/runtime/vm/instructions_ia32_test.cc
index 3ceade7..e449e21 100644
--- a/runtime/vm/instructions_ia32_test.cc
+++ b/runtime/vm/instructions_ia32_test.cc
@@ -35,10 +35,11 @@
ASSEMBLER_TEST_RUN(Jump, test) {
- JumpPattern jump1(test->entry());
+ JumpPattern jump1(test->entry(), test->code());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes(),
+ test->code());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/instructions_mips.cc b/runtime/vm/instructions_mips.cc
index 1f79afd..7215ec0 100644
--- a/runtime/vm/instructions_mips.cc
+++ b/runtime/vm/instructions_mips.cc
@@ -177,7 +177,7 @@
}
-JumpPattern::JumpPattern(uword pc) : pc_(pc) { }
+JumpPattern::JumpPattern(uword pc, const Code& code) : pc_(pc) { }
bool JumpPattern::IsValid() const {
diff --git a/runtime/vm/instructions_mips.h b/runtime/vm/instructions_mips.h
index fc2ef10..7cecc70 100644
--- a/runtime/vm/instructions_mips.h
+++ b/runtime/vm/instructions_mips.h
@@ -50,7 +50,7 @@
class JumpPattern : public ValueObject {
public:
- explicit JumpPattern(uword pc);
+ JumpPattern(uword pc, const Code& code);
// lui; ori; jr; nop (in delay slot) = 4.
static const int kLengthInBytes = 4*Instr::kInstrSize;
diff --git a/runtime/vm/instructions_mips_test.cc b/runtime/vm/instructions_mips_test.cc
index 4add9ef..7348db8 100644
--- a/runtime/vm/instructions_mips_test.cc
+++ b/runtime/vm/instructions_mips_test.cc
@@ -39,10 +39,11 @@
ASSEMBLER_TEST_RUN(Jump, test) {
- JumpPattern jump1(test->entry());
+ JumpPattern jump1(test->entry(), test->code());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes(),
+ test->code());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/instructions_x64.cc b/runtime/vm/instructions_x64.cc
index 62a4aa4..0f788073 100644
--- a/runtime/vm/instructions_x64.cc
+++ b/runtime/vm/instructions_x64.cc
@@ -11,6 +11,13 @@
namespace dart {
+intptr_t InstructionPattern::IndexFromPPLoad(uword start) {
+ int32_t offset = *reinterpret_cast<int32_t*>(start);
+ offset += kHeapObjectTag;
+ return (offset - Array::data_offset()) / kWordSize;
+}
+
+
bool InstructionPattern::TestBytesWith(const int* data, int num_bytes) const {
ASSERT(data != NULL);
const uint8_t* byte_array = reinterpret_cast<const uint8_t*>(start_);
@@ -24,13 +31,13 @@
}
-uword CallOrJumpPattern::TargetAddress() const {
+uword CallPattern::TargetAddress() const {
ASSERT(IsValid());
return *reinterpret_cast<uword*>(start() + 2);
}
-void CallOrJumpPattern::SetTargetAddress(uword target) const {
+void CallPattern::SetTargetAddress(uword target) const {
ASSERT(IsValid());
*reinterpret_cast<uword*>(start() + 2) = target;
CPU::FlushICache(start() + 2, kWordSize);
@@ -46,11 +53,27 @@
}
+uword JumpPattern::TargetAddress() const {
+ ASSERT(IsValid());
+ int index = InstructionPattern::IndexFromPPLoad(start() + 3);
+ return reinterpret_cast<uword>(object_pool_.At(index));
+}
+
+
+void JumpPattern::SetTargetAddress(uword target) const {
+ ASSERT(IsValid());
+ int index = InstructionPattern::IndexFromPPLoad(start() + 3);
+ const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(target));
+ object_pool_.SetAt(index, smi);
+ // No need to flush the instruction cache, since the code is not modified.
+}
+
+
const int* JumpPattern::pattern() const {
- // movq $target, TMP
- // jmpq TMP
+ // 00: 4d 8b 9d imm32 mov R11, [R13 + off]
+ // 07: 41 ff e3 jmpq R11
static const int kJumpPattern[kLengthInBytes] =
- {0x49, 0xBB, -1, -1, -1, -1, -1, -1, -1, -1, 0x41, 0xFF, 0xE3};
+ {0x4D, 0x8B, -1, -1, -1, -1, -1, 0x41, 0xFF, 0xE3};
return kJumpPattern;
}
diff --git a/runtime/vm/instructions_x64.h b/runtime/vm/instructions_x64.h
index c4fc155..8b91e7d 100644
--- a/runtime/vm/instructions_x64.h
+++ b/runtime/vm/instructions_x64.h
@@ -11,6 +11,7 @@
#endif
#include "vm/allocation.h"
+#include "vm/object.h"
namespace dart {
@@ -37,6 +38,8 @@
virtual const int* pattern() const = 0;
virtual int pattern_length_in_bytes() const = 0;
+ static intptr_t IndexFromPPLoad(uword start);
+
protected:
uword start() const { return start_; }
@@ -52,46 +55,47 @@
};
-class CallOrJumpPattern : public InstructionPattern {
+class CallPattern : public InstructionPattern {
public:
- virtual int pattern_length_in_bytes() const {
+ CallPattern(uword pc, const Code& code)
+ : InstructionPattern(pc),
+ code_(code) {}
+ static int InstructionLength() {
return kLengthInBytes;
}
uword TargetAddress() const;
void SetTargetAddress(uword new_target) const;
-
- protected:
- explicit CallOrJumpPattern(uword pc) : InstructionPattern(pc) {}
- static const int kLengthInBytes = 13;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CallOrJumpPattern);
-};
-
-
-class CallPattern : public CallOrJumpPattern {
- public:
- explicit CallPattern(uword pc) : CallOrJumpPattern(pc) {}
- static int InstructionLength() {
+ virtual int pattern_length_in_bytes() const {
return kLengthInBytes;
}
private:
+ static const int kLengthInBytes = 13;
virtual const int* pattern() const;
+ const Code& code_;
DISALLOW_COPY_AND_ASSIGN(CallPattern);
};
-class JumpPattern : public CallOrJumpPattern {
+class JumpPattern : public InstructionPattern {
public:
- explicit JumpPattern(uword pc) : CallOrJumpPattern(pc) {}
+ JumpPattern(uword pc, const Code& code)
+ : InstructionPattern(pc),
+ object_pool_(Array::Handle(code.ObjectPool())) {}
static int InstructionLength() {
return kLengthInBytes;
}
+ uword TargetAddress() const;
+ void SetTargetAddress(uword new_target) const;
+ virtual int pattern_length_in_bytes() const {
+ return kLengthInBytes;
+ }
private:
+ static const int kLengthInBytes = 10;
virtual const int* pattern() const;
+ const Array& object_pool_;
DISALLOW_COPY_AND_ASSIGN(JumpPattern);
};
diff --git a/runtime/vm/instructions_x64_test.cc b/runtime/vm/instructions_x64_test.cc
index fb46518..72d41ea 100644
--- a/runtime/vm/instructions_x64_test.cc
+++ b/runtime/vm/instructions_x64_test.cc
@@ -21,25 +21,28 @@
ASSEMBLER_TEST_RUN(Call, test) {
- CallPattern call(test->entry());
+ CallPattern call(test->entry(), test->code());
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
call.TargetAddress());
}
ASSEMBLER_TEST_GENERATE(Jump, assembler) {
- __ jmp(&StubCode::InstanceFunctionLookupLabel());
- __ jmp(&StubCode::AllocateArrayLabel());
+ __ EnterDartFrame(0); // 20 bytes
+ __ JmpPatchable(&StubCode::InstanceFunctionLookupLabel(), PP);
+ __ JmpPatchable(&StubCode::AllocateArrayLabel(), PP);
+ __ LeaveFrameWithPP();
__ ret();
}
ASSEMBLER_TEST_RUN(Jump, test) {
- JumpPattern jump1(test->entry());
+ JumpPattern jump1(test->entry() + 20, test->code());
jump1.IsValid();
EXPECT_EQ(StubCode::InstanceFunctionLookupLabel().address(),
jump1.TargetAddress());
- JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes());
+ JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes() + 20,
+ test->code());
EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
jump2.TargetAddress());
uword target1 = jump1.TargetAddress();
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index dfa6cd4..355949f 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -1368,6 +1368,16 @@
IsFixedLengthArrayCid(call->Type()->ToCid())) {
return call->ArgumentAt(1);
}
+ // For arrays with guarded lengths, replace the length load
+ // with a constant.
+ LoadFieldInstr* load_array = instance()->definition()->AsLoadField();
+ if (load_array != NULL) {
+ const Field* field = load_array->field();
+ if ((field != NULL) && (field->guarded_list_length() >= 0)) {
+ return flow_graph->GetConstant(
+ Smi::Handle(Smi::New(field->guarded_list_length())));
+ }
+ }
return this;
}
@@ -1569,6 +1579,11 @@
return NULL; // Nothing to guard.
}
+ if (field().guarded_list_length() != Field::kNoFixedLength) {
+ // We are still guarding the list length.
+ return this;
+ }
+
if (field().is_nullable() && value()->Type()->IsNull()) {
return NULL;
}
@@ -1610,11 +1625,6 @@
}
-void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- // Nothing to do.
-}
-
-
LocationSummary* JoinEntryInstr::MakeLocationSummary() const {
UNREACHABLE();
return NULL;
@@ -1835,9 +1845,6 @@
deopt_id(),
token_pos());
}
- if (function().name() == Symbols::EqualOperator().raw()) {
- compiler->EmitSuperEqualityCallPrologue(locs()->out().reg(), &skip_call);
- }
compiler->GenerateStaticCall(deopt_id(),
token_pos(),
function(),
@@ -2644,6 +2651,27 @@
}
+extern const RuntimeEntry kCosRuntimeEntry(
+ "libc_cos", reinterpret_cast<RuntimeFunction>(
+ static_cast<UnaryMathCFunction>(&cos)), 1, true, true);
+
+extern const RuntimeEntry kSinRuntimeEntry(
+ "libc_sin", reinterpret_cast<RuntimeFunction>(
+ static_cast<UnaryMathCFunction>(&sin)), 1, true, true);
+
+
+const RuntimeEntry& MathUnaryInstr::TargetFunction() const {
+ switch (kind()) {
+ case MethodRecognizer::kMathSin:
+ return kSinRuntimeEntry;
+ case MethodRecognizer::kMathCos:
+ return kCosRuntimeEntry;
+ default:
+ UNREACHABLE();
+ }
+ return kSinRuntimeEntry;
+}
+
#undef __
} // namespace dart
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index bf09939..33cde45 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -69,7 +69,7 @@
V(_StringBase, get:length, StringBaseLength, 1483460481) \
V(_StringBase, get:isEmpty, StringBaseIsEmpty, 1588094430) \
V(_StringBase, codeUnitAt, StringBaseCodeUnitAt, 1958436584) \
- V(_StringBase, [], StringBaseCharAt, 539412735) \
+ V(_StringBase, [], StringBaseCharAt, 1799392702) \
V(_OneByteString, _setAt, OneByteStringSetAt, 1754827784) \
V(_IntegerImplementation, toDouble, IntegerToDouble, 2141284842) \
V(_IntegerImplementation, _leftShiftWithMask32, IntegerLeftShiftWithMask32, \
@@ -4660,8 +4660,8 @@
}
Value* value() const { return inputs_[0]; }
-
MethodRecognizer::Kind kind() const { return kind_; }
+ const RuntimeEntry& TargetFunction() const;
virtual bool CanDeoptimize() const { return false; }
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index e8c1f2c..9ed4e93 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -1443,13 +1443,8 @@
new LocationSummary(kNumInputs, 0, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
const bool field_has_length = field().needs_length_check();
- const bool need_value_temp_reg =
- (field_has_length || ((value()->Type()->ToCid() == kDynamicCid) &&
- (field().guarded_cid() != kSmiCid)));
- if (need_value_temp_reg) {
- summary->AddTemp(Location::RequiresRegister());
- summary->AddTemp(Location::RequiresRegister());
- }
+ summary->AddTemp(Location::RequiresRegister());
+ summary->AddTemp(Location::RequiresRegister());
const bool need_field_temp_reg =
field_has_length || (field().guarded_cid() == kIllegalCid);
if (need_field_temp_reg) {
@@ -1464,9 +1459,6 @@
const intptr_t nullability = field().is_nullable() ? kNullCid : kIllegalCid;
const intptr_t field_length = field().guarded_list_length();
const bool field_has_length = field().needs_length_check();
- const bool needs_value_temp_reg =
- (field_has_length || ((value()->Type()->ToCid() == kDynamicCid) &&
- (field().guarded_cid() != kSmiCid)));
const bool needs_field_temp_reg =
field_has_length || (field().guarded_cid() == kIllegalCid);
if (field_has_length) {
@@ -1483,10 +1475,9 @@
Register value_reg = locs()->in(0).reg();
- Register value_cid_reg = needs_value_temp_reg ?
- locs()->temp(0).reg() : kNoRegister;
- Register temp_reg = needs_value_temp_reg ?
- locs()->temp(1).reg() : kNoRegister;
+ Register value_cid_reg = locs()->temp(0).reg();
+
+ Register temp_reg = locs()->temp(1).reg();
Register field_reg = needs_field_temp_reg ?
locs()->temp(locs()->temp_count() - 1).reg() : kNoRegister;
@@ -1518,11 +1509,8 @@
FieldAddress field_length_operand(
field_reg, Field::guarded_list_length_offset());
- if (value_cid_reg == kNoRegister) {
- ASSERT(!compiler->is_optimizing());
- value_cid_reg = R3;
- ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
- }
+ ASSERT(value_cid_reg != kNoRegister);
+ ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
if (value_cid == kDynamicCid) {
LoadValueCid(compiler, value_cid_reg, value_reg);
@@ -1536,13 +1524,53 @@
if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) {
__ ldr(temp_reg,
FieldAddress(value_reg, Array::length_offset()));
- __ CompareImmediate(temp_reg, field_length);
+ __ CompareImmediate(temp_reg, Smi::RawValue(field_length));
} else if (RawObject::IsTypedDataClassId(field_cid)) {
__ ldr(temp_reg,
FieldAddress(value_reg, TypedData::length_offset()));
- __ CompareImmediate(temp_reg, field_length);
+ __ CompareImmediate(temp_reg, Smi::RawValue(field_length));
} else {
ASSERT(field_cid == kIllegalCid);
+ ASSERT(field_length == Field::kUnknownFixedLength);
+ // At compile time we do not know the type of the field nor its
+ // length. At execution time we may have set the class id and
+ // list length so we compare the guarded length with the
+ // list length here, without this check the list length could change
+ // without triggering a deoptimization.
+ Label check_array, length_compared, no_fixed_length;
+ // If length is negative the length guard is either disabled or
+ // has not been initialized, either way it is safe to skip the
+ // length check.
+ __ ldr(IP, field_length_operand);
+ __ CompareImmediate(IP, 0);
+ __ b(&skip_length_check, LT);
+ __ CompareImmediate(value_cid_reg, kNullCid);
+ __ b(&no_fixed_length, EQ);
+ // Check for typed data array.
+ __ CompareImmediate(value_cid_reg, kTypedDataFloat32x4ArrayCid);
+ __ b(&no_fixed_length, GT);
+ __ CompareImmediate(value_cid_reg, kTypedDataInt8ArrayCid);
+ // Could still be a regular array.
+ __ b(&check_array, LT);
+ __ ldr(temp_reg,
+ FieldAddress(value_reg, TypedData::length_offset()));
+ __ ldr(IP, field_length_operand);
+ __ cmp(temp_reg, ShifterOperand(IP));
+ __ b(&length_compared);
+ // Check for regular array.
+ __ Bind(&check_array);
+ __ CompareImmediate(value_cid_reg, kImmutableArrayCid);
+ __ b(&no_fixed_length, GT);
+ __ CompareImmediate(value_cid_reg, kArrayCid);
+ __ b(&no_fixed_length, LT);
+ __ ldr(temp_reg,
+ FieldAddress(value_reg, Array::length_offset()));
+ __ ldr(IP, field_length_operand);
+ __ cmp(temp_reg, ShifterOperand(IP));
+ __ b(&length_compared);
+ __ Bind(&no_fixed_length);
+ __ b(fail);
+ __ Bind(&length_compared);
// Following branch cannot not occur, fall through.
}
__ b(fail, NE);
@@ -1561,18 +1589,26 @@
if (field_has_length) {
ASSERT(value_cid_reg != kNoRegister);
ASSERT(temp_reg != kNoRegister);
- if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) {
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
__ ldr(temp_reg,
FieldAddress(value_reg, Array::length_offset()));
- __ CompareImmediate(temp_reg, field_length);
- } else if (RawObject::IsTypedDataClassId(field_cid)) {
+ __ CompareImmediate(temp_reg, Smi::RawValue(field_length));
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
__ ldr(temp_reg,
FieldAddress(value_reg, TypedData::length_offset()));
- __ CompareImmediate(temp_reg, field_length);
+ __ CompareImmediate(temp_reg, Smi::RawValue(field_length));
+ } else if (field_cid != kIllegalCid) {
+ ASSERT(field_cid != value_cid);
+ ASSERT(field_length >= 0);
+ // Field has a known class id and length. At compile time it is
+ // known that the value's class id is not a fixed length list.
+ __ b(fail);
} else {
ASSERT(field_cid == kIllegalCid);
+ ASSERT(field_length == Field::kUnknownFixedLength);
// Following jump cannot not occur, fall through.
}
+ __ b(fail, NE);
}
// Not identical, possibly null.
__ Bind(&skip_length_check);
@@ -1587,57 +1623,58 @@
__ str(value_cid_reg, field_cid_operand);
__ str(value_cid_reg, field_nullability_operand);
if (field_has_length) {
- Label check_array, local_exit, local_fail;
+ Label check_array, length_set, no_fixed_length;
__ CompareImmediate(value_cid_reg, kNullCid);
- __ b(&local_fail, EQ);
+ __ b(&no_fixed_length, EQ);
// Check for typed data array.
__ CompareImmediate(value_cid_reg, kTypedDataFloat32x4ArrayCid);
- __ b(&local_fail, GT);
+ __ b(&no_fixed_length, GT);
__ CompareImmediate(value_cid_reg, kTypedDataInt8ArrayCid);
- __ b(&check_array, LT); // Could still be a regular array.
+ // Could still be a regular array.
+ __ b(&check_array, LT);
// Destroy value_cid_reg (safe because we are finished with it).
__ ldr(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
__ str(value_cid_reg, field_length_operand);
- __ b(&local_exit); // Updated field length typed data array.
+ __ b(&length_set); // Updated field length typed data array.
// Check for regular array.
__ Bind(&check_array);
__ CompareImmediate(value_cid_reg, kImmutableArrayCid);
- __ b(&local_fail, GT);
+ __ b(&no_fixed_length, GT);
__ CompareImmediate(value_cid_reg, kArrayCid);
- __ b(&local_fail, LT);
+ __ b(&no_fixed_length, LT);
// Destroy value_cid_reg (safe because we are finished with it).
__ ldr(value_cid_reg,
FieldAddress(value_reg, Array::length_offset()));
__ str(value_cid_reg, field_length_operand);
- __ b(&local_exit); // Updated field length from regular array.
-
- __ Bind(&local_fail);
- __ LoadImmediate(IP, Field::kNoFixedLength);
+ // Updated field length from regular array.
+ __ b(&length_set);
+ __ Bind(&no_fixed_length);
+ __ LoadImmediate(IP, Smi::RawValue(Field::kNoFixedLength));
__ str(IP, field_length_operand);
-
- __ Bind(&local_exit);
+ __ Bind(&length_set);
}
} else {
__ LoadImmediate(IP, value_cid);
__ str(IP, field_cid_operand);
__ str(IP, field_nullability_operand);
- if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ ldr(value_cid_reg,
- FieldAddress(value_reg, Array::length_offset()));
- __ str(value_cid_reg, field_length_operand);
- } else if (RawObject::IsTypedDataClassId(value_cid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ ldr(value_cid_reg,
- FieldAddress(value_reg, TypedData::length_offset()));
- __ str(value_cid_reg, field_length_operand);
- } else {
- __ LoadImmediate(IP, Field::kNoFixedLength);
- __ str(IP, field_length_operand);
+ if (field_has_length) {
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ ldr(value_cid_reg,
+ FieldAddress(value_reg, Array::length_offset()));
+ __ str(value_cid_reg, field_length_operand);
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ ldr(value_cid_reg,
+ FieldAddress(value_reg, TypedData::length_offset()));
+ __ str(value_cid_reg, field_length_operand);
+ } else {
+ __ LoadImmediate(IP, Smi::RawValue(Field::kNoFixedLength));
+ __ str(IP, field_length_operand);
+ }
}
}
-
if (!ok_is_fall_through) {
__ b(&ok);
}
@@ -3760,6 +3797,16 @@
LocationSummary* MathUnaryInstr::MakeLocationSummary() const {
+ if ((kind() == MethodRecognizer::kMathSin) ||
+ (kind() == MethodRecognizer::kMathCos)) {
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
+ summary->set_in(0, Location::FpuRegisterLocation(Q0));
+ summary->set_out(Location::FpuRegisterLocation(Q0));
+ return summary;
+ }
const intptr_t kNumInputs = 1;
const intptr_t kNumTemps = 0;
LocationSummary* summary =
@@ -3776,7 +3823,7 @@
DRegister result = EvenDRegisterOf(locs()->out().fpu_reg());
__ vsqrtd(result, val);
} else {
- UNIMPLEMENTED();
+ __ CallRuntime(TargetFunction(), InputCount());
}
}
@@ -4388,6 +4435,13 @@
}
+void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ if (!compiler->CanFallThroughTo(normal_entry())) {
+ __ b(compiler->GetJumpLabel(normal_entry()));
+ }
+}
+
+
void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
if (!compiler->is_optimizing()) {
@@ -4444,8 +4498,7 @@
// If the next block is the false successor we will fall through to it.
__ b(compiler->GetJumpLabel(true_successor()), true_condition);
} else {
- // If the next block is the true successor we negate comparison and fall
- // through to it.
+ // If the next block is not the false successor we will branch to it.
Condition false_condition = NegateCondition(true_condition);
__ b(compiler->GetJumpLabel(false_successor()), false_condition);
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index 9341603..279051c 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -1539,7 +1539,6 @@
ASSERT(!compiler->is_optimizing());
return; // Nothing to emit.
}
-
const intptr_t value_cid = value()->Type()->ToCid();
Register value_reg = locs()->in(0).reg();
@@ -1585,7 +1584,6 @@
}
LoadValueCid(compiler, value_cid_reg, value_reg);
-
Label skip_length_check;
__ cmpl(value_cid_reg, field_cid_operand);
// Value CID != Field guard CID, skip length check.
@@ -1596,17 +1594,58 @@
__ pushl(value_cid_reg);
__ movl(value_cid_reg,
FieldAddress(value_reg, Array::length_offset()));
- __ cmpl(value_cid_reg, Immediate(field_length));
+ __ cmpl(value_cid_reg, Immediate(Smi::RawValue(field_length)));
__ popl(value_cid_reg);
} else if (RawObject::IsTypedDataClassId(field_cid)) {
__ pushl(value_cid_reg);
__ movl(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
- __ cmpl(value_cid_reg, Immediate(field_length));
+ __ cmpl(value_cid_reg, Immediate(Smi::RawValue(field_length)));
__ popl(value_cid_reg);
} else {
ASSERT(field_cid == kIllegalCid);
- // Following jump cannot not occur, fall through.
+ ASSERT(field_length == Field::kUnknownFixedLength);
+ // At compile time we do not know the type of the field nor its
+ // length. At execution time we may have set the class id and
+ // list length so we compare the guarded length with the
+ // list length here, without this check the list length could change
+ // without triggering a deoptimization.
+ Label check_array, length_compared, no_fixed_length;
+ // If length is negative the length guard is either disabled or
+ // has not been initialized, either way it is safe to skip the
+ // length check.
+ __ cmpl(field_length_operand, Immediate(Smi::RawValue(0)));
+ __ j(LESS, &skip_length_check);
+ __ cmpl(value_cid_reg, Immediate(kNullCid));
+ __ j(EQUAL, &no_fixed_length, Assembler::kNearJump);
+ // Check for typed data array.
+ __ cmpl(value_cid_reg, Immediate(kTypedDataFloat32x4ArrayCid));
+ // Not a typed array or a regular array.
+ __ j(GREATER, &no_fixed_length, Assembler::kNearJump);
+ __ cmpl(value_cid_reg, Immediate(kTypedDataInt8ArrayCid));
+ // Could still be a regular array.
+ __ j(LESS, &check_array, Assembler::kNearJump);
+ __ pushl(value_cid_reg);
+ __ movl(value_cid_reg,
+ FieldAddress(value_reg, TypedData::length_offset()));
+ __ cmpl(field_length_operand, value_cid_reg);
+ __ popl(value_cid_reg);
+ __ jmp(&length_compared, Assembler::kNearJump);
+ // Check for regular array.
+ __ Bind(&check_array);
+ __ cmpl(value_cid_reg, Immediate(kImmutableArrayCid));
+ __ j(GREATER, &no_fixed_length, Assembler::kNearJump);
+ __ cmpl(value_cid_reg, Immediate(kArrayCid));
+ __ j(LESS, &no_fixed_length, Assembler::kNearJump);
+ __ pushl(value_cid_reg);
+ __ movl(value_cid_reg,
+ FieldAddress(value_reg, Array::length_offset()));
+ __ cmpl(field_length_operand, value_cid_reg);
+ __ popl(value_cid_reg);
+ __ jmp(&length_compared, Assembler::kNearJump);
+ __ Bind(&no_fixed_length);
+ __ jmp(fail);
+ __ Bind(&length_compared);
}
__ j(NOT_EQUAL, fail);
}
@@ -1625,28 +1664,25 @@
__ j(NOT_EQUAL, &skip_length_check);
// Insert length check.
if (field_has_length) {
- if (value_cid_reg == kNoRegister) {
- ASSERT(!compiler->is_optimizing());
- value_cid_reg = EDX;
- ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
- }
ASSERT(value_cid_reg != kNoRegister);
- if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) {
- __ pushl(value_cid_reg);
- __ movl(value_cid_reg,
- FieldAddress(value_reg, Array::length_offset()));
- __ cmpl(value_cid_reg, Immediate(field_length));
- __ popl(value_cid_reg);
- } else if (RawObject::IsTypedDataClassId(field_cid)) {
- __ pushl(value_cid_reg);
- __ movl(value_cid_reg,
- FieldAddress(value_reg, TypedData::length_offset()));
- __ cmpl(value_cid_reg, Immediate(field_length));
- __ popl(value_cid_reg);
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
+ __ cmpl(FieldAddress(value_reg, Array::length_offset()),
+ Immediate(Smi::RawValue(field_length)));
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
+ __ cmpl(FieldAddress(value_reg, TypedData::length_offset()),
+ Immediate(Smi::RawValue(field_length)));
+ } else if (field_cid != kIllegalCid) {
+ ASSERT(field_cid != value_cid);
+ ASSERT(field_length >= 0);
+ // Field has a known class id and length. At compile time it is
+ // known that the value's class id is not a fixed length list.
+ __ jmp(fail);
} else {
ASSERT(field_cid == kIllegalCid);
+ ASSERT(field_length == Field::kUnknownFixedLength);
// Following jump cannot not occur, fall through.
}
+ __ j(NOT_EQUAL, fail);
}
// Not identical, possibly null.
__ Bind(&skip_length_check);
@@ -1654,7 +1690,6 @@
// Jump when class id guard and list length guard are okay.
__ j(EQUAL, &ok);
-
// Check if guard field is uninitialized.
__ cmpl(field_cid_operand, Immediate(kIllegalCid));
// Jump to failure path when guard field has been initialized and
@@ -1667,58 +1702,59 @@
__ movl(field_cid_operand, value_cid_reg);
__ movl(field_nullability_operand, value_cid_reg);
if (field_has_length) {
- Label check_array, local_exit, local_fail;
+ Label check_array, length_set, no_fixed_length;
__ cmpl(value_cid_reg, Immediate(kNullCid));
- __ j(EQUAL, &local_fail);
+ __ j(EQUAL, &no_fixed_length, Assembler::kNearJump);
// Check for typed data array.
__ cmpl(value_cid_reg, Immediate(kTypedDataFloat32x4ArrayCid));
- __ j(GREATER, &local_fail); // Not a typed array or a regular array.
+ // Not a typed array or a regular array.
+ __ j(GREATER, &no_fixed_length, Assembler::kNearJump);
__ cmpl(value_cid_reg, Immediate(kTypedDataInt8ArrayCid));
- __ j(LESS, &check_array); // Could still be a regular array.
+ // Could still be a regular array.
+ __ j(LESS, &check_array, Assembler::kNearJump);
// Destroy value_cid_reg (safe because we are finished with it).
__ movl(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
__ movl(field_length_operand, value_cid_reg);
- __ jmp(&local_exit); // Updated field length typed data array.
+ // Updated field length typed data array.
+ __ jmp(&length_set, Assembler::kNearJump);
// Check for regular array.
__ Bind(&check_array);
__ cmpl(value_cid_reg, Immediate(kImmutableArrayCid));
- __ j(GREATER, &local_fail);
+ __ j(GREATER, &no_fixed_length, Assembler::kNearJump);
__ cmpl(value_cid_reg, Immediate(kArrayCid));
- __ j(LESS, &local_fail);
+ __ j(LESS, &no_fixed_length, Assembler::kNearJump);
// Destroy value_cid_reg (safe because we are finished with it).
__ movl(value_cid_reg,
FieldAddress(value_reg, Array::length_offset()));
__ movl(field_length_operand, value_cid_reg);
- __ jmp(&local_exit); // Updated field length from regular array.
-
- __ Bind(&local_fail);
- __ movl(field_length_operand, Immediate(Field::kNoFixedLength));
-
- __ Bind(&local_exit);
+ // Updated field length from regular array.
+ __ jmp(&length_set, Assembler::kNearJump);
+ __ Bind(&no_fixed_length);
+ __ movl(field_length_operand,
+ Immediate(Smi::RawValue(Field::kNoFixedLength)));
+ __ Bind(&length_set);
}
} else {
- if (value_cid_reg == kNoRegister) {
- ASSERT(!compiler->is_optimizing());
- value_cid_reg = EDX;
- ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
- }
- ASSERT(value_cid_reg != kNoRegister);
ASSERT(field_reg != kNoRegister);
__ movl(field_cid_operand, Immediate(value_cid));
__ movl(field_nullability_operand, Immediate(value_cid));
- if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ movl(value_cid_reg,
- FieldAddress(value_reg, Array::length_offset()));
- __ movl(field_length_operand, value_cid_reg);
- } else if (RawObject::IsTypedDataClassId(value_cid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ movl(value_cid_reg,
- FieldAddress(value_reg, TypedData::length_offset()));
- __ movl(field_length_operand, value_cid_reg);
- } else {
- __ movl(field_length_operand, Immediate(Field::kNoFixedLength));
+ if (field_has_length) {
+ ASSERT(value_cid_reg != kNoRegister);
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ movl(value_cid_reg,
+ FieldAddress(value_reg, Array::length_offset()));
+ __ movl(field_length_operand, value_cid_reg);
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ movl(value_cid_reg,
+ FieldAddress(value_reg, TypedData::length_offset()));
+ __ movl(field_length_operand, value_cid_reg);
+ } else {
+ __ movl(field_length_operand,
+ Immediate(Smi::RawValue(Field::kNoFixedLength)));
+ }
}
}
@@ -1727,7 +1763,6 @@
}
} else {
// Field guard class has been initialized and is known.
-
if (field_reg != kNoRegister) {
__ LoadObject(field_reg, Field::ZoneHandle(field().raw()));
}
@@ -3827,6 +3862,16 @@
LocationSummary* MathUnaryInstr::MakeLocationSummary() const {
+ if ((kind() == MethodRecognizer::kMathSin) ||
+ (kind() == MethodRecognizer::kMathCos)) {
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
+ summary->set_in(0, Location::FpuRegisterLocation(XMM1));
+ summary->set_out(Location::FpuRegisterLocation(XMM1));
+ return summary;
+ }
const intptr_t kNumInputs = 1;
const intptr_t kNumTemps = 0;
LocationSummary* summary =
@@ -3840,23 +3885,14 @@
void MathUnaryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
if (kind() == MethodRecognizer::kMathSqrt) {
__ sqrtsd(locs()->out().fpu_reg(), locs()->in(0).fpu_reg());
- } else if ((kind() == MethodRecognizer::kMathCos) ||
- (kind() == MethodRecognizer::kMathSin)) {
- __ pushl(EAX);
- __ pushl(EAX);
+ } else {
+ __ EnterFrame(0);
+ __ ReserveAlignedFrameSpace(kDoubleSize * InputCount());
__ movsd(Address(ESP, 0), locs()->in(0).fpu_reg());
- __ fldl(Address(ESP, 0));
- if (kind() == MethodRecognizer::kMathSin) {
- __ fsin();
- } else {
- ASSERT(kind() == MethodRecognizer::kMathCos);
- __ fcos();
- }
+ __ CallRuntime(TargetFunction(), InputCount());
__ fstpl(Address(ESP, 0));
__ movsd(locs()->out().fpu_reg(), Address(ESP, 0));
- __ addl(ESP, Immediate(2 * kWordSize));
- } else {
- UNREACHABLE();
+ __ leave();
}
}
@@ -4734,6 +4770,13 @@
}
+void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ if (!compiler->CanFallThroughTo(normal_entry())) {
+ __ jmp(compiler->GetJumpLabel(normal_entry()));
+ }
+}
+
+
void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
if (!compiler->is_optimizing()) {
@@ -4828,11 +4871,10 @@
void ControlInstruction::EmitBranchOnCondition(FlowGraphCompiler* compiler,
Condition true_condition) {
if (compiler->CanFallThroughTo(false_successor())) {
- // If the next block is the false successor we will fall through to it.
+ // If the next block is the false successor, fall through to it.
__ j(true_condition, compiler->GetJumpLabel(true_successor()));
} else {
- // If the next block is the true successor we negate comparison and fall
- // through to it.
+ // If the next block is not the false successor, branch to it.
Condition false_condition = NegateCondition(true_condition);
__ j(false_condition, compiler->GetJumpLabel(false_successor()));
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index 8fc1040..a1d5b18 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -1591,15 +1591,48 @@
// Field guard may have remembered list length, check it.
if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) {
__ lw(TMP, FieldAddress(value_reg, Array::length_offset()));
- __ LoadImmediate(CMPRES1, field_length);
+ __ LoadImmediate(CMPRES1, Smi::RawValue(field_length));
__ subu(CMPRES1, TMP, CMPRES1);
} else if (RawObject::IsTypedDataClassId(field_cid)) {
__ lw(TMP, FieldAddress(value_reg, TypedData::length_offset()));
- __ LoadImmediate(CMPRES1, field_length);
+ __ LoadImmediate(CMPRES1, Smi::RawValue(field_length));
__ subu(CMPRES1, TMP, CMPRES1);
} else {
ASSERT(field_cid == kIllegalCid);
- __ LoadImmediate(CMPRES1, 0x1);
+ ASSERT(field_length == Field::kUnknownFixedLength);
+ // At compile time we do not know the type of the field nor its
+ // length. At execution time we may have set the class id and
+ // list length so we compare the guarded length with the
+ // list length here, without this check the list length could change
+ // without triggering a deoptimization.
+ Label check_array, length_compared, no_fixed_length;
+ // If length is negative the length guard is either disabled or
+ // has not been initialized, either way it is safe to skip the
+ // length check.
+ __ lw(CMPRES1, field_length_operand);
+ __ BranchSignedLess(CMPRES1, 0, &skip_length_check);
+ __ BranchEqual(value_cid_reg, kNullCid, &no_fixed_length);
+ // Check for typed data array.
+ __ BranchSignedGreater(value_cid_reg, kTypedDataFloat32x4ArrayCid,
+ &no_fixed_length);
+ __ BranchSignedLess(value_cid_reg, kTypedDataInt8ArrayCid,
+ &check_array);
+ __ lw(TMP, FieldAddress(value_reg, TypedData::length_offset()));
+ __ lw(CMPRES1, field_length_operand);
+ __ subu(CMPRES1, TMP, CMPRES1);
+ __ b(&length_compared);
+ // Check for regular array.
+ __ Bind(&check_array);
+ __ BranchSignedGreater(value_cid_reg, kImmutableArrayCid,
+ &no_fixed_length);
+ __ BranchSignedLess(value_cid_reg, kArrayCid, &no_fixed_length);
+ __ lw(TMP, FieldAddress(value_reg, Array::length_offset()));
+ __ lw(CMPRES1, field_length_operand);
+ __ subu(CMPRES1, TMP, CMPRES1);
+ __ b(&length_compared);
+ __ Bind(&no_fixed_length);
+ __ b(fail);
+ __ Bind(&length_compared);
}
__ bne(CMPRES1, ZR, fail);
}
@@ -1620,24 +1653,27 @@
__ bne(CMPRES, ZR, &skip_length_check);
// Insert length check.
if (field_has_length) {
- if (value_cid_reg == kNoRegister) {
- ASSERT(!compiler->is_optimizing());
- value_cid_reg = A1;
- ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
- }
ASSERT(value_cid_reg != kNoRegister);
- if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) {
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
__ lw(TMP, FieldAddress(value_reg, Array::length_offset()));
- __ LoadImmediate(CMPRES, field_length);
+ __ LoadImmediate(CMPRES, Smi::RawValue(field_length));
__ subu(CMPRES, TMP, CMPRES);
- } else if (RawObject::IsTypedDataClassId(field_cid)) {
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
__ lw(TMP, FieldAddress(value_reg, TypedData::length_offset()));
- __ LoadImmediate(CMPRES, field_length);
+ __ LoadImmediate(CMPRES, Smi::RawValue(field_length));
__ subu(CMPRES, TMP, CMPRES);
+ } else if (field_cid != kIllegalCid) {
+ ASSERT(field_cid != value_cid);
+ ASSERT(field_length >= 0);
+ // Field has a known class id and length. At compile time it is
+ // known that the value's class id is not a fixed length list.
+ __ b(fail);
} else {
ASSERT(field_cid == kIllegalCid);
- __ LoadImmediate(CMPRES, 0x1);
+ ASSERT(field_length == Field::kUnknownFixedLength);
+ // Following jump cannot not occur, fall through.
}
+ __ bne(CMPRES, ZR, fail);
}
__ Bind(&skip_length_check);
}
@@ -1650,64 +1686,60 @@
__ sw(value_cid_reg, field_cid_operand);
__ sw(value_cid_reg, field_nullability_operand);
if (field_has_length) {
- Label check_array, local_exit, local_fail;
- __ BranchEqual(value_cid_reg, kNullCid, &local_fail);
+ Label check_array, length_set, no_fixed_length;
+ __ BranchEqual(value_cid_reg, kNullCid, &no_fixed_length);
// Check for typed data array.
__ BranchSignedGreater(value_cid_reg, kTypedDataFloat32x4ArrayCid,
- &local_fail);
+ &no_fixed_length);
__ BranchSignedLess(value_cid_reg, kTypedDataInt8ArrayCid,
&check_array);
// Destroy value_cid_reg (safe because we are finished with it).
__ lw(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
__ sw(value_cid_reg, field_length_operand);
- __ b(&local_exit); // Updated field length typed data array.
+ // Updated field length typed data array.
+ __ b(&length_set);
// Check for regular array.
__ Bind(&check_array);
__ BranchSignedGreater(value_cid_reg, kImmutableArrayCid,
- &local_fail);
- __ BranchSignedLess(value_cid_reg, kArrayCid, &local_fail);
+ &no_fixed_length);
+ __ BranchSignedLess(value_cid_reg, kArrayCid, &no_fixed_length);
// Destroy value_cid_reg (safe because we are finished with it).
__ lw(value_cid_reg,
FieldAddress(value_reg, Array::length_offset()));
__ sw(value_cid_reg, field_length_operand);
- __ b(&local_exit); // Updated field length from regular array.
-
- __ Bind(&local_fail);
+ // Updated field length from regular array.
+ __ b(&length_set);
+ __ Bind(&no_fixed_length);
// TODO(regis): TMP1 may conflict. Revisit.
- __ LoadImmediate(TMP1, Field::kNoFixedLength);
+ __ LoadImmediate(TMP1, Smi::RawValue(Field::kNoFixedLength));
__ sw(TMP1, field_length_operand);
-
- __ Bind(&local_exit);
+ __ Bind(&length_set);
}
} else {
- if (value_cid_reg == kNoRegister) {
- ASSERT(!compiler->is_optimizing());
- value_cid_reg = A1;
- ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
- }
- ASSERT(value_cid_reg != kNoRegister);
ASSERT(field_reg != kNoRegister);
__ LoadImmediate(TMP1, value_cid);
__ sw(TMP1, field_cid_operand);
__ sw(TMP1, field_nullability_operand);
- if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ lw(value_cid_reg,
- FieldAddress(value_reg, Array::length_offset()));
- __ sw(value_cid_reg, field_length_operand);
- } else if (RawObject::IsTypedDataClassId(value_cid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ lw(value_cid_reg,
- FieldAddress(value_reg, TypedData::length_offset()));
- __ sw(value_cid_reg, field_length_operand);
- } else {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ LoadImmediate(value_cid_reg, Field::kNoFixedLength);
- __ sw(value_cid_reg, field_length_operand);
+ if (field_has_length) {
+ ASSERT(value_cid_reg != kNoRegister);
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ lw(value_cid_reg,
+ FieldAddress(value_reg, Array::length_offset()));
+ __ sw(value_cid_reg, field_length_operand);
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ lw(value_cid_reg,
+ FieldAddress(value_reg, TypedData::length_offset()));
+ __ sw(value_cid_reg, field_length_operand);
+ } else {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ LoadImmediate(value_cid_reg, Smi::RawValue(Field::kNoFixedLength));
+ __ sw(value_cid_reg, field_length_operand);
+ }
}
}
-
if (!ok_is_fall_through) {
__ b(&ok);
}
@@ -1775,7 +1807,7 @@
__ lw(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
}
- __ LoadImmediate(TMP1, field_length);
+ __ LoadImmediate(TMP1, Smi::RawValue(field_length));
__ subu(CMPRES, value_cid_reg, TMP1);
if (ok_is_fall_through) {
__ bne(CMPRES, ZR, fail);
@@ -3184,6 +3216,16 @@
LocationSummary* MathUnaryInstr::MakeLocationSummary() const {
+ if ((kind() == MethodRecognizer::kMathSin) ||
+ (kind() == MethodRecognizer::kMathCos)) {
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
+ summary->set_in(0, Location::FpuRegisterLocation(D6));
+ summary->set_out(Location::FpuRegisterLocation(D0));
+ return summary;
+ }
const intptr_t kNumInputs = 1;
const intptr_t kNumTemps = 0;
LocationSummary* summary =
@@ -3198,7 +3240,7 @@
if (kind() == MethodRecognizer::kMathSqrt) {
__ sqrtd(locs()->out().fpu_reg(), locs()->in(0).fpu_reg());
} else {
- UNIMPLEMENTED();
+ __ CallRuntime(TargetFunction(), InputCount());
}
}
@@ -3786,6 +3828,13 @@
}
+void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ if (!compiler->CanFallThroughTo(normal_entry())) {
+ __ b(compiler->GetJumpLabel(normal_entry()));
+ }
+}
+
+
void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
if (!compiler->is_optimizing()) {
@@ -3863,12 +3912,11 @@
Condition true_condition) {
__ TraceSimMsg("ControlInstruction::EmitBranchOnCondition");
if (compiler->CanFallThroughTo(false_successor())) {
- // If the next block is the false successor we will fall through to it.
+ // If the next block is the false successor, fall through to it.
Label* label = compiler->GetJumpLabel(true_successor());
EmitBranchAfterCompare(compiler, true_condition, label);
} else {
- // If the next block is the true successor we negate comparison and fall
- // through to it.
+ // If the next block is not the false successor, branch to it.
Condition false_condition = NegateCondition(true_condition);
Label* label = compiler->GetJumpLabel(false_successor());
EmitBranchAfterCompare(compiler, false_condition, label);
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index d03bbb9..de1647a 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -96,20 +96,8 @@
__ Bind(&done);
}
#endif
- __ LeaveFrame();
- __ ret();
- // Generate 8 bytes of NOPs so that the debugger can patch the
- // return pattern with a call to the debug stub.
- // Note that the nop(8) byte pattern is not recognized by the debugger.
- __ nop(1);
- __ nop(1);
- __ nop(1);
- __ nop(1);
- __ nop(1);
- __ nop(1);
- __ nop(1);
- __ nop(1);
+ __ ReturnPatchable();
compiler->AddCurrentDescriptor(PcDescriptors::kReturn,
Isolate::kNoDeoptId,
token_pos());
@@ -315,7 +303,7 @@
// The register allocator drops constant definitions that have no uses.
if (!locs()->out().IsInvalid()) {
Register result = locs()->out().reg();
- __ LoadObject(result, value());
+ __ LoadObject(result, value(), PP);
}
}
@@ -465,12 +453,11 @@
const Array& kNoArgumentNames = Object::null_array();
const int kNumArgumentsChecked = 2;
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label check_identity;
- __ cmpq(Address(RSP, 0 * kWordSize), raw_null);
+ __ LoadObject(TMP, Object::null_object(), PP);
+ __ cmpq(Address(RSP, 0 * kWordSize), TMP);
__ j(EQUAL, &check_identity);
- __ cmpq(Address(RSP, 1 * kWordSize), raw_null);
+ __ cmpq(Address(RSP, 1 * kWordSize), TMP);
__ j(EQUAL, &check_identity);
ICData& equality_ic_data = ICData::ZoneHandle(original_ic_data.raw());
@@ -511,10 +498,10 @@
__ popq(RDX);
__ cmpq(RAX, RDX);
__ j(EQUAL, &is_true);
- __ LoadObject(RAX, Bool::Get(kind != Token::kEQ));
+ __ LoadObject(RAX, Bool::Get(kind != Token::kEQ), PP);
__ jmp(&equality_done);
__ Bind(&is_true);
- __ LoadObject(RAX, Bool::Get(kind == Token::kEQ));
+ __ LoadObject(RAX, Bool::Get(kind == Token::kEQ), PP);
if (kind == Token::kNE) {
// Skip not-equal result conversion.
__ jmp(&equality_done);
@@ -524,7 +511,7 @@
// necessary.
Register ic_data_reg = locs->temp(0).reg();
ASSERT(ic_data_reg == RBX); // Stub depends on it.
- __ LoadObject(ic_data_reg, equality_ic_data);
+ __ LoadObject(ic_data_reg, equality_ic_data, PP);
compiler->GenerateCall(token_pos,
&StubCode::EqualityWithNullArgLabel(),
PcDescriptors::kRuntimeCall,
@@ -537,10 +524,10 @@
// Negate the condition: true label returns false and vice versa.
__ CompareObject(RAX, Bool::True());
__ j(EQUAL, &true_label, Assembler::kNearJump);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ jmp(&done, Assembler::kNearJump);
__ Bind(&true_label);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
__ Bind(&done);
}
__ Bind(&equality_done);
@@ -610,10 +597,10 @@
Register result = locs->out().reg();
Label load_true;
__ j(cond, &load_true, Assembler::kNearJump);
- __ LoadObject(result, Bool::False());
+ __ LoadObject(result, Bool::False(), PP);
__ jmp(&done);
__ Bind(&load_true);
- __ LoadObject(result, Bool::True());
+ __ LoadObject(result, Bool::True(), PP);
}
} else {
const int kNumberOfArguments = 2;
@@ -629,10 +616,10 @@
Label false_label;
__ CompareObject(RAX, Bool::True());
__ j(EQUAL, &false_label, Assembler::kNearJump);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ jmp(&done);
__ Bind(&false_label);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
}
} else {
if (branch->is_checked()) {
@@ -666,12 +653,11 @@
__ testq(left, Immediate(kSmiTagMask));
__ j(ZERO, deopt);
// 'left' is not Smi.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
+
Label identity_compare;
- __ cmpq(right, raw_null);
+ __ CompareObject(right, Object::null_object());
__ j(EQUAL, &identity_compare);
- __ cmpq(left, raw_null);
+ __ CompareObject(left, Object::null_object());
__ j(EQUAL, &identity_compare);
__ LoadClassId(temp, left);
@@ -692,10 +678,10 @@
Register result = locs.out().reg();
__ j(EQUAL, &is_equal, Assembler::kNearJump);
// Not equal.
- __ LoadObject(result, Bool::Get(kind != Token::kEQ));
+ __ LoadObject(result, Bool::Get(kind != Token::kEQ), PP);
__ jmp(&done, Assembler::kNearJump);
__ Bind(&is_equal);
- __ LoadObject(result, Bool::Get(kind == Token::kEQ));
+ __ LoadObject(result, Bool::Get(kind == Token::kEQ), PP);
__ Bind(&done);
} else {
Condition cond = TokenKindToSmiCondition(kind);
@@ -718,12 +704,11 @@
ASSERT(!ic_data.IsNull() && (ic_data.NumberOfChecks() > 0));
Register left = locs->in(0).reg();
Register right = locs->in(1).reg();
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
+
Label done, identity_compare, non_null_compare;
- __ cmpq(right, raw_null);
+ __ CompareObject(right, Object::null_object());
__ j(EQUAL, &identity_compare, Assembler::kNearJump);
- __ cmpq(left, raw_null);
+ __ CompareObject(left, Object::null_object());
__ j(NOT_EQUAL, &non_null_compare, Assembler::kNearJump);
// Comparison with NULL is "===".
__ Bind(&identity_compare);
@@ -735,10 +720,10 @@
Register result = locs->out().reg();
Label load_true;
__ j(cond, &load_true, Assembler::kNearJump);
- __ LoadObject(result, Bool::False());
+ __ LoadObject(result, Bool::False(), PP);
__ jmp(&done);
__ Bind(&load_true);
- __ LoadObject(result, Bool::True());
+ __ LoadObject(result, Bool::True(), PP);
}
__ jmp(&done);
__ Bind(&non_null_compare); // Receiver is not null.
@@ -796,10 +781,10 @@
Register result = locs.out().reg();
Label done, is_true;
__ j(true_condition, &is_true);
- __ LoadObject(result, Bool::False());
+ __ LoadObject(result, Bool::False(), PP);
__ jmp(&done);
__ Bind(&is_true);
- __ LoadObject(result, Bool::True());
+ __ LoadObject(result, Bool::True(), PP);
__ Bind(&done);
}
}
@@ -1527,7 +1512,7 @@
ASSERT((field_reg != value_reg) && (field_reg != value_cid_reg));
}
- __ LoadObject(field_reg, Field::ZoneHandle(field().raw()));
+ __ LoadObject(field_reg, Field::ZoneHandle(field().raw()), PP);
FieldAddress field_cid_operand(field_reg, Field::guarded_cid_offset());
FieldAddress field_nullability_operand(
@@ -1553,17 +1538,58 @@
__ pushq(value_cid_reg);
__ movq(value_cid_reg,
FieldAddress(value_reg, Array::length_offset()));
- __ cmpq(value_cid_reg, Immediate(field_length));
+ __ cmpq(value_cid_reg, Immediate(Smi::RawValue(field_length)));
__ popq(value_cid_reg);
} else if (RawObject::IsTypedDataClassId(field_cid)) {
__ pushq(value_cid_reg);
__ movq(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
- __ cmpq(value_cid_reg, Immediate(field_length));
+ __ cmpq(value_cid_reg, Immediate(Smi::RawValue(field_length)));
__ popq(value_cid_reg);
} else {
ASSERT(field_cid == kIllegalCid);
- // Following jump cannot not occur, fall through.
+ ASSERT(field_length == Field::kUnknownFixedLength);
+ // At compile time we do not know the type of the field nor its
+ // length. At execution time we may have set the class id and
+ // list length so we compare the guarded length with the
+ // list length here, without this check the list length could change
+ // without triggering a deoptimization.
+ Label check_array, length_compared, no_fixed_length;
+ // If length is negative the length guard is either disabled or
+ // has not been initialized, either way it is safe to skip the
+ // length check.
+ __ cmpq(field_length_operand, Immediate(Smi::RawValue(0)));
+ __ j(LESS, &skip_length_check);
+ __ cmpq(value_cid_reg, Immediate(kNullCid));
+ __ j(EQUAL, &no_fixed_length, Assembler::kNearJump);
+ // Check for typed data array.
+ __ cmpq(value_cid_reg, Immediate(kTypedDataFloat32x4ArrayCid));
+ // Not a typed array or a regular array.
+ __ j(GREATER, &no_fixed_length, Assembler::kNearJump);
+ __ cmpq(value_cid_reg, Immediate(kTypedDataInt8ArrayCid));
+ // Could still be a regular array.
+ __ j(LESS, &check_array, Assembler::kNearJump);
+ __ pushq(value_cid_reg);
+ __ movq(value_cid_reg,
+ FieldAddress(value_reg, TypedData::length_offset()));
+ __ cmpq(field_length_operand, value_cid_reg);
+ __ popq(value_cid_reg);
+ __ jmp(&length_compared, Assembler::kNearJump);
+ // Check for regular array.
+ __ Bind(&check_array);
+ __ cmpq(value_cid_reg, Immediate(kImmutableArrayCid));
+ __ j(GREATER, &no_fixed_length, Assembler::kNearJump);
+ __ cmpq(value_cid_reg, Immediate(kArrayCid));
+ __ j(LESS, &no_fixed_length, Assembler::kNearJump);
+ __ pushq(value_cid_reg);
+ __ movq(value_cid_reg,
+ FieldAddress(value_reg, Array::length_offset()));
+ __ cmpq(field_length_operand, value_cid_reg);
+ __ popq(value_cid_reg);
+ __ jmp(&length_compared, Assembler::kNearJump);
+ __ Bind(&no_fixed_length);
+ __ jmp(fail);
+ __ Bind(&length_compared);
}
__ j(NOT_EQUAL, fail);
}
@@ -1578,28 +1604,25 @@
__ j(NOT_EQUAL, &skip_length_check);
// Insert length check.
if (field_has_length) {
- if (value_cid_reg == kNoRegister) {
- ASSERT(!compiler->is_optimizing());
- value_cid_reg = RDX;
- ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
- }
ASSERT(value_cid_reg != kNoRegister);
- if ((field_cid == kArrayCid) || (field_cid == kImmutableArrayCid)) {
- __ pushq(value_cid_reg);
- __ movq(value_cid_reg,
- FieldAddress(value_reg, Array::length_offset()));
- __ cmpq(value_cid_reg, Immediate(field_length));
- __ popq(value_cid_reg);
- } else if (RawObject::IsTypedDataClassId(field_cid)) {
- __ pushq(value_cid_reg);
- __ movq(value_cid_reg,
- FieldAddress(value_reg, TypedData::length_offset()));
- __ cmpq(value_cid_reg, Immediate(field_length));
- __ popq(value_cid_reg);
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
+ __ cmpq(FieldAddress(value_reg, Array::length_offset()),
+ Immediate(Smi::RawValue(field_length)));
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
+ __ cmpq(FieldAddress(value_reg, TypedData::length_offset()),
+ Immediate(Smi::RawValue(field_length)));
+ } else if (field_cid != kIllegalCid) {
+ ASSERT(field_cid != value_cid);
+ ASSERT(field_length >= 0);
+ // Field has a known class id and length. At compile time it is
+ // known that the value's class id is not a fixed length list.
+ __ jmp(fail);
} else {
ASSERT(field_cid == kIllegalCid);
+ ASSERT(field_length == Field::kUnknownFixedLength);
// Following jump cannot not occur, fall through.
}
+ __ j(NOT_EQUAL, fail);
}
// Not identical, possibly null.
__ Bind(&skip_length_check);
@@ -1613,67 +1636,67 @@
__ movq(field_cid_operand, value_cid_reg);
__ movq(field_nullability_operand, value_cid_reg);
if (field_has_length) {
- Label check_array, local_exit, local_fail;
+ Label check_array, length_set, no_fixed_length;
__ cmpq(value_cid_reg, Immediate(kNullCid));
- __ j(EQUAL, &local_fail);
+ __ j(EQUAL, &no_fixed_length, Assembler::kNearJump);
// Check for typed data array.
__ cmpq(value_cid_reg, Immediate(kTypedDataFloat32x4ArrayCid));
- __ j(GREATER, &local_fail); // Not a typed array or a regular array.
+ // Not a typed array or a regular array.
+ __ j(GREATER, &no_fixed_length);
__ cmpq(value_cid_reg, Immediate(kTypedDataInt8ArrayCid));
- __ j(LESS, &check_array); // Could still be a regular array.
+ // Could still be a regular array.
+ __ j(LESS, &check_array, Assembler::kNearJump);
// Destroy value_cid_reg (safe because we are finished with it).
__ movq(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
__ movq(field_length_operand, value_cid_reg);
- __ jmp(&local_exit); // Updated field length typed data array.
+ // Updated field length typed data array.
+ __ jmp(&length_set);
// Check for regular array.
__ Bind(&check_array);
__ cmpq(value_cid_reg, Immediate(kImmutableArrayCid));
- __ j(GREATER, &local_fail);
+ __ j(GREATER, &no_fixed_length, Assembler::kNearJump);
__ cmpq(value_cid_reg, Immediate(kArrayCid));
- __ j(LESS, &local_fail);
+ __ j(LESS, &no_fixed_length, Assembler::kNearJump);
// Destroy value_cid_reg (safe because we are finished with it).
__ movq(value_cid_reg,
FieldAddress(value_reg, Array::length_offset()));
__ movq(field_length_operand, value_cid_reg);
- __ jmp(&local_exit); // Updated field length from regular array.
-
- __ Bind(&local_fail);
- __ movq(field_length_operand, Immediate(Field::kNoFixedLength));
-
- __ Bind(&local_exit);
+ // Updated field length from regular array.
+ __ jmp(&length_set, Assembler::kNearJump);
+ __ Bind(&no_fixed_length);
+ __ movq(field_length_operand,
+ Immediate(Smi::RawValue(Field::kNoFixedLength)));
+ __ Bind(&length_set);
}
} else {
- if (value_cid_reg == kNoRegister) {
- ASSERT(!compiler->is_optimizing());
- value_cid_reg = RDX;
- ASSERT((value_cid_reg != value_reg) && (field_reg != value_cid_reg));
- }
- ASSERT(value_cid_reg != kNoRegister);
ASSERT(field_reg != kNoRegister);
__ movq(field_cid_operand, Immediate(value_cid));
__ movq(field_nullability_operand, Immediate(value_cid));
- if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ movq(value_cid_reg,
- FieldAddress(value_reg, Array::length_offset()));
- __ movq(field_length_operand, value_cid_reg);
- } else if (RawObject::IsTypedDataClassId(value_cid)) {
- // Destroy value_cid_reg (safe because we are finished with it).
- __ movq(value_cid_reg,
- FieldAddress(value_reg, TypedData::length_offset()));
- __ movq(field_length_operand, value_cid_reg);
- } else {
- __ movq(field_length_operand, Immediate(Field::kNoFixedLength));
+ if (field_has_length) {
+ ASSERT(value_cid_reg != kNoRegister);
+ if ((value_cid == kArrayCid) || (value_cid == kImmutableArrayCid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ movq(value_cid_reg,
+ FieldAddress(value_reg, Array::length_offset()));
+ __ movq(field_length_operand, value_cid_reg);
+ } else if (RawObject::IsTypedDataClassId(value_cid)) {
+ // Destroy value_cid_reg (safe because we are finished with it).
+ __ movq(value_cid_reg,
+ FieldAddress(value_reg, TypedData::length_offset()));
+ __ movq(field_length_operand, value_cid_reg);
+ } else {
+ __ movq(field_length_operand,
+ Immediate(Smi::RawValue(Field::kNoFixedLength)));
+ }
}
}
-
if (!ok_is_fall_through) {
__ jmp(&ok);
}
} else {
if (field_reg != kNoRegister) {
- __ LoadObject(field_reg, Field::ZoneHandle(field().raw()));
+ __ LoadObject(field_reg, Field::ZoneHandle(field().raw()), PP);
}
if (value_cid == kDynamicCid) {
@@ -1709,9 +1732,7 @@
if (field().is_nullable() && (field_cid != kNullCid)) {
__ j(EQUAL, &ok);
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ cmpq(value_reg, raw_null);
+ __ CompareObject(value_reg, Object::null_object());
}
if (ok_is_fall_through) {
@@ -1736,7 +1757,7 @@
__ movq(value_cid_reg,
FieldAddress(value_reg, TypedData::length_offset()));
}
- __ cmpq(value_cid_reg, Immediate(field_length));
+ __ cmpq(value_cid_reg, Immediate(Smi::RawValue(field_length)));
if (ok_is_fall_through) {
__ j(NOT_EQUAL, fail);
}
@@ -1836,7 +1857,7 @@
Register value = locs()->in(0).reg();
Register temp = locs()->temp(0).reg();
- __ LoadObject(temp, field());
+ __ LoadObject(temp, field(), PP);
if (this->value()->NeedsStoreBuffer()) {
__ StoreIntoObject(temp,
FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
@@ -1990,9 +2011,7 @@
Label type_arguments_instantiated;
const intptr_t len = type_arguments().Length();
if (type_arguments().IsRawInstantiatedRaw(len)) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ cmpq(instantiator_reg, raw_null);
+ __ CompareObject(instantiator_reg, Object::null_object());
__ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
}
// Instantiate non-null type arguments.
@@ -2040,14 +2059,13 @@
// the type arguments.
Label type_arguments_instantiated;
ASSERT(type_arguments().IsRawInstantiatedRaw(type_arguments().Length()));
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ cmpq(instantiator_reg, raw_null);
+
+ __ CompareObject(instantiator_reg, Object::null_object());
__ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
// Instantiate non-null type arguments.
// In the non-factory case, we rely on the allocation stub to
// instantiate the type arguments.
- __ LoadObject(result_reg, type_arguments());
+ __ LoadObject(result_reg, type_arguments(), PP);
// result_reg: uninstantiated type arguments.
__ Bind(&type_arguments_instantiated);
@@ -2082,10 +2100,9 @@
// instantiated from null becomes a vector of dynamic, then use null as
// the type arguments and do not pass the instantiator.
ASSERT(type_arguments().IsRawInstantiatedRaw(type_arguments().Length()));
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
+
Label instantiator_not_null;
- __ cmpq(instantiator_reg, raw_null);
+ __ CompareObject(instantiator_reg, Object::null_object());
__ j(NOT_EQUAL, &instantiator_not_null, Assembler::kNearJump);
// Null was used in VisitExtractConstructorTypeArguments as the
// instantiated type arguments, no proper instantiator needed.
@@ -2161,6 +2178,10 @@
compiler->assembler()->CodeSize(),
catch_handler_types_,
needs_stacktrace());
+
+ // Restore the pool pointer.
+ __ LoadPoolPointer(PP);
+
if (HasParallelMove()) {
compiler->parallel_move_resolver()->EmitNativeCode(parallel_move());
}
@@ -2241,7 +2262,7 @@
// In unoptimized code check the usage counter to trigger OSR at loop
// stack checks. Use progressively higher thresholds for more deeply
// nested loops to attempt to hit outer loops with OSR when possible.
- __ LoadObject(temp, compiler->parsed_function().function());
+ __ LoadObject(temp, compiler->parsed_function().function(), PP);
intptr_t threshold =
FLAG_optimization_counter_threshold * (loop_depth() + 1);
__ cmpq(FieldAddress(temp, Function::usage_counter_offset()),
@@ -3688,10 +3709,10 @@
__ addq(RSP, Immediate(16));
__ testl(result, result);
__ j(NOT_ZERO, &non_zero, Assembler::kNearJump);
- __ LoadObject(result, Bool::False());
+ __ LoadObject(result, Bool::False(), PP);
__ jmp(&done);
__ Bind(&non_zero);
- __ LoadObject(result, Bool::True());
+ __ LoadObject(result, Bool::True(), PP);
__ Bind(&done);
}
@@ -3853,6 +3874,20 @@
LocationSummary* MathUnaryInstr::MakeLocationSummary() const {
+ if ((kind() == MethodRecognizer::kMathSin) ||
+ (kind() == MethodRecognizer::kMathCos)) {
+ // Calling convention on x64 uses XMM0 and XMM1 to pass the first two
+ // double arguments and XMM0 to return the result. Unfortunately
+ // currently we can't specify these registers because ParallelMoveResolver
+ // assumes that XMM0 is free at all times.
+ // TODO(vegorov): allow XMM0 to be used.
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(InputCount(), kNumTemps, LocationSummary::kCall);
+ summary->set_in(0, Location::FpuRegisterLocation(XMM1));
+ summary->set_out(Location::FpuRegisterLocation(XMM1));
+ return summary;
+ }
const intptr_t kNumInputs = 1;
const intptr_t kNumTemps = 0;
LocationSummary* summary =
@@ -3866,23 +3901,13 @@
void MathUnaryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
if (kind() == MethodRecognizer::kMathSqrt) {
__ sqrtsd(locs()->out().fpu_reg(), locs()->in(0).fpu_reg());
- } else if ((kind() == MethodRecognizer::kMathCos) ||
- (kind() == MethodRecognizer::kMathSin)) {
- __ pushq(RAX);
- __ movsd(Address(RSP, 0), locs()->in(0).fpu_reg());
- __ fldl(Address(RSP, 0));
- if (kind() == MethodRecognizer::kMathSin) {
- __ fsin();
- } else {
- ASSERT(kind() == MethodRecognizer::kMathCos);
- __ fcos();
- }
- __ fstpl(Address(RSP, 0));
- __ movsd(locs()->out().fpu_reg(), Address(RSP, 0));
- __ addq(RSP, Immediate(kWordSize));
- return;
} else {
- UNREACHABLE();
+ __ EnterFrame(0);
+ __ ReserveAlignedFrameSpace(0);
+ __ movaps(XMM0, locs()->in(0).fpu_reg());
+ __ CallRuntime(TargetFunction(), InputCount());
+ __ movaps(locs()->out().fpu_reg(), XMM0);
+ __ leave();
}
}
@@ -4205,9 +4230,9 @@
Label check_base_is_one;
// Check if exponent is 0.0 -> return 1.0;
- __ LoadObject(temp, Double::ZoneHandle(Double::NewCanonical(0)));
+ __ LoadObject(temp, Double::ZoneHandle(Double::NewCanonical(0)), PP);
__ movsd(zero_temp, FieldAddress(temp, Double::value_offset()));
- __ LoadObject(temp, Double::ZoneHandle(Double::NewCanonical(1)));
+ __ LoadObject(temp, Double::ZoneHandle(Double::NewCanonical(1)), PP);
__ movsd(result, FieldAddress(temp, Double::value_offset()));
// 'result' contains 1.0.
__ comisd(exp, zero_temp);
@@ -4305,9 +4330,8 @@
if (IsNullCheck()) {
Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptCheckClass);
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ cmpq(locs()->in(0).reg(), raw_null);
+ __ CompareObject(locs()->in(0).reg(),
+ Object::null_object());
__ j(EQUAL, deopt);
return;
}
@@ -4506,6 +4530,13 @@
}
+void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ if (!compiler->CanFallThroughTo(normal_entry())) {
+ __ jmp(compiler->GetJumpLabel(normal_entry()));
+ }
+}
+
+
void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ Bind(compiler->GetJumpLabel(this));
if (!compiler->is_optimizing()) {
@@ -4517,7 +4548,7 @@
counter.SetAt(0, Smi::Handle(Smi::New(0)));
Label done;
__ Comment("Edge counter");
- __ LoadObject(RAX, counter);
+ __ LoadObject(RAX, counter, PP);
__ addq(FieldAddress(RAX, Array::element_offset(0)),
Immediate(Smi::RawValue(1)));
__ j(NO_OVERFLOW, &done);
@@ -4548,7 +4579,7 @@
counter.SetAt(0, Smi::Handle(Smi::New(0)));
Label done;
__ Comment("Edge counter");
- __ LoadObject(RAX, counter);
+ __ LoadObject(RAX, counter, PP);
__ addq(FieldAddress(RAX, Array::element_offset(0)),
Immediate(Smi::RawValue(1)));
__ j(NO_OVERFLOW, &done);
@@ -4581,11 +4612,10 @@
void ControlInstruction::EmitBranchOnCondition(FlowGraphCompiler* compiler,
Condition true_condition) {
if (compiler->CanFallThroughTo(false_successor())) {
- // If the next block is the false successor we will fall through to it.
+ // If the next block is the false successor, fall through to it.
__ j(true_condition, compiler->GetJumpLabel(true_successor()));
} else {
- // If the next block is the true successor we negate comparison and fall
- // through to it.
+ // If the next block is not the false successor, branch to it.
Condition false_condition = NegateCondition(true_condition);
__ j(false_condition, compiler->GetJumpLabel(false_successor()));
@@ -4631,7 +4661,7 @@
const bool result = (kind() == Token::kEQ_STRICT) ?
left.constant().raw() == right.constant().raw() :
left.constant().raw() != right.constant().raw();
- __ LoadObject(locs()->out().reg(), Bool::Get(result));
+ __ LoadObject(locs()->out().reg(), Bool::Get(result), PP);
return;
}
if (left.IsConstant()) {
@@ -4655,10 +4685,10 @@
Label load_true, done;
Condition true_condition = (kind() == Token::kEQ_STRICT) ? EQUAL : NOT_EQUAL;
__ j(true_condition, &load_true, Assembler::kNearJump);
- __ LoadObject(result, Bool::False());
+ __ LoadObject(result, Bool::False(), PP);
__ jmp(&done, Assembler::kNearJump);
__ Bind(&load_true);
- __ LoadObject(result, Bool::True());
+ __ LoadObject(result, Bool::True(), PP);
__ Bind(&done);
}
@@ -4717,7 +4747,7 @@
const Array& arguments_descriptor =
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names()));
- __ LoadObject(temp_reg, arguments_descriptor);
+ __ LoadObject(temp_reg, arguments_descriptor, PP);
ASSERT(temp_reg == R10);
compiler->GenerateDartCall(deopt_id(),
token_pos(),
@@ -4740,10 +4770,10 @@
Register result = locs()->out().reg();
Label done;
- __ LoadObject(result, Bool::True());
+ __ LoadObject(result, Bool::True(), PP);
__ CompareRegisters(result, value);
__ j(NOT_EQUAL, &done, Assembler::kNearJump);
- __ LoadObject(result, Bool::False());
+ __ LoadObject(result, Bool::False(), PP);
__ Bind(&done);
}
diff --git a/runtime/vm/intrinsifier.cc b/runtime/vm/intrinsifier.cc
index 12c15ec..e7edd62 100644
--- a/runtime/vm/intrinsifier.cc
+++ b/runtime/vm/intrinsifier.cc
@@ -102,10 +102,10 @@
#define SETUP_FUNCTION(class_name, function_name, destination, fp) \
if (strcmp(#class_name, "::") == 0) { \
str = String::New(#function_name); \
- func = lib.LookupFunctionAllowPrivate(str, NULL); \
+ func = lib.LookupFunctionAllowPrivate(str); \
} else { \
str = String::New(#class_name); \
- cls = lib.LookupClassAllowPrivate(str, NULL); \
+ cls = lib.LookupClassAllowPrivate(str); \
ASSERT(!cls.IsNull()); \
error = cls.EnsureIsFinalized(isolate); \
ASSERT(error.IsNull()); \
diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h
index 04d4199..9b54c7f 100644
--- a/runtime/vm/intrinsifier.h
+++ b/runtime/vm/intrinsifier.h
@@ -17,6 +17,7 @@
// build and run to get the correct fingerprint from the mismatch error.
#define CORE_LIB_INTRINSIC_LIST(V) \
V(_Smi, ~, Smi_bitNegate, 635678453) \
+ V(_Smi, get:bitLength, Smi_bitLength, 383357874) \
V(_Double, >, Double_greaterThan, 1021232334) \
V(_Double, >=, Double_greaterEqualThan, 324955595) \
V(_Double, <, Double_lessThan, 978151157) \
@@ -32,17 +33,17 @@
V(_Double, .fromInteger, Double_fromInteger, 475441744) \
V(_ObjectArray, ., ObjectArray_Allocate, 1930677134) \
V(_ObjectArray, get:length, Array_getLength, 259323113) \
- V(_ObjectArray, [], Array_getIndexed, 93386978) \
- V(_ObjectArray, []=, Array_setIndexed, 1296046137) \
+ V(_ObjectArray, [], Array_getIndexed, 1353366945) \
+ V(_ObjectArray, []=, Array_setIndexed, 1492559642) \
V(_GrowableObjectArray, .withData, GrowableArray_Allocate, 1012992871) \
V(_GrowableObjectArray, get:length, GrowableArray_getLength, 1160357614) \
V(_GrowableObjectArray, get:_capacity, GrowableArray_getCapacity, 1509781988)\
- V(_GrowableObjectArray, [], GrowableArray_getIndexed, 500679426) \
- V(_GrowableObjectArray, []=, GrowableArray_setIndexed, 211112998) \
+ V(_GrowableObjectArray, [], GrowableArray_getIndexed, 1760659393) \
+ V(_GrowableObjectArray, []=, GrowableArray_setIndexed, 407626503) \
V(_GrowableObjectArray, _setLength, GrowableArray_setLength, 1922121178) \
V(_GrowableObjectArray, _setData, GrowableArray_setData, 236295352) \
V(_GrowableObjectArray, add, GrowableArray_add, 1442410650) \
- V(_ImmutableArray, [], ImmutableArray_getIndexed, 894753724) \
+ V(_ImmutableArray, [], ImmutableArray_getIndexed, 7250043) \
V(_ImmutableArray, get:length, ImmutableArray_getLength, 1341942416) \
V(Object, ==, Object_equal, 677817295) \
V(_StringBase, get:hashCode, String_getHashCode, 654483446) \
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 6bae006..734b889 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -168,7 +168,7 @@
static intptr_t ComputeObjectArrayTypeArgumentsOffset() {
const Library& core_lib = Library::Handle(Library::CoreLibrary());
const Class& cls = Class::Handle(
- core_lib.LookupClassAllowPrivate(Symbols::ObjectArray(), NULL));
+ core_lib.LookupClassAllowPrivate(Symbols::ObjectArray()));
ASSERT(!cls.IsNull());
ASSERT(cls.HasTypeArguments());
ASSERT(cls.NumTypeArguments() == 1);
@@ -1071,6 +1071,11 @@
}
+void Intrinsifier::Smi_bitLength(Assembler* assembler) {
+ // TODO(sra): Implement as word-length - CLZ.
+}
+
+
// Check if the last argument is a double, jump to label 'is_smi' if smi
// (easy to convert to double), otherwise jump to label 'not_double_smi',
// Returns the last argument in R0.
@@ -1332,7 +1337,7 @@
const Library& math_lib = Library::Handle(Library::MathLibrary());
ASSERT(!math_lib.IsNull());
const Class& random_class = Class::Handle(
- math_lib.LookupClassAllowPrivate(Symbols::_Random(), NULL));
+ math_lib.LookupClassAllowPrivate(Symbols::_Random()));
ASSERT(!random_class.IsNull());
const Field& state_field = Field::ZoneHandle(
random_class.LookupInstanceField(Symbols::_state()));
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index 251ba78..64758e6 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -173,7 +173,7 @@
static intptr_t ComputeObjectArrayTypeArgumentsOffset() {
const Library& core_lib = Library::Handle(Library::CoreLibrary());
const Class& cls = Class::Handle(
- core_lib.LookupClassAllowPrivate(Symbols::ObjectArray(), NULL));
+ core_lib.LookupClassAllowPrivate(Symbols::ObjectArray()));
ASSERT(!cls.IsNull());
ASSERT(cls.HasTypeArguments());
ASSERT(cls.NumTypeArguments() == 1);
@@ -1087,6 +1087,22 @@
}
+void Intrinsifier::Smi_bitLength(Assembler* assembler) {
+ ASSERT(kSmiTagShift == 1);
+ __ movl(EAX, Address(ESP, + 1 * kWordSize)); // Index.
+ // XOR with sign bit to complement bits if value is negative.
+ __ movl(ECX, EAX);
+ __ sarl(ECX, Immediate(31)); // All 0 or all 1.
+ __ xorl(EAX, ECX);
+ // BSR does not write the destination register if source is zero. Put a 1 in
+ // the Smi tag bit to ensure BSR writes to destination register.
+ __ orl(EAX, Immediate(kSmiTagMask));
+ __ bsrl(EAX, EAX);
+ __ SmiTag(EAX);
+ __ ret();
+}
+
+
// Check if the last argument is a double, jump to label 'is_smi' if smi
// (easy to convert to double), otherwise jump to label 'not_double_smi',
// Returns the last argument in EAX.
@@ -1393,7 +1409,7 @@
const Library& math_lib = Library::Handle(Library::MathLibrary());
ASSERT(!math_lib.IsNull());
const Class& random_class = Class::Handle(
- math_lib.LookupClassAllowPrivate(Symbols::_Random(), NULL));
+ math_lib.LookupClassAllowPrivate(Symbols::_Random()));
ASSERT(!random_class.IsNull());
const Field& state_field = Field::ZoneHandle(
random_class.LookupInstanceField(Symbols::_state()));
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 6eb52e8..de39ced8 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -176,7 +176,7 @@
static intptr_t ComputeObjectArrayTypeArgumentsOffset() {
const Library& core_lib = Library::Handle(Library::CoreLibrary());
const Class& cls = Class::Handle(
- core_lib.LookupClassAllowPrivate(Symbols::ObjectArray(), NULL));
+ core_lib.LookupClassAllowPrivate(Symbols::ObjectArray()));
ASSERT(!cls.IsNull());
ASSERT(cls.HasTypeArguments());
ASSERT(cls.NumTypeArguments() == 1);
@@ -1093,6 +1093,11 @@
}
+void Intrinsifier::Smi_bitLength(Assembler* assembler) {
+ // TODO(sra): Implement.
+}
+
+
// Check if the last argument is a double, jump to label 'is_smi' if smi
// (easy to convert to double), otherwise jump to label 'not_double_smi',
// Returns the last argument in T0.
@@ -1394,7 +1399,7 @@
const Library& math_lib = Library::Handle(Library::MathLibrary());
ASSERT(!math_lib.IsNull());
const Class& random_class = Class::Handle(
- math_lib.LookupClassAllowPrivate(Symbols::_Random(), NULL));
+ math_lib.LookupClassAllowPrivate(Symbols::_Random()));
ASSERT(!random_class.IsNull());
const Field& state_field = Field::ZoneHandle(
random_class.LookupInstanceField(Symbols::_state()));
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index 8b23532..cdfa888 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -117,15 +117,14 @@
// RCX: new object end address.
// RDI: iterator which initially points to the start of the variable
// data area to be initialized.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ LoadObject(R12, Object::null_object(), PP);
__ leaq(RDI, FieldAddress(RAX, sizeof(RawArray)));
Label done;
Label init_loop;
__ Bind(&init_loop);
__ cmpq(RDI, RCX);
__ j(ABOVE_EQUAL, &done, Assembler::kNearJump);
- __ movq(Address(RDI, 0), raw_null);
+ __ movq(Address(RDI, 0), R12);
__ addq(RDI, Immediate(kWordSize));
__ jmp(&init_loop, Assembler::kNearJump);
__ Bind(&done);
@@ -392,9 +391,7 @@
__ StoreIntoObject(RDX,
FieldAddress(RDX, RCX, TIMES_4, Array::data_offset()),
RAX);
- const Immediate& raw_null =
- Immediate(reinterpret_cast<int64_t>(Object::null()));
- __ movq(RAX, raw_null);
+ __ LoadObject(RAX, Object::null_object(), PP);
__ ret();
__ Bind(&fall_through);
}
@@ -879,10 +876,10 @@
// RAX contains the right argument.
__ cmpq(Address(RSP, + 2 * kWordSize), RAX);
__ j(true_condition, &true_label, Assembler::kNearJump);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
__ ret();
__ Bind(&true_label);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ ret();
__ Bind(&fall_through);
}
@@ -919,34 +916,39 @@
void Intrinsifier::Integer_equalToInteger(Assembler* assembler) {
Label fall_through, true_label, check_for_mint;
// For integer receiver '===' check first.
- __ movq(RAX, Address(RSP, + 1 * kWordSize));
- __ movq(RCX, Address(RSP, + 2 * kWordSize));
+ // Entering a dart frame so we can use the PP for loading True and False.
+ __ EnterDartFrame(0);
+ __ movq(RAX, Address(RSP, + 4 * kWordSize));
+ __ movq(RCX, Address(RSP, + 5 * kWordSize));
__ cmpq(RAX, RCX);
__ j(EQUAL, &true_label, Assembler::kNearJump);
__ orq(RAX, RCX);
__ testq(RAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &check_for_mint, Assembler::kNearJump);
// Both arguments are smi, '===' is good enough.
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
+ __ LeaveFrameWithPP();
__ ret();
__ Bind(&true_label);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
+ __ LeaveFrameWithPP();
__ ret();
// At least one of the arguments was not Smi.
Label receiver_not_smi;
__ Bind(&check_for_mint);
- __ movq(RAX, Address(RSP, + 2 * kWordSize)); // Receiver.
+ __ movq(RAX, Address(RSP, + 5 * kWordSize)); // Receiver.
__ testq(RAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &receiver_not_smi);
// Left (receiver) is Smi, return false if right is not Double.
// Note that an instance of Mint or Bigint never contains a value that can be
// represented by Smi.
- __ movq(RAX, Address(RSP, + 1 * kWordSize));
+ __ movq(RAX, Address(RSP, + 4 * kWordSize));
__ CompareClassId(RAX, kDoubleCid);
__ j(EQUAL, &fall_through);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
+ __ LeaveFrameWithPP();
__ ret();
__ Bind(&receiver_not_smi);
@@ -954,14 +956,17 @@
__ CompareClassId(RAX, kMintCid);
__ j(NOT_EQUAL, &fall_through);
// Receiver is Mint, return false if right is Smi.
- __ movq(RAX, Address(RSP, + 1 * kWordSize)); // Right argument.
+ __ movq(RAX, Address(RSP, + 4 * kWordSize)); // Right argument.
__ testq(RAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &fall_through);
- __ LoadObject(RAX, Bool::False()); // Smi == Mint -> false.
+ // Smi == Mint -> false.
+ __ LoadObject(RAX, Bool::False(), PP);
+ __ LeaveFrameWithPP();
__ ret();
// TODO(srdjan): Implement Mint == Mint comparison.
__ Bind(&fall_through);
+ __ LeaveFrameWithPP();
}
@@ -1004,6 +1009,11 @@
}
+void Intrinsifier::Smi_bitLength(Assembler* assembler) {
+ // TODO(sra): Implement using bsrq.
+}
+
+
// Check if the last argument is a double, jump to label 'is_smi' if smi
// (easy to convert to double), otherwise jump to label 'not_double_smi',
// Returns the last argument in RAX.
@@ -1036,10 +1046,10 @@
__ j(true_condition, &is_true, Assembler::kNearJump);
// Fall through false.
__ Bind(&is_false);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
__ ret();
__ Bind(&is_true);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ ret();
__ Bind(&is_smi);
__ SmiUntag(RAX);
@@ -1173,10 +1183,10 @@
__ movsd(XMM0, FieldAddress(RAX, Double::value_offset()));
__ comisd(XMM0, XMM0);
__ j(PARITY_EVEN, &is_true, Assembler::kNearJump); // NaN -> true;
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
__ ret();
__ Bind(&is_true);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ ret();
}
@@ -1191,10 +1201,10 @@
__ j(EQUAL, &is_zero, Assembler::kNearJump); // Check for negative zero.
__ j(ABOVE_EQUAL, &is_false, Assembler::kNearJump); // >= 0 -> false.
__ Bind(&is_true);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ ret();
__ Bind(&is_false);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
__ ret();
__ Bind(&is_zero);
// Check for negative zero (get the sign bit).
@@ -1305,7 +1315,7 @@
const Library& math_lib = Library::Handle(Library::MathLibrary());
ASSERT(!math_lib.IsNull());
const Class& random_class = Class::Handle(
- math_lib.LookupClassAllowPrivate(Symbols::_Random(), NULL));
+ math_lib.LookupClassAllowPrivate(Symbols::_Random()));
ASSERT(!random_class.IsNull());
const Field& state_field = Field::ZoneHandle(
random_class.LookupInstanceField(Symbols::_state()));
@@ -1342,17 +1352,23 @@
}
-
// Identity comparison.
void Intrinsifier::Object_equal(Assembler* assembler) {
Label is_true;
- __ movq(RAX, Address(RSP, + 1 * kWordSize));
- __ cmpq(RAX, Address(RSP, + 2 * kWordSize));
+ // This intrinsic is used from the API even when we have not entered any
+ // Dart frame, yet, so the PP would otherwise be null in this case unless
+ // we enter a Dart frame here.
+ __ EnterDartFrame(0);
+ __ movq(RAX, Address(RSP, + 4 * kWordSize));
+ __ cmpq(RAX, Address(RSP, + 5 * kWordSize));
__ j(EQUAL, &is_true, Assembler::kNearJump);
- __ LoadObject(RAX, Bool::False());
+ __ movq(RAX, Immediate(reinterpret_cast<int64_t>(Bool::False().raw())));
+ __ LoadObject(RAX, Bool::False(), PP);
+ __ LeaveFrameWithPP();
__ ret();
__ Bind(&is_true);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -1412,10 +1428,10 @@
__ movq(RAX, FieldAddress(RAX, String::length_offset()));
__ cmpq(RAX, Immediate(Smi::RawValue(0)));
__ j(EQUAL, &is_true, Assembler::kNearJump);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
__ ret();
__ Bind(&is_true);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ ret();
}
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index a572e76..3673b92 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -10,6 +10,7 @@
#include "lib/mirrors.h"
#include "vm/code_observers.h"
#include "vm/compiler_stats.h"
+#include "vm/coverage.h"
#include "vm/dart_api_state.h"
#include "vm/dart_entry.h"
#include "vm/debugger.h"
@@ -712,8 +713,6 @@
void Isolate::PrintInvokedFunctions() {
ASSERT(this == Isolate::Current());
- StackZone zone(this);
- HandleScope handle_scope(this);
const GrowableObjectArray& libraries =
GrowableObjectArray::Handle(object_store()->libraries());
Library& library = Library::Handle();
@@ -758,51 +757,54 @@
ASSERT(top_resource() == NULL);
ASSERT((heap_ == NULL) || heap_->Verify());
- if (FLAG_print_object_histogram) {
+ // Create an area where we do have a zone and a handle scope so that we can
+ // call VM functions while tearing this isolate down.
+ {
StackZone stack_zone(this);
HandleScope handle_scope(this);
- heap()->CollectAllGarbage();
- object_histogram()->Print();
+
+ if (FLAG_print_object_histogram) {
+ heap()->CollectAllGarbage();
+ object_histogram()->Print();
+ }
+
+ // Clean up debugger resources.
+ debugger()->Shutdown();
+
+ // Close all the ports owned by this isolate.
+ PortMap::ClosePorts(message_handler());
+
+ // Fail fast if anybody tries to post any more messsages to this isolate.
+ delete message_handler();
+ set_message_handler(NULL);
+
+ // Dump all accumalated timer data for the isolate.
+ timer_list_.ReportTimers();
+ if (FLAG_report_usage_count) {
+ PrintInvokedFunctions();
+ }
+
+ if (FLAG_print_coverage) {
+ CodeCoverage::Print(this);
+ }
+
+ // Finalize any weak persistent handles with a non-null referent.
+ FinalizeWeakPersistentHandlesVisitor visitor;
+ api_state()->weak_persistent_handles().VisitHandles(&visitor);
+
+ CompilerStats::Print();
+ // TODO(asiva): Move this code to Dart::Cleanup when we have that method
+ // as the cleanup for Dart::InitOnce.
+ CodeObservers::DeleteAll();
+ if (FLAG_trace_isolates) {
+ heap()->PrintSizes();
+ megamorphic_cache_table()->PrintSizes();
+ Symbols::DumpStats();
+ OS::Print("[-] Stopping isolate:\n"
+ "\tisolate: %s\n", name());
+ }
}
- // Clean up debugger resources. Shutting down the debugger
- // requires a handle zone. We must set up a temporary zone because
- // Isolate::Shutdown is called without a zone.
- {
- StackZone zone(this);
- HandleScope handle_scope(this);
- debugger_->Shutdown();
- }
-
- // Close all the ports owned by this isolate.
- PortMap::ClosePorts(message_handler());
-
- // Fail fast if anybody tries to post any more messsages to this isolate.
- delete message_handler();
- set_message_handler(NULL);
-
- // Finalize any weak persistent handles with a non-null referent.
- FinalizeWeakPersistentHandlesVisitor visitor;
- api_state()->weak_persistent_handles().VisitHandles(&visitor);
-
- // Dump all accumalated timer data for the isolate.
- timer_list_.ReportTimers();
- if (FLAG_report_usage_count) {
- PrintInvokedFunctions();
- }
- CompilerStats::Print();
- // TODO(asiva): Move this code to Dart::Cleanup when we have that method
- // as the cleanup for Dart::InitOnce.
- CodeObservers::DeleteAll();
- if (FLAG_trace_isolates) {
- StackZone zone(this);
- HandleScope handle_scope(this);
- heap()->PrintSizes();
- megamorphic_cache_table()->PrintSizes();
- Symbols::DumpStats();
- OS::Print("[-] Stopping isolate:\n"
- "\tisolate: %s\n", name());
- }
// TODO(5411455): For now just make sure there are no current isolates
// as we are shutting down the isolate.
SetCurrent(NULL);
@@ -918,7 +920,7 @@
intptr_t frame_index = isolate->stack_frame_index_;
if (frame_index >= stack->Length()) {
// Frame no longer available.
- return NULL;
+ return false;
}
ActivationFrame* frame = stack->FrameAt(frame_index);
TextBuffer buffer(256);
diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc
index ace783e..1b89e24 100644
--- a/runtime/vm/json_stream.cc
+++ b/runtime/vm/json_stream.cc
@@ -9,9 +9,7 @@
namespace dart {
-JSONStream::JSONStream(TextBuffer* buffer) {
- ASSERT(buffer != NULL);
- buffer_ = buffer;
+JSONStream::JSONStream(intptr_t buf_size) : buffer_(buf_size) {
open_objects_ = 0;
arguments_ = NULL;
num_arguments_ = 0;
@@ -26,7 +24,7 @@
void JSONStream::Clear() {
- buffer_->Clear();
+ buffer_.Clear();
open_objects_ = 0;
}
@@ -37,14 +35,14 @@
if (property_name != NULL) {
PrintPropertyName(property_name);
}
- buffer_->AddChar('{');
+ buffer_.AddChar('{');
}
void JSONStream::CloseObject() {
ASSERT(open_objects_ > 0);
open_objects_--;
- buffer_->AddChar('}');
+ buffer_.AddChar('}');
}
@@ -54,40 +52,40 @@
PrintPropertyName(property_name);
}
open_objects_++;
- buffer_->AddChar('[');
+ buffer_.AddChar('[');
}
void JSONStream::CloseArray() {
ASSERT(open_objects_ > 0);
open_objects_--;
- buffer_->AddChar(']');
+ buffer_.AddChar(']');
}
void JSONStream::PrintValueBool(bool b) {
PrintCommaIfNeeded();
- buffer_->Printf("%s", b ? "true" : "false");
+ buffer_.Printf("%s", b ? "true" : "false");
}
void JSONStream::PrintValue(intptr_t i) {
PrintCommaIfNeeded();
- buffer_->Printf("%" Pd "", i);
+ buffer_.Printf("%" Pd "", i);
}
void JSONStream::PrintValue(double d) {
PrintCommaIfNeeded();
- buffer_->Printf("%f", d);
+ buffer_.Printf("%f", d);
}
void JSONStream::PrintValue(const char* s) {
PrintCommaIfNeeded();
- buffer_->AddChar('"');
- buffer_->AddEscapedString(s);
- buffer_->AddChar('"');
+ buffer_.AddChar('"');
+ buffer_.AddEscapedString(s);
+ buffer_.AddChar('"');
}
@@ -103,9 +101,9 @@
intptr_t len2 = OS::VSNPrint(p, len+1, format, args);
va_end(args);
ASSERT(len == len2);
- buffer_->AddChar('"');
- buffer_->AddEscapedString(p);
- buffer_->AddChar('"');
+ buffer_.AddChar('"');
+ buffer_.AddEscapedString(p);
+ buffer_.AddChar('"');
free(p);
}
@@ -151,9 +149,9 @@
intptr_t len2 = OS::VSNPrint(p, len+1, format, args);
va_end(args);
ASSERT(len == len2);
- buffer_->AddChar('"');
- buffer_->AddEscapedString(p);
- buffer_->AddChar('"');
+ buffer_.AddChar('"');
+ buffer_.AddEscapedString(p);
+ buffer_.AddChar('"');
free(p);
}
@@ -182,28 +180,70 @@
void JSONStream::PrintPropertyName(const char* name) {
ASSERT(name != NULL);
PrintCommaIfNeeded();
- buffer_->AddChar('"');
- buffer_->AddEscapedString(name);
- buffer_->AddChar('"');
- buffer_->AddChar(':');
+ buffer_.AddChar('"');
+ buffer_.AddEscapedString(name);
+ buffer_.AddChar('"');
+ buffer_.AddChar(':');
}
void JSONStream::PrintCommaIfNeeded() {
if (NeedComma()) {
- buffer_->AddChar(',');
+ buffer_.AddChar(',');
}
}
bool JSONStream::NeedComma() {
- const char* buffer = buffer_->buf();
- intptr_t length = buffer_->length();
+ const char* buffer = buffer_.buf();
+ intptr_t length = buffer_.length();
if (length == 0) {
return false;
}
char ch = buffer[length-1];
- return ch != '[' && ch != '{' && ch != ':' && ch != ',';
+ return (ch != '[') && (ch != '{') && (ch != ':') && (ch != ',');
+}
+
+
+JSONObject::JSONObject(const JSONArray* arr) : stream_(arr->stream_) {
+ stream_->OpenObject();
+}
+
+
+void JSONObject::AddPropertyF(const char* name,
+ const char* format, ...) const {
+ stream_->PrintPropertyName(name);
+ va_list args;
+ va_start(args, format);
+ intptr_t len = OS::VSNPrint(NULL, 0, format, args);
+ va_end(args);
+ char* p = reinterpret_cast<char*>(malloc(len+1));
+ va_start(args, format);
+ intptr_t len2 = OS::VSNPrint(p, len+1, format, args);
+ va_end(args);
+ ASSERT(len == len2);
+ stream_->buffer_.AddChar('"');
+ stream_->buffer_.AddEscapedString(p);
+ stream_->buffer_.AddChar('"');
+ free(p);
+}
+
+
+void JSONArray::AddValueF(const char* format, ...) const {
+ stream_->PrintCommaIfNeeded();
+ va_list args;
+ va_start(args, format);
+ intptr_t len = OS::VSNPrint(NULL, 0, format, args);
+ va_end(args);
+ char* p = reinterpret_cast<char*>(malloc(len+1));
+ va_start(args, format);
+ intptr_t len2 = OS::VSNPrint(p, len+1, format, args);
+ va_end(args);
+ ASSERT(len == len2);
+ stream_->buffer_.AddChar('"');
+ stream_->buffer_.AddEscapedString(p);
+ stream_->buffer_.AddChar('"');
+ free(p);
}
} // namespace dart
diff --git a/runtime/vm/json_stream.h b/runtime/vm/json_stream.h
index 79b1c69..ebbd8ae 100644
--- a/runtime/vm/json_stream.h
+++ b/runtime/vm/json_stream.h
@@ -6,16 +6,38 @@
#define VM_JSON_STREAM_H_
#include "platform/json.h"
+#include "vm/allocation.h"
namespace dart {
class Object;
+class JSONObject;
+class JSONArray;
class JSONStream : ValueObject {
public:
- explicit JSONStream(TextBuffer* buffer);
+ explicit JSONStream(intptr_t buf_size = 256);
~JSONStream();
+ const char* ToCString() { return buffer_.buf(); }
+
+ void SetArguments(const char** arguments, intptr_t num_arguments);
+ void SetOptions(const char** option_keys, const char** option_values,
+ intptr_t num_options);
+
+ intptr_t num_arguments() const { return num_arguments_; }
+ const char* GetArgument(intptr_t i) const {
+ return arguments_[i];
+ }
+ intptr_t num_options() const { return num_options_; }
+ const char* GetOptionKey(intptr_t i) const {
+ return option_keys_[i];
+ }
+ const char* GetOptionValue(intptr_t i) const {
+ return option_values_[i];
+ }
+
+ private:
void Clear();
void OpenObject(const char* property_name = NULL);
@@ -36,36 +58,101 @@
void PrintProperty(const char* name, double d);
void PrintProperty(const char* name, const char* s);
void PrintfProperty(const char* name, const char* format, ...)
- PRINTF_ATTRIBUTE(3, 4);
+ PRINTF_ATTRIBUTE(3, 4);
void PrintProperty(const char* name, const Object& o, bool ref = true);
- void SetArguments(const char** arguments, intptr_t num_arguments);
- void SetOptions(const char** option_keys, const char** option_values,
- intptr_t num_options);
-
- intptr_t num_arguments() const { return num_arguments_; }
- const char* GetArgument(intptr_t i) const {
- return arguments_[i];
- }
- intptr_t num_options() const { return num_options_; }
- const char* GetOptionKey(intptr_t i) const {
- return option_keys_[i];
- }
- const char* GetOptionValue(intptr_t i) const {
- return option_values_[i];
- }
-
- private:
void PrintPropertyName(const char* name);
void PrintCommaIfNeeded();
bool NeedComma();
+
+ intptr_t nesting_level() const { return open_objects_; }
+
intptr_t open_objects_;
- TextBuffer* buffer_;
+ TextBuffer buffer_;
const char** arguments_;
intptr_t num_arguments_;
const char** option_keys_;
const char** option_values_;
intptr_t num_options_;
+
+ friend class JSONObject;
+ friend class JSONArray;
+};
+
+
+class JSONObject : public ValueObject {
+ public:
+ explicit JSONObject(JSONStream* stream) : stream_(stream) {
+ stream_->OpenObject();
+ }
+ JSONObject(const JSONObject* obj, const char* name) : stream_(obj->stream_) {
+ stream_->OpenObject(name);
+ }
+ explicit JSONObject(const JSONArray* arr);
+
+ ~JSONObject() {
+ stream_->CloseObject();
+ }
+
+ void AddProperty(const char* name, bool b) const {
+ stream_->PrintPropertyBool(name, b);
+ }
+ void AddProperty(const char* name, intptr_t i) const {
+ stream_->PrintProperty(name, i);
+ }
+ void AddProperty(const char* name, double d) const {
+ stream_->PrintProperty(name, d);
+ }
+ void AddProperty(const char* name, const char* s) const {
+ stream_->PrintProperty(name, s);
+ }
+ void AddProperty(const char* name, const Object& obj, bool ref = true) const {
+ stream_->PrintProperty(name, obj, ref);
+ }
+ void AddPropertyF(const char* name, const char* format, ...) const
+ PRINTF_ATTRIBUTE(3, 4);
+
+ private:
+ JSONStream* stream_;
+
+ friend class JSONArray;
+
+ DISALLOW_ALLOCATION();
+ DISALLOW_COPY_AND_ASSIGN(JSONObject);
+};
+
+
+class JSONArray : public ValueObject {
+ public:
+ explicit JSONArray(JSONStream* stream) : stream_(stream) {
+ stream_->OpenArray();
+ }
+ JSONArray(const JSONObject* obj, const char* name) : stream_(obj->stream_) {
+ stream_->OpenArray(name);
+ }
+ explicit JSONArray(const JSONArray* arr) : stream_(arr->stream_) {
+ stream_->OpenArray();
+ }
+ ~JSONArray() {
+ stream_->CloseArray();
+ }
+
+ void AddValue(bool b) const { stream_->PrintValueBool(b); }
+ void AddValue(intptr_t i) const { stream_->PrintValue(i); }
+ void AddValue(double d) const { stream_->PrintValue(d); }
+ void AddValue(const char* s) const { stream_->PrintValue(s); }
+ void AddValue(const Object& obj, bool ref = true) const {
+ stream_->PrintValue(obj, ref);
+ }
+ void AddValueF(const char* format, ...) const PRINTF_ATTRIBUTE(2, 3);
+
+ private:
+ JSONStream* stream_;
+
+ friend class JSONObject;
+
+ DISALLOW_ALLOCATION();
+ DISALLOW_COPY_AND_ASSIGN(JSONArray);
};
} // namespace dart
diff --git a/runtime/vm/json_test.cc b/runtime/vm/json_test.cc
index 89a1b5b..efe7a67 100644
--- a/runtime/vm/json_test.cc
+++ b/runtime/vm/json_test.cc
@@ -139,143 +139,176 @@
TEST_CASE(JSON_JSONStream_Primitives) {
- TextBuffer tb(256);
- JSONStream js(&tb);
-
- js.OpenObject();
- js.CloseObject();
-
- EXPECT_STREQ("{}", tb.buf());
-
- js.Clear();
- js.OpenArray();
- js.CloseArray();
- EXPECT_STREQ("[]", tb.buf());
-
- js.Clear();
- js.PrintValueBool(true);
- EXPECT_STREQ("true", tb.buf());
-
- js.Clear();
- js.PrintValueBool(false);
- EXPECT_STREQ("false", tb.buf());
-
- js.Clear();
- js.PrintValue(static_cast<intptr_t>(4));
- EXPECT_STREQ("4", tb.buf());
-
- js.Clear();
- js.PrintValue(1.0);
- EXPECT_STREQ("1.000000", tb.buf());
-
- js.Clear();
- js.PrintValue("hello");
- EXPECT_STREQ("\"hello\"", tb.buf());
+ {
+ JSONStream js;
+ {
+ JSONObject jsobj(&js);
+ }
+ EXPECT_STREQ("{}", js.ToCString());
+ }
+ {
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ }
+ EXPECT_STREQ("[]", js.ToCString());
+ }
+ {
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue(true);
+ }
+ EXPECT_STREQ("[true]", js.ToCString());
+ }
+ {
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue(false);
+ }
+ EXPECT_STREQ("[false]", js.ToCString());
+ }
+ {
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue(static_cast<intptr_t>(4));
+ }
+ EXPECT_STREQ("[4]", js.ToCString());
+ }
+ {
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue(1.0);
+ }
+ EXPECT_STREQ("[1.000000]", js.ToCString());
+ }
+ {
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue("hello");
+ }
+ EXPECT_STREQ("[\"hello\"]", js.ToCString());
+ }
+ {
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValueF("h%s", "elo");
+ }
+ EXPECT_STREQ("[\"helo\"]", js.ToCString());
+ }
}
TEST_CASE(JSON_JSONStream_Array) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.Clear();
- js.OpenArray();
- js.PrintValueBool(true);
- js.PrintValueBool(false);
- js.CloseArray();
- EXPECT_STREQ("[true,false]", tb.buf());
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue(true);
+ jsarr.AddValue(false);
+ }
+ EXPECT_STREQ("[true,false]", js.ToCString());
}
TEST_CASE(JSON_JSONStream_Object) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.Clear();
- js.OpenObject();
- js.PrintProperty("key1", "a");
- js.PrintProperty("key2", "b");
- js.CloseObject();
- EXPECT_STREQ("{\"key1\":\"a\",\"key2\":\"b\"}", tb.buf());
+ JSONStream js;
+ {
+ JSONObject jsobj(&js);
+ jsobj.AddProperty("key1", "a");
+ jsobj.AddProperty("key2", "b");
+ }
+ EXPECT_STREQ("{\"key1\":\"a\",\"key2\":\"b\"}", js.ToCString());
}
TEST_CASE(JSON_JSONStream_NestedObject) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.OpenObject();
- js.OpenObject("key");
- js.PrintProperty("key1", "d");
- js.CloseObject();
- js.CloseObject();
- EXPECT_STREQ("{\"key\":{\"key1\":\"d\"}}", tb.buf());
+ JSONStream js;
+ {
+ JSONObject jsobj(&js);
+ JSONObject jsobj1(&jsobj, "key");
+ jsobj1.AddProperty("key1", "d");
+ }
+ EXPECT_STREQ("{\"key\":{\"key1\":\"d\"}}", js.ToCString());
}
TEST_CASE(JSON_JSONStream_ObjectArray) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.OpenArray();
- js.OpenObject();
- js.PrintProperty("key", "e");
- js.CloseObject();
- js.OpenObject();
- js.PrintProperty("yek", "f");
- js.CloseObject();
- js.CloseArray();
- EXPECT_STREQ("[{\"key\":\"e\"},{\"yek\":\"f\"}]", tb.buf());
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ {
+ JSONObject jsobj(&jsarr);
+ jsobj.AddProperty("key", "e");
+ }
+ {
+ JSONObject jsobj(&jsarr);
+ jsobj.AddProperty("yek", "f");
+ }
+ }
+ EXPECT_STREQ("[{\"key\":\"e\"},{\"yek\":\"f\"}]", js.ToCString());
}
TEST_CASE(JSON_JSONStream_ArrayArray) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.OpenArray();
- js.OpenArray();
- js.PrintValue((intptr_t)4);
- js.CloseArray();
- js.OpenArray();
- js.PrintValueBool(false);
- js.CloseArray();
- js.CloseArray();
- EXPECT_STREQ("[[4],[false]]", tb.buf());
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ {
+ JSONArray jsarr1(&jsarr);
+ jsarr1.AddValue(static_cast<intptr_t>(4));
+ }
+ {
+ JSONArray jsarr1(&jsarr);
+ jsarr1.AddValue(false);
+ }
+ }
+ EXPECT_STREQ("[[4],[false]]", js.ToCString());
}
TEST_CASE(JSON_JSONStream_Printf) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.OpenArray();
- js.PrintfValue("%d %s", 2, "hello");
- js.CloseArray();
- EXPECT_STREQ("[\"2 hello\"]", tb.buf());
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValueF("%d %s", 2, "hello");
+ }
+ EXPECT_STREQ("[\"2 hello\"]", js.ToCString());
}
TEST_CASE(JSON_JSONStream_ObjectPrintf) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.OpenObject();
- js.PrintfProperty("key", "%d %s", 2, "hello");
- js.CloseObject();
- EXPECT_STREQ("{\"key\":\"2 hello\"}", tb.buf());
+ JSONStream js;
+ {
+ JSONObject jsobj(&js);
+ jsobj.AddPropertyF("key", "%d %s", 2, "hello");
+ }
+ EXPECT_STREQ("{\"key\":\"2 hello\"}", js.ToCString());
}
TEST_CASE(JSON_JSONStream_DartObject) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.OpenArray();
- js.PrintValue(Object::Handle(Object::null()));
- js.OpenObject();
- js.PrintProperty("object_key", Object::Handle(Object::null()));
- js.CloseArray();
- EXPECT_STREQ("[{\"type\":\"null\"},{\"object_key\":{\"type\":\"null\"}]",
- tb.buf());
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue(Object::Handle(Object::null()));
+ JSONObject jsobj(&jsarr);
+ jsobj.AddProperty("object_key", Object::Handle(Object::null()));
+ }
+ EXPECT_STREQ("[{\"type\":\"null\"},{\"object_key\":{\"type\":\"null\"}}]",
+ js.ToCString());
}
TEST_CASE(JSON_JSONStream_EscapedString) {
- TextBuffer tb(256);
- JSONStream js(&tb);
- js.PrintValue("Hel\"\"lo\r\n\t");
- EXPECT_STREQ("\"Hel\\\"\\\"lo\\r\\n\\t\"", tb.buf());
+ JSONStream js;
+ {
+ JSONArray jsarr(&js);
+ jsarr.AddValue("Hel\"\"lo\r\n\t");
+ }
+ EXPECT_STREQ("[\"Hel\\\"\\\"lo\\r\\n\\t\"]", js.ToCString());
}
diff --git a/runtime/vm/mirrors_api_impl.cc b/runtime/vm/mirrors_api_impl.cc
index e463b20..4fd535f 100644
--- a/runtime/vm/mirrors_api_impl.cc
+++ b/runtime/vm/mirrors_api_impl.cc
@@ -163,26 +163,21 @@
const Library& lib = Library::Cast(obj);
// Case 1. Lookup the unmodified function name.
- String& ambiguity_error_msg = String::Handle(isolate);
- func = lib.LookupFunctionAllowPrivate(func_name, &ambiguity_error_msg);
+ func = lib.LookupFunctionAllowPrivate(func_name);
// Case 2. Lookup the function without the external setter suffix
// '='. Make sure to do this check after the regular lookup, so
// that we don't interfere with operator lookups (like ==).
- if (func.IsNull() && ambiguity_error_msg.IsNull() &&
- HasExternalSetterSuffix(func_name)) {
+ if (func.IsNull() && HasExternalSetterSuffix(func_name)) {
tmp_name = RemoveExternalSetterSuffix(func_name);
tmp_name = Field::SetterName(tmp_name);
- func = lib.LookupFunctionAllowPrivate(tmp_name, &ambiguity_error_msg);
+ func = lib.LookupFunctionAllowPrivate(tmp_name);
}
// Case 3. Lookup the function with the getter prefix prepended.
- if (func.IsNull() && ambiguity_error_msg.IsNull()) {
+ if (func.IsNull()) {
tmp_name = Field::GetterName(func_name);
- func = lib.LookupFunctionAllowPrivate(tmp_name, &ambiguity_error_msg);
- }
- if (!ambiguity_error_msg.IsNull()) {
- return Api::NewError("%s.", ambiguity_error_msg.ToCString());
+ func = lib.LookupFunctionAllowPrivate(tmp_name);
}
} else {
return Api::NewError(
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 29725c7..bdfac3c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -64,6 +64,7 @@
DECLARE_FLAG(bool, trace_deoptimization);
DECLARE_FLAG(bool, trace_deoptimization_verbose);
DECLARE_FLAG(bool, verbose_stacktrace);
+DECLARE_FLAG(bool, print_coverage);
static const char* kGetterPrefix = "get:";
static const intptr_t kGetterPrefixLength = strlen(kGetterPrefix);
@@ -169,8 +170,7 @@
const char* function_name) {
ASSERT(!lib.IsNull());
const Class& cls = Class::Handle(
- lib.LookupClassAllowPrivate(String::Handle(String::New(class_name)),
- NULL)); // No ambiguity error expected.
+ lib.LookupClassAllowPrivate(String::Handle(String::New(class_name))));
ASSERT(!cls.IsNull());
const Function& function =
Function::Handle(
@@ -1512,6 +1512,16 @@
}
+RawType* Class::RareType() const {
+ const Type& type = Type::Handle(Type::New(
+ *this,
+ Object::null_abstract_type_arguments(),
+ Scanner::kDummyTokenIndex));
+ return Type::RawCast(
+ ClassFinalizer::FinalizeType(*this, type, ClassFinalizer::kCanonicalize));
+}
+
+
template <class FakeObject>
RawClass* Class::New() {
ASSERT(Object::class_class() != Class::null());
@@ -2223,12 +2233,8 @@
RawClass* Class::NewNativeWrapper(const Library& library,
const String& name,
int field_count) {
- String& ambiguity_error_msg = String::Handle();
- Class& cls = Class::Handle(library.LookupClass(name, &ambiguity_error_msg));
+ Class& cls = Class::Handle(library.LookupClass(name));
if (cls.IsNull()) {
- if (!ambiguity_error_msg.IsNull()) {
- return Class::null();
- }
cls = New(name, Script::Handle(), Scanner::kDummyTokenIndex);
cls.SetFields(Object::empty_array());
cls.SetFunctions(Object::empty_array());
@@ -2923,20 +2929,13 @@
const char* class_name = String::Handle(UserVisibleName()).ToCString();
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
- if (ref) {
- stream->OpenObject();
- stream->PrintProperty("type", "@Class");
- stream->PrintProperty("id", id);
- stream->PrintProperty("name", class_name);
- stream->CloseObject();
- return;
+ JSONObject jsobj(stream);
+ jsobj.AddProperty("type", JSONType(ref));
+ jsobj.AddProperty("id", id);
+ jsobj.AddProperty("name", class_name);
+ if (!ref) {
+ jsobj.AddProperty("library", Object::Handle(library()));
}
- stream->OpenObject();
- stream->PrintProperty("type", "Class");
- stream->PrintProperty("id", id);
- stream->PrintProperty("name", class_name);
- stream->PrintProperty("library", Object::Handle(library()));
- stream->CloseObject();
}
@@ -3020,8 +3019,7 @@
void UnresolvedClass::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -3244,8 +3242,7 @@
void AbstractTypeArguments::PrintToJSONStream(JSONStream* stream,
bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -3585,8 +3582,7 @@
void TypeArguments::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -3674,8 +3670,7 @@
void InstantiatedTypeArguments::PrintToJSONStream(JSONStream* stream,
bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -3691,8 +3686,7 @@
void PatchClass::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -4139,6 +4133,9 @@
bool Function::is_optimizable() const {
+ if (FLAG_print_coverage) {
+ return false;
+ }
if (OptimizableBit::decode(raw_ptr()->kind_tag_) &&
(script() != Script::null()) &&
((end_token_pos() - token_pos()) < FLAG_huge_method_cutoff_in_tokens)) {
@@ -4181,10 +4178,7 @@
bool Function::IsInlineable() const {
- // '==' call is handled specially.
- return InlinableBit::decode(raw_ptr()->kind_tag_) &&
- HasCode() &&
- name() != Symbols::EqualOperator().raw();
+ return InlinableBit::decode(raw_ptr()->kind_tag_) && HasCode();
}
@@ -5100,22 +5094,15 @@
String::Handle(QualifiedUserVisibleName()).ToCString();
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
- if (ref) {
- stream->OpenObject();
- stream->PrintProperty("type", "@Function");
- stream->PrintProperty("id", id);
- stream->PrintProperty("name", function_name);
- stream->CloseObject();
- return;
- }
- stream->OpenObject();
- stream->PrintProperty("type", "Function");
- stream->PrintProperty("name", function_name);
- stream->PrintProperty("id", id);
- stream->PrintPropertyBool("is_static", is_static());
- stream->PrintPropertyBool("is_const", is_const());
- stream->PrintPropertyBool("is_optimizable", is_optimizable());
- stream->PrintPropertyBool("is_inlinable", IsInlineable());
+ JSONObject jsobj(stream);
+ jsobj.AddProperty("type", JSONType(ref));
+ jsobj.AddProperty("id", id);
+ jsobj.AddProperty("name", function_name);
+ if (ref) return;
+ jsobj.AddProperty("is_static", is_static());
+ jsobj.AddProperty("is_const", is_const());
+ jsobj.AddProperty("is_optimizable", is_optimizable());
+ jsobj.AddProperty("is_inlinable", IsInlineable());
const char* kind_string = NULL;
switch (kind()) {
case RawFunction::kRegularFunction:
@@ -5151,15 +5138,13 @@
default:
UNREACHABLE();
}
- stream->PrintProperty("kind", kind_string);
- stream->PrintProperty("unoptimized_code", Object::Handle(unoptimized_code()));
- stream->PrintProperty("usage_counter", usage_counter());
- stream->PrintProperty("optimized_call_site_count",
- optimized_call_site_count());
- stream->PrintProperty("code", Object::Handle(CurrentCode()));
- stream->PrintProperty("deoptimizations",
- static_cast<intptr_t>(deoptimization_counter()));
- stream->CloseObject();
+ jsobj.AddProperty("kind", kind_string);
+ jsobj.AddProperty("unoptimized_code", Object::Handle(unoptimized_code()));
+ jsobj.AddProperty("usage_counter", usage_counter());
+ jsobj.AddProperty("optimized_call_site_count", optimized_call_site_count());
+ jsobj.AddProperty("code", Object::Handle(CurrentCode()));
+ jsobj.AddProperty("deoptimizations",
+ static_cast<intptr_t>(deoptimization_counter()));
}
@@ -5207,8 +5192,7 @@
void ClosureData::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -5243,8 +5227,7 @@
void RedirectionData::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -5365,7 +5348,12 @@
result.set_has_initializer(false);
result.set_guarded_cid(kIllegalCid);
result.set_is_nullable(false);
- result.set_guarded_list_length(Field::kUnknownFixedLength);
+ // Presently, we only attempt to remember the list length for final fields.
+ if (is_final) {
+ result.set_guarded_list_length(Field::kUnknownFixedLength);
+ } else {
+ result.set_guarded_list_length(Field::kNoFixedLength);
+ }
result.set_dependent_code(Object::null_array());
return result.raw();
}
@@ -5393,6 +5381,16 @@
}
+intptr_t Field::guarded_list_length() const {
+ return Smi::Value(raw_ptr()->guarded_list_length_);
+}
+
+
+void Field::set_guarded_list_length(intptr_t list_length) const {
+ raw_ptr()->guarded_list_length_ = Smi::New(list_length);
+}
+
+
const char* Field::ToCString() const {
if (IsNull()) {
return "Field::null";
@@ -5413,8 +5411,7 @@
void Field::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -5585,7 +5582,6 @@
// List length unchanged.
return;
}
-
// Multiple list lengths assigned here, stop tracking length.
set_guarded_list_length(Field::kNoFixedLength);
DeoptimizeDependentCode();
@@ -5637,8 +5633,7 @@
void LiteralToken::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -6053,8 +6048,7 @@
void TokenStream::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -6405,8 +6399,7 @@
void Script::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -6724,8 +6717,9 @@
}
-// Lookup a name in the library's export namespace.
-RawObject* Library::LookupExport(const String& name) const {
+// Lookup a name in the library's re-export namespace. The name is
+// unmangled, i.e. no getter or setter names should be looked up.
+RawObject* Library::LookupReExport(const String& name) const {
if (HasExports()) {
const Array& exports = Array::Handle(this->exports());
// Break potential export cycle while looking up name.
@@ -6789,6 +6783,23 @@
cls.set_library(*this);
}
+static void AddScriptIfUnique(const GrowableObjectArray& scripts,
+ Script& candidate) {
+ if (candidate.IsNull()) {
+ return;
+ }
+ Script& script_obj = Script::Handle();
+
+ for (int i = 0; i < scripts.Length(); i++) {
+ script_obj ^= scripts.At(i);
+ if (script_obj.raw() == candidate.raw()) {
+ // We already have a reference to this script.
+ return;
+ }
+ }
+ // Add script to the list of scripts.
+ scripts.Add(candidate);
+}
RawArray* Library::LoadedScripts() const {
// We compute the list of loaded scripts lazily. The result is
@@ -6799,13 +6810,19 @@
GrowableObjectArray::Handle(GrowableObjectArray::New(8));
Object& entry = Object::Handle();
Class& cls = Class::Handle();
+ Class& patch_cls = Class::Handle();
Script& owner_script = Script::Handle();
+ Script& patch_script = Script::Handle();
DictionaryIterator it(*this);
- Script& script_obj = Script::Handle();
while (it.HasNext()) {
entry = it.GetNext();
if (entry.IsClass()) {
owner_script = Class::Cast(entry).script();
+ patch_cls = Class::Cast(entry).patch_class();
+ if (!patch_cls.IsNull()) {
+ patch_script = patch_cls.script();
+ AddScriptIfUnique(scripts, patch_script);
+ }
} else if (entry.IsFunction()) {
owner_script = Function::Cast(entry).script();
} else if (entry.IsField()) {
@@ -6814,22 +6831,7 @@
} else {
continue;
}
- if (owner_script.IsNull()) {
- continue;
- }
- bool is_unique = true;
- for (int i = 0; i < scripts.Length(); i++) {
- script_obj ^= scripts.At(i);
- if (script_obj.raw() == owner_script.raw()) {
- // We already have a reference to this script.
- is_unique = false;
- break;
- }
- }
- if (is_unique) {
- // Add script to the list of scripts.
- scripts.Add(owner_script);
- }
+ AddScriptIfUnique(scripts, owner_script);
}
// Create the array of scripts and cache it in loaded_scripts_.
@@ -6895,10 +6897,8 @@
}
-RawField* Library::LookupFieldAllowPrivate(const String& name,
- String* ambiguity_error_msg) const {
- Object& obj = Object::Handle(
- LookupObjectAllowPrivate(name, ambiguity_error_msg));
+RawField* Library::LookupFieldAllowPrivate(const String& name) const {
+ Object& obj = Object::Handle(LookupObjectAllowPrivate(name));
if (obj.IsField()) {
return Field::Cast(obj).raw();
}
@@ -6915,11 +6915,8 @@
}
-RawFunction* Library::LookupFunctionAllowPrivate(
- const String& name,
- String* ambiguity_error_msg) const {
- Object& obj = Object::Handle(
- LookupObjectAllowPrivate(name, ambiguity_error_msg));
+RawFunction* Library::LookupFunctionAllowPrivate(const String& name) const {
+ Object& obj = Object::Handle(LookupObjectAllowPrivate(name));
if (obj.IsFunction()) {
return Function::Cast(obj).raw();
}
@@ -6948,9 +6945,7 @@
}
-RawObject* Library::LookupObjectAllowPrivate(
- const String& name,
- String* ambiguity_error_msg) const {
+RawObject* Library::LookupObjectAllowPrivate(const String& name) const {
// First check if name is found in the local scope of the library.
Object& obj = Object::Handle(LookupLocalObjectAllowPrivate(name));
if (!obj.IsNull()) {
@@ -6962,30 +6957,27 @@
return Object::null();
}
- // Now check if name is found in any imported libs. It is a compile-time error
- // if the name is found in more than one import and actually used.
- return LookupImportedObject(name, ambiguity_error_msg);
+ // Now check if name is found in any imported libs.
+ return LookupImportedObject(name);
}
-RawObject* Library::LookupObject(const String& name,
- String* ambiguity_error_msg) const {
+RawObject* Library::LookupObject(const String& name) const {
// First check if name is found in the local scope of the library.
Object& obj = Object::Handle(LookupLocalObject(name));
if (!obj.IsNull()) {
return obj.raw();
}
- // Now check if name is found in any imported libs. It is a compile-time error
- // if the name is found in more than one import and actually used.
- return LookupImportedObject(name, ambiguity_error_msg);
+ // Now check if name is found in any imported libs.
+ return LookupImportedObject(name);
}
-RawObject* Library::LookupImportedObject(const String& name,
- String* ambiguity_error_msg) const {
+RawObject* Library::LookupImportedObject(const String& name) const {
Object& obj = Object::Handle();
Namespace& import = Namespace::Handle();
Library& import_lib = Library::Handle();
+ String& import_lib_url = String::Handle();
String& first_import_lib_url = String::Handle();
Object& found_obj = Object::Handle();
for (intptr_t i = 0; i < num_imports(); i++) {
@@ -6993,46 +6985,33 @@
obj = import.Lookup(name);
if (!obj.IsNull()) {
import_lib = import.library();
- if (!first_import_lib_url.IsNull()) {
- // Found duplicate definition.
- const intptr_t kMessageBufferSize = 512;
- char message_buffer[kMessageBufferSize];
- if (first_import_lib_url.raw() == url()) {
- OS::SNPrint(message_buffer,
- kMessageBufferSize,
- "ambiguous reference to '%s', "
- "as library '%s' is imported multiple times",
- name.ToCString(),
- first_import_lib_url.ToCString());
+ import_lib_url = import_lib.url();
+ if (found_obj.raw() != obj.raw()) {
+ if (first_import_lib_url.IsNull() ||
+ first_import_lib_url.StartsWith(Symbols::DartScheme())) {
+ // This is the first object we found, or the
+ // previously found object is exported from a Dart
+ // system library. The newly found object hides the one
+ // from the Dart library.
+ first_import_lib_url = import_lib.url();
+ found_obj = obj.raw();
+ } else if (import_lib_url.StartsWith(Symbols::DartScheme())) {
+ // The newly found object is exported from a Dart system
+ // library. It is hidden by the previously found object.
+ // We continue to search.
} else {
- OS::SNPrint(message_buffer,
- kMessageBufferSize,
- "ambiguous reference: "
- "'%s' is defined in library '%s' and also in '%s'",
- name.ToCString(),
- first_import_lib_url.ToCString(),
- String::Handle(url()).ToCString());
+ // We found two different objects with the same name.
+ return Object::null();
}
- // If the caller does not expect an ambiguity error, it may pass NULL as
- // ambiguity_error_msg in order to avoid the allocation of a handle.
- // It typically does so when looking up an object in the core library,
- // which is guaranteed not to contain ambiguities, unless the core lib
- // is under development, in which case the assert below may fail.
- ASSERT(ambiguity_error_msg != NULL); // No ambiguity error expected.
- *ambiguity_error_msg = String::New(message_buffer);
- return Object::null();
}
- first_import_lib_url = url();
- found_obj = obj.raw();
}
}
return found_obj.raw();
}
-RawClass* Library::LookupClass(const String& name,
- String* ambiguity_error_msg) const {
- Object& obj = Object::Handle(LookupObject(name, ambiguity_error_msg));
+RawClass* Library::LookupClass(const String& name) const {
+ Object& obj = Object::Handle(LookupObject(name));
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
@@ -7049,13 +7028,11 @@
}
-RawClass* Library::LookupClassAllowPrivate(const String& name,
- String* ambiguity_error_msg) const {
+RawClass* Library::LookupClassAllowPrivate(const String& name) const {
// See if the class is available in this library or in the top level
// scope of any imported library.
Isolate* isolate = Isolate::Current();
- const Class& cls = Class::Handle(isolate, LookupClass(name,
- ambiguity_error_msg));
+ const Class& cls = Class::Handle(isolate, LookupClass(name));
if (!cls.IsNull()) {
return cls.raw();
}
@@ -7265,6 +7242,7 @@
Class& temp_class =
Class::Handle(Class::New(Symbols::TopLevel(), script, 0));
temp_class.set_library(*this);
+ temp_class.set_is_finalized();
return temp_class.Evaluate(expr);
}
@@ -7457,36 +7435,29 @@
const char* library_url = String::Handle(url()).ToCString();
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
- if (ref) {
- // Print a reference
- stream->OpenObject();
- stream->PrintProperty("type", "@Library");
- stream->PrintProperty("id", id);
- stream->PrintProperty("name", library_name);
- stream->CloseObject();
- return;
+ JSONObject jsobj(stream);
+ jsobj.AddProperty("type", JSONType(ref));
+ jsobj.AddProperty("id", id);
+ jsobj.AddProperty("name", library_name);
+ if (ref) return;
+ jsobj.AddProperty("url", library_url);
+ {
+ JSONArray jsarr(&jsobj, "classes");
+ ClassDictionaryIterator class_iter(*this);
+ Class& klass = Class::Handle();
+ while (class_iter.HasNext()) {
+ klass = class_iter.GetNextClass();
+ jsarr.AddValue(klass);
+ }
}
- stream->OpenObject();
- stream->PrintProperty("type", "Library");
- stream->PrintProperty("id", id);
- stream->PrintProperty("name", library_name);
- stream->PrintProperty("url", library_url);
- ClassDictionaryIterator class_iter(*this);
- stream->OpenArray("classes");
- Class& klass = Class::Handle();
- while (class_iter.HasNext()) {
- klass = class_iter.GetNextClass();
- stream->PrintValue(klass);
+ {
+ JSONArray jsarr(&jsobj, "libraries");
+ Library& lib = Library::Handle();
+ for (intptr_t i = 0; i < num_imports(); i++) {
+ lib = ImportLibraryAt(i);
+ jsarr.AddValue(lib);
+ }
}
- stream->CloseArray();
- stream->OpenArray("libraries");
- Library& lib = Library::Handle();
- for (intptr_t i = 0; i < num_imports(); i++) {
- lib = ImportLibraryAt(i);
- stream->PrintValue(lib);
- }
- stream->CloseArray();
- stream->CloseObject();
}
@@ -7537,58 +7508,48 @@
}
-RawClass* LibraryPrefix::LookupClass(const String& class_name,
- String* ambiguity_error_msg) const {
+RawObject* LibraryPrefix::LookupObject(const String& name) const {
Array& imports = Array::Handle(this->imports());
Object& obj = Object::Handle();
Namespace& import = Namespace::Handle();
Library& import_lib = Library::Handle();
+ String& import_lib_url = String::Handle();
String& first_import_lib_url = String::Handle();
Object& found_obj = Object::Handle();
for (intptr_t i = 0; i < num_imports(); i++) {
import ^= imports.At(i);
- obj = import.Lookup(class_name);
+ obj = import.Lookup(name);
if (!obj.IsNull()) {
import_lib = import.library();
- if (!first_import_lib_url.IsNull()) {
- // Found duplicate definition.
- const intptr_t kMessageBufferSize = 512;
- char message_buffer[kMessageBufferSize];
- if (first_import_lib_url.raw() == import_lib.url()) {
- OS::SNPrint(message_buffer,
- kMessageBufferSize,
- "ambiguous reference to '%s', "
- "as library '%s' is imported multiple times via "
- "prefix '%s'",
- class_name.ToCString(),
- first_import_lib_url.ToCString(),
- String::Handle(name()).ToCString());
+ import_lib_url = import_lib.url();
+ if (found_obj.raw() != obj.raw()) {
+ if (first_import_lib_url.IsNull() ||
+ first_import_lib_url.StartsWith(Symbols::DartScheme())) {
+ // This is the first object we found, or the
+ // previously found object is exported from a Dart
+ // system library. The newly found object hides the one
+ // from the Dart library.
+ first_import_lib_url = import_lib.url();
+ found_obj = obj.raw();
+ } else if (import_lib_url.StartsWith(Symbols::DartScheme())) {
+ // The newly found object is exported from a Dart system
+ // library. It is hidden by the previously found object.
+ // We continue to search.
} else {
- OS::SNPrint(message_buffer,
- kMessageBufferSize,
- "ambiguous reference: "
- "'%s' is defined in library '%s' and also in '%s', "
- "both imported via prefix '%s'",
- class_name.ToCString(),
- first_import_lib_url.ToCString(),
- String::Handle(import_lib.url()).ToCString(),
- String::Handle(name()).ToCString());
+ // We found two different objects with the same name.
+ return Object::null();
}
- // If the caller does not expect an ambiguity error, it may pass NULL as
- // ambiguity_error_msg in order to avoid the allocation of a handle.
- // It typically does so when looking up a class in the core library,
- // which is guaranteed not to contain ambiguities, unless the core lib
- // is under development, in which case the assert below may fail.
- ASSERT(ambiguity_error_msg != NULL); // No ambiguity error expected.
- *ambiguity_error_msg = String::New(message_buffer);
- return Class::null();
}
- first_import_lib_url = import_lib.url();
- found_obj = obj.raw();
}
}
- if (found_obj.IsClass()) {
- return Class::Cast(found_obj).raw();
+ return found_obj.raw();
+}
+
+
+RawClass* LibraryPrefix::LookupClass(const String& class_name) const {
+ const Object& obj = Object::Handle(LookupObject(class_name));
+ if (obj.IsClass()) {
+ return Class::Cast(obj).raw();
}
return Class::null();
}
@@ -7641,8 +7602,7 @@
void LibraryPrefix::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -7657,8 +7617,7 @@
void Namespace::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -7707,18 +7666,35 @@
}
+// Look up object with given name in library and filter out hidden
+// names. Also look up getters and setters.
RawObject* Namespace::Lookup(const String& name) const {
Isolate* isolate = Isolate::Current();
const Library& lib = Library::Handle(isolate, library());
intptr_t ignore = 0;
+
// Lookup the name in the library's symbols.
+ const String* filter_name = &name;
Object& obj = Object::Handle(isolate, lib.LookupEntry(name, &ignore));
+ if (Field::IsGetterName(name)) {
+ filter_name = &String::Handle(Field::NameFromGetter(name));
+ } else if (Field::IsSetterName(name)) {
+ filter_name = &String::Handle(Field::NameFromGetter(name));
+ } else {
+ if (obj.IsNull() || obj.IsLibraryPrefix()) {
+ obj = lib.LookupEntry(String::Handle(Field::GetterName(name)), &ignore);
+ if (obj.IsNull()) {
+ obj = lib.LookupEntry(String::Handle(Field::SetterName(name)), &ignore);
+ }
+ }
+ }
+
// Library prefixes are not exported.
if (obj.IsNull() || obj.IsLibraryPrefix()) {
// Lookup in the re-exported symbols.
- obj = lib.LookupExport(name);
+ obj = lib.LookupReExport(name);
}
- if (obj.IsNull() || HidesName(name) || obj.IsLibraryPrefix()) {
+ if (obj.IsNull() || HidesName(*filter_name) || obj.IsLibraryPrefix()) {
return Object::null();
}
return obj.raw();
@@ -7792,10 +7768,10 @@
const Library& lib = *libs[l];
if (strcmp(class_name, "::") == 0) {
func_str = Symbols::New(function_name);
- func = lib.LookupFunctionAllowPrivate(func_str, NULL);
+ func = lib.LookupFunctionAllowPrivate(func_str);
} else {
class_str = String::New(class_name);
- cls = lib.LookupClassAllowPrivate(class_str, NULL);
+ cls = lib.LookupClassAllowPrivate(class_str);
if (!cls.IsNull()) {
func_str = String::New(function_name);
if (function_name[0] == '.') {
@@ -7905,8 +7881,7 @@
void Instructions::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -8060,8 +8035,7 @@
void PcDescriptors::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -8216,8 +8190,7 @@
void Stackmap::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -8287,8 +8260,7 @@
void LocalVarDescriptors::PrintToJSONStream(JSONStream* stream,
bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -8472,8 +8444,7 @@
void ExceptionHandlers::PrintToJSONStream(JSONStream* stream,
bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -8571,8 +8542,7 @@
void DeoptInfo::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -8967,27 +8937,23 @@
void Code::PrintToJSONStream(JSONStream* stream, bool ref) const {
ObjectIdRing* ring = Isolate::Current()->object_id_ring();
intptr_t id = ring->GetIdForObject(raw());
+ JSONObject jsobj(stream);
if (ref) {
- stream->OpenObject();
- stream->PrintProperty("type", "@Code");
- stream->PrintProperty("id", id);
- stream->CloseObject();
+ jsobj.AddProperty("type", "@Code");
+ jsobj.AddProperty("id", id);
return;
}
- stream->OpenObject();
- stream->PrintProperty("type", "Code");
- stream->PrintProperty("id", id);
- stream->PrintPropertyBool("is_optimized", is_optimized());
- stream->PrintPropertyBool("is_alive", is_alive());
- stream->PrintProperty("function", Object::Handle(function()));
- stream->OpenArray("disassembly");
- DisassembleToJSONStream formatter(stream);
+ jsobj.AddProperty("type", "Code");
+ jsobj.AddProperty("id", id);
+ jsobj.AddProperty("is_optimized", is_optimized());
+ jsobj.AddProperty("is_alive", is_alive());
+ jsobj.AddProperty("function", Object::Handle(function()));
+ JSONArray jsarr(&jsobj, "disassembly");
+ DisassembleToJSONStream formatter(jsarr);
const Instructions& instr = Instructions::Handle(instructions());
uword start = instr.EntryPoint();
Disassembler::Disassemble(start, start + instr.size(), &formatter,
comments());
- stream->CloseArray();
- stream->CloseObject();
}
@@ -9118,8 +9084,7 @@
void Context::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -9238,8 +9203,7 @@
void ContextScope::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -9711,8 +9675,7 @@
void ICData::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -9828,8 +9791,7 @@
void MegamorphicCache::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -9905,8 +9867,7 @@
void SubtypeTestCache::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -9924,8 +9885,7 @@
void Error::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -9970,8 +9930,7 @@
void ApiError::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -10016,8 +9975,7 @@
void LanguageError::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -10091,8 +10049,7 @@
void UnhandledException::PrintToJSONStream(JSONStream* stream,
bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -10128,8 +10085,7 @@
void UnwindError::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -10483,8 +10439,7 @@
void Instance::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -10842,8 +10797,7 @@
void AbstractType::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -11290,8 +11244,7 @@
void Type::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -11478,8 +11431,7 @@
void TypeParameter::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -11647,34 +11599,59 @@
void BoundedType::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
+}
+
+
+intptr_t MixinAppType::token_pos() const {
+ return Class::Handle(MixinAppAt(0)).token_pos();
+}
+
+
+intptr_t MixinAppType::Depth() const {
+ const Array& mixin_apps = Array::Handle(mixins());
+ return mixin_apps.Length();
}
RawString* MixinAppType::Name() const {
- return String::New("MixinApplication");
+ return String::New("MixinAppType");
}
const char* MixinAppType::ToCString() const {
- return "MixinAppType";
+ const char* format = "MixinAppType: super type: %s; first mixin app: %s";
+ const char* super_type_cstr = String::Handle(AbstractType::Handle(
+ SuperType()).Name()).ToCString();
+ const char* first_mixin_app_cstr = String::Handle(Class::Handle(
+ MixinAppAt(0)).Name()).ToCString();
+ intptr_t len = OS::SNPrint(
+ NULL, 0, format, super_type_cstr, first_mixin_app_cstr) + 1;
+ char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
+ OS::SNPrint(chars, len, format, super_type_cstr, first_mixin_app_cstr);
+ return chars;
}
void MixinAppType::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
-void MixinAppType::set_super_type(const AbstractType& value) const {
- StorePointer(&raw_ptr()->super_type_, value.raw());
+RawAbstractType* MixinAppType::SuperType() const {
+ return Class::Handle(MixinAppAt(0)).super_type();
}
-void MixinAppType::set_mixin_types(const Array& value) const {
- StorePointer(&raw_ptr()->mixin_types_, value.raw());
+RawClass* MixinAppType::MixinAppAt(intptr_t depth) const {
+ Class& mixin_app = Class::Handle();
+ mixin_app ^= Array::Handle(mixins()).At(depth);
+ return mixin_app.raw();
+}
+
+
+void MixinAppType::set_mixins(const Array& value) const {
+ StorePointer(&raw_ptr()->mixins_, value.raw());
}
@@ -11690,11 +11667,9 @@
}
-RawMixinAppType* MixinAppType::New(const AbstractType& super_type,
- const Array& mixin_types) {
+RawMixinAppType* MixinAppType::New(const Array& mixins) {
const MixinAppType& result = MixinAppType::Handle(MixinAppType::New());
- result.set_super_type(super_type);
- result.set_mixin_types(mixin_types);
+ result.set_mixins(mixins);
return result.raw();
}
@@ -11707,8 +11682,7 @@
void Number::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -11720,8 +11694,7 @@
void Integer::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -12139,8 +12112,7 @@
void Smi::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -12259,8 +12231,7 @@
void Mint::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -12366,8 +12337,7 @@
void Double::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -12538,8 +12508,7 @@
void Bigint::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -13142,8 +13111,7 @@
void String::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -13864,8 +13832,7 @@
void Bool::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -13948,8 +13915,7 @@
void Array::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14160,8 +14126,7 @@
void GrowableObjectArray::PrintToJSONStream(JSONStream* stream,
bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14266,8 +14231,7 @@
void Float32x4::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14372,8 +14336,7 @@
void Uint32x4::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14422,8 +14385,7 @@
void TypedData::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14459,8 +14421,7 @@
void ExternalTypedData::PrintToJSONStream(JSONStream* stream,
bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14647,8 +14608,7 @@
void Stacktrace::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14845,8 +14805,7 @@
void JSRegExp::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
@@ -14866,8 +14825,7 @@
void WeakProperty::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
RawAbstractType* MirrorReference::GetAbstractTypeReferent() const {
@@ -14929,8 +14887,7 @@
void MirrorReference::PrintToJSONStream(JSONStream* stream, bool ref) const {
- stream->OpenObject();
- stream->CloseObject();
+ JSONObject jsobj(stream);
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index dd06b66..a23c441 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -126,6 +126,9 @@
/* with an object id is printed. If ref is false the object is fully */ \
/* printed. */ \
virtual void PrintToJSONStream(JSONStream* stream, bool ref = true) const; \
+ virtual const char* JSONType(bool ref) const { \
+ return ref ? "@"#object : ""#object; \
+ } \
static const ClassId kClassId = k##object##Cid; \
private: /* NOLINT */ \
/* Initialize the handle based on the raw_ptr in the presence of null. */ \
@@ -254,16 +257,12 @@
}
virtual void PrintToJSONStream(JSONStream* stream, bool ref = true) const {
- if (IsNull()) {
- stream->OpenObject();
- stream->PrintProperty("type", "null");
- stream->CloseObject();
- return;
- }
- ASSERT(!IsNull());
- stream->OpenObject();
- stream->PrintProperty("type", "Object");
- stream->CloseObject();
+ JSONObject jsobj(stream);
+ jsobj.AddProperty("type", JSONType(ref));
+ }
+
+ virtual const char* JSONType(bool ref) const {
+ return IsNull() ? "null" : "Object";
}
// Returns the name that is used to identify an object in the
@@ -703,6 +702,11 @@
// function.
RawType* SignatureType() const;
+ // Return the Type with type parameters declared by this class filled in with
+ // dynamic and type parameters declared in superclasses filled in as declared
+ // in superclass clauses.
+ RawType* RareType() const;
+
RawLibrary* library() const { return raw_ptr()->library_; }
void set_library(const Library& value) const;
@@ -2013,12 +2017,8 @@
// to have. If length is kUnknownFixedLength the length has not
// been determined. If length is kNoFixedLength this field has multiple
// list lengths associated with it and cannot be predicted.
- intptr_t guarded_list_length() const {
- return raw_ptr()->guarded_list_length_;
- }
- void set_guarded_list_length(intptr_t list_length) const {
- raw_ptr()->guarded_list_length_ = list_length;
- }
+ intptr_t guarded_list_length() const;
+ void set_guarded_list_length(intptr_t list_length) const;
static intptr_t guarded_list_length_offset() {
return OFFSET_OF(RawField, guarded_list_length_);
}
@@ -2370,24 +2370,18 @@
void AddClass(const Class& cls) const;
void AddObject(const Object& obj, const String& name) const;
void ReplaceObject(const Object& obj, const String& name) const;
- RawObject* LookupExport(const String& name) const;
- RawObject* LookupObject(const String& name,
- String* ambiguity_error_msg) const;
- RawObject* LookupObjectAllowPrivate(const String& name,
- String* ambiguity_error_msg) const;
+ RawObject* LookupReExport(const String& name) const;
+ RawObject* LookupObject(const String& name) const;
+ RawObject* LookupObjectAllowPrivate(const String& name) const;
RawObject* LookupLocalObjectAllowPrivate(const String& name) const;
RawObject* LookupLocalObject(const String& name) const;
- RawObject* LookupImportedObject(const String& name,
- String* ambiguity_error_msg) const;
- RawClass* LookupClass(const String& name, String* ambiguity_error_msg) const;
- RawClass* LookupClassAllowPrivate(const String& name,
- String* ambiguity_error_msg) const;
+ RawObject* LookupImportedObject(const String& name) const;
+ RawClass* LookupClass(const String& name) const;
+ RawClass* LookupClassAllowPrivate(const String& name) const;
RawClass* LookupLocalClass(const String& name) const;
- RawField* LookupFieldAllowPrivate(const String& name,
- String* ambiguity_error_msg) const;
+ RawField* LookupFieldAllowPrivate(const String& name) const;
RawField* LookupLocalField(const String& name) const;
- RawFunction* LookupFunctionAllowPrivate(const String& name,
- String* ambiguity_error_msg) const;
+ RawFunction* LookupFunctionAllowPrivate(const String& name) const;
RawFunction* LookupLocalFunction(const String& name) const;
RawLibraryPrefix* LookupLocalLibraryPrefix(const String& name) const;
RawScript* LookupScript(const String& url) const;
@@ -2525,8 +2519,8 @@
bool ContainsLibrary(const Library& library) const;
RawLibrary* GetLibrary(int index) const;
void AddImport(const Namespace& import) const;
- RawClass* LookupClass(const String& class_name,
- String* ambiguity_error_msg) const;
+ RawObject* LookupObject(const String& name) const;
+ RawClass* LookupClass(const String& class_name) const;
static intptr_t InstanceSize() {
return RoundedAllocationSize(sizeof(RawLibraryPrefix));
@@ -2548,6 +2542,8 @@
};
+// A Namespace contains the names in a library dictionary, filtered by
+// the show/hide combinators.
class Namespace : public Object {
public:
RawLibrary* library() const { return raw_ptr()->library_; }
@@ -4170,13 +4166,14 @@
// A MixinAppType represents a mixin application clause, e.g.
-// "S<T> with M<U, N>". The class finalizer builds the type
+// "S<T> with M<U>, N<V>". The class finalizer builds the type
// parameters and arguments at finalization time.
-// MixinType objects do not survive finalization, so they do not
+// MixinAppType objects do not survive finalization, so they do not
// need to be written to and read from snapshots.
class MixinAppType : public AbstractType {
public:
- // MixinAppType objects are replaced with their actual finalized type.
+ // A MixinAppType object is replaced at class finalization time with a
+ // finalized Type or BoundedType object.
virtual bool IsFinalized() const { return false; }
// TODO(regis): Handle malformed and malbounded MixinAppType.
virtual bool IsMalformed() const { return false; }
@@ -4184,28 +4181,32 @@
virtual bool IsResolved() const { return false; }
virtual bool HasResolvedTypeClass() const { return false; }
virtual RawString* Name() const;
+ virtual intptr_t token_pos() const;
- virtual intptr_t token_pos() const {
- return AbstractType::Handle(super_type()).token_pos();
- }
+ // Returns the mixin composition depth of this mixin application type.
+ intptr_t Depth() const;
- virtual RawAbstractTypeArguments* arguments() const {
- return AbstractTypeArguments::null();
- }
+ // Returns the declared super type of the mixin application, which is also
+ // the super type of the first synthesized class, e.g. "S&M" refers to
+ // super type "S<T>".
+ RawAbstractType* SuperType() const;
- RawAbstractType* super_type() const { return raw_ptr()->super_type_; }
- RawArray* mixin_types() const { return raw_ptr()->mixin_types_; }
+ // Returns the synthesized class representing the mixin application at
+ // the given mixin composition depth, e.g. class "S&M" at depth 0, class
+ // "S&M&N" at depth 1.
+ // Each class refers to its mixin type, e.g. "S&M" refers to mixin type "M<U>"
+ // and "S&M&N" refers to mixin type "N<V>".
+ RawClass* MixinAppAt(intptr_t depth) const;
static intptr_t InstanceSize() {
return RoundedAllocationSize(sizeof(RawMixinAppType));
}
- static RawMixinAppType* New(const AbstractType& super_type,
- const Array& mixin_types);
+ static RawMixinAppType* New(const Array& mixins);
private:
- void set_super_type(const AbstractType& value) const;
- void set_mixin_types(const Array& value) const;
+ RawArray* mixins() const { return raw_ptr()->mixins_; }
+ void set_mixins(const Array& value) const;
static RawMixinAppType* New();
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 8158e58..82a5d8d 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -3252,10 +3252,8 @@
static RawClass* GetClass(const Library& lib, const char* name) {
- String& ambiguity_error_msg = String::Handle();
const Class& cls = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New(name)),
- &ambiguity_error_msg));
+ lib.LookupClass(String::Handle(Symbols::New(name))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
return cls.raw();
}
@@ -3396,9 +3394,9 @@
EXPECT(!lib.IsNull());
const Class& class_a = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("A")), NULL));
+ lib.LookupClass(String::Handle(Symbols::New("A"))));
const Class& class_b = Class::Handle(
- lib.LookupClass(String::Handle(Symbols::New("B")), NULL));
+ lib.LookupClass(String::Handle(Symbols::New("B"))));
const Function& a_test1 =
Function::Handle(GetStaticFunction(class_a, "test1"));
const Function& b_test1 =
diff --git a/runtime/vm/object_x64_test.cc b/runtime/vm/object_x64_test.cc
index 74c0e5c..42e872c 100644
--- a/runtime/vm/object_x64_test.cc
+++ b/runtime/vm/object_x64_test.cc
@@ -34,7 +34,9 @@
void GenerateEmbedStringInCode(Assembler* assembler, const char* str) {
const String& string_object =
String::ZoneHandle(String::New(str, Heap::kOld));
- __ LoadObject(RAX, string_object);
+ __ EnterDartFrame(0);
+ __ LoadObject(RAX, string_object, PP);
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -43,7 +45,7 @@
// This is used to test Embedded Smi objects in the instructions.
void GenerateEmbedSmiInCode(Assembler* assembler, intptr_t value) {
const Smi& smi_object = Smi::ZoneHandle(Smi::New(value));
- __ LoadObject(RAX, smi_object);
+ __ LoadObject(RAX, smi_object, PP);
__ ret();
}
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 5a6219a..2cd3f23 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -775,9 +775,9 @@
const Script& script = Script::Handle(isolate, func.script());
const Class& owner = Class::Handle(isolate, func.Owner());
ASSERT(!owner.IsNull());
- const Library& lib = Library::Handle(isolate, owner.library());
- Parser parser(script, lib, func.token_pos());
- parser.set_current_class(owner);
+ ParsedFunction* parsed_function = new ParsedFunction(
+ Function::ZoneHandle(func.raw()));
+ Parser parser(script, parsed_function, func.token_pos());
parser.SkipFunctionPreamble();
ParamList params;
parser.ParseFormalParameterList(true, true, ¶ms);
@@ -1700,7 +1700,7 @@
name = String::Concat(name, String::Handle(core_lib.private_key()));
name = Symbols::New(name);
}
- return core_lib.LookupClass(name, NULL); // No ambiguity error expected.
+ return core_lib.LookupClass(name);
}
@@ -1904,7 +1904,55 @@
op_arguments = BuildNoSuchMethodArguments(
operator_pos, operator_function_name, *op_arguments);
}
- super_op = new StaticCallNode(operator_pos, super_operator, op_arguments);
+ if (super_operator.name() == Symbols::EqualOperator().raw()) {
+ // Expand super.== call to match correct == semantics into:
+ // Let t1 = left, t2 = right {
+ // (t1 === null || t2 === null) ? t1 === t2
+ // : static_call(super.==, t1, t2)
+ // }
+ // Normal == calls are not expanded at the AST level to produce
+ // more compact code and enable more optimization opportunities.
+ ASSERT(!is_no_such_method); // == is always found.
+ EnsureExpressionTemp(); // Needed for ConditionalExprNode.
+ LetNode* result = new LetNode(operator_pos);
+ AstNode* left =
+ new LoadLocalNode(operator_pos,
+ result->AddInitializer(op_arguments->NodeAt(0)));
+ AstNode* right =
+ new LoadLocalNode(operator_pos,
+ result->AddInitializer(op_arguments->NodeAt(1)));
+ LiteralNode* null_operand =
+ new LiteralNode(operator_pos, Instance::ZoneHandle());
+ ComparisonNode* is_left_null = new ComparisonNode(operator_pos,
+ Token::kEQ_STRICT,
+ left,
+ null_operand);
+ ComparisonNode* is_right_null = new ComparisonNode(operator_pos,
+ Token::kEQ_STRICT,
+ right,
+ null_operand);
+ BinaryOpNode* null_check = new BinaryOpNode(operator_pos,
+ Token::kOR,
+ is_left_null,
+ is_right_null);
+ ArgumentListNode* new_arguments = new ArgumentListNode(operator_pos);
+ new_arguments->Add(left);
+ new_arguments->Add(right);
+ StaticCallNode* call = new StaticCallNode(operator_pos,
+ super_operator,
+ new_arguments);
+ ComparisonNode* strict_eq = new ComparisonNode(operator_pos,
+ Token::kEQ_STRICT,
+ left,
+ right);
+ result->AddNode(new ConditionalExprNode(operator_pos,
+ null_check,
+ strict_eq,
+ call));
+ super_op = result;
+ } else {
+ super_op = new StaticCallNode(operator_pos, super_operator, op_arguments);
+ }
if (negate_result) {
super_op = new UnaryOpNode(operator_pos, Token::kNOT, super_op);
}
@@ -3569,6 +3617,13 @@
}
ASSERT(member.name != NULL);
+ if (member.kind != RawFunction::kConstructor) {
+ if (member.name->Equals(members->class_name())) {
+ ErrorMsg(member.name_pos,
+ "class member must not have the same name as its class");
+ }
+ }
+
if (CurrentToken() == Token::kLPAREN || member.IsGetter()) {
// Constructor or method.
if (member.type == NULL) {
@@ -4306,18 +4361,16 @@
RawAbstractType* Parser::ParseMixins(const AbstractType& super_type) {
TRACE_PARSER("ParseMixins");
ASSERT(CurrentToken() == Token::kWITH);
+ ASSERT(super_type.IsType()); // TODO(regis): Could be a BoundedType.
+ AbstractType& mixin_super_type = AbstractType::Handle(super_type.raw());
const GrowableObjectArray& mixin_apps =
GrowableObjectArray::Handle(GrowableObjectArray::New());
AbstractType& mixin_type = AbstractType::Handle();
- AbstractTypeArguments& mixin_type_arguments =
- AbstractTypeArguments::Handle();
- Class& mixin_application = Class::Handle();
- Type& mixin_application_type = Type::Handle();
- Type& mixin_super_type = Type::Handle();
- ASSERT(super_type.IsType());
- mixin_super_type ^= super_type.raw();
- Array& mixin_application_interfaces = Array::Handle();
+ Class& mixin_app_class = Class::Handle();
+ Array& mixin_app_interfaces = Array::Handle();
+ String& mixin_app_class_name = String::Handle();
+ String& mixin_type_class_name = String::Handle();
do {
ConsumeToken();
const intptr_t mixin_pos = TokenPos();
@@ -4329,42 +4382,38 @@
}
// The name of the mixin application class is a combination of
- // the superclass and mixin class.
- String& mixin_app_name = String::Handle();
- mixin_app_name = mixin_super_type.ClassName();
- mixin_app_name = String::Concat(mixin_app_name, Symbols::Ampersand());
- mixin_app_name = String::Concat(mixin_app_name,
- String::Handle(mixin_type.ClassName()));
- mixin_app_name = Symbols::New(mixin_app_name);
+ // the super class name and mixin class name.
+ mixin_app_class_name = mixin_super_type.ClassName();
+ mixin_app_class_name = String::Concat(mixin_app_class_name,
+ Symbols::Ampersand());
+ mixin_type_class_name = mixin_type.ClassName();
+ mixin_app_class_name = String::Concat(mixin_app_class_name,
+ mixin_type_class_name);
+ mixin_app_class_name = Symbols::New(mixin_app_class_name);
- mixin_application = Class::New(mixin_app_name, script_, mixin_pos);
- mixin_application.set_super_type(mixin_super_type);
- mixin_application.set_mixin(Type::Cast(mixin_type));
- mixin_application.set_library(library_);
- mixin_application.set_is_synthesized_class();
+ mixin_app_class = Class::New(mixin_app_class_name, script_, mixin_pos);
+ mixin_app_class.set_super_type(mixin_super_type);
+ mixin_app_class.set_mixin(Type::Cast(mixin_type));
+ mixin_app_class.set_library(library_);
+ mixin_app_class.set_is_synthesized_class();
// Add the mixin type to the interfaces that the mixin application
// class implements. This is necessary so that type tests work.
- mixin_application_interfaces = Array::New(1);
- mixin_application_interfaces.SetAt(0, mixin_type);
- mixin_application.set_interfaces(mixin_application_interfaces);
+ mixin_app_interfaces = Array::New(1);
+ mixin_app_interfaces.SetAt(0, mixin_type);
+ mixin_app_class.set_interfaces(mixin_app_interfaces);
- // For the type arguments of the mixin application type, we need
- // a copy of the type arguments to the mixin type. The simplest way
- // to get the copy is to rewind the parser, parse the mixin type
- // again and steal its type arguments.
- SetPosition(mixin_pos);
- mixin_type = ParseType(ClassFinalizer::kResolveTypeParameters);
- mixin_type_arguments = mixin_type.arguments();
+ // Add the synthesized class to the list of mixin apps.
+ mixin_apps.Add(mixin_app_class);
- mixin_application_type = Type::New(mixin_application,
- mixin_type_arguments,
- mixin_pos);
- mixin_super_type = mixin_application_type.raw();
- mixin_apps.Add(mixin_application_type);
+ // This mixin application class becomes the type class of the super type of
+ // the next mixin application class. It is however too early to provide the
+ // correct super type arguments. We use the raw type for now.
+ mixin_super_type = Type::New(mixin_app_class,
+ Object::null_abstract_type_arguments(),
+ mixin_pos);
} while (CurrentToken() == Token::kCOMMA);
- return MixinAppType::New(super_type,
- Array::Handle(Array::MakeArray(mixin_apps)));
+ return MixinAppType::New(Array::Handle(Array::MakeArray(mixin_apps)));
}
@@ -4759,7 +4808,7 @@
}
ConsumeToken();
String& prefix = String::Handle();
- if (is_import && IsLiteral("as")) {
+ if (is_import && (CurrentToken() == Token::kAS)) {
ConsumeToken();
prefix = ExpectIdentifier("prefix identifier expected")->raw();
}
@@ -7291,9 +7340,6 @@
(left_operand->AsPrimaryNode()->IsSuper())) {
ErrorMsg(left_operand->token_pos(), "illegal use of 'super'");
}
- if (IsLiteral("as")) { // Not a reserved word.
- token_kind_ = Token::kAS;
- }
int current_preced = Token::Precedence(CurrentToken());
while (current_preced >= min_preced) {
while (Token::Precedence(CurrentToken()) == current_preced) {
@@ -7552,7 +7598,7 @@
if (result == NULL) {
String& name = String::ZoneHandle();
if (original->IsTypeNode()) {
- name = Symbols::New(original->Name());
+ name = Symbols::New(original->AsTypeNode()->TypeName());
} else if ((left_ident != NULL) &&
(original->IsLiteralNode() ||
original->IsLoadLocalNode() ||
@@ -8226,8 +8272,10 @@
selector = ParseInstanceCall(receiver, name);
}
} else if (primary->primary().IsTypeParameter()) {
+ // TODO(regis): Issue 13134. Make sure the error message is the
+ // one we want here and add a test covering this code.
const String& name = String::ZoneHandle(
- Symbols::New(primary->Name()));
+ TypeParameter::Cast(primary->primary()).name());
selector = ThrowNoSuchMethodError(primary->token_pos(),
current_class(),
name,
@@ -8402,52 +8450,15 @@
// to resolve it too early to an imported class of the same name.
if (finalization > ClassFinalizer::kResolveTypeParameters) {
// Resolve classname in the scope of the current library.
- Error& error = Error::Handle();
resolved_type_class = ResolveClassInCurrentLibraryScope(
- unresolved_class.token_pos(),
- unresolved_class_name,
- &error);
- if (!error.IsNull()) {
- if ((finalization == ClassFinalizer::kCanonicalizeWellFormed) ||
- FLAG_error_on_bad_type) {
- *type = ClassFinalizer::NewFinalizedMalformedType(
- error,
- scope_class,
- unresolved_class.token_pos(),
- "cannot resolve class '%s'",
- unresolved_class_name.ToCString());
- } else {
- // Map the malformed type to dynamic and ignore type arguments.
- *type = Type::DynamicType();
- }
- return;
- }
+ unresolved_class_name);
}
} else {
LibraryPrefix& lib_prefix =
LibraryPrefix::Handle(unresolved_class.library_prefix());
// Resolve class name in the scope of the library prefix.
- Error& error = Error::Handle();
- resolved_type_class = ResolveClassInPrefixScope(
- unresolved_class.token_pos(),
- lib_prefix,
- unresolved_class_name,
- &error);
- if (!error.IsNull()) {
- if ((finalization == ClassFinalizer::kCanonicalizeWellFormed) ||
- FLAG_error_on_bad_type) {
- *type = ClassFinalizer::NewFinalizedMalformedType(
- error,
- scope_class,
- unresolved_class.token_pos(),
- "cannot resolve class '%s'",
- unresolved_class_name.ToCString());
- } else {
- // Map the malformed type to dynamic and ignore type arguments.
- *type = Type::DynamicType();
- }
- return;
- }
+ resolved_type_class =
+ ResolveClassInPrefixScope(lib_prefix, unresolved_class_name);
}
// At this point, we can only have a parameterized_type.
const Type& parameterized_type = Type::Cast(*type);
@@ -8835,98 +8846,24 @@
}
-static RawObject* LookupNameInImport(Isolate* isolate,
- const Namespace& ns,
- const String& name) {
- // If the given name is filtered out by the import, don't look it up, nor its
- // getter and setter names.
- if (ns.HidesName(name)) {
- return Object::null();
- }
- Object& obj = Object::Handle(isolate);
- obj = ns.Lookup(name);
- if (!obj.IsNull()) {
- return obj.raw();
- }
- String& accessor_name = String::Handle(isolate, Field::GetterName(name));
- obj = ns.Lookup(accessor_name);
- if (!obj.IsNull()) {
- return obj.raw();
- }
- accessor_name = Field::SetterName(name);
- obj = ns.Lookup(accessor_name);
- return obj.raw();
-}
-
-
// Resolve a name by checking the global scope of the current
// library. If not found in the current library, then look in the scopes
// of all libraries that are imported without a library prefix.
-// Issue an error if the name is not found in the global scope
-// of the current library, but is defined in more than one imported
-// library, i.e. if the name cannot be resolved unambiguously.
-RawObject* Parser::ResolveNameInCurrentLibraryScope(intptr_t ident_pos,
- const String& name,
- Error* error) {
+RawObject* Parser::ResolveNameInCurrentLibraryScope(const String& name) {
TRACE_PARSER("ResolveNameInCurrentLibraryScope");
HANDLESCOPE(isolate());
Object& obj = Object::Handle(isolate(),
LookupNameInLibrary(isolate(), library_, name));
- if (obj.IsNull()) {
- // Name is not found in current library. Check scope of all
- // imported libraries.
- String& first_lib_url = String::Handle(isolate());
- Namespace& import = Namespace::Handle(isolate());
- intptr_t num_imports = library_.num_imports();
- Object& imported_obj = Object::Handle(isolate());
- Library& lib = Library::Handle(isolate());
- for (int i = 0; i < num_imports; i++) {
- import = library_.ImportAt(i);
- imported_obj = LookupNameInImport(isolate(), import, name);
- if (!imported_obj.IsNull()) {
- lib ^= import.library();
- if (!first_lib_url.IsNull()) {
- // Found duplicate definition.
- Error& ambiguous_ref_error = Error::Handle();
- if (first_lib_url.raw() == lib.url()) {
- ambiguous_ref_error = FormatErrorMsg(
- script_, ident_pos, "Error",
- "ambiguous reference to '%s', "
- "as library '%s' is imported multiple times",
- name.ToCString(),
- first_lib_url.ToCString());
- } else {
- ambiguous_ref_error = FormatErrorMsg(
- script_, ident_pos, "Error",
- "ambiguous reference: "
- "'%s' is defined in library '%s' and also in '%s'",
- name.ToCString(),
- first_lib_url.ToCString(),
- String::Handle(lib.url()).ToCString());
- }
- if (error == NULL) {
- // Report a compile time error since the caller is not interested
- // in the error.
- ErrorMsg(ambiguous_ref_error);
- }
- *error = ambiguous_ref_error.raw();
- return Object::null();
- } else {
- first_lib_url = lib.url();
- obj = imported_obj.raw();
- }
- }
- }
+ if (!obj.IsNull()) {
+ return obj.raw();
}
- return obj.raw();
+ return library_.LookupImportedObject(name);
}
-RawClass* Parser::ResolveClassInCurrentLibraryScope(intptr_t ident_pos,
- const String& name,
- Error* error) {
+RawClass* Parser::ResolveClassInCurrentLibraryScope(const String& name) {
const Object& obj =
- Object::Handle(ResolveNameInCurrentLibraryScope(ident_pos, name, error));
+ Object::Handle(ResolveNameInCurrentLibraryScope(name));
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
@@ -8937,14 +8874,11 @@
// Resolve an identifier by checking the global scope of the current
// library. If not found in the current library, then look in the scopes
// of all libraries that are imported without a library prefix.
-// Issue an error if the identifier is not found in the global scope
-// of the current library, but is defined in more than one imported
-// library, i.e. if the identifier cannot be resolved unambiguously.
AstNode* Parser::ResolveIdentInCurrentLibraryScope(intptr_t ident_pos,
const String& ident) {
TRACE_PARSER("ResolveIdentInCurrentLibraryScope");
const Object& obj =
- Object::Handle(ResolveNameInCurrentLibraryScope(ident_pos, ident, NULL));
+ Object::Handle(ResolveNameInCurrentLibraryScope(ident));
if (obj.IsClass()) {
const Class& cls = Class::Cast(obj);
return new PrimaryNode(ident_pos, Class::ZoneHandle(cls.raw()));
@@ -8973,64 +8907,17 @@
}
-RawObject* Parser::ResolveNameInPrefixScope(intptr_t ident_pos,
- const LibraryPrefix& prefix,
- const String& name,
- Error* error) {
- TRACE_PARSER("ResolveNameInPrefixScope");
+RawObject* Parser::ResolveNameInPrefixScope(const LibraryPrefix& prefix,
+ const String& name) {
HANDLESCOPE(isolate());
- Namespace& import = Namespace::Handle(isolate());
- String& first_lib_url = String::Handle(isolate());
- Object& obj = Object::Handle(isolate());
- Object& resolved_obj = Object::Handle(isolate());
- const Array& imports = Array::Handle(isolate(), prefix.imports());
- Library& lib = Library::Handle(isolate());
- for (intptr_t i = 0; i < prefix.num_imports(); i++) {
- import ^= imports.At(i);
- resolved_obj = LookupNameInImport(isolate(), import, name);
- if (!resolved_obj.IsNull()) {
- obj = resolved_obj.raw();
- lib = import.library();
- if (first_lib_url.IsNull()) {
- first_lib_url = lib.url();
- } else {
- // Found duplicate definition.
- Error& ambiguous_ref_error = Error::Handle();
- if (first_lib_url.raw() == lib.url()) {
- ambiguous_ref_error = FormatErrorMsg(
- script_, ident_pos, "Error",
- "ambiguous reference: '%s.%s' is imported multiple times",
- String::Handle(prefix.name()).ToCString(),
- name.ToCString());
- } else {
- ambiguous_ref_error = FormatErrorMsg(
- script_, ident_pos, "Error",
- "ambiguous reference: '%s.%s' is defined in '%s' and '%s'",
- String::Handle(prefix.name()).ToCString(),
- name.ToCString(),
- first_lib_url.ToCString(),
- String::Handle(lib.url()).ToCString());
- }
- if (error == NULL) {
- // Report a compile time error since the caller is not interested
- // in the error.
- ErrorMsg(ambiguous_ref_error);
- }
- *error = ambiguous_ref_error.raw();
- return Object::null();
- }
- }
- }
- return obj.raw();
+ return prefix.LookupObject(name);
}
-RawClass* Parser::ResolveClassInPrefixScope(intptr_t ident_pos,
- const LibraryPrefix& prefix,
- const String& name,
- Error* error) {
+RawClass* Parser::ResolveClassInPrefixScope(const LibraryPrefix& prefix,
+ const String& name) {
const Object& obj =
- Object::Handle(ResolveNameInPrefixScope(ident_pos, prefix, name, error));
+ Object::Handle(ResolveNameInPrefixScope(prefix, name));
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
@@ -9040,21 +8927,20 @@
// Do a lookup for the identifier in the scope of the specified
// library prefix. This means trying to resolve it locally in all of the
-// libraries present in the library prefix. If there are multiple libraries
-// with the name, issue an ambiguous reference error.
+// libraries present in the library prefix.
AstNode* Parser::ResolveIdentInPrefixScope(intptr_t ident_pos,
const LibraryPrefix& prefix,
const String& ident) {
TRACE_PARSER("ResolveIdentInPrefixScope");
- Object& obj =
- Object::Handle(ResolveNameInPrefixScope(ident_pos, prefix, ident, NULL));
+ Object& obj = Object::Handle(ResolveNameInPrefixScope(prefix, ident));
if (obj.IsNull()) {
// Unresolved prefixed primary identifier.
- ErrorMsg(ident_pos, "identifier '%s.%s' cannot be resolved",
- String::Handle(prefix.name()).ToCString(),
- ident.ToCString());
- }
- if (obj.IsClass()) {
+ String& qualified_name = String::ZoneHandle(prefix.name());
+ qualified_name = String::Concat(qualified_name, Symbols::Dot());
+ qualified_name = String::Concat(qualified_name, ident);
+ qualified_name = Symbols::New(qualified_name);
+ return new PrimaryNode(ident_pos, qualified_name);
+ } else if (obj.IsClass()) {
const Class& cls = Class::Cast(obj);
return new PrimaryNode(ident_pos, Class::ZoneHandle(cls.raw()));
} else if (obj.IsField()) {
@@ -9074,21 +8960,18 @@
} else {
return new PrimaryNode(ident_pos, Function::ZoneHandle(func.raw()));
}
- } else {
- // TODO(hausner): Should this be an error? It is not meaningful to
- // reference a library prefix defined in an imported library.
- ASSERT(obj.IsLibraryPrefix());
}
- // Lexically unresolved primary identifiers are referenced by their name.
- return new PrimaryNode(ident_pos, ident);
+ // All possible object types are handled above.
+ UNREACHABLE();
+ return NULL;
}
// Resolve identifier. Issue an error message if
// the ident refers to a method and allow_closure_names is false.
// If the name cannot be resolved, turn it into an instance field access
-// if we're compiling an instance method, or issue an error message
-// if we're compiling a static method.
+// if we're compiling an instance method, or generate
+// throw NoSuchMethodError if we're compiling a static method.
AstNode* Parser::ResolveIdent(intptr_t ident_pos,
const String& ident,
bool allow_closure_names) {
@@ -10074,6 +9957,34 @@
primary = ResolveIdentInPrefixScope(qual_ident.ident_pos,
*qual_ident.lib_prefix,
*qual_ident.ident);
+ // If the identifier could not be resolved, throw a NoSuchMethodError.
+ // Note: unlike in the case of an unqualified identifier, do not
+ // interpret the unresolved identifier as an instance method or
+ // instance getter call when compiling an instance method.
+ // TODO(hausner): Ideally we should generate the NoSuchMethodError
+ // later, when we know more about how the unresolved name is used.
+ // For example, we don't know yet whether the unresolved name
+ // refers to a getter or a setter. However, it is more awkward
+ // to distinuish four NoSuchMethodError cases all over the place
+ // in the parser. The four cases are: prefixed vs non-prefixed
+ // name, static vs dynamic context in which the unresolved name
+ // is used. We cheat a little here by looking at the next token
+ // to determine whether we have an unresolved method call or
+ // field access.
+ if (primary->IsPrimaryNode() &&
+ primary->AsPrimaryNode()->primary().IsString()) {
+ InvocationMirror::Type call_type =
+ CurrentToken() == Token::kLPAREN ?
+ InvocationMirror::kMethod : InvocationMirror::kGetter;
+ const String& unresolved_name =
+ String::Cast(primary->AsPrimaryNode()->primary());
+ primary = ThrowNoSuchMethodError(primary->token_pos(),
+ current_class(),
+ unresolved_name,
+ NULL, // No arguments.
+ InvocationMirror::kTopLevel,
+ call_type);
+ }
}
ASSERT(primary != NULL);
} else if (CurrentToken() == Token::kTHIS) {
@@ -10449,18 +10360,18 @@
const int min_prec = Token::Precedence(Token::kOR);
const int max_prec = Token::Precedence(Token::kMUL);
while (((min_prec <= Token::Precedence(CurrentToken())) &&
- (Token::Precedence(CurrentToken()) <= max_prec)) ||
- IsLiteral("as")) {
- Token::Kind last_token = IsLiteral("as") ? Token::kAS : CurrentToken();
- ConsumeToken();
- if (last_token == Token::kIS) {
+ (Token::Precedence(CurrentToken()) <= max_prec))) {
+ if (CurrentToken() == Token::kIS) {
+ ConsumeToken();
if (CurrentToken() == Token::kNOT) {
ConsumeToken();
}
SkipType(false);
- } else if (last_token == Token::kAS) {
+ } else if (CurrentToken() == Token::kAS) {
+ ConsumeToken();
SkipType(false);
} else {
+ ConsumeToken();
SkipUnaryExpr();
}
}
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 832cd99..b286b69 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -598,23 +598,15 @@
const String& ident);
// Find class with the given name in the library or prefix scope.
- RawClass* ResolveClassInCurrentLibraryScope(intptr_t ident_pos,
- const String& name,
- Error* error);
- RawClass* ResolveClassInPrefixScope(intptr_t ident_pos,
- const LibraryPrefix& prefix,
- const String& name,
- Error* error);
+ RawClass* ResolveClassInCurrentLibraryScope(const String& name);
+ RawClass* ResolveClassInPrefixScope(const LibraryPrefix& prefix,
+ const String& name);
// Find name in the library or prefix scope and return the corresponding
// object (field, class, function etc).
- RawObject* ResolveNameInCurrentLibraryScope(intptr_t ident_pos,
- const String& ident,
- Error* error);
- RawObject* ResolveNameInPrefixScope(intptr_t ident_pos,
- const LibraryPrefix& prefix,
- const String& name,
- Error* error);
+ RawObject* ResolveNameInCurrentLibraryScope(const String& ident);
+ RawObject* ResolveNameInPrefixScope(const LibraryPrefix& prefix,
+ const String& name);
AstNode* ResolveIdent(intptr_t ident_pos,
const String& ident,
diff --git a/runtime/vm/parser_test.cc b/runtime/vm/parser_test.cc
index b8e9412..2e4c4b7 100644
--- a/runtime/vm/parser_test.cc
+++ b/runtime/vm/parser_test.cc
@@ -16,9 +16,8 @@
void DumpFunction(const Library& lib, const char* cname, const char* fname) {
const String& classname = String::Handle(Symbols::New(cname));
- String& ambiguity_error_msg = String::Handle();
- Class& cls = Class::Handle(lib.LookupClass(classname, &ambiguity_error_msg));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ Class& cls = Class::Handle(lib.LookupClass(classname));
+ EXPECT(!cls.IsNull());
String& funcname = String::Handle(String::New(fname));
Function& function = Function::ZoneHandle(cls.LookupStaticFunction(funcname));
@@ -51,9 +50,8 @@
bool expect_static,
bool is_final) {
const String& classname = String::Handle(Symbols::New(class_name));
- String& ambiguity_error_msg = String::Handle();
- Class& cls = Class::Handle(lib.LookupClass(classname, &ambiguity_error_msg));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ Class& cls = Class::Handle(lib.LookupClass(classname));
+ EXPECT(!cls.IsNull());
String& fieldname = String::Handle(String::New(field_name));
String& functionname = String::Handle();
@@ -87,9 +85,8 @@
const char* function_name,
bool expect_static) {
const String& classname = String::Handle(Symbols::New(class_name));
- String& ambiguity_error_msg = String::Handle();
- Class& cls = Class::Handle(lib.LookupClass(classname, &ambiguity_error_msg));
- EXPECT(!cls.IsNull()); // No ambiguity error expected.
+ Class& cls = Class::Handle(lib.LookupClass(classname));
+ EXPECT(!cls.IsNull());
String& functionname = String::Handle(String::New(function_name));
Function& function = Function::Handle();
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 8bbd4b2..32f5d83 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -680,15 +680,15 @@
RawAbstractType* type_;
RawInstance* value_; // Offset in words for instance and value for static.
RawArray* dependent_code_;
+ RawSmi* guarded_list_length_;
RawObject** to() {
- return reinterpret_cast<RawObject**>(&ptr()->dependent_code_);
+ return reinterpret_cast<RawObject**>(&ptr()->guarded_list_length_);
}
intptr_t token_pos_;
intptr_t guarded_cid_;
intptr_t is_nullable_; // kNullCid if field can contain null value and
// any other value otherwise.
- intptr_t guarded_list_length_;
uint8_t kind_bits_; // static, final, const, has initializer.
};
@@ -1201,12 +1201,11 @@
RAW_HEAP_OBJECT_IMPLEMENTATION(MixinAppType);
RawObject** from() {
- return reinterpret_cast<RawObject**>(&ptr()->super_type_);
+ return reinterpret_cast<RawObject**>(&ptr()->mixins_);
}
- RawAbstractType* super_type_;
- RawArray* mixin_types_;
+ RawArray* mixins_; // Array of synthesized mixin application classes.
RawObject** to() {
- return reinterpret_cast<RawObject**>(&ptr()->mixin_types_);
+ return reinterpret_cast<RawObject**>(&ptr()->mixins_);
}
};
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index bf7f27e..21cb797 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -821,7 +821,6 @@
field.set_token_pos(reader->ReadIntptrValue());
field.set_guarded_cid(reader->ReadIntptrValue());
field.set_is_nullable(reader->ReadIntptrValue());
- field.set_guarded_list_length(reader->ReadIntptrValue());
field.set_kind_bits(reader->Read<uint8_t>());
// Set all the object fields.
@@ -855,7 +854,6 @@
writer->WriteIntptrValue(ptr()->token_pos_);
writer->WriteIntptrValue(ptr()->guarded_cid_);
writer->WriteIntptrValue(ptr()->is_nullable_);
- writer->WriteIntptrValue(ptr()->guarded_list_length_);
writer->Write<uint8_t>(ptr()->kind_bits_);
// Write out all the object pointer fields.
diff --git a/runtime/vm/resolver.cc b/runtime/vm/resolver.cc
index 1150b81..4021d71 100644
--- a/runtime/vm/resolver.cc
+++ b/runtime/vm/resolver.cc
@@ -143,14 +143,12 @@
const String& function_name,
intptr_t num_arguments,
const Array& argument_names,
- StaticResolveType resolve_type,
- String* ambiguity_error_msg) {
+ StaticResolveType resolve_type) {
ASSERT(!library.IsNull());
Function& function = Function::Handle();
if (class_name.IsNull() || (class_name.Length() == 0)) {
// Check if we are referring to a top level function.
- const Object& object = Object::Handle(
- library.LookupObject(function_name, ambiguity_error_msg));
+ const Object& object = Object::Handle(library.LookupObject(function_name));
if (!object.IsNull() && object.IsFunction()) {
function ^= object.raw();
if (!function.AreValidArguments(num_arguments, argument_names, NULL)) {
@@ -168,18 +166,15 @@
}
} else {
if (FLAG_trace_resolving) {
- OS::Print("ResolveStatic error '%s': %s.\n",
- function_name.ToCString(),
- ambiguity_error_msg->IsNull() ? "top level function not found"
- : ambiguity_error_msg->ToCString());
+ OS::Print("ResolveStatic error: function '%s' not found.\n",
+ function_name.ToCString());
}
}
} else {
// Lookup class_name in the library's class dictionary to get at
// the dart class object. If class_name is not found in the dictionary
// ResolveStatic will return a NULL function object.
- const Class& cls = Class::Handle(
- library.LookupClass(class_name, ambiguity_error_msg));
+ const Class& cls = Class::Handle(library.LookupClass(class_name));
if (!cls.IsNull()) {
function = ResolveStatic(cls,
function_name,
@@ -188,11 +183,9 @@
resolve_type);
}
if (FLAG_trace_resolving && function.IsNull()) {
- OS::Print("ResolveStatic error '%s.%s': %s.\n",
+ OS::Print("ResolveStatic error: function '%s.%s' not found.\n",
class_name.ToCString(),
- function_name.ToCString(),
- ambiguity_error_msg->IsNull() ? "static function not found"
- : ambiguity_error_msg->ToCString());
+ function_name.ToCString());
}
}
return function.raw();
diff --git a/runtime/vm/resolver.h b/runtime/vm/resolver.h
index df8158a..a0f2b33 100644
--- a/runtime/vm/resolver.h
+++ b/runtime/vm/resolver.h
@@ -53,8 +53,7 @@
const String& function_name,
intptr_t num_arguments,
const Array& argument_names,
- StaticResolveType resolve_type,
- String* ambiguity_error_msg);
+ StaticResolveType resolve_type);
// Resolve specified dart static function.
static RawFunction* ResolveStaticByName(const Class& cls,
diff --git a/runtime/vm/resolver_test.cc b/runtime/vm/resolver_test.cc
index 83017a4..6ede57c 100644
--- a/runtime/vm/resolver_test.cc
+++ b/runtime/vm/resolver_test.cc
@@ -92,7 +92,6 @@
const String& class_name = String::Handle(String::New(test_class_name));
const String& static_function_name =
String::Handle(String::New(test_static_function_name));
- String& ambiguity_error_msg = String::Handle();
// Now try to resolve and invoke the static function in this class.
{
@@ -103,8 +102,7 @@
static_function_name,
kNumArguments,
Object::empty_array(),
- kResolveType,
- &ambiguity_error_msg));
+ kResolveType));
EXPECT(!function.IsNull()); // No ambiguity error expected.
const Array& args = Array::Handle(Array::New(kNumArguments));
const String& arg0 = String::Handle(String::New("junk"));
@@ -125,8 +123,7 @@
static_function_name,
kNumArguments,
Object::empty_array(),
- kResolveType,
- &ambiguity_error_msg));
+ kResolveType));
EXPECT(bad_function.IsNull()); // No ambiguity error expected.
}
@@ -142,8 +139,7 @@
super_static_function_name,
kNumArguments,
Object::empty_array(),
- kResolveType,
- &ambiguity_error_msg));
+ kResolveType));
EXPECT(!super_function.IsNull()); // No ambiguity error expected.
}
}
@@ -165,9 +161,8 @@
const String& lib_name = String::Handle(String::New(test_library_name));
const Library& lib = Library::Handle(Library::LookupLibrary(lib_name));
ASSERT(!lib.IsNull());
- String& ambiguity_error_msg = String::Handle();
const Class& cls = Class::Handle(lib.LookupClass(
- String::Handle(Symbols::New(test_class_name)), &ambiguity_error_msg));
+ String::Handle(Symbols::New(test_class_name))));
EXPECT(!cls.IsNull()); // No ambiguity error expected.
Instance& receiver = Instance::Handle(Instance::New(cls));
diff --git a/runtime/vm/runtime_entry_x64.cc b/runtime/vm/runtime_entry_x64.cc
index 13fa2a1..5683738 100644
--- a/runtime/vm/runtime_entry_x64.cc
+++ b/runtime/vm/runtime_entry_x64.cc
@@ -30,7 +30,7 @@
// informative error message.
__ movq(RBX, Immediate(GetEntryPoint()));
__ movq(R10, Immediate(argument_count));
- __ call(&StubCode::CallToRuntimeLabel());
+ __ Call(&StubCode::CallToRuntimeLabel(), PP);
}
}
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 687f2e5..d7cea49 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -77,8 +77,7 @@
FindServiceMessageHandler(pathSegment.ToCString());
ASSERT(handler != NULL);
{
- TextBuffer buffer(256);
- JSONStream js(&buffer);
+ JSONStream js;
// Setup JSONStream arguments and options. The arguments and options
// are zone allocated and will be freed immediately after handling the
@@ -110,7 +109,7 @@
}
handler(isolate, &js);
- const String& reply = String::Handle(String::New(buffer.buf()));
+ const String& reply = String::Handle(String::New(js.ToCString()));
ASSERT(!reply.IsNull());
PostReply(reply, reply_port);
}
@@ -118,51 +117,51 @@
}
-static void PrintArgumentsAndOptions(JSONStream* js) {
- js->OpenObject("message");
- js->OpenArray("arguments");
- for (intptr_t i = 0; i < js->num_arguments(); i++) {
- js->PrintValue(js->GetArgument(i));
+static void PrintArgumentsAndOptions(const JSONObject& obj, JSONStream* js) {
+ JSONObject jsobj(&obj, "message");
+ {
+ JSONArray jsarr(&jsobj, "arguments");
+ for (intptr_t i = 0; i < js->num_arguments(); i++) {
+ jsarr.AddValue(js->GetArgument(i));
+ }
}
- js->CloseArray();
- js->OpenArray("option_keys");
- for (intptr_t i = 0; i < js->num_options(); i++) {
- js->PrintValue(js->GetOptionKey(i));
+ {
+ JSONArray jsarr(&jsobj, "option_keys");
+ for (intptr_t i = 0; i < js->num_options(); i++) {
+ jsarr.AddValue(js->GetOptionKey(i));
+ }
}
- js->CloseArray();
- js->OpenArray("option_values");
- for (intptr_t i = 0; i < js->num_options(); i++) {
- js->PrintValue(js->GetOptionValue(i));
+ {
+ JSONArray jsarr(&jsobj, "option_values");
+ for (intptr_t i = 0; i < js->num_options(); i++) {
+ jsarr.AddValue(js->GetOptionValue(i));
+ }
}
- js->CloseArray();
- js->CloseObject();
}
static void PrintCollectionErrorResponse(const char* collection_name,
JSONStream* js) {
- js->OpenObject();
- js->PrintProperty("type", "error");
- js->PrintfProperty("text", "Must specify collection object id: /%s/id",
+ JSONObject jsobj(js);
+ jsobj.AddProperty("type", "error");
+ jsobj.AddPropertyF("text", "Must specify collection object id: /%s/id",
collection_name);
- js->CloseObject();
}
static void HandleName(Isolate* isolate, JSONStream* js) {
- js->OpenObject();
- js->PrintProperty("type", "IsolateName");
- js->PrintProperty("id", static_cast<intptr_t>(isolate->main_port()));
- js->PrintProperty("name", isolate->name());
- js->CloseObject();
+ JSONObject jsobj(js);
+ jsobj.AddProperty("type", "IsolateName");
+ jsobj.AddProperty("id", static_cast<intptr_t>(isolate->main_port()));
+ jsobj.AddProperty("name", isolate->name());
}
static void HandleStackTrace(Isolate* isolate, JSONStream* js) {
DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
- js->OpenObject();
- js->PrintProperty("type", "StackTrace");
- js->OpenArray("members");
+ JSONObject jsobj(js);
+ jsobj.AddProperty("type", "StackTrace");
+ JSONArray jsarr(&jsobj, "members");
intptr_t n_frames = stack->Length();
String& url = String::Handle();
String& function = String::Handle();
@@ -170,26 +169,22 @@
ActivationFrame* frame = stack->FrameAt(i);
url ^= frame->SourceUrl();
function ^= frame->function().UserVisibleName();
- js->OpenObject();
- js->PrintProperty("name", function.ToCString());
- js->PrintProperty("url", url.ToCString());
- js->PrintProperty("line", frame->LineNumber());
- js->PrintProperty("function", frame->function());
- js->PrintProperty("code", frame->code());
- js->CloseObject();
+ JSONObject jsobj(&jsarr);
+ jsobj.AddProperty("name", function.ToCString());
+ jsobj.AddProperty("url", url.ToCString());
+ jsobj.AddProperty("line", frame->LineNumber());
+ jsobj.AddProperty("function", frame->function());
+ jsobj.AddProperty("code", frame->code());
}
- js->CloseArray();
- js->CloseObject();
}
static void HandleObjectHistogram(Isolate* isolate, JSONStream* js) {
ObjectHistogram* histogram = Isolate::Current()->object_histogram();
if (histogram == NULL) {
- js->OpenObject();
- js->PrintProperty("type", "error");
- js->PrintProperty("text", "Run with --print_object_histogram");
- js->CloseObject();
+ JSONObject jsobj(js);
+ jsobj.AddProperty("type", "error");
+ jsobj.AddProperty("text", "Run with --print_object_histogram");
return;
}
histogram->PrintToJSONStream(js);
@@ -197,10 +192,9 @@
static void HandleEcho(Isolate* isolate, JSONStream* js) {
- js->OpenObject();
- js->PrintProperty("type", "message");
- PrintArgumentsAndOptions(js);
- js->CloseObject();
+ JSONObject jsobj(js);
+ jsobj.AddProperty("type", "message");
+ PrintArgumentsAndOptions(jsobj, js);
}
// Print an error message if there is no ID argument.
@@ -221,15 +215,17 @@
/* Object is not type, replace with null. */ \
obj = Object::null(); \
} \
- js->PrintValue(obj, false)
+ obj.PrintToJSONStream(js, false)
static void HandleLibraries(Isolate* isolate, JSONStream* js) {
if (js->num_arguments() == 1) {
- js->PrintValue(Library::Handle(isolate->object_store()->root_library()));
- return;
+ const Library& lib =
+ Library::Handle(isolate->object_store()->root_library());
+ lib.PrintToJSONStream(js, true);
+ } else {
+ PRINT_RING_OBJ(Library);
}
- PRINT_RING_OBJ(Library);
}
@@ -264,11 +260,10 @@
static void HandleFallthrough(Isolate* isolate, JSONStream* js) {
- js->OpenObject();
- js->PrintProperty("type", "error");
- js->PrintProperty("text", "request not understood.");
- PrintArgumentsAndOptions(js);
- js->CloseObject();
+ JSONObject jsobj(js);
+ jsobj.AddProperty("type", "error");
+ jsobj.AddProperty("text", "request not understood.");
+ PrintArgumentsAndOptions(jsobj, js);
}
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index d7e551b..560d803 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -206,7 +206,7 @@
library_ = Library::LookupLibrary(str_);
ASSERT(!library_.IsNull());
str_ ^= ReadObjectImpl();
- cls = library_.LookupClass(str_, NULL); // No ambiguity error expected.
+ cls = library_.LookupClass(str_);
cls.EnsureIsFinalized(isolate());
ASSERT(!cls.IsNull());
return cls.raw();
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index 0aac461..fdcf6c0 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -131,6 +131,15 @@
CompareDartCObjects(root, new_root);
}
+
+static void ExpectEncodeFail(Dart_CObject* root) {
+ uint8_t* buffer = NULL;
+ ApiMessageWriter writer(&buffer, &malloc_allocator);
+ const bool result = writer.WriteCMessage(root);
+ EXPECT_EQ(false, result);
+}
+
+
TEST_CASE(SerializeNull) {
StackZone zone(Isolate::Current());
@@ -580,6 +589,58 @@
}
+TEST_CASE(FailSerializeLargeArray) {
+ Dart_CObject root;
+ root.type = Dart_CObject_kArray;
+ root.value.as_array.length = Array::kMaxElements + 1;
+ root.value.as_array.values = NULL;
+ ExpectEncodeFail(&root);
+}
+
+
+TEST_CASE(FailSerializeLargeNestedArray) {
+ Dart_CObject parent;
+ Dart_CObject child;
+ Dart_CObject* values[1] = { &child };
+
+ parent.type = Dart_CObject_kArray;
+ parent.value.as_array.length = 1;
+ parent.value.as_array.values = values;
+ child.type = Dart_CObject_kArray;
+ child.value.as_array.length = Array::kMaxElements + 1;
+ ExpectEncodeFail(&parent);
+}
+
+
+TEST_CASE(FailSerializeLargeTypedDataInt8) {
+ Dart_CObject root;
+ root.type = Dart_CObject_kTypedData;
+ root.value.as_typed_data.type = Dart_TypedData_kInt8;
+ root.value.as_typed_data.length =
+ TypedData::MaxElements(kTypedDataInt8ArrayCid) + 1;
+ ExpectEncodeFail(&root);
+}
+
+
+TEST_CASE(FailSerializeLargeTypedDataUint8) {
+ Dart_CObject root;
+ root.type = Dart_CObject_kTypedData;
+ root.value.as_typed_data.type = Dart_TypedData_kUint8;
+ root.value.as_typed_data.length =
+ TypedData::MaxElements(kTypedDataUint8ArrayCid) + 1;
+ ExpectEncodeFail(&root);
+}
+
+
+TEST_CASE(FailSerializeLargeExternalTypedData) {
+ Dart_CObject root;
+ root.type = Dart_CObject_kExternalTypedData;
+ root.value.as_typed_data.length =
+ ExternalTypedData::MaxElements(kExternalTypedDataUint8ArrayCid) + 1;
+ ExpectEncodeFail(&root);
+}
+
+
TEST_CASE(SerializeEmptyArray) {
StackZone zone(Isolate::Current());
diff --git a/runtime/vm/stack_frame_x64.h b/runtime/vm/stack_frame_x64.h
index fb6e11e..33c87b6 100644
--- a/runtime/vm/stack_frame_x64.h
+++ b/runtime/vm/stack_frame_x64.h
@@ -11,11 +11,14 @@
| | <- TOS
Callee frame | ... |
+ | saved PP |
+ | callee's PC marker |
| saved RBP | (RBP of current frame)
| saved PC | (PC of current frame)
+--------------------+
Current frame | ... | <- RSP of current frame
| first local |
+ | caller's PP |
| PC marker | (current frame's code entry + offset)
| caller's RBP | <- RBP of current frame
| caller's ret addr | (PC of caller frame)
@@ -24,21 +27,22 @@
| ... |
*/
-static const int kDartFrameFixedSize = 3; // PC marker, RBP, PC.
+static const int kDartFrameFixedSize = 4; // PC marker, RBP, PP, PC.
static const int kSavedPcSlotFromSp = -1;
-static const int kFirstLocalSlotFromFp = -2;
+
+static const int kFirstLocalSlotFromFp = -3;
+static const int kSavedCallerPpSlotFromFp = -2;
static const int kPcMarkerSlotFromFp = -1;
static const int kSavedCallerFpSlotFromFp = 0;
static const int kSavedCallerPcSlotFromFp = 1;
+
static const int kParamEndSlotFromFp = 1; // One slot past last parameter.
static const int kCallerSpSlotFromFp = 2;
-
-// No pool pointer on X64 (indicated by aliasing saved fp).
-static const int kSavedCallerPpSlotFromFp = kSavedCallerFpSlotFromFp;
+static const int kSavedAboveReturnAddress = 3; // Saved above return address.
// Entry and exit frame layout.
-static const int kSavedContextSlotFromEntryFp = -9;
-static const int kExitLinkSlotFromEntryFp = -8;
+static const int kSavedContextSlotFromEntryFp = -10;
+static const int kExitLinkSlotFromEntryFp = -9;
} // namespace dart
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index e4f18a2..3e185ee 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -182,7 +182,8 @@
StubEntry* name##_entry_;
STUB_CODE_LIST(STUB_CODE_ENTRY);
#undef STUB_CODE_ENTRY
- // This dummy field is needed so that we can intialize the stubs from a macro.
+ // This dummy field is needed so that we can initialize
+ // the stubs from a macro.
void* dummy_;
// Generate the stub and finalize the generated code into the stub
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index 3039a97..a9d3777 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -83,9 +83,8 @@
__ movq(RBX, Address(CTX, Isolate::top_context_offset()));
// Reset Context pointer in Isolate structure.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ movq(Address(CTX, Isolate::top_context_offset()), raw_null);
+ __ LoadObject(R12, Object::null_object(), PP);
+ __ movq(Address(CTX, Isolate::top_context_offset()), R12);
// Cache Context pointer into CTX while executing Dart code.
__ movq(CTX, RBX);
@@ -172,9 +171,8 @@
__ movq(R8, Address(CTX, Isolate::top_context_offset()));
// Reset Context pointer in Isolate structure.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ movq(Address(CTX, Isolate::top_context_offset()), raw_null);
+ __ LoadObject(R12, Object::null_object(), PP);
+ __ movq(Address(CTX, Isolate::top_context_offset()), R12);
// Cache Context pointer into CTX while executing Dart code.
__ movq(CTX, R8);
@@ -240,9 +238,8 @@
__ movq(R8, Address(CTX, Isolate::top_context_offset()));
// Reset Context pointer in Isolate structure.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ movq(Address(CTX, Isolate::top_context_offset()), raw_null);
+ __ LoadObject(R12, Object::null_object(), PP);
+ __ movq(Address(CTX, Isolate::top_context_offset()), R12);
// Cache Context pointer into CTX while executing Dart code.
__ movq(CTX, R8);
@@ -255,11 +252,10 @@
// Input parameters:
// R10: arguments descriptor array.
void StubCode::GenerateCallStaticFunctionStub(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
__ EnterStubFrame();
__ pushq(R10); // Preserve arguments descriptor array.
- __ pushq(raw_null); // Setup space on stack for return value.
+ // Setup space on stack for return value.
+ __ PushObject(Object::null_object());
__ CallRuntime(kPatchStaticCallRuntimeEntry, 0);
__ popq(RAX); // Get Code object result.
__ popq(R10); // Restore arguments descriptor array.
@@ -276,11 +272,10 @@
// (invalid because its function was optimized or deoptimized).
// R10: arguments descriptor array.
void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
__ EnterStubFrame();
__ pushq(R10); // Preserve arguments descriptor array.
- __ pushq(raw_null); // Setup space on stack for return value.
+ // Setup space on stack for return value.
+ __ PushObject(Object::null_object());
__ CallRuntime(kFixCallersTargetRuntimeEntry, 0);
__ popq(RAX); // Get Code object.
__ popq(R10); // Restore arguments descriptor array.
@@ -296,11 +291,9 @@
// R10: smi-tagged argument count, may be zero.
// RBP[kParamEndSlotFromFp + 1]: last argument.
static void PushArgumentsArray(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
-
+ __ LoadObject(R12, Object::null_object(), PP);
// Allocate array to store arguments of caller.
- __ movq(RBX, raw_null); // Null element type for raw Array.
+ __ movq(RBX, R12); // Null element type for raw Array.
__ call(&StubCode::AllocateArrayLabel());
__ SmiUntag(R10);
// RAX: newly allocated array.
@@ -330,18 +323,15 @@
// called, the stub accesses the receiver from this location directly
// when trying to resolve the call.
void StubCode::GenerateInstanceFunctionLookupStub(Assembler* assembler) {
- __ EnterStubFrame();
-
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ pushq(raw_null); // Space for the return value.
+ __ EnterStubFrameWithPP();
+ __ PushObject(Object::null_object()); // Space for the return value.
// Push the receiver as an argument. Load the smi-tagged argument
// count into R13 to index the receiver in the stack. There are
- // three words (null, stub's pc marker, saved fp) above the return
+ // four words (null, stub's pc marker, saved pp, saved fp) above the return
// address.
__ movq(R13, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
- __ pushq(Address(RSP, R13, TIMES_4, (3 * kWordSize)));
+ __ pushq(Address(RSP, R13, TIMES_4, (4 * kWordSize)));
__ pushq(RBX); // Pass IC data object.
__ pushq(R10); // Pass arguments descriptor array.
@@ -355,7 +345,7 @@
// Remove arguments.
__ Drop(4);
__ popq(RAX); // Get result into RAX.
- __ LeaveFrame();
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -378,7 +368,9 @@
// - Fill the unoptimized frame.
// - Materialize objects that require allocation (e.g. Double instances).
// GC can occur only after frame is fully rewritten.
-// Stack after EnterDartFrame(0) below:
+// Stack after EnterDartFrame(0, PP, kNoRegister) below:
+// +------------------+
+// | Saved PP | <- PP
// +------------------+
// | PC marker | <- TOS
// +------------------+
@@ -391,8 +383,12 @@
// Parts of the code cannot GC, part of the code can GC.
static void GenerateDeoptimizationSequence(Assembler* assembler,
bool preserve_result) {
- // Leaf runtime function DeoptimizeCopyFrame expects a Dart frame.
- __ EnterDartFrame(0);
+ // DeoptimizeCopyFrame expects a Dart frame, i.e. EnterDartFrame(0), but there
+ // is no need to set the correct PC marker or load PP, since they get patched.
+ __ EnterFrame(0);
+ __ pushq(Immediate(0));
+ __ pushq(PP);
+
// The code in this frame may not cause GC. kDeoptimizeCopyFrameRuntimeEntry
// and kDeoptimizeFillFrameRuntimeEntry are leaf runtime calls.
const intptr_t saved_result_slot_from_fp =
@@ -422,14 +418,21 @@
__ movq(RBX, Address(RBP, saved_result_slot_from_fp * kWordSize));
}
+ // There is a Dart Frame on the stack. We just need the PP.
+ __ movq(PP, Address(RBP, -2 * kWordSize));
__ LeaveFrame();
+
__ popq(RCX); // Preserve return address.
__ movq(RSP, RBP); // Discard optimized frame.
__ subq(RSP, RAX); // Reserve space for deoptimized frame.
__ pushq(RCX); // Restore return address.
- // Leaf runtime function DeoptimizeFillFrame expects a Dart frame.
- __ EnterDartFrame(0);
+ // DeoptimizeFillFrame expects a Dart frame, i.e. EnterDartFrame(0), but there
+ // is no need to set the correct PC marker or load PP, since they get patched.
+ __ EnterFrame(0);
+ __ pushq(Immediate(0));
+ __ pushq(PP);
+
if (preserve_result) {
__ pushq(RBX); // Preserve result as first local.
}
@@ -441,6 +444,8 @@
__ movq(RBX, Address(RBP, kFirstLocalSlotFromFp * kWordSize));
}
// Code above cannot cause GC.
+ // There is a Dart Frame on the stack. We just need the PP.
+ __ movq(PP, Address(RBP, -2 * kWordSize));
__ LeaveFrame();
// Frame is fully rewritten at this point and it is safe to perform a GC.
@@ -486,20 +491,20 @@
void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
- __ EnterStubFrame();
+ __ EnterStubFrameWithPP();
// Load the receiver into RAX. The argument count in the arguments
// descriptor in R10 is a smi.
__ movq(RAX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
- // Two words (saved fp, stub's pc marker) in the stack above the return
- // address.
- __ movq(RAX, Address(RSP, RAX, TIMES_4, 2 * kWordSize));
+ // Three words (saved pp, saved fp, stub's pc marker)
+ // in the stack above the return address.
+ __ movq(RAX, Address(RSP, RAX, TIMES_4,
+ kSavedAboveReturnAddress * kWordSize));
// Preserve IC data and arguments descriptor.
__ pushq(RBX);
__ pushq(R10);
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Instructions::null()));
- __ pushq(raw_null); // Space for the result of the runtime call.
+ // Space for the result of the runtime call.
+ __ PushObject(Object::null_object());
__ pushq(RAX); // Receiver.
__ pushq(RBX); // IC data.
__ pushq(R10); // Arguments descriptor.
@@ -511,10 +516,10 @@
__ popq(RAX); // Return value from the runtime call (instructions).
__ popq(R10); // Restore arguments descriptor.
__ popq(RBX); // Restore IC data.
- __ LeaveFrame();
+ __ LeaveFrameWithPP();
Label lookup;
- __ cmpq(RAX, raw_null);
+ __ CompareObject(RAX, Object::null_object());
__ j(EQUAL, &lookup, Assembler::kNearJump);
__ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
__ jmp(RAX);
@@ -532,8 +537,6 @@
// The newly allocated object is returned in RAX.
void StubCode::GenerateAllocateArrayStub(Assembler* assembler) {
Label slow_case;
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
if (FLAG_inline_alloc) {
// Compute the size to be allocated, it is based on the array length
@@ -622,13 +625,14 @@
__ leaq(RBX, FieldAddress(RAX, Array::data_offset()));
// RBX: iterator which initially points to the start of the variable
// data area to be initialized.
+ __ LoadObject(R13, Object::null_object(), PP);
Label done;
Label init_loop;
__ Bind(&init_loop);
__ cmpq(RBX, R12);
__ j(ABOVE_EQUAL, &done, Assembler::kNearJump);
// TODO(cshapiro): StoreIntoObjectNoBarrier
- __ movq(Address(RBX, 0), raw_null);
+ __ movq(Address(RBX, 0), R13);
__ addq(RBX, Immediate(kWordSize));
__ jmp(&init_loop, Assembler::kNearJump);
__ Bind(&done);
@@ -645,7 +649,8 @@
// Create a stub frame as we are pushing some objects on the stack before
// calling into the runtime.
__ EnterStubFrame();
- __ pushq(raw_null); // Setup space on stack for return value.
+ // Setup space on stack for return value.
+ __ PushObject(Object::null_object());
__ pushq(R10); // Array length as Smi.
__ pushq(RBX); // Element type.
__ CallRuntime(kAllocateArrayRuntimeEntry, 2);
@@ -663,17 +668,16 @@
// called, the stub accesses the closure from this location directly
// when trying to resolve the call.
void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
-
// Load num_args.
__ movq(RAX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
// Load closure object in R13.
__ movq(R13, Address(RSP, RAX, TIMES_4, 0)); // RAX is a Smi.
+ __ LoadObject(R12, Object::null_object(), PP);
+
// Verify that R13 is a closure by checking its class.
Label not_closure;
- __ cmpq(R13, raw_null);
+ __ cmpq(R13, R12);
// Not a closure, but null object.
__ j(EQUAL, ¬_closure);
__ testq(R13, Immediate(kSmiTagMask));
@@ -682,7 +686,7 @@
// class.signature_function() is not null.
__ LoadClass(RAX, R13);
__ movq(RAX, FieldAddress(RAX, Class::signature_function_offset()));
- __ cmpq(RAX, raw_null);
+ __ cmpq(RAX, R12);
// Actual class is not a closure class.
__ j(EQUAL, ¬_closure, Assembler::kNearJump);
@@ -694,7 +698,7 @@
// Load closure function code in RAX.
__ movq(RAX, FieldAddress(RBX, Function::code_offset()));
- __ cmpq(RAX, raw_null);
+ __ cmpq(RAX, R12);
Label function_compiled;
__ j(NOT_EQUAL, &function_compiled, Assembler::kNearJump);
@@ -733,8 +737,8 @@
// Create a stub frame as we are pushing some objects on the stack before
// calling into the runtime.
__ EnterStubFrame();
-
- __ pushq(raw_null); // Setup space on stack for result from call.
+ // Setup space on stack for result from call.
+ __ pushq(R12);
__ pushq(R10); // Arguments descriptor.
// Load smi-tagged arguments array length, including the non-closure.
__ movq(R10, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
@@ -761,12 +765,12 @@
// RCX : new context containing the current isolate pointer.
void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
// Save frame pointer coming in.
- __ EnterFrame(0);
+ __ EnterStubFrame();
// Save arguments descriptor array and new context.
- const intptr_t kArgumentsDescOffset = -1 * kWordSize;
+ const intptr_t kArgumentsDescOffset = -2 * kWordSize;
__ pushq(RSI);
- const intptr_t kNewContextOffset = -2 * kWordSize;
+ const intptr_t kNewContextOffset = -3 * kWordSize;
__ pushq(RCX);
// Save C++ ABI callee-saved registers.
@@ -792,7 +796,7 @@
// StackFrameIterator reads the top exit frame info saved in this frame.
// The constant kExitLinkSlotFromEntryFp must be kept in sync with the
// code below.
- ASSERT(kExitLinkSlotFromEntryFp == -8);
+ ASSERT(kExitLinkSlotFromEntryFp == -9);
__ movq(RAX, Address(R8, Isolate::top_exit_frame_info_offset()));
__ pushq(RAX);
__ movq(Address(R8, Isolate::top_exit_frame_info_offset()), Immediate(0));
@@ -804,7 +808,7 @@
// EntryFrame::SavedContext reads the context saved in this frame.
// The constant kSavedContextSlotFromEntryFp must be kept in sync with
// the code below.
- ASSERT(kSavedContextSlotFromEntryFp == -9);
+ ASSERT(kSavedContextSlotFromEntryFp == -10);
__ movq(RAX, Address(R8, Isolate::top_context_offset()));
__ pushq(RAX);
@@ -881,8 +885,7 @@
// Output:
// RAX: new allocated RawContext object.
void StubCode::GenerateAllocateContextStub(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ LoadObject(R12, Object::null_object(), PP);
if (FLAG_inline_alloc) {
const Class& context_class = Class::ZoneHandle(Object::context_class());
Label slow_case;
@@ -957,12 +960,10 @@
// R13: Isolate, not an object.
__ movq(FieldAddress(RAX, Context::isolate_offset()), R13);
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
// Setup the parent field.
// RAX: new object.
// R10: number of context variables.
- __ movq(FieldAddress(RAX, Context::parent_offset()), raw_null);
+ __ movq(FieldAddress(RAX, Context::parent_offset()), R12);
// Initialize the context variables.
// RAX: new object.
@@ -974,7 +975,7 @@
__ jmp(&entry, Assembler::kNearJump);
__ Bind(&loop);
__ decq(R10);
- __ movq(Address(R13, R10, TIMES_8, 0), raw_null);
+ __ movq(Address(R13, R10, TIMES_8, 0), R12);
__ Bind(&entry);
__ cmpq(R10, Immediate(0));
__ j(NOT_EQUAL, &loop, Assembler::kNearJump);
@@ -988,7 +989,7 @@
}
// Create a stub frame.
__ EnterStubFrame();
- __ pushq(raw_null); // Setup space on stack for the return value.
+ __ pushq(R12); // Setup space on stack for the return value.
__ SmiTag(R10);
__ pushq(R10); // Push number of context variables.
__ CallRuntime(kAllocateContextRuntimeEntry, 1); // Allocate context.
@@ -1072,8 +1073,6 @@
const Class& cls) {
const intptr_t kObjectTypeArgumentsOffset = 2 * kWordSize;
const intptr_t kInstantiatorTypeArgumentsOffset = 1 * kWordSize;
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
// The generated code is different if the class is parameterized.
const bool is_cls_parameterized = cls.HasTypeArguments();
ASSERT(!cls.HasTypeArguments() ||
@@ -1085,6 +1084,7 @@
const intptr_t instance_size = cls.instance_size();
ASSERT(instance_size > 0);
const intptr_t type_args_size = InstantiatedTypeArguments::InstanceSize();
+ __ LoadObject(R12, Object::null_object(), PP);
if (FLAG_inline_alloc &&
Heap::IsAllocatableInNewSpace(instance_size + type_args_size)) {
Label slow_case;
@@ -1168,9 +1168,6 @@
__ movq(Address(RAX, Instance::tags_offset()), Immediate(tags));
// Initialize the remaining words of the object.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
-
// RAX: new object start.
// RBX: next object start.
// RDI: new object type arguments (if is_cls_parameterized).
@@ -1181,7 +1178,7 @@
for (intptr_t current_offset = sizeof(RawObject);
current_offset < instance_size;
current_offset += kWordSize) {
- __ movq(Address(RAX, current_offset), raw_null);
+ __ movq(Address(RAX, current_offset), R12);
}
} else {
__ leaq(RCX, Address(RAX, sizeof(RawObject)));
@@ -1195,7 +1192,7 @@
__ Bind(&init_loop);
__ cmpq(RCX, RBX);
__ j(ABOVE_EQUAL, &done, Assembler::kNearJump);
- __ movq(Address(RCX, 0), raw_null);
+ __ movq(Address(RCX, 0), R12);
__ addq(RCX, Immediate(kWordSize));
__ jmp(&init_loop, Assembler::kNearJump);
__ Bind(&done);
@@ -1217,14 +1214,14 @@
__ movq(RDX, Address(RSP, kInstantiatorTypeArgumentsOffset));
}
// Create a stub frame.
- __ EnterStubFrame();
- __ pushq(raw_null); // Setup space on stack for return value.
+ __ EnterStubFrameWithPP();
+ __ pushq(R12); // Setup space on stack for return value.
__ PushObject(cls); // Push class of object to be allocated.
if (is_cls_parameterized) {
__ pushq(RAX); // Push type arguments of object to be allocated.
__ pushq(RDX); // Push type arguments of instantiator.
} else {
- __ pushq(raw_null); // Push null type arguments.
+ __ pushq(R12); // Push null type arguments.
__ pushq(Immediate(Smi::RawValue(StubCode::kNoInstantiator)));
}
__ CallRuntime(kAllocateObjectRuntimeEntry, 3); // Allocate object.
@@ -1234,7 +1231,7 @@
__ popq(RAX); // Pop result (newly allocated object).
// RAX: new object
// Restore the frame pointer.
- __ LeaveFrame();
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -1246,16 +1243,17 @@
// RSP : points to return address.
void StubCode::GenerateAllocationStubForClosure(Assembler* assembler,
const Function& func) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
ASSERT(func.IsClosureFunction());
ASSERT(!func.IsImplicitStaticClosureFunction());
const bool is_implicit_instance_closure =
func.IsImplicitInstanceClosureFunction();
const Class& cls = Class::ZoneHandle(func.signature_class());
const bool has_type_arguments = cls.HasTypeArguments();
- const intptr_t kTypeArgumentsOffset = 1 * kWordSize;
- const intptr_t kReceiverOffset = 2 * kWordSize;
+
+ __ EnterStubFrameWithPP(); // Uses pool pointer to refer to function.
+ __ LoadObject(R12, Object::null_object(), PP);
+ const intptr_t kTypeArgumentsOffset = 4 * kWordSize;
+ const intptr_t kReceiverOffset = 5 * kWordSize;
const intptr_t closure_size = Closure::InstanceSize();
const intptr_t context_size = Context::InstanceSize(1); // Captured receiver.
if (FLAG_inline_alloc &&
@@ -1298,7 +1296,8 @@
// RAX: new closure object.
// RBX: new context object (only if is_implicit_closure).
// R13: next object start.
- __ LoadObject(R10, func); // Load function of closure to be allocated.
+ // Load function of closure to be allocated.
+ __ LoadObject(R10, func, PP);
__ movq(Address(RAX, Closure::function_offset()), R10);
// Setup the context for this closure.
@@ -1320,7 +1319,7 @@
__ movq(Address(RBX, Context::isolate_offset()), R10);
// Set the parent to null.
- __ movq(Address(RBX, Context::parent_offset()), raw_null);
+ __ movq(Address(RBX, Context::parent_offset()), R12);
// Initialize the context variable to the receiver.
__ movq(R10, Address(RSP, kReceiverOffset));
@@ -1340,6 +1339,7 @@
// Done allocating and initializing the instance.
// RAX: new object.
__ addq(RAX, Immediate(kHeapObjectTag));
+ __ LeaveFrameWithPP();
__ ret();
__ Bind(&slow_case);
@@ -1350,9 +1350,8 @@
if (is_implicit_instance_closure) {
__ movq(RAX, Address(RSP, kReceiverOffset));
}
- // Create the stub frame.
- __ EnterStubFrame();
- __ pushq(raw_null); // Setup space on stack for the return value.
+
+ __ pushq(R12); // Setup space on stack for the return value.
__ PushObject(func);
if (is_implicit_instance_closure) {
__ pushq(RAX); // Receiver.
@@ -1360,7 +1359,7 @@
if (has_type_arguments) {
__ pushq(RCX); // Push type arguments of closure to be allocated.
} else {
- __ pushq(raw_null); // Push null type arguments.
+ __ pushq(R12); // Push null type arguments.
}
if (is_implicit_instance_closure) {
__ CallRuntime(kAllocateImplicitInstanceClosureRuntimeEntry, 3);
@@ -1375,7 +1374,7 @@
__ popq(RAX); // Pop the result.
// RAX: New closure object.
// Restore the calling frame.
- __ LeaveFrame();
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -1395,9 +1394,8 @@
__ movq(R13, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
__ movq(RAX, Address(RBP, R13, TIMES_4, kParamEndSlotFromFp * kWordSize));
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ pushq(raw_null); // Setup space on stack for result from noSuchMethod.
+ __ LoadObject(R12, Object::null_object(), PP);
+ __ pushq(R12); // Setup space on stack for result from noSuchMethod.
__ pushq(RAX); // Receiver.
__ pushq(RBX); // IC data array.
__ pushq(R10); // Arguments descriptor array.
@@ -1545,8 +1543,7 @@
__ j(NOT_EQUAL, &loop, Assembler::kNearJump);
// IC miss.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ LoadObject(R12, Object::null_object(), PP);
// Compute address of arguments (first read number of arguments from
// arguments descriptor array and then compute address on the stack).
__ movq(RAX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
@@ -1554,7 +1551,7 @@
__ EnterStubFrame();
__ pushq(R10); // Preserve arguments descriptor array.
__ pushq(RBX); // Preserve IC data object.
- __ pushq(raw_null); // Setup space on stack for result (target code object).
+ __ pushq(R12); // Setup space on stack for result (target code object).
// Push call arguments.
for (intptr_t i = 0; i < num_args; i++) {
__ movq(RCX, Address(RAX, -kWordSize * i));
@@ -1571,7 +1568,7 @@
__ popq(R10); // Restore arguments descriptor array.
__ LeaveFrame();
Label call_target_function;
- __ cmpq(RAX, raw_null);
+ __ cmpq(RAX, R12);
__ j(NOT_EQUAL, &call_target_function, Assembler::kNearJump);
// NoSuchMethod or closure.
// Mark IC call that it may be a closure call that does not collect
@@ -1737,13 +1734,12 @@
Immediate(Smi::RawValue(Smi::kMaxValue)));
__ Bind(&increment_done);
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label target_is_compiled;
// Get function and call it, if possible.
__ movq(R13, Address(R12, target_offset));
__ movq(RAX, FieldAddress(R13, Function::code_offset()));
- __ cmpq(RAX, raw_null);
+ __ LoadObject(R12, Object::null_object(), PP);
+ __ cmpq(RAX, R12);
__ j(NOT_EQUAL, &target_is_compiled, Assembler::kNearJump);
__ EnterStubFrame();
@@ -1783,9 +1779,8 @@
__ pushq(R10);
// Room for result. Debugger stub returns address of the
// unpatched runtime stub.
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ pushq(raw_null); // Room for result.
+ __ LoadObject(R12, Object::null_object(), PP);
+ __ pushq(R12); // Room for result.
__ CallRuntime(kBreakpointRuntimeHandlerRuntimeEntry, 0);
__ popq(RAX); // Address of original.
__ popq(R10); // Restore arguments.
@@ -1798,11 +1793,10 @@
// RBX: ICData (unoptimized static call)
// TOS(0): return address (Dart code).
void StubCode::GenerateBreakpointStaticStub(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
__ EnterStubFrame();
+ __ LoadObject(R12, Object::null_object(), PP);
__ pushq(RBX); // Preserve IC data for unoptimized call.
- __ pushq(raw_null); // Room for result.
+ __ pushq(R12); // Room for result.
__ CallRuntime(kBreakpointStaticHandlerRuntimeEntry, 0);
__ popq(RAX); // Code object.
__ popq(RBX); // Restore IC data.
@@ -1827,7 +1821,7 @@
__ LeaveFrame();
__ popq(R11); // discard return address of call to this stub.
- __ LeaveFrame();
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -1868,17 +1862,16 @@
// Result in RCX: null -> not found, otherwise result (true or false).
static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n) {
ASSERT((1 <= n) && (n <= 3));
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
const intptr_t kInstantiatorTypeArgumentsInBytes = 1 * kWordSize;
const intptr_t kInstanceOffsetInBytes = 2 * kWordSize;
const intptr_t kCacheOffsetInBytes = 3 * kWordSize;
__ movq(RAX, Address(RSP, kInstanceOffsetInBytes));
+ __ LoadObject(R12, Object::null_object(), PP);
if (n > 1) {
__ LoadClass(R10, RAX);
// Compute instance type arguments into R13.
Label has_no_type_arguments;
- __ movq(R13, raw_null);
+ __ movq(R13, R12);
__ movq(RDI, FieldAddress(R10,
Class::type_arguments_field_offset_in_words_offset()));
__ cmpq(RDI, Immediate(Class::kNoTypeArguments));
@@ -1900,7 +1893,7 @@
__ SmiTag(R10);
__ Bind(&loop);
__ movq(RDI, Address(RDX, kWordSize * SubtypeTestCache::kInstanceClassId));
- __ cmpq(RDI, raw_null);
+ __ cmpq(RDI, R12);
__ j(EQUAL, ¬_found, Assembler::kNearJump);
__ cmpq(RDI, R10);
if (n == 1) {
@@ -1927,7 +1920,7 @@
__ jmp(&loop, Assembler::kNearJump);
// Fall through to not found.
__ Bind(¬_found);
- __ movq(RCX, raw_null);
+ __ movq(RCX, R12);
__ ret();
__ Bind(&found);
@@ -2060,10 +2053,10 @@
__ movq(RAX, Address(RSP, 1 * kWordSize));
__ cmpq(RAX, Address(RSP, 2 * kWordSize));
__ j(EQUAL, &true_label, Assembler::kNearJump);
- __ LoadObject(RAX, Bool::False());
+ __ LoadObject(RAX, Bool::False(), PP);
__ ret();
__ Bind(&true_label);
- __ LoadObject(RAX, Bool::True());
+ __ LoadObject(RAX, Bool::True(), PP);
__ ret();
__ Bind(&get_class_id_as_smi);
@@ -2081,7 +2074,7 @@
__ Bind(&update_ic_data);
- // RCX: ICData
+ // RBX: ICData
__ movq(RAX, Address(RSP, 1 * kWordSize));
__ movq(R13, Address(RSP, 2 * kWordSize));
__ EnterStubFrame();
@@ -2100,11 +2093,10 @@
// RDI: function to be reoptimized.
// R10: argument descriptor (preserved).
void StubCode::GenerateOptimizeFunctionStub(Assembler* assembler) {
- const Immediate& raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ EnterStubFrame();
+ __ EnterStubFrameWithPP();
+ __ LoadObject(R12, Object::null_object(), PP);
__ pushq(R10);
- __ pushq(raw_null); // Setup space on stack for return value.
+ __ pushq(R12); // Setup space on stack for return value.
__ pushq(RDI);
__ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry, 1);
__ popq(RAX); // Disard argument.
@@ -2112,7 +2104,7 @@
__ popq(R10); // Restore argument descriptor.
__ movq(RAX, FieldAddress(RAX, Code::instructions_offset()));
__ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
- __ LeaveFrame();
+ __ LeaveFrameWithPP();
__ jmp(RAX);
__ int3();
}
diff --git a/runtime/vm/stub_code_x64_test.cc b/runtime/vm/stub_code_x64_test.cc
index 769134d..3efc152 100644
--- a/runtime/vm/stub_code_x64_test.cc
+++ b/runtime/vm/stub_code_x64_test.cc
@@ -45,8 +45,8 @@
const Object& result = Object::ZoneHandle();
const Context& context = Context::ZoneHandle(Context::New(0, Heap::kOld));
ASSERT(context.isolate() == Isolate::Current());
- __ enter(Immediate(0));
- __ LoadObject(CTX, context);
+ __ EnterStubFrameWithPP();
+ __ LoadObject(CTX, context, PP);
__ PushObject(result); // Push Null object for return value.
__ PushObject(smi1); // Push argument 1 smi1.
__ PushObject(smi2); // Push argument 2 smi2.
@@ -54,7 +54,7 @@
__ CallRuntime(kTestSmiSubRuntimeEntry, argc); // Call SmiSub runtime func.
__ AddImmediate(RSP, Immediate(argc * kWordSize));
__ popq(RAX); // Pop return value from return slot.
- __ leave();
+ __ LeaveFrameWithPP();
__ ret();
}
@@ -84,8 +84,8 @@
const Smi& smi2 = Smi::ZoneHandle(Smi::New(value2));
__ enter(Immediate(0));
__ ReserveAlignedFrameSpace(0);
- __ LoadObject(RDI, smi1); // Set up argument 1 smi1.
- __ LoadObject(RSI, smi2); // Set up argument 2 smi2.
+ __ LoadObject(RDI, smi1, PP); // Set up argument 1 smi1.
+ __ LoadObject(RSI, smi2, PP); // Set up argument 2 smi2.
__ CallRuntime(kTestLeafSmiAddRuntimeEntry, 2); // Call SmiAdd runtime func.
__ leave();
__ ret(); // Return value is in RAX.
diff --git a/runtime/vm/token.h b/runtime/vm/token.h
index 1b58858..a52c97a 100644
--- a/runtime/vm/token.h
+++ b/runtime/vm/token.h
@@ -108,9 +108,6 @@
/* Internal token for !(expr is Type) negative type test operator */ \
TOK(kISNOT, "", 10, kNoAttribute) \
\
- /* Internal token for (expr as Type) type cast operator */ \
- TOK(kAS, "", 10, kNoAttribute) \
- \
TOK(kINDEX, "[]", 0, kNoAttribute) \
TOK(kASSIGN_INDEX, "[]=", 0, kNoAttribute) \
TOK(kNEGATE, "unary-", 0, kNoAttribute) \
@@ -139,7 +136,8 @@
// to update kFirstKeyword and kLastKeyword below.
#define DART_KEYWORD_LIST(KW) \
KW(kABSTRACT, "abstract", 0, kPseudoKeyword) /* == kFirstKeyword */ \
- KW(kASSERT, "assert", 0, kKeyword) \
+ KW(kAS, "as", 10, kPseudoKeyword) \
+ KW(kASSERT, "assert", 10, kKeyword) \
KW(kBREAK, "break", 0, kKeyword) \
KW(kCASE, "case", 0, kKeyword) \
KW(kCATCH, "catch", 0, kKeyword) \
diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi
index 34348f8..edcd75d 100644
--- a/runtime/vm/vm_sources.gypi
+++ b/runtime/vm/vm_sources.gypi
@@ -87,6 +87,8 @@
'constants_ia32.h',
'constants_mips.h',
'constants_x64.h',
+ 'coverage.cc',
+ 'coverage.h',
'cpu.h',
'cpu_arm.cc',
'cpu_ia32.cc',
@@ -164,6 +166,7 @@
'globals.h',
'growable_array.h',
'growable_array_test.cc',
+ 'guard_field_test.cc',
'handles.cc',
'handles.h',
'handles_impl.h',
diff --git a/sdk/lib/_internal/compiler/implementation/apiimpl.dart b/sdk/lib/_internal/compiler/implementation/apiimpl.dart
index d211e94..5832103 100644
--- a/sdk/lib/_internal/compiler/implementation/apiimpl.dart
+++ b/sdk/lib/_internal/compiler/implementation/apiimpl.dart
@@ -176,7 +176,8 @@
// TODO(johnniwinther): Wrap the result from [provider] in a specialized
// [Future] to ensure that we never execute an asynchronous action without setting
// up the current element of the compiler.
- return new Future.sync(() => provider(resourceUri)).then((String text) {
+ return new Future.sync(() => callUserProvider(resourceUri))
+ .then((String text) {
SourceFile sourceFile = new SourceFile(resourceUri.toString(), text);
// We use [readableUri] as the URI for the script since need to preserve
// the scheme in the script because [Script.uri] is used for resolving
@@ -283,10 +284,10 @@
// [:span.uri:] might be [:null:] in case of a [Script] with no [uri]. For
// instance in the [Types] constructor in typechecker.dart.
if (span == null || span.uri == null) {
- handler(null, null, null, message, kind);
+ callUserHandler(null, null, null, message, kind);
} else {
- handler(translateUri(span.uri, null), span.begin, span.end,
- message, kind);
+ callUserHandler(
+ translateUri(span.uri, null), span.begin, span.end, message, kind);
}
}
@@ -294,4 +295,30 @@
return mockableLibraryUsed
&& (options.indexOf('--allow-mock-compilation') != -1);
}
+
+ void callUserHandler(Uri uri, int begin, int end,
+ String message, api.Diagnostic kind) {
+ try {
+ handler(uri, begin, end, message, kind);
+ } catch (ex, s) {
+ diagnoseCrashInUserCode(
+ 'Uncaught exception in diagnostic handler', ex, s);
+ rethrow;
+ }
+ }
+
+ Future callUserProvider(Uri uri) {
+ try {
+ return provider(uri);
+ } catch (ex, s) {
+ diagnoseCrashInUserCode('Uncaught exception in input provider', ex, s);
+ rethrow;
+ }
+ }
+
+ void diagnoseCrashInUserCode(String message, exception, stackTrace) {
+ hasCrashed = true;
+ print('$message: ${tryToString(exception)}');
+ print(tryToString(stackTrace));
+ }
}
diff --git a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
index 4d43a2a..f9339f0 100644
--- a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
+++ b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
@@ -487,7 +487,8 @@
Constant makeTypeConstant(Element element) {
DartType elementType = element.computeType(compiler).asRaw();
- compiler.backend.registerTypeLiteral(element, elements);
+ compiler.backend.registerTypeLiteral(
+ element, compiler.enqueuer.codegen, elements);
DartType constantType =
compiler.backend.typeImplementation.computeType(compiler);
Constant constant = new TypeConstant(elementType, constantType);
diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart
index fe2403e..daf9daf 100644
--- a/sdk/lib/_internal/compiler/implementation/compiler.dart
+++ b/sdk/lib/_internal/compiler/implementation/compiler.dart
@@ -152,7 +152,9 @@
/// Called during resolution to notify to the backend that the
/// program uses a type literal.
- void registerTypeLiteral(Element element, TreeElements elements) {}
+ void registerTypeLiteral(Element element,
+ Enqueuer enqueuer,
+ TreeElements elements) {}
/// Called during resolution to notify to the backend that the
/// program has a catch statement with a stack trace.
@@ -164,7 +166,9 @@
TreeElements elements) {}
/// Register an as check to the backend.
- void registerAsCheck(DartType type, TreeElements elements) {}
+ void registerAsCheck(DartType type,
+ Enqueuer enqueuer,
+ TreeElements elements) {}
/// Register that the application may throw a [NoSuchMethodError].
void registerThrowNoSuchMethod(TreeElements elements) {}
@@ -459,6 +463,14 @@
Element get currentElement => _currentElement;
+ String tryToString(object) {
+ try {
+ return object.toString();
+ } catch (_) {
+ return '<exception in toString()>';
+ }
+ }
+
/**
* Perform an operation, [f], returning the return value from [f]. If an
* error occurs then report it as having occurred during compilation of
@@ -469,27 +481,31 @@
_currentElement = element;
try {
return f();
- } on SpannableAssertionFailure catch (ex) {
+ } on SpannableAssertionFailure catch (ex, s) {
if (!hasCrashed) {
+ String message = (ex.message != null) ? tryToString(ex.message)
+ : tryToString(ex);
SourceSpan span = spanFromSpannable(ex.node);
- reportError(ex.node, MessageKind.GENERIC, {'text': ex.message});
- pleaseReportCrash();
+ reportError(ex.node, MessageKind.GENERIC, {'text': message});
+ pleaseReportCrash(s, 'The compiler crashed: $message.');
}
hasCrashed = true;
- rethrow;
+ throw new CompilerCancelledException('The compiler crashed.');
} on CompilerCancelledException catch (ex) {
rethrow;
} on StackOverflowError catch (ex) {
// We cannot report anything useful in this case, because we
// do not have enough stack space.
rethrow;
- } catch (ex) {
+ } catch (ex, s) {
+ if (hasCrashed) rethrow;
+ String message = 'The compiler crashed: ${tryToString(ex)}.';
try {
- unhandledExceptionOnElement(element);
+ unhandledExceptionOnElement(element, s, message);
} catch (doubleFault) {
// Ignoring exceptions in exception handling.
}
- rethrow;
+ throw new CompilerCancelledException(message);
} finally {
_currentElement = old;
}
@@ -668,17 +684,25 @@
internalError(message, element: element);
}
- void unhandledExceptionOnElement(Element element) {
+ void unhandledExceptionOnElement(Element element,
+ StackTrace stackTrace,
+ String message) {
if (hasCrashed) return;
hasCrashed = true;
reportDiagnostic(spanFromElement(element),
MessageKind.COMPILER_CRASHED.error().toString(),
api.Diagnostic.CRASH);
- pleaseReportCrash();
+ pleaseReportCrash(stackTrace, message);
}
- void pleaseReportCrash() {
+ void pleaseReportCrash(StackTrace stackTrace, String message) {
print(MessageKind.PLEASE_REPORT_THE_CRASH.message({'buildId': buildId}));
+ if (message != null) {
+ print(message);
+ }
+ if (stackTrace != null) {
+ print(stackTrace);
+ }
}
void cancel(String reason, {Node node, Token token,
@@ -742,7 +766,8 @@
reportDiagnostic(new SourceSpan(uri, 0, 0),
MessageKind.COMPILER_CRASHED.error().toString(),
api.Diagnostic.CRASH);
- pleaseReportCrash();
+ String message = 'The compiler crashed.';
+ pleaseReportCrash(getAttachedStackTrace(error), message);
}
} catch (doubleFault) {
// Ignoring exceptions in exception handling.
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/placeholder_collector.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/placeholder_collector.dart
index c97e070..eb211bb 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/placeholder_collector.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/placeholder_collector.dart
@@ -314,9 +314,7 @@
return;
}
if (element == compiler.dynamicClass) {
- internalError(
- 'Should never make element placeholder for dynamic type element',
- node: node);
+ return;
}
elementNodes.putIfAbsent(element, () => new Set<Node>()).add(node);
}
diff --git a/sdk/lib/_internal/compiler/implementation/elements/elements.dart b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
index cf456b6..6aa85be 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/elements.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
@@ -680,6 +680,7 @@
abstract class TypedefElement extends Element
implements TypeDeclarationElement {
+ TypedefType get thisType;
TypedefType get rawType;
DartType get alias;
FunctionSignature get functionSignature;
@@ -790,6 +791,7 @@
* declarations and typedefs.
*/
abstract class TypeDeclarationElement extends Element {
+ GenericType get thisType;
GenericType get rawType;
/**
@@ -823,6 +825,7 @@
int get supertypeLoadState;
int get resolutionState;
+ bool get isResolved;
SourceString get nativeTagInfo;
bool get isMixinApplication;
diff --git a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
index 3ab0a44..bacfc87 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
@@ -849,14 +849,24 @@
class TypedefElementX extends ElementX implements TypedefElement {
Typedef cachedNode;
- TypedefType cachedType;
/**
- * Canonicalize raw version of [cachedType].
+ * The type of this typedef in which the type arguments are the type
+ * variables.
+ *
+ * This resembles the [ClassElement.thisType] though a typedef has no notion
+ * of [:this:].
+ *
+ * This type is computed in [computeType].
+ */
+ TypedefType thisType;
+
+ /**
+ * Canonicalized raw version of [thisType].
*
* See [ClassElement.rawType] for motivation.
*
- * The [rawType] is computed together with [cachedType] in [computeType].
+ * The [rawType] is computed together with [thisType] in [computeType].
*/
TypedefType rawType;
@@ -884,13 +894,13 @@
FunctionSignature functionSignature;
TypedefType computeType(Compiler compiler) {
- if (cachedType != null) return cachedType;
+ if (thisType != null) return thisType;
Typedef node = parseNode(compiler);
Link<DartType> parameters =
TypeDeclarationElementX.createTypeVariables(this, node.typeParameters);
- cachedType = new TypedefType(this, parameters);
+ thisType = new TypedefType(this, parameters);
if (parameters.isEmpty) {
- rawType = cachedType;
+ rawType = thisType;
} else {
var dynamicParameters = const Link<DartType>();
parameters.forEach((_) {
@@ -900,10 +910,10 @@
rawType = new TypedefType(this, dynamicParameters);
}
compiler.resolveTypedef(this);
- return cachedType;
+ return thisType;
}
- Link<DartType> get typeVariables => cachedType.typeArguments;
+ Link<DartType> get typeVariables => thisType.typeArguments;
Scope buildScope() {
return new TypeDeclarationScope(enclosingElement.buildScope(), this);
@@ -1510,6 +1520,7 @@
SourceString nativeTagInfo;
int supertypeLoadState;
int resolutionState;
+ bool get isResolved => resolutionState == STATE_DONE;
// backendMembers are members that have been added by the backend to simplify
// compilation. They don't have any user-side counter-part.
diff --git a/sdk/lib/_internal/compiler/implementation/enqueue.dart b/sdk/lib/_internal/compiler/implementation/enqueue.dart
index 22f8351..622c20a 100644
--- a/sdk/lib/_internal/compiler/implementation/enqueue.dart
+++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart
@@ -117,7 +117,12 @@
elements.registerDependency(cls);
cls.ensureResolved(compiler);
universe.instantiatedTypes.add(type);
- if (!cls.isAbstract(compiler)) {
+ if (!cls.isAbstract(compiler)
+ // We can't use the closed-world assumption with native abstract
+ // classes; a native abstract class may have non-abstract subclasses
+ // not declared to the program. Instances of these classes are
+ // indistinguishable from the abstract class.
+ || cls.isNative()) {
universe.instantiatedClasses.add(cls);
}
onRegisterInstantiatedClass(cls);
@@ -131,7 +136,7 @@
void registerTypeLiteral(Element element, TreeElements elements) {
registerInstantiatedClass(compiler.typeClass, elements);
- compiler.backend.registerTypeLiteral(element, elements);
+ compiler.backend.registerTypeLiteral(element, this, elements);
}
bool checkNoEnqueuedInvokedInstanceMethods() {
@@ -495,7 +500,7 @@
void registerAsCheck(DartType type, TreeElements elements) {
registerIsCheck(type, elements);
- compiler.backend.registerAsCheck(type, elements);
+ compiler.backend.registerAsCheck(type, this, elements);
}
void registerGenericCallMethod(Element element, TreeElements elements) {
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
index e834e1a..35e7b10 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
@@ -90,7 +90,10 @@
if (type.containsTypeVariables) {
ClassElement contextClass = Types.getClassContext(type);
- String contextName = codegen.backend.namer.getName(contextClass);
+ // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
+ // string concatenation in the implementation), and may prevent
+ // segmentation of '$'.
+ String contextName = codegen.backend.namer.getNameForRti(contextClass);
arguments.add(js.string(contextName));
if (node.contextIsTypeArguments) {
@@ -190,8 +193,8 @@
Element jsArrayRemoveLast;
Element jsArrayAdd;
Element jsStringSplit;
- Element jsStringConcat;
Element jsStringToString;
+ Element jsStringOperatorAdd;
Element objectEquals;
ClassElement typeLiteralClass;
@@ -341,9 +344,76 @@
/// List of elements that the backend may use.
final Set<Element> helpersUsed = new Set<Element>();
+
/// Set of typedefs that are used as type literals.
final Set<TypedefElement> typedefTypeLiterals = new Set<TypedefElement>();
+ /// All the checked mode helpers.
+ static const checkedModeHelpers = const [
+ const CheckedModeHelper(const SourceString('voidTypeCheck')),
+ const CheckedModeHelper(const SourceString('stringTypeCast')),
+ const CheckedModeHelper(const SourceString('stringTypeCheck')),
+ const CheckedModeHelper(const SourceString('doubleTypeCast')),
+ const CheckedModeHelper(const SourceString('doubleTypeCheck')),
+ const CheckedModeHelper(const SourceString('numTypeCast')),
+ const CheckedModeHelper(const SourceString('numTypeCheck')),
+ const CheckedModeHelper(const SourceString('boolTypeCast')),
+ const CheckedModeHelper(const SourceString('boolTypeCheck')),
+ const CheckedModeHelper(const SourceString('intTypeCast')),
+ const CheckedModeHelper(const SourceString('intTypeCheck')),
+ const PropertyCheckedModeHelper(
+ const SourceString('numberOrStringSuperNativeTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('numberOrStringSuperNativeTypeCheck')),
+ const PropertyCheckedModeHelper(
+ const SourceString('numberOrStringSuperTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('numberOrStringSuperTypeCheck')),
+ const PropertyCheckedModeHelper(
+ const SourceString('stringSuperNativeTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('stringSuperNativeTypeCheck')),
+ const PropertyCheckedModeHelper(
+ const SourceString('stringSuperTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('stringSuperTypeCheck')),
+ const CheckedModeHelper(const SourceString('listTypeCast')),
+ const CheckedModeHelper(const SourceString('listTypeCheck')),
+ const PropertyCheckedModeHelper(
+ const SourceString('listSuperNativeTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('listSuperNativeTypeCheck')),
+ const PropertyCheckedModeHelper(
+ const SourceString('listSuperTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('listSuperTypeCheck')),
+ const PropertyCheckedModeHelper(
+ const SourceString('interceptedTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('interceptedTypeCheck')),
+ const SubtypeCheckedModeHelper(
+ const SourceString('subtypeCast')),
+ const SubtypeCheckedModeHelper(
+ const SourceString('assertSubtype')),
+ const TypeVariableCheckedModeHelper(
+ const SourceString('subtypeOfRuntimeTypeCast')),
+ const TypeVariableCheckedModeHelper(
+ const SourceString('assertSubtypeOfRuntimeType')),
+ const FunctionTypeCheckedModeHelper(
+ const SourceString('functionSubtypeCast')),
+ const FunctionTypeCheckedModeHelper(
+ const SourceString('assertFunctionSubtype')),
+ const PropertyCheckedModeHelper(
+ const SourceString('propertyTypeCast')),
+ const PropertyCheckedModeHelper(
+ const SourceString('propertyTypeCheck')) ];
+
+ // Checked mode helpers indexed by name.
+ Map<String, CheckedModeHelper> checkedModeHelperByName =
+ new Map<String, CheckedModeHelper>.fromIterable(
+ checkedModeHelpers,
+ key: (helper) => helper.name.slowToString());
+
JavaScriptBackend(Compiler compiler, bool generateSourceMap, bool disableEval)
: namer = determineNamer(compiler),
oneShotInterceptors = new Map<String, Selector>(),
@@ -547,8 +617,8 @@
jsStringClass.ensureResolved(compiler);
jsStringSplit = compiler.lookupElementIn(
jsStringClass, const SourceString('split'));
- jsStringConcat = compiler.lookupElementIn(
- jsStringClass, const SourceString('concat'));
+ jsStringOperatorAdd = compiler.lookupElementIn(
+ jsStringClass, const SourceString('+'));
jsStringToString = compiler.lookupElementIn(
jsStringClass, const SourceString('toString'));
@@ -609,14 +679,16 @@
Set<Element> set = interceptedElements.putIfAbsent(
member.name, () => new Set<Element>());
set.add(member);
- if (classElement == jsInterceptorClass) return;
- if (classElement.isMixinApplication) {
- MixinApplicationElement mixinApplication = classElement;
- assert(member.getEnclosingClass() == mixinApplication.mixin);
- classesMixedIntoNativeClasses.add(mixinApplication.mixin);
- }
},
includeSuperAndInjectedMembers: true);
+
+ // Walk superclass chain to find mixins.
+ for (; cls != null; cls = cls.superclass) {
+ if (cls.isMixinApplication) {
+ MixinApplicationElement mixinApplication = cls;
+ classesMixedIntoNativeClasses.add(mixinApplication.mixin);
+ }
+ }
}
}
@@ -777,6 +849,7 @@
compiler.findHelper(const SourceString('boolConversionCheck'));
if (e != null) enqueue(world, e, elements);
}
+ registerCheckedModeHelpers(elements);
}
onResolutionComplete() => rti.computeClassesNeedingRti();
@@ -808,7 +881,10 @@
enqueueInResolution(getCyclicThrowHelper(), elements);
}
- void registerTypeLiteral(Element element, TreeElements elements) {
+ void registerTypeLiteral(Element element,
+ Enqueuer enqueuer,
+ TreeElements elements) {
+ enqueuer.registerInstantiatedClass(typeImplementation, elements);
enqueueInResolution(getCreateRuntimeType(), elements);
// TODO(ahe): Might want to register [element] as an instantiated class
// when reflection is used. However, as long as we disable tree-shaking
@@ -879,11 +955,21 @@
// [registerIsCheck] is also called for checked mode checks, so we
// need to register checked mode helpers.
if (inCheckedMode) {
- CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: false);
- if (helper != null) enqueue(world, helper.getElement(compiler), elements);
- // We also need the native variant of the check (for DOM types).
- helper = getNativeCheckedModeHelper(type, typeCast: false);
- if (helper != null) enqueue(world, helper.getElement(compiler), elements);
+ if (!world.isResolutionQueue) {
+ // All helpers are added to resolution queue in enqueueHelpers. These
+ // calls to enqueueInResolution serve as assertions that the helper was
+ // in fact added.
+ // TODO(13155): Find a way to enqueue helpers lazily.
+ CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: false);
+ if (helper != null) {
+ enqueue(world, helper.getElement(compiler), elements);
+ }
+ // We also need the native variant of the check (for DOM types).
+ helper = getNativeCheckedModeHelper(type, typeCast: false);
+ if (helper != null) {
+ enqueue(world, helper.getElement(compiler), elements);
+ }
+ }
}
bool isTypeVariable = type.kind == TypeKind.TYPE_VARIABLE;
if (!type.isRaw || type.containsTypeVariables) {
@@ -913,16 +999,22 @@
compiler.findHelper(const SourceString('defineProperty')),
elements);
}
- }
+ }
- void registerAsCheck(DartType type, TreeElements elements) {
+ void registerAsCheck(DartType type, Enqueuer world, TreeElements elements) {
type = type.unalias(compiler);
- CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: true);
- enqueueInResolution(helper.getElement(compiler), elements);
- // We also need the native variant of the check (for DOM types).
- helper = getNativeCheckedModeHelper(type, typeCast: true);
- if (helper != null) {
+ if (!world.isResolutionQueue) {
+ // All helpers are added to resolution queue in enqueueHelpers. These
+ // calls to enqueueInResolution serve as assertions that the helper was in
+ // fact added.
+ // TODO(13155): Find a way to enqueue helpers lazily.
+ CheckedModeHelper helper = getCheckedModeHelper(type, typeCast: true);
enqueueInResolution(helper.getElement(compiler), elements);
+ // We also need the native variant of the check (for DOM types).
+ helper = getNativeCheckedModeHelper(type, typeCast: true);
+ if (helper != null) {
+ enqueueInResolution(helper.getElement(compiler), elements);
+ }
}
}
@@ -1172,6 +1264,17 @@
CheckedModeHelper getCheckedModeHelperInternal(DartType type,
{bool typeCast,
bool nativeCheckOnly}) {
+ String name = getCheckedModeHelperNameInternal(type,
+ typeCast: typeCast, nativeCheckOnly: nativeCheckOnly);
+ if (name == null) return null;
+ CheckedModeHelper helper = checkedModeHelperByName[name];
+ assert(helper != null);
+ return helper;
+ }
+
+ String getCheckedModeHelperNameInternal(DartType type,
+ {bool typeCast,
+ bool nativeCheckOnly}) {
assert(type.kind != TypeKind.TYPEDEF);
Element element = type.element;
bool nativeCheck = nativeCheckOnly ||
@@ -1179,121 +1282,107 @@
if (type == compiler.types.voidType) {
assert(!typeCast); // Cannot cast to void.
if (nativeCheckOnly) return null;
- return const CheckedModeHelper(const SourceString('voidTypeCheck'));
+ return 'voidTypeCheck';
} else if (element == jsStringClass || element == compiler.stringClass) {
if (nativeCheckOnly) return null;
return typeCast
- ? const CheckedModeHelper(const SourceString("stringTypeCast"))
- : const CheckedModeHelper(const SourceString('stringTypeCheck'));
+ ? 'stringTypeCast'
+ : 'stringTypeCheck';
} else if (element == jsDoubleClass || element == compiler.doubleClass) {
if (nativeCheckOnly) return null;
return typeCast
- ? const CheckedModeHelper(const SourceString("doubleTypeCast"))
- : const CheckedModeHelper(const SourceString('doubleTypeCheck'));
+ ? 'doubleTypeCast'
+ : 'doubleTypeCheck';
} else if (element == jsNumberClass || element == compiler.numClass) {
if (nativeCheckOnly) return null;
return typeCast
- ? const CheckedModeHelper(const SourceString("numTypeCast"))
- : const CheckedModeHelper(const SourceString('numTypeCheck'));
+ ? 'numTypeCast'
+ : 'numTypeCheck';
} else if (element == jsBoolClass || element == compiler.boolClass) {
if (nativeCheckOnly) return null;
return typeCast
- ? const CheckedModeHelper(const SourceString("boolTypeCast"))
- : const CheckedModeHelper(const SourceString('boolTypeCheck'));
+ ? 'boolTypeCast'
+ : 'boolTypeCheck';
} else if (element == jsIntClass || element == compiler.intClass) {
if (nativeCheckOnly) return null;
return typeCast
- ? const CheckedModeHelper(const SourceString("intTypeCast"))
- : const CheckedModeHelper(const SourceString('intTypeCheck'));
+ ? 'intTypeCast'
+ : 'intTypeCheck';
} else if (Elements.isNumberOrStringSupertype(element, compiler)) {
if (nativeCheck) {
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString("numberOrStringSuperNativeTypeCast"))
- : const PropertyCheckedModeHelper(
- const SourceString('numberOrStringSuperNativeTypeCheck'));
+ ? 'numberOrStringSuperNativeTypeCast'
+ : 'numberOrStringSuperNativeTypeCheck';
} else {
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString("numberOrStringSuperTypeCast"))
- : const PropertyCheckedModeHelper(
- const SourceString('numberOrStringSuperTypeCheck'));
+ ? 'numberOrStringSuperTypeCast'
+ : 'numberOrStringSuperTypeCheck';
}
} else if (Elements.isStringOnlySupertype(element, compiler)) {
if (nativeCheck) {
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString("stringSuperNativeTypeCast"))
- : const PropertyCheckedModeHelper(
- const SourceString('stringSuperNativeTypeCheck'));
+ ? 'stringSuperNativeTypeCast'
+ : 'stringSuperNativeTypeCheck';
} else {
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString("stringSuperTypeCast"))
- : const PropertyCheckedModeHelper(
- const SourceString('stringSuperTypeCheck'));
+ ? 'stringSuperTypeCast'
+ : 'stringSuperTypeCheck';
}
} else if ((element == compiler.listClass || element == jsArrayClass) &&
type.isRaw) {
if (nativeCheckOnly) return null;
return typeCast
- ? const CheckedModeHelper(const SourceString("listTypeCast"))
- : const CheckedModeHelper(const SourceString('listTypeCheck'));
+ ? 'listTypeCast'
+ : 'listTypeCheck';
} else {
if (Elements.isListSupertype(element, compiler)) {
if (nativeCheck) {
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString("listSuperNativeTypeCast"))
- : const PropertyCheckedModeHelper(
- const SourceString('listSuperNativeTypeCheck'));
+ ? 'listSuperNativeTypeCast'
+ : 'listSuperNativeTypeCheck';
} else {
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString("listSuperTypeCast"))
- : const PropertyCheckedModeHelper(
- const SourceString('listSuperTypeCheck'));
+ ? 'listSuperTypeCast'
+ : 'listSuperTypeCheck';
}
} else {
if (nativeCheck) {
// TODO(karlklose): can we get rid of this branch when we use
// interceptors?
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString("interceptedTypeCast"))
- : const PropertyCheckedModeHelper(
- const SourceString('interceptedTypeCheck'));
+ ? 'interceptedTypeCast'
+ : 'interceptedTypeCheck';
} else {
if (type.kind == TypeKind.INTERFACE && !type.isRaw) {
return typeCast
- ? const SubtypeCheckedModeHelper(
- const SourceString('subtypeCast'))
- : const SubtypeCheckedModeHelper(
- const SourceString('assertSubtype'));
+ ? 'subtypeCast'
+ : 'assertSubtype';
} else if (type.kind == TypeKind.TYPE_VARIABLE) {
return typeCast
- ? const TypeVariableCheckedModeHelper(
- const SourceString('subtypeOfRuntimeTypeCast'))
- : const TypeVariableCheckedModeHelper(
- const SourceString('assertSubtypeOfRuntimeType'));
+ ? 'subtypeOfRuntimeTypeCast'
+ : 'assertSubtypeOfRuntimeType';
} else if (type.kind == TypeKind.FUNCTION) {
return typeCast
- ? const FunctionTypeCheckedModeHelper(
- const SourceString('functionSubtypeCast'))
- : const FunctionTypeCheckedModeHelper(
- const SourceString('assertFunctionSubtype'));
+ ? 'functionSubtypeCast'
+ : 'assertFunctionSubtype';
} else {
return typeCast
- ? const PropertyCheckedModeHelper(
- const SourceString('propertyTypeCast'))
- : const PropertyCheckedModeHelper(
- const SourceString('propertyTypeCheck'));
+ ? 'propertyTypeCast'
+ : 'propertyTypeCheck';
}
}
}
}
}
+ void registerCheckedModeHelpers(TreeElements elements) {
+ // We register all the helpers in the resolution queue.
+ // TODO(13155): Find a way to register fewer helpers.
+ for (CheckedModeHelper helper in checkedModeHelpers) {
+ enqueueInResolution(helper.getElement(compiler), elements);
+ }
+ }
+
/**
* Returns [:true:] if the checking of [type] is performed directly on the
* object and not on an interceptor.
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
index ae5c1f0..9ed0647 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
@@ -277,10 +277,7 @@
JavaScriptBackend get backend => compiler.backend;
jsAst.PropertyAccess getHelperProperty(Element helper) {
- String helperName = backend.namer.getName(helper);
- return new jsAst.PropertyAccess.field(
- new jsAst.VariableUse(namer.CURRENT_ISOLATE),
- helperName);
+ return backend.namer.elementAccess(helper);
}
jsAst.Expression visitType(TypeConstant constant) {
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index 3118874..8804282 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -4,6 +4,9 @@
part of js_backend;
+/// Enables debugging of fast/slow objects using V8-specific primitives.
+const DEBUG_FAST_OBJECTS = false;
+
/**
* A function element that represents a closure call. The signature is copied
* from the given element.
@@ -584,7 +587,7 @@
// Startup code that loops over the method names and puts handlers on the
// Object class to catch noSuchMethod invocations.
ClassElement objectClass = compiler.objectClass;
- String createInvocationMirror = namer.getName(
+ String createInvocationMirror = namer.isolateAccess(
backend.getCreateInvocationMirror());
String noSuchMethodName = namer.publicInstanceMethodNameByArity(
Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
@@ -592,11 +595,13 @@
if (useDiffEncoding) {
statements.addAll([
js('var objectClassObject = '
- ' collectedClasses["${namer.getName(objectClass)}"],'
+ ' collectedClasses["${namer.getNameOfClass(objectClass)}"],'
' shortNames = "$diffEncoding".split(","),'
' nameNumber = 0,'
' diffEncodedString = shortNames[0],'
' calculatedShortNames = [0, 1]'), // 0, 1 are args for splice.
+ js.if_('objectClassObject instanceof Array',
+ js('objectClassObject = objectClassObject[1]')),
js.for_('var i = 0', 'i < diffEncodedString.length', 'i++', [
js('var codes = [],'
' diff = 0,'
@@ -631,9 +636,12 @@
',longNames = "${longs.join(",")}".split(",")';
statements.add(
js('var objectClassObject = '
- ' collectedClasses["${namer.getName(objectClass)}"],'
+ ' collectedClasses["${namer.getNameOfClass(objectClass)}"],'
' shortNames = "$diffEncoding".split(",")'
' $longNamesConstant'));
+ statements.add(
+ js.if_('objectClassObject instanceof Array',
+ js('objectClassObject = objectClassObject[1]')));
}
String sliceOffset = ', (j < $firstNormalSelector) ? 1 : 0';
@@ -666,7 +674,7 @@
[js.return_(js(
'this.$noSuchMethodName('
'this, '
- '${namer.CURRENT_ISOLATE}.$createInvocationMirror('
+ '$createInvocationMirror('
'name, short, type, '
'$slice(arguments$sliceOffsetParam), []))'))]))]))
])
@@ -689,12 +697,24 @@
List<jsAst.Node> statements = [
js('var pendingClasses = {}'),
+ js.if_('!init.allClasses', js('init.allClasses = {}')),
+ js('var allClasses = init.allClasses'),
js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
+ optional(
+ DEBUG_FAST_OBJECTS,
+ js('print("Number of classes: "'
+ r' + Object.getOwnPropertyNames($$).length)')),
+
js.forIn('cls', 'collectedClasses', [
js.if_('hasOwnProperty.call(collectedClasses, cls)', [
js('var desc = collectedClasses[cls]'),
+ js('var globalObject = isolateProperties'),
+ js.if_('desc instanceof Array', [
+ js('globalObject = desc[0] || isolateProperties'),
+ js('desc = desc[1]')
+ ]),
/* The 'fields' are either a constructor function or a
* string encoding fields, constructor and superclass. Get
@@ -733,6 +753,7 @@
js('s = supr.split("+")'),
js('supr = s[0]'),
js('var mixin = collectedClasses[s[1]]'),
+ js.if_('mixin instanceof Array', js('mixin = mixin[1]')),
js.forIn('d', 'mixin', [
js.if_('hasOwnProperty.call(mixin, d)'
'&& !hasOwnProperty.call(desc, d)',
@@ -743,7 +764,8 @@
js('var constructor = defineClass(name, cls, fields, desc)'),
optional(backend.isTreeShakingDisabled,
js('constructor["${namer.metadataField}"] = desc')),
- js('isolateProperties[cls] = constructor'),
+ js('allClasses[cls] = constructor'),
+ js('globalObject[cls] = constructor'),
js.if_('supr', js('pendingClasses[cls] = supr'))
])
]),
@@ -792,8 +814,8 @@
// we have a string.
js.if_('!superclass || typeof superclass != "string"', js.return_()),
js('finishClass(superclass)'),
- js('var constructor = isolateProperties[cls]'),
- js('var superConstructor = isolateProperties[superclass]'),
+ js('var constructor = allClasses[cls]'),
+ js('var superConstructor = allClasses[superclass]'),
js.if_(js('!superConstructor'),
js('superConstructor ='
@@ -888,10 +910,6 @@
js('str += "}\\n"'),
- js('var Constants = #', js.fun('', [])),
- // Install 'C' as a prototype to ensure it has a hidden class.
- js('Constants.prototype = ${namer.globalObjectForConstant(null)}'),
-
js('var newIsolate = new Function(str)'),
js('newIsolate.prototype = isolatePrototype'),
js('isolatePrototype.constructor = newIsolate'),
@@ -907,8 +925,6 @@
}
jsAst.Fun get lazyInitializerFunction {
- String isolate = namer.CURRENT_ISOLATE;
-
// function(prototype, staticName, fieldName, getterName, lazyValue) {
var parameters = <String>['prototype', 'staticName', 'fieldName',
'getterName', 'lazyValue'];
@@ -1103,7 +1119,8 @@
parametersBuffer, argumentsBuffer,
indexOfLastOptionalArgumentInParameters);
} else {
- body = [js.return_(js('this')[namer.getName(member)](argumentsBuffer))];
+ body = [js.return_(
+ js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))];
}
jsAst.Fun function = js.fun(parametersBuffer, body);
@@ -1270,7 +1287,7 @@
if (member.isAbstract(compiler)) return;
jsAst.Expression code = backend.generatedCode[member];
if (code == null) return;
- String name = namer.getName(member);
+ String name = namer.getNameOfInstanceMember(member);
if (backend.isInterceptedMethod(member)) {
interceptorInvocationNames.add(name);
}
@@ -1490,6 +1507,7 @@
}
void emitRuntimeTypeSupport(CodeBuffer buffer) {
+ addComment('Runtime type support', buffer);
RuntimeTypes rti = backend.rti;
TypeChecks typeChecks = rti.requiredChecks;
@@ -1612,7 +1630,7 @@
|| needsSetter) {
String accessorName = isShadowed
? namer.shadowedFieldName(field)
- : namer.getName(field);
+ : namer.getNameOfField(field);
String fieldName = field.hasFixedBackendName()
? field.fixedBackendName()
: (isMixinNativeField ? name.slowToString() : accessorName);
@@ -1952,18 +1970,18 @@
assert(invariant(classElement, !classElement.isNative() || onlyForRti));
needsDefineClass = true;
- String className = namer.getName(classElement);
+ String className = namer.getNameOfClass(classElement);
ClassElement superclass = classElement.superclass;
String superName = "";
if (superclass != null) {
- superName = namer.getName(superclass);
+ superName = namer.getNameOfClass(superclass);
}
String runtimeName =
namer.getPrimitiveInterceptorRuntimeName(classElement);
if (classElement.isMixinApplication) {
- String mixinName = namer.getName(computeMixinClass(classElement));
+ String mixinName = namer.getNameOfClass(computeMixinClass(classElement));
superName = '$superName+$mixinName';
needsMixinSupport = true;
}
@@ -2332,7 +2350,7 @@
for (Element element in Elements.sortedByPosition(elements)) {
CodeBuffer buffer = bufferForElement(element, eagerBuffer);
jsAst.Expression code = backend.generatedCode[element];
- String name = namer.getName(element);
+ String name = namer.getNameOfGlobalFunction(element);
code = extendWithMetadata(element, code);
emitStaticFunction(buffer, name, code);
String reflectionName = getReflectionName(element, name);
@@ -2362,17 +2380,20 @@
final Map<Element, Element> staticGetters = new Map<Element, Element>();
void emitStaticFunctionGetters(CodeBuffer eagerBuffer) {
+ addComment('Static function getters', mainBuffer);
for (FunctionElement element in
Elements.sortedByPosition(staticGetters.keys)) {
Element closure = staticGetters[element];
CodeBuffer buffer = isDeferred(element) ? deferredConstants : eagerBuffer;
String closureClass = namer.isolateAccess(closure);
String name = namer.getStaticClosureName(element);
- String staticName = namer.getName(element);
String closureName = namer.getStaticClosureName(element);
- jsAst.Node assignment = js('$isolateProperties.$name = '
- 'new $closureClass($isolateProperties.$staticName, "$closureName")');
+ jsAst.Node assignment = js(
+ 'init.globalFunctions["$closureName"] ='
+ ' ${namer.globalObjectFor(element)}.$name ='
+ ' new $closureClass(#, "$closureName")',
+ namer.elementAccess(element));
buffer.write(jsAst.prettyPrint(assignment, compiler));
buffer.write('$N');
}
@@ -2383,8 +2404,7 @@
compiler.codegenWorld.staticFunctionsNeedingGetter;
for (FunctionElement element in
Elements.sortedByPosition(functionsNeedingGetter)) {
- String staticName = namer.getName(element);
- String superName = namer.getName(compiler.closureClass);
+ String superName = namer.getNameOfClass(compiler.closureClass);
String name = 'Closure\$${element.name.slowToString()}';
assert(instantiatedClasses.contains(compiler.closureClass));
@@ -2400,7 +2420,7 @@
element);
String invocationName = namer.instanceMethodName(callElement);
- String mangledName = namer.getName(closureClassElement);
+ String mangledName = namer.getNameOfClass(closureClassElement);
// Define the constructor with a name so that Object.toString can
// find the class name of the closure class.
@@ -2418,7 +2438,8 @@
// closures, and static closures that have common type checks.
boundClosures.add(
js('$classesCollector.$mangledName = #',
- closureBuilder.toObjectInitializer()));
+ js('[${namer.globalObjectFor(closureClassElement)}, #]',
+ closureBuilder.toObjectInitializer())));
staticGetters[element] = closureClassElement;
@@ -2497,7 +2518,7 @@
}
List<String> fieldNames = <String>[];
compiler.boundClosureClass.forEachInstanceField((_, Element field) {
- fieldNames.add(namer.getName(field));
+ fieldNames.add(namer.getNameOfInstanceMember(field));
});
DartType memberType = member.computeType(compiler);
@@ -2527,8 +2548,8 @@
ClassElement closureClassElement = new ClosureClassElement(
null, new SourceString(name), compiler, member,
member.getCompilationUnit());
- String mangledName = namer.getName(closureClassElement);
- String superName = namer.getName(closureClassElement.superclass);
+ String mangledName = namer.getNameOfClass(closureClassElement);
+ String superName = namer.getNameOfClass(closureClassElement.superclass);
// Define the constructor with a name so that Object.toString can
// find the class name of the closure class.
@@ -2582,7 +2603,8 @@
boundClosures.add(
js('$classesCollector.$mangledName = #',
- boundClosureBuilder.toObjectInitializer()));
+ js('[${namer.globalObjectFor(closureClassElement)}, #]',
+ boundClosureBuilder.toObjectInitializer())));
closureClass = namer.isolateAccess(closureClassElement);
@@ -2693,7 +2715,7 @@
compiler.withCurrentElement(element, () {
Constant initialValue = handler.getInitialValueFor(element);
jsAst.Expression init =
- js('$isolateProperties.${namer.getName(element)} = #',
+ js('$isolateProperties.${namer.getNameOfGlobalField(element)} = #',
constantEmitter.referenceInInitializationContext(initialValue));
buffer.write(jsAst.prettyPrint(init, compiler));
buffer.write('$N');
@@ -2722,7 +2744,7 @@
List<jsAst.Expression> arguments = <jsAst.Expression>[];
arguments.add(js(isolateProperties));
arguments.add(js.string(element.name.slowToString()));
- arguments.add(js.string(namer.getName(element)));
+ arguments.add(js.string(namer.getNameX(element)));
arguments.add(js.string(namer.getLazyInitializerName(element)));
arguments.add(code);
jsAst.Expression getter = buildLazyInitializedGetter(element);
@@ -2746,7 +2768,6 @@
List<Constant> constants = handler.getConstantsForEmission(
compareConstants);
bool addedMakeConstantList = false;
- eagerBuffer.write('var ${namer.globalObjectForConstant(null)}$_=$_{}$N');
for (Constant constant in constants) {
if (isConstantInlinedOrAlreadyEmitted(constant)) continue;
String name = namer.constantName(constant);
@@ -2847,10 +2868,6 @@
String noSuchMethodName = namer.publicInstanceMethodNameByArity(
Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
- Element createInvocationMirrorElement = backend.getCreateInvocationMirror();
- String createInvocationMirrorName =
- namer.getName(createInvocationMirrorElement);
-
// Keep track of the JavaScript names we've already added so we
// do not introduce duplicates (bad for code size).
Map<String, Selector> addedJsNames = new Map<String, Selector>();
@@ -2909,13 +2926,10 @@
return null;
}
- String createInvocationMirror = namer.getName(
- backend.getCreateInvocationMirror());
-
assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
jsAst.Expression expression = js('this.$noSuchMethodName')(
[js('this'),
- js(namer.CURRENT_ISOLATE)[createInvocationMirror]([
+ namer.elementAccess(backend.getCreateInvocationMirror())([
js.string(compiler.enableMinification ?
internalName : methodName),
js.string(internalName),
@@ -2948,7 +2962,6 @@
Element appMain,
Element isolateMain) {
String mainAccess = "${namer.isolateStaticClosureAccess(appMain)}";
- String currentIsolate = "${namer.CURRENT_ISOLATE}";
// Since we pass the closurized version of the main method to
// the isolate method, we must make sure that it exists.
return "${namer.isolateAccess(isolateMain)}($mainAccess)";
@@ -3016,12 +3029,6 @@
})(function(currentScript) {
init.currentScript = currentScript;
- if (typeof console !== "undefined" && typeof document !== "undefined" &&
- document.readyState == "loading") {
- console.warn("Dart script executed synchronously, use <script src='" +
- currentScript.src + "' defer></scr" + "ipt> to execute after parsing " +
- "has completed. See also http://dartbug.com/12281.");
- }
if (typeof dartMainRunner === "function") {
dartMainRunner(function() { ${mainCall}; });
} else {
@@ -3189,7 +3196,8 @@
}
buffer.write(jsAst.prettyPrint(
- js('$isolateProperties.$key = #', js.fun(['receiver'], block)),
+ js('${namer.globalObjectFor(compiler.interceptorsLibrary)}.$key = #',
+ js.fun(['receiver'], block)),
compiler));
buffer.write(N);
}
@@ -3198,6 +3206,7 @@
* Emit all versions of the [:getInterceptor:] method.
*/
void emitGetInterceptorMethods(CodeBuffer buffer) {
+ addComment('getInterceptor methods', buffer);
Map<String, Set<ClassElement>> specializedGetInterceptors =
backend.specializedGetInterceptors;
for (String name in specializedGetInterceptors.keys.toList()..sort()) {
@@ -3519,12 +3528,13 @@
}
String invocationName = backend.namer.invocationName(selector);
+ String globalObject = namer.globalObjectFor(compiler.interceptorsLibrary);
body.add(js.return_(
- js(isolateProperties)[getInterceptorName]('receiver')[invocationName](
+ js(globalObject)[getInterceptorName]('receiver')[invocationName](
arguments)));
jsAst.Expression assignment =
- js('$isolateProperties.$name = #', js.fun(parameters, body));
+ js('${globalObject}.$name = #', js.fun(parameters, body));
buffer.write(jsAst.prettyPrint(assignment, compiler));
buffer.write(N);
@@ -3543,7 +3553,10 @@
// compile time, it can be generated automatically at runtime given
// subclasses of Interceptor (which can easily be identified).
if (!compiler.enabledInvokeOn) return;
- String name = backend.namer.getName(backend.interceptedNames);
+
+ // TODO(ahe): We should roll this into
+ // [emitStaticNonFinalFieldInitializations].
+ String name = backend.namer.getNameOfGlobalField(backend.interceptedNames);
int index = 0;
var invocationNames = interceptorInvocationNames.toList()..sort();
@@ -3585,7 +3598,8 @@
}
jsAst.ArrayInitializer array = new jsAst.ArrayInitializer.from(elements);
- String name = backend.namer.getName(backend.mapTypeToInterceptor);
+ String name =
+ backend.namer.getNameOfGlobalField(backend.mapTypeToInterceptor);
jsAst.Expression assignment = js('$isolateProperties.$name = #', array);
buffer.write(jsAst.prettyPrint(assignment, compiler));
@@ -3696,7 +3710,7 @@
Elements.sortedByPosition(literals);
var properties = [];
for (TypedefElement literal in literals) {
- var key = namer.getName(literal);
+ var key = namer.getNameX(literal);
var value = js.toExpression(reifyType(literal.rawType));
properties.add(new jsAst.Property(js.string(key), value));
}
@@ -3718,6 +3732,26 @@
buffer.write('];$n');
}
+ void emitConvertToFastObjectFunction() {
+ mainBuffer.add(r'''
+function convertToFastObject(properties) {
+ function makeConstructor() {
+ var str = "{\n";
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+ for (var property in properties) {
+ if (hasOwnProperty.call(properties, property)) {
+ str += "this." + property + "= properties." + property + ";\n";
+ }
+ }
+ str += "}\n";
+ return new Function("properties", str);
+ };
+ var constructor = makeConstructor();
+ return makeConstructor.prototype = new constructor(properties);
+}
+''');
+ }
+
String assembleProgram() {
measure(() {
// Compute the required type checks to know which classes need a
@@ -3733,6 +3767,17 @@
mainBuffer.add('(function(${namer.CURRENT_ISOLATE})$_{$n');
}
+ for (String globalObject in Namer.reservedGlobalObjectNames) {
+ // The global objects start as so-called "slow objects". For V8, this
+ // means that it won't try to make map transitions as we add properties
+ // to these objects. Later on, we attempt to turn these objects into
+ // fast objects by calling "convertToFastObject" (see
+ // [emitConvertToFastObjectFunction]).
+ mainBuffer
+ ..write('var ${globalObject}$_=$_{}$N')
+ ..write('delete ${globalObject}.x$N');
+ }
+
mainBuffer.add('function ${namer.isolateName}()$_{}\n');
mainBuffer.add('init()$N$n');
// Shorten the code by using [namer.CURRENT_ISOLATE] as temporary.
@@ -3894,6 +3939,8 @@
..write(metadata == null
? "" : jsAst.prettyPrint(metadata, compiler))
..write(',$_')
+ ..write(namer.globalObjectFor(library))
+ ..write(',$_')
..write('{$n')
..addBuffer(buffer)
..write('}');
@@ -3908,6 +3955,8 @@
..write('["${library.getLibraryOrScriptName()}",$_')
..write('"${uri}",$_')
..write('[],$_')
+ ..write(namer.globalObjectFor(library))
+ ..write(',$_')
..write('{$n')
..addBuffer(buffer)
..write('}],$n');
@@ -3952,6 +4001,53 @@
mainBuffer.add(
'${namer.CURRENT_ISOLATE}$_=${_}new ${namer.isolateName}()$N');
+ emitConvertToFastObjectFunction();
+ for (String globalObject in Namer.reservedGlobalObjectNames) {
+ mainBuffer.add('$globalObject = convertToFastObject($globalObject)$N');
+ }
+ if (DEBUG_FAST_OBJECTS) {
+ ClassElement primitives =
+ compiler.findHelper(const SourceString('Primitives'));
+ FunctionElement printHelper =
+ compiler.lookupElementIn(
+ primitives, const SourceString('printString'));
+ String printHelperName = namer.isolateAccess(printHelper);
+
+ mainBuffer.add('''
+// The following only works on V8 when run with option "--allow-natives-syntax".
+if (typeof $printHelperName === "function") {
+ $printHelperName("Size of global helper object: "
+ + String(Object.getOwnPropertyNames(H).length)
+ + ", fast properties " + %HasFastProperties(H));
+ $printHelperName("Size of global platform object: "
+ + String(Object.getOwnPropertyNames(P).length)
+ + ", fast properties " + %HasFastProperties(P));
+ $printHelperName("Size of global dart:html object: "
+ + String(Object.getOwnPropertyNames(W).length)
+ + ", fast properties " + %HasFastProperties(W));
+ $printHelperName("Size of isolate properties object: "
+ + String(Object.getOwnPropertyNames(\$).length)
+ + ", fast properties " + %HasFastProperties(\$));
+ $printHelperName("Size of constant object: "
+ + String(Object.getOwnPropertyNames(C).length)
+ + ", fast properties " + %HasFastProperties(C));
+ var names = Object.getOwnPropertyNames(\$);
+ for (var i = 0; i < names.length; i++) {
+ $printHelperName("\$." + names[i]);
+ }
+}
+''');
+ for (String object in Namer.userGlobalObjects) {
+ mainBuffer.add('''
+if (typeof $printHelperName === "function") {
+ $printHelperName("Size of $object: "
+ + String(Object.getOwnPropertyNames($object).length)
+ + ", fast properties " + %HasFastProperties($object));
+}
+''');
+ }
+ }
+
emitMain(mainBuffer);
emitInitFunction(mainBuffer);
if (!areAnyElementsDeferred) {
@@ -4119,6 +4215,7 @@
if (!init.mangledGlobalNames) init.mangledGlobalNames = map();
if (!init.statics) init.statics = map();
if (!init.interfaces) init.interfaces = map();
+ if (!init.globalFunctions) init.globalFunctions = map();
var libraries = init.libraries;
var mangledNames = init.mangledNames;
var mangledGlobalNames = init.mangledGlobalNames;
@@ -4131,8 +4228,9 @@
// 0. The library name (not unique).
// 1. The library URI (unique).
// 2. A function returning the metadata associated with this library.
-// 3. An object literal listing the members of the library.
-// 4. This element is optional and if present it is true and signals that this
+// 3. The global object to use for this library.
+// 4. An object literal listing the members of the library.
+// 5. This element is optional and if present it is true and signals that this
// library is the root library (see dart:mirrors IsolateMirror.rootLibrary).
//
// The entries of [data] are built in [assembleProgram] above.
@@ -4140,8 +4238,9 @@
var name = data[0];
var uri = data[1];
var metadata = data[2];
- var descriptor = data[3];
- var isRoot = !!data[4];
+ var globalObject = data[3];
+ var descriptor = data[4];
+ var isRoot = !!data[5];
var fields = descriptor && descriptor[""];
var classes = [];
var functions = [];
@@ -4162,8 +4261,9 @@
property = property.substring(1);
${namer.CURRENT_ISOLATE}[property][$metadataField] = element;
} else if (typeof element === "function") {
- ${namer.CURRENT_ISOLATE}[previousProperty = property] = element;
+ globalObject[previousProperty = property] = element;
functions.push(property);
+ init.globalFunctions[property] = element;
} else {
previousProperty = property;
var newDesc = {};
@@ -4183,13 +4283,14 @@
newDesc[previousProp = prop] = element[prop];
}
}
- $classesCollector[property] = newDesc;
+ $classesCollector[property] = [globalObject, newDesc];
classes.push(property);
}
}
}
processStatics(descriptor);
- libraries.push([name, uri, classes, functions, metadata, fields, isRoot]);
+ libraries.push([name, uri, classes, functions, metadata, fields, isRoot,
+ globalObject]);
}
})''';
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter_no_eval.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter_no_eval.dart
index ff65688..ae6c90e 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter_no_eval.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter_no_eval.dart
@@ -113,9 +113,7 @@
}
jsAst.Expression buildLazyInitializedGetter(VariableElement element) {
- String isolate = namer.CURRENT_ISOLATE;
- String name = namer.getName(element);
- return js.fun([], js.return_(js('$isolate.$name')));
+ return js.fun([], js.return_(namer.elementAccess(element)));
}
jsAst.Fun get lazyInitializerFunction {
@@ -154,4 +152,34 @@
js('Isolate.${namer.isolatePropertiesName} = isolateProperties'),
js.return_('Isolate')]);
}
+
+ void emitConvertToFastObjectFunction() {
+ // Create an instance that uses 'properties' as prototype. This should make
+ // 'properties' a fast object.
+ mainBuffer.add(r'''function convertToFastObject(properties) {
+ function MyClass() {};
+ MyClass.prototype = properties;
+ new MyClass();
+''');
+ if (DEBUG_FAST_OBJECTS) {
+ ClassElement primitives =
+ compiler.findHelper(const SourceString('Primitives'));
+ FunctionElement printHelper =
+ compiler.lookupElementIn(
+ primitives, const SourceString('printString'));
+ String printHelperName = namer.isolateAccess(printHelper);
+ mainBuffer.add('''
+// The following only works on V8 when run with option "--allow-natives-syntax".
+if (typeof $printHelperName === "function") {
+ $printHelperName("Size of global object: "
+ + String(Object.getOwnPropertyNames(properties).length)
+ + ", fast properties " + %HasFastProperties(properties));
+}
+''');
+ }
+mainBuffer.add(r'''
+ return properties;
+}
+''');
+ }
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
index cfb179c..10c423d 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
@@ -146,11 +146,44 @@
// next-generation plugin, this results in starting a new Java process.
"java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
"JavaArray", "JavaMember",
-
- // Global object for constants.
- "C",
];
+ static const reservedGlobalObjectNames = const <String>[
+ "A",
+ "B",
+ "C", // Global object for *C*onstants.
+ "D",
+ "E",
+ "F",
+ "G",
+ "H", // Global object for internal (*H*elper) libraries.
+ // I is used for used for the Isolate function.
+ "J", // Global object for the interceptor library.
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P", // Global object for other *P*latform libraries.
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W", // Global object for *W*eb libraries (dart:html).
+ "X",
+ "Y",
+ "Z",
+ ];
+
+ static final userGlobalObjects = new List.from(reservedGlobalObjectNames)
+ ..remove('C')
+ ..remove('H')
+ ..remove('J')
+ ..remove('P')
+ ..remove('W');
+
Set<String> _jsReserved = null;
/// Names that cannot be used by members, top level and static
/// methods.
@@ -171,6 +204,9 @@
_jsVariableReserved.addAll(javaScriptKeywords);
_jsVariableReserved.addAll(reservedPropertySymbols);
_jsVariableReserved.addAll(reservedGlobalSymbols);
+ _jsVariableReserved.addAll(reservedGlobalObjectNames);
+ // 26 letters in the alphabet, 25 not counting I.
+ assert(reservedGlobalObjectNames.length == 25);
}
return _jsVariableReserved;
}
@@ -324,7 +360,7 @@
// If a library name does not start with the [LIBRARY_PREFIX] then our
// assumptions about clashing with mangled private members do not hold.
- String libraryName = getName(library);
+ String libraryName = getNameOfLibrary(library);
assert(shouldMinify || libraryName.startsWith(LIBRARY_PREFIX));
// TODO(erikcorry): Fix this with other manglings to avoid clashes.
return '_lib$libraryName\$$nameString';
@@ -425,8 +461,8 @@
// classes.
assert (!fieldElement.hasFixedBackendName());
- String libraryName = getName(fieldElement.getLibrary());
- String className = getName(fieldElement.getEnclosingClass());
+ String libraryName = getNameOfLibrary(fieldElement.getLibrary());
+ String className = getNameOfClass(fieldElement.getEnclosingClass());
String instanceName = instanceFieldName(fieldElement);
return getMappedInstanceName('$libraryName\$$className\$$instanceName');
}
@@ -591,7 +627,7 @@
// If the base Interceptor class is in the set of intercepted classes, we
// need to go through the generic getInterceptorMethod, since any subclass
// of the base Interceptor could match.
- return getName(element);
+ return getNameOfInstanceMember(element);
}
String suffix = getInterceptorSuffix(classes);
return getMappedGlobalName("${element.name.slowToString()}\$$suffix");
@@ -630,7 +666,7 @@
// are minifying, but it doesn't really make much difference. The
// important thing is that it is a unique name. We add $bailout and, if we
// are minifying, we minify the minified name and '$bailout'.
- String unminifiedName = '${getName(element)}\$bailout';
+ String unminifiedName = '${getNameX(element)}\$bailout';
if (global) {
name = getMappedGlobalName(unminifiedName);
} else {
@@ -657,10 +693,14 @@
/// Returns the runtime name for [element]. The result is not safe as an id.
String getRuntimeTypeName(Element element) {
+ if (identical(element, compiler.dynamicClass)) return 'dynamic';
JavaScriptBackend backend = compiler.backend;
element = backend.getImplementationClass(element);
String name = getPrimitiveInterceptorRuntimeName(element);
- return name != null ? name : getName(element);
+ // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
+ // string concatenation in the implementation), and may prevent
+ // segmentation of '$'.
+ return name != null ? name : getNameForRti(element);
}
/**
@@ -694,7 +734,9 @@
* For accessing statics consider calling
* [isolateAccess]/[isolateBailoutAccess] or [isolatePropertyAccess] instead.
*/
- String getName(Element element) {
+ // TODO(ahe): This is an internal method to the Namer (and its subclasses)
+ // and should not be call from outside.
+ String getNameX(Element element) {
if (element.isInstanceMember()) {
if (element.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY
|| element.kind == ElementKind.FUNCTION) {
@@ -750,35 +792,84 @@
}
}
+ String getNameForRti(Element element) => getNameX(element);
+
+ String getNameOfLibrary(LibraryElement library) => getNameX(library);
+
+ String getNameOfClass(ClassElement cls) => getNameX(cls);
+
+ String getNameOfField(VariableElement field) => getNameX(field);
+
+ String getNameOfInstanceMember(Element member) => getNameX(member);
+
+ String getNameOfGlobalField(VariableElement field) => getNameX(field);
+
+ String getNameOfGlobalFunction(FunctionElement element) => getNameX(element);
+
+ /// Returns true if [element] is stored on CURRENT_ISOLATE ('$'). We intend
+ /// to store only mutable static state in [CURRENT_ISOLATE], constants are
+ /// stored in 'C', and functions, accessors, classes, etc. are stored in one
+ /// of the other objects in [reservedGlobalObjectNames].
+ bool isPropertyOfCurrentIsolate(Element element) {
+ // TODO(ahe): Make sure this method's documentation is always true and
+ // remove the word "intend".
+ return
+ // TODO(ahe): Re-write these tests to be positive (so it only returns
+ // true for static/top-level mutable fields). Right now, a number of
+ // other elements, such as bound closures also live in CURRENT_ISOLATE.
+ !element.isAccessor() &&
+ !element.isClass() &&
+ !element.isConstructor() &&
+ !element.isFunction() &&
+ !element.isLibrary();
+ }
+
+ /// Returns [CURRENT_ISOLATE] or one of [reservedGlobalObjectNames].
+ String globalObjectFor(Element element) {
+ if (isPropertyOfCurrentIsolate(element)) return CURRENT_ISOLATE;
+ LibraryElement library = element.getLibrary();
+ if (library == compiler.interceptorsLibrary) return 'J';
+ if (library.isInternalLibrary) return 'H';
+ if (library.isPlatformLibrary) {
+ if ('${library.canonicalUri}' == 'dart:html') return 'W';
+ return 'P';
+ }
+ return userGlobalObjects[
+ library.getLibraryOrScriptName().hashCode % userGlobalObjects.length];
+ }
+
+ jsAst.PropertyAccess elementAccess(Element element) {
+ String name = getNameX(element);
+ return new jsAst.PropertyAccess.field(
+ new jsAst.VariableUse(globalObjectFor(element)),
+ name);
+ }
+
String getLazyInitializerName(Element element) {
assert(Elements.isStaticOrTopLevelField(element));
- return getMappedGlobalName("$getterPrefix${getName(element)}");
+ return getMappedGlobalName("$getterPrefix${getNameX(element)}");
}
String getStaticClosureName(Element element) {
assert(Elements.isStaticOrTopLevelFunction(element));
- return getMappedGlobalName("${getName(element)}\$closure");
- }
-
- String isolatePropertiesAccess(Element element) {
- return "$isolateName.$isolatePropertiesName.${getName(element)}";
+ return getMappedGlobalName("${getNameX(element)}\$closure");
}
String isolateAccess(Element element) {
- return "$CURRENT_ISOLATE.${getName(element)}";
+ return "${globalObjectFor(element)}.${getNameX(element)}";
}
String isolateBailoutAccess(Element element) {
- String newName = getMappedGlobalName('${getName(element)}\$bailout');
- return '$CURRENT_ISOLATE.$newName';
+ String newName = getMappedGlobalName('${getNameX(element)}\$bailout');
+ return '${globalObjectFor(element)}.$newName';
}
String isolateLazyInitializerAccess(Element element) {
- return "$CURRENT_ISOLATE.${getLazyInitializerName(element)}";
+ return "${globalObjectFor(element)}.${getLazyInitializerName(element)}";
}
String isolateStaticClosureAccess(Element element) {
- return "$CURRENT_ISOLATE.${getStaticClosureName(element)}";
+ return "${globalObjectFor(element)}.${getStaticClosureName(element)}";
}
String globalObjectForConstant(Constant constant) => 'C';
@@ -840,7 +931,10 @@
}
String substitutionName(Element element) {
- return '${operatorAsPrefix()}${getName(element)}';
+ // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
+ // string concatenation in the implementation), and may prevent
+ // segmentation of '$'.
+ return '${operatorAsPrefix()}${getNameForRti(element)}';
}
String signatureLocation(FunctionType type) {
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
index c5ccf96..67401f6 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
@@ -105,11 +105,25 @@
return backend.namer.isolateAccess(element);
}
- List<String> nativeTagsOfClass(ClassElement cls) {
+ // The tags string contains comma-separated 'words' which are either dispatch
+ // tags (having JavaScript identifier syntax) and directives that begin with
+ // `!`.
+ List<String> nativeTagsOfClassRaw(ClassElement cls) {
String quotedName = cls.nativeTagInfo.slowToString();
return quotedName.substring(1, quotedName.length - 1).split(',');
}
+ List<String> nativeTagsOfClass(ClassElement cls) {
+ return nativeTagsOfClassRaw(cls).where((s) => !s.startsWith('!')).toList();
+ }
+
+ bool nativeHasTagsMarker(ClassElement cls, String marker) {
+ return nativeTagsOfClassRaw(cls).contains(marker);
+ }
+
+ bool nativeForcedNonLeaf(ClassElement cls) =>
+ nativeHasTagsMarker(cls, '!nonleaf');
+
/**
* Writes the class definitions for the interceptors to [mainBuffer].
* Writes code to associate dispatch tags with interceptors to [nativeBuffer].
@@ -197,6 +211,10 @@
} else if (extensionPoints.containsKey(classElement)) {
needed = true;
}
+ if (classElement.isNative() && nativeForcedNonLeaf(classElement)) {
+ needed = true;
+ nonleafClasses.add(classElement);
+ }
if (needed || neededClasses.contains(classElement)) {
neededClasses.add(classElement);
@@ -266,7 +284,7 @@
if (neededClasses.contains(classElement)) {
// Define interceptor class for [classElement].
emitter.emitClassBuilderWithReflectionData(
- backend.namer.getName(classElement),
+ backend.namer.getNameOfClass(classElement),
classElement, builders[classElement],
emitter.bufferForElement(classElement, mainBuffer));
emitter.needsDefineClass = true;
@@ -318,7 +336,7 @@
superclass = backend.jsInterceptorClass;
}
- String superName = backend.namer.getName(superclass);
+ String superName = backend.namer.getNameOfClass(superclass);
ClassBuilder builder = new ClassBuilder();
emitter.emitClassConstructor(classElement, builder);
@@ -464,19 +482,21 @@
return false;
}
+ if (backend.classesMixedIntoNativeClasses.contains(element)) return true;
+
return subtypes[element] != null;
}
bool requiresNativeIsCheck(Element element) {
// TODO(sra): Remove this function. It determines if a native type may
- // satisfy a check against [element], in whcih case an interceptor must be
+ // satisfy a check against [element], in which case an interceptor must be
// used. We should also use an interceptor if the check can't be satisfied
- // by a native class in case we get a natibe instance that tries to spoof
+ // by a native class in case we get a native instance that tries to spoof
// the type info. i.e the criteria for whether or not to use an interceptor
// is whether the receiver can be native, not the type of the test.
if (!element.isClass()) return false;
ClassElement cls = element;
- if (cls.isNative()) return true;
+ if (Elements.isNativeOrExtendsNative(cls)) return true;
return isSupertypeOfNativeClass(element);
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
index 9308bec..5f9041f 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/runtime_types.dart
@@ -231,6 +231,8 @@
}
// Find all supertypes of [element] in [checkedArguments] and add checks
// and precompute the substitutions for them.
+ assert(invariant(element, element.allSupertypes != null,
+ message: 'Supertypes have not been computed for $element.'));
for (DartType supertype in element.allSupertypes) {
ClassElement superelement = supertype.element;
if (checked.contains(superelement)) {
@@ -516,14 +518,12 @@
getTypeEncoding(type, alwaysGenerateFunction: true);
if (contextClass != null) {
JavaScriptBackend backend = compiler.backend;
- String computeSignature =
- backend.namer.getName(backend.getComputeSignature());
- String contextName = backend.namer.getName(contextClass);
+ String contextName = backend.namer.getNameOfClass(contextClass);
List<jsAst.Expression> arguments =
<jsAst.Expression>[encoding, this_, js.string(contextName)];
return js.fun([], js.return_(
new jsAst.Call(
- js(backend.namer.GLOBAL_OBJECT)[js.string(computeSignature)],
+ backend.namer.elementAccess(backend.getComputeSignature()),
arguments)));
} else {
return encoding;
@@ -820,11 +820,13 @@
jsAst.Expression getCode(RuntimeTypes rti, bool ensureIsFunction) {
jsAst.Expression declaration(TypeVariableType variable) {
- return new jsAst.Parameter(variable.name.slowToString());
+ return new jsAst.Parameter(
+ rti.backend.namer.safeVariableName(variable.name.slowToString()));
}
jsAst.Expression use(TypeVariableType variable) {
- return new jsAst.VariableUse(variable.name.slowToString());
+ return new jsAst.VariableUse(
+ rti.backend.namer.safeVariableName(variable.name.slowToString()));
}
jsAst.Expression value =
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart
index 5fc7944..553f9de 100644
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart
@@ -501,7 +501,11 @@
}
onFirstNativeClass() {
- staticUse(name) => world.registerStaticUse(compiler.findHelper(name));
+ staticUse(name) {
+ JavaScriptBackend backend = compiler.backend;
+ backend.enqueue(
+ world, compiler.findHelper(name), compiler.globalDependencies);
+ }
staticUse(const SourceString('dynamicFunction'));
staticUse(const SourceString('dynamicSetMetadata'));
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
index 4aed80b..130943c 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
@@ -26,6 +26,14 @@
Selector setMoveNextSelector(ForIn node, Selector selector);
Selector setCurrentSelector(ForIn node, Selector selector);
+ /**
+ * Returns [:true:] if [node] is a type literal.
+ *
+ * Resolution marks this by setting the type on the node to be the
+ * [:Type:] type.
+ */
+ bool isTypeLiteral(Send node);
+
/// Register additional dependencies required by [currentElement].
/// For example, elements that are used by a backend.
void registerDependency(Element element);
@@ -132,6 +140,10 @@
return selectors[node.inToken];
}
+ bool isTypeLiteral(Send node) {
+ return getType(node) != null;
+ }
+
void registerDependency(Element element) {
otherDependencies.add(element.implementation);
}
@@ -1544,6 +1556,7 @@
type = reportFailureAndCreateType(
MessageKind.NOT_A_TYPE, {'node': node.typeName});
} else {
+ bool addTypeVariableBoundsCheck = false;
if (identical(element, compiler.types.voidType.element) ||
identical(element, compiler.dynamicClass)) {
type = checkNoTypeArguments(element.computeType(compiler));
@@ -1563,6 +1576,7 @@
type = cls.rawType;
} else {
type = new InterfaceType(cls.declaration, arguments.toLink());
+ addTypeVariableBoundsCheck = true;
}
}
} else if (element.isTypedef()) {
@@ -1580,7 +1594,8 @@
if (arguments.isEmpty) {
type = typdef.rawType;
} else {
- type = new TypedefType(typdef, arguments.toLink());
+ type = new TypedefType(typdef, arguments.toLink());
+ addTypeVariableBoundsCheck = true;
}
}
} else if (element.isTypeVariable()) {
@@ -1605,11 +1620,43 @@
compiler.cancel("unexpected element kind ${element.kind}",
node: node);
}
+ // TODO(johnniwinther): We should not resolve type annotations after the
+ // resolution queue has been closed. Currently the dart backend does so.
+ // Remove the guarded when this is fixed.
+ if (!compiler.enqueuer.resolution.queueIsClosed &&
+ addTypeVariableBoundsCheck) {
+ compiler.enqueuer.resolution.addPostProcessAction(
+ visitor.enclosingElement,
+ () => checkTypeVariableBounds(node, type));
+ }
}
visitor.useType(node, type);
return type;
}
+ /// Checks the type arguments of [type] against the type variable bounds.
+ void checkTypeVariableBounds(TypeAnnotation node, GenericType type) {
+ TypeDeclarationElement element = type.element;
+ Link<DartType> typeArguments = type.typeArguments;
+ Link<DartType> typeVariables = element.typeVariables;
+ while (!typeVariables.isEmpty && !typeArguments.isEmpty) {
+ TypeVariableType typeVariable = typeVariables.head;
+ DartType bound = typeVariable.element.bound.subst(
+ type.typeArguments, element.typeVariables);
+ DartType typeArgument = typeArguments.head;
+ if (!compiler.types.isSubtype(typeArgument, bound)) {
+ compiler.reportWarningCode(node,
+ MessageKind.INVALID_TYPE_VARIABLE_BOUND,
+ {'typeVariable': typeVariable,
+ 'bound': bound,
+ 'typeArgument': typeArgument,
+ 'thisType': element.thisType});
+ }
+ typeVariables = typeVariables.tail;
+ typeArguments = typeArguments.tail;
+ }
+ }
+
/**
* Resolves the type arguments of [node] and adds these to [arguments].
*
@@ -1751,8 +1798,7 @@
ResolutionEnqueuer get world => compiler.enqueuer.resolution;
- Element lookup(Node node, SourceString name) {
- Element result = scope.lookup(name);
+ Element reportLookupErrorIfAny(Element result, Node node, SourceString name) {
if (!Elements.isUnresolved(result)) {
if (!inInstanceContext && result.isInstanceMember()) {
compiler.reportError(
@@ -1828,7 +1874,12 @@
}
return null;
} else {
- Element element = lookup(node, node.source);
+ SourceString name = node.source;
+ Element element = scope.lookup(name);
+ if (Elements.isUnresolved(element) && name.slowToString() == 'dynamic') {
+ element = compiler.dynamicClass;
+ }
+ element = reportLookupErrorIfAny(element, node, node.source);
if (element == null) {
if (!inInstanceContext) {
element = warnAndCreateErroneousElement(
@@ -2309,7 +2360,7 @@
// Set the type of the node to [Type] to mark this send as a
// type variable expression.
mapping.setType(node, compiler.typeClass.computeType(compiler));
- world.registerInstantiatedClass(compiler.typeClass, mapping);
+ world.registerTypeLiteral(target, mapping);
} else if (target.impliesType() && !sendIsMemberAccess) {
// Set the type of the node to [Type] to mark this send as a
// type literal.
@@ -2473,8 +2524,10 @@
}
if (identical(source, '++')) {
registerBinaryOperator(const SourceString('+'));
+ world.registerInstantiatedClass(compiler.intClass, mapping);
} else if (identical(source, '--')) {
registerBinaryOperator(const SourceString('-'));
+ world.registerInstantiatedClass(compiler.intClass, mapping);
} else if (source.endsWith('=')) {
registerBinaryOperator(Elements.mapToUserOperator(operatorName));
}
@@ -3281,7 +3334,8 @@
resolveTypeVariableBounds(node.typeParameters);
element.functionSignature = SignatureResolver.analyze(
- compiler, node.formals, node.returnType, element);
+ compiler, node.formals, node.returnType, element,
+ defaultValuesAllowed: false);
element.alias = compiler.computeFunctionType(
element, element.functionSignature);
@@ -3753,7 +3807,15 @@
SourceString visitSendSet(SendSet node) {
assert(node.arguments.tail.isEmpty); // Sanity check
- resolver.visit(node.arguments.head);
+ Identifier identifier = node.selector;
+ SourceString name = identifier.source;
+ VariableDefinitionScope scope =
+ new VariableDefinitionScope(resolver.scope, name);
+ resolver.visitIn(node.arguments.head, scope);
+ if (scope.variableReferencedInInitializer) {
+ resolver.error(identifier, MessageKind.REFERENCE_IN_INITIALIZATION,
+ {'variableName': name.toString()});
+ }
return visit(node.selector);
}
@@ -3779,12 +3841,16 @@
*/
class SignatureResolver extends CommonResolverVisitor<Element> {
final Element enclosingElement;
+ final bool defaultValuesAllowed;
Link<Element> optionalParameters = const Link<Element>();
int optionalParameterCount = 0;
bool optionalParametersAreNamed = false;
VariableDefinitions currentDefinitions;
- SignatureResolver(Compiler compiler, this.enclosingElement) : super(compiler);
+ SignatureResolver(Compiler compiler,
+ this.enclosingElement,
+ {this.defaultValuesAllowed: true})
+ : super(compiler);
Element visitNodeList(NodeList node) {
// This must be a list of optional arguments.
@@ -3876,6 +3942,7 @@
return element;
}
+ /// A [SendSet] node is an optional parameter with a default value.
Element visitSendSet(SendSet node) {
Element element;
if (node.receiver != null) {
@@ -3890,9 +3957,13 @@
element = new VariableElementX(source, variables,
ElementKind.PARAMETER, node);
}
+ Node defaultValue = node.arguments.head;
+ if (!defaultValuesAllowed) {
+ error(defaultValue, MessageKind.TYPEDEF_FORMAL_WITH_DEFAULT);
+ }
// Visit the value. The compile time constant handler will
// make sure it's a compile time constant.
- resolveExpression(node.arguments.head);
+ resolveExpression(defaultValue);
return element;
}
@@ -3925,8 +3996,10 @@
static FunctionSignature analyze(Compiler compiler,
NodeList formalParameters,
Node returnNode,
- Element element) {
- SignatureResolver visitor = new SignatureResolver(compiler, element);
+ Element element,
+ {bool defaultValuesAllowed: true}) {
+ SignatureResolver visitor = new SignatureResolver(compiler, element,
+ defaultValuesAllowed: defaultValuesAllowed);
Link<Element> parameters = const Link<Element>();
int requiredParameterCount = 0;
if (formalParameters == null) {
@@ -4128,7 +4201,8 @@
Element visitIdentifier(Identifier node) {
SourceString name = node.source;
- Element e = resolver.lookup(node, name);
+ Element e = resolver.reportLookupErrorIfAny(
+ resolver.scope.lookup(name), node, name);
// TODO(johnniwinther): Change errors to warnings, cf. 11.11.1.
if (e == null) {
return failOrReturnErroneousElement(resolver.enclosingElement, node, name,
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/scope.dart b/sdk/lib/_internal/compiler/implementation/resolution/scope.dart
index 0af9fd7..81eadae 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/scope.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/scope.dart
@@ -41,6 +41,24 @@
}
}
+class VariableDefinitionScope extends NestedScope {
+ final SourceString variableName;
+ bool variableReferencedInInitializer = false;
+
+ VariableDefinitionScope(Scope parent, this.variableName) : super(parent);
+
+ Element localLookup(SourceString name) {
+ if (name == variableName) {
+ variableReferencedInInitializer = true;
+ }
+ return null;
+ }
+
+ Element add(Element newElement) {
+ throw "Cannot add element to VariableDefinitionScope";
+ }
+}
+
/**
* [TypeDeclarationScope] defines the outer scope of a type declaration in
* which the declared type variables and the entities in the enclosing scope are
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/scanner.dart b/sdk/lib/_internal/compiler/implementation/scanner/scanner.dart
index 75b9cb5..16c2384 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/scanner.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/scanner.dart
@@ -464,12 +464,15 @@
next = advance();
if ($0 <= next && next <= $9) {
continue;
- } else if (identical(next, $PERIOD)) {
- return tokenizeFractionPart(advance(), start);
- } else if (identical(next, $e) || identical(next, $E)
- || identical(next, $d) || identical(next, $D)) {
+ } else if (identical(next, $e) || identical(next, $E)) {
return tokenizeFractionPart(next, start);
} else {
+ if (identical(next, $PERIOD)) {
+ int nextnext = peek();
+ if ($0 <= nextnext && nextnext <= $9) {
+ return tokenizeFractionPart(advance(), start);
+ }
+ }
appendByteStringToken(INT_INFO, asciiString(start, 0));
return next;
}
@@ -543,9 +546,6 @@
appendPrecedenceToken(PERIOD_INFO);
return bigSwitch(next);
}
- if (identical(next, $d) || identical(next, $D)) {
- next = advance();
- }
appendByteStringToken(DOUBLE_INFO, asciiString(start, 0));
return next;
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index a747014..d2a303d 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -2815,7 +2815,7 @@
if (type.containsTypeVariables) {
ClassElement contextClass = Types.getClassContext(type);
contextName = graph.addConstantString(
- new DartString.literal(backend.namer.getName(contextClass)),
+ new DartString.literal(backend.namer.getNameOfClass(contextClass)),
node, compiler);
if (currentElement.isInstanceMember()) {
context = localsHandler.readThis();
@@ -3060,18 +3060,6 @@
argument, string.dartString.slowToString())));
}
- void handleForeignJsSetupObject(Send node) {
- if (!node.arguments.isEmpty) {
- compiler.cancel(
- 'Too many arguments to JS_GLOBAL_OBJECT', node: node);
- }
-
- String name = backend.namer.GLOBAL_OBJECT;
- push(new HForeign(new js.LiteralString(name),
- HType.UNKNOWN,
- <HInstruction>[]));
- }
-
void handleForeignJsCallInIsolate(Send node) {
Link<Node> link = node.arguments;
if (!compiler.hasIsolateSupport()) {
@@ -3183,8 +3171,6 @@
handleForeignJs(node);
} else if (name == const SourceString('JS_CURRENT_ISOLATE_CONTEXT')) {
handleForeignJsCurrentIsolateContext(node);
- } else if (name == const SourceString('JS_GLOBAL_OBJECT')) {
- handleForeignJsSetupObject(node);
} else if (name == const SourceString('JS_CALL_IN_ISOLATE')) {
handleForeignJsCallInIsolate(node);
} else if (name == const SourceString('DART_CLOSURE_TO_JS')) {
@@ -3350,7 +3336,10 @@
TypeVariableElement variable) {
assert(currentElement.isInstanceMember());
int index = RuntimeTypes.getTypeVariableIndex(variable);
- String substitutionNameString = backend.namer.getName(cls);
+ // TODO(ahe): Creating a string here is unfortunate. It is slow (due to
+ // string concatenation in the implementation), and may prevent
+ // segmentation of '$'.
+ String substitutionNameString = backend.namer.getNameForRti(cls);
HInstruction substitutionName = graph.addConstantString(
new LiteralDartString(substitutionNameString), null, compiler);
HInstruction target = localsHandler.readThis();
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
index 868b361..6f1fe66 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
@@ -1480,7 +1480,8 @@
backend.registerSpecializedGetInterceptor(node.interceptedClasses);
String name = backend.namer.getInterceptorName(
backend.getInterceptorMethod, node.interceptedClasses);
- var isolate = new js.VariableUse(backend.namer.CURRENT_ISOLATE);
+ var isolate = new js.VariableUse(
+ backend.namer.globalObjectFor(compiler.interceptorsLibrary));
use(node.receiver);
List<js.Expression> arguments = <js.Expression>[pop()];
push(jsPropertyCall(isolate, name, arguments), node);
@@ -1506,7 +1507,7 @@
// list class is instantiated.
world.registerInstantiatedClass(
compiler.listClass, work.resolutionTree);
- } else if (target == backend.jsStringConcat) {
+ } else if (target == backend.jsStringOperatorAdd) {
push(new js.Binary('+', object, arguments[0]), node);
return;
} else if (target.isNative() && target.isFunction()
@@ -1528,7 +1529,7 @@
void visitInvokeConstructorBody(HInvokeConstructorBody node) {
use(node.inputs[0]);
js.Expression object = pop();
- String methodName = backend.namer.getName(node.element);
+ String methodName = backend.namer.getNameOfInstanceMember(node.element);
List<js.Expression> arguments = visitArguments(node.inputs);
push(jsPropertyCall(object, methodName, arguments), node);
world.registerStaticUse(node.element);
@@ -1536,7 +1537,8 @@
void visitOneShotInterceptor(HOneShotInterceptor node) {
List<js.Expression> arguments = visitArguments(node.inputs);
- var isolate = new js.VariableUse(backend.namer.CURRENT_ISOLATE);
+ var isolate = new js.VariableUse(
+ backend.namer.globalObjectFor(compiler.interceptorsLibrary));
Selector selector = getOptimizedSelectorFor(node, node.selector);
String methodName = backend.registerOneShotInterceptor(selector);
push(jsPropertyCall(isolate, methodName, arguments), node);
@@ -1654,7 +1656,7 @@
push(access, node);
}
} else {
- String methodName = backend.namer.getName(superMethod);
+ String methodName = backend.namer.getNameOfInstanceMember(superMethod);
String className = backend.namer.isolateAccess(superClass);
js.VariableUse classReference = new js.VariableUse(className);
js.PropertyAccess prototype =
@@ -1708,7 +1710,7 @@
String _fieldPropertyName(Element element) => element.hasFixedBackendName()
? element.fixedBackendName()
- : backend.namer.getName(element);
+ : backend.namer.getNameOfInstanceMember(element);
visitLocalGet(HLocalGet node) {
use(node.receiver);
@@ -2039,12 +2041,7 @@
void visitStaticStore(HStaticStore node) {
world.registerStaticUse(node.element);
- js.VariableUse isolate = new js.VariableUse(backend.namer.CURRENT_ISOLATE);
- // Create a property access to make sure expressions and variable
- // declarations recognizers don't see this assignment as a local
- // assignment.
- js.Node variable = new js.PropertyAccess.field(
- isolate, backend.namer.getName(node.element));
+ js.Node variable = backend.namer.elementAccess(node.element);
use(node.inputs[0]);
push(new js.Assignment(variable, pop()), node);
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
index ff83098..c7770c8 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
@@ -737,14 +737,20 @@
return validator.isValid;
}
- // TODO(ngeoffray): Cache the information if this method ends up
- // being hot.
+ Map<HBasicBlock, bool> dominatesCache;
+
bool dominates(HBasicBlock other) {
+ if (dominatesCache == null) {
+ dominatesCache = new Map<HBasicBlock, bool>();
+ } else {
+ bool res = dominatesCache[other];
+ if (res != null) return res;
+ }
do {
- if (identical(this, other)) return true;
+ if (identical(this, other)) return dominatesCache[other] = true;
other = other.dominator;
} while (other != null && other.id >= id);
- return false;
+ return dominatesCache[other] = false;
}
}
@@ -2416,24 +2422,26 @@
void addBackEdge(HBasicBlock predecessor) {
backEdges.add(predecessor);
- addBlock(predecessor);
+ List<HBasicBlock> workQueue = <HBasicBlock>[predecessor];
+ do {
+ HBasicBlock current = workQueue.removeLast();
+ addBlock(current, workQueue);
+ } while (!workQueue.isEmpty);
}
// Adds a block and transitively all its predecessors in the loop as
// loop blocks.
- void addBlock(HBasicBlock block) {
+ void addBlock(HBasicBlock block, List<HBasicBlock> workQueue) {
if (identical(block, header)) return;
HBasicBlock parentHeader = block.parentLoopHeader;
if (identical(parentHeader, header)) {
// Nothing to do in this case.
} else if (parentHeader != null) {
- addBlock(parentHeader);
+ workQueue.add(parentHeader);
} else {
block.parentLoopHeader = header;
blocks.add(block);
- for (int i = 0, length = block.predecessors.length; i < length; i++) {
- addBlock(block.predecessors[i]);
- }
+ workQueue.addAll(block.predecessors);
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index 33bab0b..463e198 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -285,7 +285,7 @@
Selector selector = node.selector;
HInstruction input = node.inputs[1];
- if (selector.isCall()) {
+ if (selector.isCall() || selector.isOperator()) {
Element target;
if (input.isExtendableArray(compiler)) {
if (selector.applies(backend.jsArrayRemoveLast, compiler)) {
@@ -302,11 +302,11 @@
if (node.inputs[2].isString(compiler)) {
target = backend.jsStringSplit;
}
- } else if (selector.applies(backend.jsStringConcat, compiler)) {
- // `concat` is turned into a JavaScript '+' so we need to
+ } else if (selector.applies(backend.jsStringOperatorAdd, compiler)) {
+ // `operator+` is turned into a JavaScript '+' so we need to
// make sure the receiver is not null.
if (node.inputs[2].isString(compiler) && !input.canBeNull()) {
- target = backend.jsStringConcat;
+ target = backend.jsStringOperatorAdd;
}
} else if (selector.applies(backend.jsStringToString, compiler)
&& !input.canBeNull()) {
@@ -1153,6 +1153,12 @@
}
}
+class GvnWorkItem {
+ final HBasicBlock block;
+ final ValueSet valueSet;
+ GvnWorkItem(this.block, this.valueSet);
+}
+
class SsaGlobalValueNumberer implements OptimizationPhase {
final String name = "SsaGlobalValueNumberer";
final Compiler compiler;
@@ -1166,7 +1172,12 @@
void visitGraph(HGraph graph) {
computeChangesFlags(graph);
moveLoopInvariantCode(graph);
- visitBasicBlock(graph.entry, new ValueSet());
+ List<GvnWorkItem> workQueue =
+ <GvnWorkItem>[new GvnWorkItem(graph.entry, new ValueSet())];
+ do {
+ GvnWorkItem item = workQueue.removeLast();
+ visitBasicBlock(item.block, item.valueSet, workQueue);
+ } while (!workQueue.isEmpty);
}
void moveLoopInvariantCode(HGraph graph) {
@@ -1243,7 +1254,8 @@
return input.block.id > dominator.id;
}
- void visitBasicBlock(HBasicBlock block, ValueSet values) {
+ void visitBasicBlock(
+ HBasicBlock block, ValueSet values, List<GvnWorkItem> workQueue) {
HInstruction instruction = block.first;
if (block.isLoopHeader()) {
int flags = loopChangesFlags[block.id];
@@ -1280,10 +1292,16 @@
assert(block.id < dominated.id);
if (!successorValues.isEmpty && block.id + 1 < dominated.id) {
visited.clear();
- int changesFlags = getChangesFlagsForDominatedBlock(block, dominated);
+ List<HBasicBlock> workQueue = <HBasicBlock>[dominated];
+ int changesFlags = 0;
+ do {
+ HBasicBlock current = workQueue.removeLast();
+ changesFlags |=
+ getChangesFlagsForDominatedBlock(block, current, workQueue);
+ } while (!workQueue.isEmpty);
successorValues.kill(changesFlags);
}
- visitBasicBlock(dominated, successorValues);
+ workQueue.add(new GvnWorkItem(dominated, successorValues));
}
}
@@ -1329,7 +1347,8 @@
}
int getChangesFlagsForDominatedBlock(HBasicBlock dominator,
- HBasicBlock dominated) {
+ HBasicBlock dominated,
+ List<HBasicBlock> workQueue) {
int changesFlags = 0;
List<HBasicBlock> predecessors = dominated.predecessors;
for (int i = 0, length = predecessors.length; i < length; i++) {
@@ -1344,7 +1363,7 @@
// Loop bodies might not be on the path from dominator to dominated,
// but they can invalidate values.
changesFlags |= loopChangesFlags[id];
- changesFlags |= getChangesFlagsForDominatedBlock(dominator, block);
+ workQueue.add(block);
}
}
return changesFlags;
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart b/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart
index 257864c..2f4f70a 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/value_range_analyzer.dart
@@ -47,6 +47,12 @@
Range newNormalizedRange(Value low, Value up) {
return new Range.normalize(low, up, this);
}
+
+ Range newMarkerRange() {
+ return new Range(new MarkerValue(false, this),
+ new MarkerValue(true, this),
+ this);
+ }
}
/**
@@ -89,6 +95,33 @@
}
/**
+ * The [MarkerValue] class is used to recognize ranges of loop
+ * updates.
+ */
+class MarkerValue extends Value {
+ /// If [positive] is true (respectively false), the marker goes
+ /// to [MaxIntValue] (respectively [MinIntValue]) when being added
+ /// to a positive (respectively negative) value.
+ final bool positive;
+
+ const MarkerValue(this.positive, info) : super(info);
+
+ Value operator +(Value other) {
+ if (other.isPositive && positive) return const MaxIntValue();
+ if (other.isNegative && !positive) return const MinIntValue();
+ if (other is IntValue) return this;
+ return const UnknownValue();
+ }
+
+ Value operator -(Value other) {
+ if (other.isPositive && !positive) return const MinIntValue();
+ if (other.isNegative && positive) return const MaxIntValue();
+ if (other is IntValue) return this;
+ return const UnknownValue();
+ }
+}
+
+/**
* An [IntValue] contains a constant integer value.
*/
class IntValue extends Value {
@@ -454,7 +487,7 @@
lower.min(other.lower), upper.max(other.upper));
}
- intersection(Range other) {
+ Range intersection(Range other) {
Value low = lower.max(other.lower);
Value up = upper.min(other.upper);
// If we could not compute max or min, pick a value in the two
@@ -621,7 +654,7 @@
// phase is not necessarily run before the [ValueRangeAnalyzer].
if (phi.inputs.any((i) => !i.isInteger())) return info.newUnboundRange();
if (phi.block.isLoopHeader()) {
- Range range = tryInferLoopPhiRange(phi);
+ Range range = new LoopUpdateRecognizer(ranges, info).run(phi);
if (range == null) return info.newUnboundRange();
return range;
}
@@ -633,11 +666,6 @@
return range;
}
- Range tryInferLoopPhiRange(HPhi phi) {
- HInstruction update = phi.inputs[1];
- return update.accept(new LoopUpdateRecognizer(phi, ranges, info));
- }
-
Range visitConstant(HConstant constant) {
if (!constant.isInteger()) return info.newUnboundRange();
IntConstant constantInt = constant.constant;
@@ -908,52 +936,48 @@
}
/**
- * Recognizes a number of patterns in a loop update instruction and
- * tries to infer a range for the loop phi.
+ * Tries to find a range for the update instruction of a loop phi.
*/
class LoopUpdateRecognizer extends HBaseVisitor {
- final HPhi loopPhi;
final Map<HInstruction, Range> ranges;
final ValueRangeInfo info;
- LoopUpdateRecognizer(this.loopPhi, this.ranges, this.info);
+ LoopUpdateRecognizer(this.ranges, this.info);
- Range visitAdd(HAdd operation) {
- Range range = getRangeForRecognizableOperation(operation);
- if (range == null) return info.newUnboundRange();
- Range initial = ranges[loopPhi.inputs[0]];
- if (range.isPositive) {
- return info.newNormalizedRange(initial.lower, const MaxIntValue());
- } else if (range.isNegative) {
- return info.newNormalizedRange(const MinIntValue(), initial.upper);
- }
- return info.newUnboundRange();
+ Range run(HPhi loopPhi) {
+ // Create a marker range for the loop phi, so that if the update
+ // uses the loop phi, it has a range to use.
+ ranges[loopPhi] = info.newMarkerRange();
+ Range updateRange = visit(loopPhi.inputs[1]);
+ ranges[loopPhi] = null;
+ if (updateRange == null) return null;
+ Range startRange = ranges[loopPhi.inputs[0]];
+ // If the lower (respectively upper) value is the marker, we know
+ // the loop does not change it, so we can just use the
+ // [startRange]'s lower (upper) value. Otherwise the lower (upper) value
+ // is the minimum of the [startRange]'s lower (upper) and the
+ // [updateRange]'s lower (upper).
+ Value low = updateRange.lower is MarkerValue
+ ? startRange.lower
+ : updateRange.lower.min(startRange.lower);
+ Value up = updateRange.upper is MarkerValue
+ ? startRange.upper
+ : updateRange.upper.max(startRange.upper);
+ return info.newNormalizedRange(low, up);
}
- Range visitSubtract(HSubtract operation) {
- Range range = getRangeForRecognizableOperation(operation);
- if (range == null) return info.newUnboundRange();
- Range initial = ranges[loopPhi.inputs[0]];
- if (range.isPositive) {
- return info.newNormalizedRange(const MinIntValue(), initial.upper);
- } else if (range.isNegative) {
- return info.newNormalizedRange(initial.lower, const MaxIntValue());
- }
- return info.newUnboundRange();
+ Range visit(HInstruction instruction) {
+ if (!instruction.isInteger()) return null;
+ if (ranges[instruction] != null) return ranges[instruction];
+ return instruction.accept(this);
}
Range visitPhi(HPhi phi) {
+ // If the update of a loop phi involves another loop phi, we give
+ // up.
+ if (phi.block.isLoopHeader()) return null;
Range phiRange;
for (HInstruction input in phi.inputs) {
- HInstruction instruction = unwrap(input);
- // If one of the inputs is the loop phi, then we're only
- // interested in the other inputs: a loop phi feeding itself means
- // it is not being updated.
- if (instruction == loopPhi) continue;
-
- // If another loop phi is involved, it's too complex to analyze.
- if (instruction is HPhi && instruction.block.isLoopHeader()) return null;
-
- Range inputRange = instruction.accept(this);
+ Range inputRange = visit(input);
if (inputRange == null) return null;
if (phiRange == null) {
phiRange = inputRange;
@@ -964,49 +988,23 @@
return phiRange;
}
- /**
- * If [operation] is recognizable, returns the inferred range.
- * Otherwise returns [null].
- */
- Range getRangeForRecognizableOperation(HBinaryArithmetic operation) {
- if (!operation.left.isInteger()) return null;
- if (!operation.right.isInteger()) return null;
- HInstruction left = unwrap(operation.left);
- HInstruction right = unwrap(operation.right);
- // We only recognize operations that operate on the loop phi.
- bool isLeftLoopPhi = (left == loopPhi);
- bool isRightLoopPhi = (right == loopPhi);
- if (!isLeftLoopPhi && !isRightLoopPhi) return null;
-
- var other = isLeftLoopPhi ? right : left;
- // If the analysis already computed range for the update, use it.
- if (ranges[other] != null) return ranges[other];
-
- // We currently only handle constants in updates if the
- // update does not have a range.
- if (other.isConstant()) {
- Value value = info.newIntValue(other.constant.value);
- return info.newNormalizedRange(value, value);
- }
- return null;
+ Range visitCheck(HCheck instruction) {
+ return visit(instruction.checkedInput);
}
- /**
- * [HCheck] instructions may check the loop phi. Since we only
- * recognize updates on the loop phi, we must [unwrap] the [HCheck]
- * instruction to check if it references the loop phi.
- */
- HInstruction unwrap(instruction) {
- if (instruction is HCheck) return unwrap(instruction.checkedInput);
- // [HPhi] might have two different [HCheck] instructions as
- // inputs, checking the same instruction.
- if (instruction is HPhi && !instruction.block.isLoopHeader()) {
- HInstruction result = unwrap(instruction.inputs[0]);
- for (int i = 1; i < instruction.inputs.length; i++) {
- if (result != unwrap(instruction.inputs[i])) return instruction;
- }
- return result;
- }
- return instruction;
+ Range visitAdd(HAdd operation) {
+ return handleBinaryOperation(operation);
+ }
+
+ Range visitSubtract(HSubtract operation) {
+ return handleBinaryOperation(operation);
+ }
+
+ Range handleBinaryOperation(HBinaryArithmetic instruction) {
+ Range leftRange = visit(instruction.left);
+ Range rightRange = visit(instruction.right);
+ if (leftRange == null || rightRange == null) return null;
+ BinaryOperation operation = instruction.operation(info.constantSystem);
+ return operation.apply(leftRange, rightRange);
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/typechecker.dart b/sdk/lib/_internal/compiler/implementation/typechecker.dart
index 3c8bae6..61191fc 100644
--- a/sdk/lib/_internal/compiler/implementation/typechecker.dart
+++ b/sdk/lib/_internal/compiler/implementation/typechecker.dart
@@ -592,7 +592,7 @@
return const DynamicAccess();
} else if (element.impliesType()) {
// The literal `Foo` where Foo is a class, a typedef, or a type variable.
- if (elements.getType(node) != null) {
+ if (elements.isTypeLiteral(node)) {
assert(invariant(node, identical(compiler.typeClass,
elements.getType(node).element),
message: 'Expected type literal type: '
diff --git a/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart b/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart
index aa6512e..49d14b1 100644
--- a/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/concrete_types_inferrer.dart
@@ -1065,10 +1065,14 @@
return result == const DynamicTypeMask() ? null : result;
}
+ Iterable<TypeMask> get containerTypes {
+ throw new UnsupportedError("");
+ }
+
void clear() {}
Iterable<Element> getCallersOf(Element element) {
- throw "Don't use me";
+ throw new UnsupportedError("");
}
// --- analysis ---
diff --git a/sdk/lib/_internal/compiler/implementation/types/container_tracer.dart b/sdk/lib/_internal/compiler/implementation/types/container_tracer.dart
index 22271b5..0abae5d 100644
--- a/sdk/lib/_internal/compiler/implementation/types/container_tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/container_tracer.dart
@@ -264,6 +264,9 @@
// [potentialType] can be null if we did not find any instruction
// that adds elements to the list.
if (potentialType == null) {
+ if (_VERBOSE) {
+ print('Found empty type for $analyzedNode $startElement');
+ }
mask.elementType = new TypeMask.nonNullEmpty();
return;
}
@@ -462,6 +465,7 @@
LocalsHandler closureLocals = new LocalsHandler<TypeMask>.from(
locals, node, useOtherTryBlock: false);
new ContainerTracerVisitor(function, tracer, closureLocals).run();
+ return types.functionType;
} else {
// Visiting [analyzedElement].
FunctionSignature signature = function.computeSignature(compiler);
@@ -472,8 +476,8 @@
visit(node.initializers);
visitingInitializers = false;
visit(node.body);
+ return null;
}
- return types.functionType;
}
TypeMask visitLiteralList(LiteralList node) {
diff --git a/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart b/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
index a4d3936..9569030 100644
--- a/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/inferrer_visitor.dart
@@ -384,17 +384,28 @@
void mergeOneBranch(LocalsHandler<T> other) {
other.locals.forEachOwnLocal((Element local, T type) {
T myType = locals[local];
- if (myType == null) return;
+ if (myType == null) return; // Variable is only defined in [other].
if (type == myType) return;
locals[local] = types.allocateDiamondPhi(myType, type);
});
}
+ void inPlaceUpdateOneBranch(LocalsHandler<T> other) {
+ other.locals.forEachOwnLocal((Element local, T type) {
+ T myType = locals[local];
+ if (myType == null) return; // Variable is only defined in [other].
+ if (type == myType) return;
+ locals[local] = type;
+ });
+ }
+
if (thenBranch.aborts) {
if (elseBranch == null) return;
- mergeOneBranch(elseBranch);
- } else if (elseBranch == null || elseBranch.aborts) {
+ inPlaceUpdateOneBranch(elseBranch);
+ } else if (elseBranch == null) {
mergeOneBranch(thenBranch);
+ } else if (elseBranch.aborts) {
+ inPlaceUpdateOneBranch(thenBranch);
} else {
void mergeLocal(Element local) {
T myType = locals[local];
@@ -678,7 +689,9 @@
}
T visitTypeReferenceSend(Send node) {
- return types.typeType;
+ // If [node] is not a type literal is the class name of a static access,
+ // in which case we don't use the type mask.
+ return elements.isTypeLiteral(node) ? types.typeType : null;
}
bool isThisOrSuper(Node node) => node.isThis() || node.isSuper();
diff --git a/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart b/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
index 7d305fd..6079d4b 100644
--- a/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart
@@ -169,7 +169,7 @@
/**
* Callers of an element.
*/
- Map<Element, int> get callers => null;
+ Map<Element, Set<Spannable>> get callers => null;
/**
* Number of times the element has been processed.
@@ -183,20 +183,20 @@
TypeMask get returnType => null;
void set returnType(value) {}
- void addCaller(Element caller) {
+ void addCaller(Element caller, Spannable node) {
if (callers.containsKey(caller)) {
- callers[caller]++;
+ callers[caller].add(node);
} else {
- callers[caller] = 1;
+ callers[caller] = new Set<Spannable>()..add(node);
}
}
- void removeCall(Element caller) {
+ void removeCall(Element caller, Spannable node) {
if (!callers.containsKey(caller)) return;
- if (callers[caller] == 1) {
+ Set<Spannable> calls = callers[caller];
+ calls.remove(node);
+ if (calls.isEmpty) {
callers.remove(caller);
- } else {
- callers[caller]--;
}
}
@@ -208,7 +208,7 @@
}
class FunctionTypeInformation extends TypeInformation {
- Map<Element, int> callers = new Map<Element, int>();
+ Map<Element, Set<Spannable>> callers = new Map<Element, Set<Spannable>>();
TypeMask returnType;
int analyzeCount = 0;
bool canBeClosurized = false;
@@ -230,7 +230,7 @@
class FieldTypeInformation extends TypeInformation {
TypeMask type;
- Map<Element, int> callers = new Map<Element, int>();
+ Map<Element, Set<Spannable>> callers = new Map<Element, Set<Spannable>>();
Map<Spannable, TypeMask> assignments = new Map<Spannable, TypeMask>();
int analyzeCount = 0;
@@ -333,9 +333,10 @@
* type information about visited nodes, as well as to request type
* information of elements.
*/
-abstract class InferrerEngine<T> implements MinimalInferrerEngine<T> {
+abstract class InferrerEngine<T, V extends TypeSystem>
+ implements MinimalInferrerEngine<T> {
final Compiler compiler;
- final TypeSystem<T> types;
+ final V types;
final Map<Node, T> concreteTypes = new Map<Node, T>();
InferrerEngine(this.compiler, this.types);
@@ -361,11 +362,6 @@
T returnTypeOfElement(Element element);
/**
- * Returns the type returned by a call to this [selector].
- */
- T returnTypeOfSelector(Selector selector);
-
- /**
* Records that [node] sets final field [element] to be of type [type].
*
* [nodeHolder] is the element holder of [node].
@@ -590,10 +586,44 @@
return returnTypeOfElement(element);
}
}
+
+ void updateSelectorInTree(Element owner, Node node, Selector selector) {
+ var elements = compiler.enqueuer.resolution.getCachedElements(owner);
+ if (node.asSendSet() != null) {
+ if (selector.isSetter() || selector.isIndexSet()) {
+ elements.setSelector(node, selector);
+ } else if (selector.isGetter() || selector.isIndex()) {
+ elements.setGetterSelectorInComplexSendSet(node, selector);
+ } else {
+ assert(selector.isOperator());
+ elements.setOperatorSelectorInComplexSendSet(node, selector);
+ }
+ } else if (node.asSend() != null) {
+ elements.setSelector(node, selector);
+ } else {
+ assert(node.asForIn() != null);
+ if (selector.asUntyped == compiler.iteratorSelector) {
+ elements.setIteratorSelector(node, selector);
+ } else if (selector.asUntyped == compiler.currentSelector) {
+ elements.setCurrentSelector(node, selector);
+ } else {
+ assert(selector.asUntyped == compiler.moveNextSelector);
+ elements.setMoveNextSelector(node, selector);
+ }
+ }
+ }
+
+ bool isNativeElement(Element element) {
+ if (element.isNative()) return true;
+ return element.isMember()
+ && element.getEnclosingClass().isNative()
+ && element.isField();
+ }
}
class InternalSimpleTypesInferrer
- extends InferrerEngine<TypeMask> implements TypesInferrer {
+ extends InferrerEngine<TypeMask, TypeMaskSystem>
+ implements TypesInferrer {
/**
* Maps a class to a [ClassTypeInformation] to help collect type
* information of final fields.
@@ -1023,13 +1053,6 @@
&& newType != types.nullType;
}
- bool isNativeElement(Element element) {
- if (element.isNative()) return true;
- return element.isMember()
- && element.getEnclosingClass().isNative()
- && element.isField();
- }
-
TypeMask checkTypeAnnotation(Element analyzedElement, TypeMask newType) {
if (compiler.trustTypeAnnotations
// Parameters are being checked by the method, and we can
@@ -1181,11 +1204,11 @@
return outermost.declaration == element.declaration;
}
- void addCaller(Element caller, Element callee) {
+ void addCaller(Element caller, Element callee, Spannable node) {
assert(caller.isImplementation);
assert(callee.isImplementation);
assert(isNotClosure(caller));
- typeInformationOf(callee).addCaller(caller);
+ typeInformationOf(callee).addCaller(caller, node);
}
bool addArguments(Spannable node,
@@ -1293,7 +1316,7 @@
assert(isNotClosure(caller));
callee = callee.implementation;
- addCaller(caller, callee);
+ addCaller(caller, callee, node);
if (selector != null && selector.isSetter() && callee.isField()) {
recordTypeOfNonFinalField(
@@ -1333,7 +1356,7 @@
Selector selector,
Element caller,
Element callee) {
- typeInformationOf(callee).removeCall(caller);
+ typeInformationOf(callee).removeCall(caller, node);
if (callee.isField()) {
if (selector.isSetter()) {
Map<Spannable, TypeMask> assignments =
@@ -1378,6 +1401,9 @@
TypeMask handleIntrisifiedSelector(Selector selector,
ArgumentsTypes arguments) {
+ // If [:compiler.intClass:] has not been resolved, there are no int values
+ // in the program.
+ if (!compiler.intClass.isResolved) return null;
TypeMask intType = types.intType;
if (selector.mask != intType) return null;
if (!selector.isCall() && !selector.isOperator()) return null;
@@ -1597,13 +1623,13 @@
}
class SimpleTypeInferrerVisitor<T>
- extends InferrerVisitor<T, InferrerEngine<T>> {
+ extends InferrerVisitor<T, InferrerEngine<T, TypeSystem<T>>> {
T returnType;
bool visitingInitializers = false;
bool isConstructorRedirect = false;
SideEffects sideEffects = new SideEffects.empty();
final Element outermostElement;
- final InferrerEngine<T> inferrer;
+ final InferrerEngine<T, TypeSystem<T>> inferrer;
final Set<Element> capturedVariables = new Set<Element>();
SimpleTypeInferrerVisitor.internal(analyzedElement,
@@ -1616,7 +1642,7 @@
factory SimpleTypeInferrerVisitor(Element element,
Compiler compiler,
- InferrerEngine<T> inferrer,
+ InferrerEngine<T, TypeSystem<T>> inferrer,
[LocalsHandler<T> handler]) {
Element outermostElement =
element.getOutermostEnclosingMemberOrTopLevel().implementation;
@@ -1774,12 +1800,18 @@
// We only set the type once. We don't need to re-visit the children
// when re-analyzing the node.
return inferrer.concreteTypes.putIfAbsent(node, () {
- T elementType = types.nonNullEmpty();
+ T elementType;
int length = 0;
for (Node element in node.elements.nodes) {
+ T type = visit(element);
+ elementType = elementType == null
+ ? types.allocatePhi(null, null, type)
+ : types.addPhiInput(null, elementType, type);
length++;
- elementType = types.computeLUB(elementType, visit(element));
}
+ elementType = elementType == null
+ ? types.nonNullEmpty()
+ : types.simplifyPhi(null, null, elementType);
return types.allocateContainer(
types.constListType,
node,
@@ -2047,7 +2079,7 @@
isThisExposed = true;
if (node.isPropertyAccess) {
return handleStaticSend(node, selector, element, null);
- } else if (element.isFunction()) {
+ } else if (element.isFunction() || element.isGenerativeConstructor()) {
if (!selector.applies(element, compiler)) return types.dynamicType;
ArgumentsTypes arguments = analyzeArguments(node.arguments);
return handleStaticSend(node, selector, element, arguments);
@@ -2176,31 +2208,6 @@
sideEffects, inLoop);
}
- void updateSelectorInTree(Node node, Selector selector) {
- if (node.asSendSet() != null) {
- if (selector.isSetter() || selector.isIndexSet()) {
- elements.setSelector(node, selector);
- } else if (selector.isGetter() || selector.isIndex()) {
- elements.setGetterSelectorInComplexSendSet(node, selector);
- } else {
- assert(selector.isOperator());
- elements.setOperatorSelectorInComplexSendSet(node, selector);
- }
- } else if (node.asSend() != null) {
- elements.setSelector(node, selector);
- } else {
- assert(node.asForIn() != null);
- if (selector.asUntyped == compiler.iteratorSelector) {
- elements.setIteratorSelector(node, selector);
- } else if (selector.asUntyped == compiler.currentSelector) {
- elements.setCurrentSelector(node, selector);
- } else {
- assert(selector.asUntyped == compiler.moveNextSelector);
- elements.setMoveNextSelector(node, selector);
- }
- }
- }
-
T handleDynamicSend(Node node,
Selector selector,
T receiverType,
@@ -2211,12 +2218,12 @@
selector = (receiverType == types.dynamicType)
? selector.asUntyped
: types.newTypedSelector(receiverType, selector);
- updateSelectorInTree(node, selector);
+ inferrer.updateSelectorInTree(analyzedElement, node, selector);
}
// If the receiver of the call is a local, we may know more about
// its type by refining it with the potential targets of the
- // calls.
+ // calls.
if (node.asSend() != null) {
Node receiver = node.asSend().receiver;
if (receiver != null) {
diff --git a/sdk/lib/_internal/compiler/implementation/types/type_graph_inferrer.dart b/sdk/lib/_internal/compiler/implementation/types/type_graph_inferrer.dart
new file mode 100644
index 0000000..0430004
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/types/type_graph_inferrer.dart
@@ -0,0 +1,1359 @@
+// Copyright (c) 2013, 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.
+
+library type_graph_inferrer;
+
+import 'dart:collection' show Queue, LinkedHashSet, IterableBase, HashMap;
+import '../dart_types.dart' show DartType, InterfaceType, TypeKind;
+import '../elements/elements.dart';
+import '../tree/tree.dart' show Node;
+import 'types.dart' show TypeMask, ContainerTypeMask, TypesInferrer;
+import '../universe/universe.dart' show Selector, TypedSelector, SideEffects;
+import '../dart2jslib.dart' show Compiler, SourceString, TreeElementMapping;
+import 'inferrer_visitor.dart' show TypeSystem, ArgumentsTypes, CallSite;
+import '../native_handler.dart' as native;
+import '../util/util.dart' show Spannable;
+import 'simple_types_inferrer.dart';
+import '../dart2jslib.dart' show invariant;
+
+/**
+ * Common class for all nodes in the graph. The current nodes are:
+ *
+ * - Concrete types
+ * - Elements
+ * - Call sites
+ * - Narrowing instructions
+ * - Phi instructions
+ * - Containers (for lists)
+ * - Type of the element in a container
+ *
+ * A node has a set of assignments and users. Assignments are used to
+ * compute the type of the node ([TypeInformation.refine]). Users are
+ * added to the inferrer's work queue when the type of the node
+ * changes.
+ */
+abstract class TypeInformation {
+ var /* List|Set */ users;
+ var /* List|ParameterAssignments */ assignments;
+
+ /// The type the inferrer has found for this [TypeInformation].
+ /// Initially dynamic.
+ TypeMask type;
+
+ /// We give up on inferencing for special elements, as well as for
+ /// complicated cyclic dependencies.
+ bool abandonInferencing = false;
+
+ /// Number of times this [TypeInformation] has changed type.
+ int refineCount = 0;
+
+ /// Whether this [TypeInformation] is currently in the inferrer's
+ /// work queue.
+ bool inQueue = false;
+
+ // TypeInformations are unique.
+ static int staticHashCode = 0;
+ final int hashCode = staticHashCode++;
+
+ bool get isConcrete => false;
+
+ TypeInformation(this.type, [users, assignments])
+ : users = (users == null) ? new Set<TypeInformation>() : users,
+ assignments = (assignments == null) ? <TypeInformation>[] : assignments;
+
+
+ void addUser(TypeInformation user) {
+ assert(!user.isConcrete);
+ users.add(user);
+ }
+
+ void removeUser(TypeInformation user) {
+ assert(!user.isConcrete);
+ users.remove(user);
+ }
+
+ void addAssignment(TypeInformation assignment) {
+ if (abandonInferencing) return;
+ // Cheap one-level cycle detection.
+ if (assignment == this) return;
+ assignments.add(assignment);
+ assignment.addUser(this);
+ }
+
+ void removeAssignment(TypeInformation assignment) {
+ if (!abandonInferencing) {
+ assignments.remove(assignment);
+ }
+ // We can have multiple assignments of the same [TypeInformation].
+ if (!assignments.contains(assignment)) {
+ assignment.removeUser(this);
+ }
+ }
+
+ TypeMask refine(TypeGraphInferrerEngine inferrer) {
+ return type;
+ }
+
+ TypeMask refineOptimistic(TypeGraphInferrerEngine inferrer) {
+ return refine(inferrer);
+ }
+
+ void giveUp(TypeGraphInferrerEngine inferrer) {
+ abandonInferencing = true;
+ type = inferrer.types.dynamicType.type;
+ assignments = const <TypeInformation>[];
+ }
+
+ void clear() {
+ assignments = const <TypeInformation>[];
+ users = const <TypeInformation>[];
+ }
+}
+
+/**
+ * Parameters of instance functions behave differently than other
+ * elements because the inferrer may remove assignments. This happens
+ * when the receiver of a dynamic call site can be refined
+ * to a type where we know more about which instance method is being
+ * called.
+ */
+class ParameterAssignments extends IterableBase<TypeInformation> {
+ final Map<TypeInformation, int> assignments =
+ new HashMap<TypeInformation, int>();
+
+ void remove(TypeInformation info) {
+ int existing = assignments[info];
+ if (existing == null) return;
+ if (existing == 1) {
+ assignments.remove(info);
+ } else {
+ assignments[info] = existing - 1;
+ }
+ }
+
+ void add(TypeInformation info) {
+ int existing = assignments[info];
+ if (existing == null) {
+ assignments[info] = 1;
+ } else {
+ assignments[info] = existing + 1;
+ }
+ }
+
+ Iterator<TypeInformation> get iterator => assignments.keys.iterator;
+ Iterable<TypeInformation> where(Function f) => assignments.keys.where(f);
+
+ bool contains(TypeInformation info) => assignments.containsKey(info);
+}
+
+/**
+ * A node representing a resolved element of the program. The kind of
+ * elements that need an [ElementTypeRepresentation] are:
+ *
+ * - Functions (including getters and setters)
+ * - Constructors (factory or generative)
+ * - Fields
+ * - Parameters
+ * - Local variables mutated in closures
+ *
+ * The [ElementTypeInformation] of a function and a constructor is its
+ * return type.
+ *
+ * Note that a few elements of these kinds must be treated specially,
+ * and they are dealt in [ElementTypeInformation.handleSpecialCase]:
+ *
+ * - Parameters of closures, [noSuchMethod] and [call] instance
+ * methods: we currently do not infer types for those.
+ *
+ * - Fields and parameters being assigned by synthesized calls done by
+ * the backend: we do not know what types the backend will use.
+ *
+ * - Native functions and fields: because native methods contain no Dart
+ * code, and native fields do not have Dart assignments, we just
+ * trust their type annotation.
+ *
+ */
+class ElementTypeInformation extends TypeInformation {
+ final Element element;
+ final Map<Element, Set<Spannable>> callers =
+ new Map<Element, Set<Spannable>>();
+
+ ElementTypeInformation.internal(this.element, type, assignments)
+ : super(type, null, assignments);
+
+ factory ElementTypeInformation(Element element, TypeMask type) {
+ var assignments = null;
+ if (element.enclosingElement.isInstanceMember()
+ && (element.isParameter() || element.isFieldParameter())) {
+ assignments = new ParameterAssignments();
+ }
+ return new ElementTypeInformation.internal(element, type, assignments);
+ }
+
+ void addCall(Element caller, Spannable node) {
+ callers.putIfAbsent(caller, () => new Set<Spannable>()).add(node);
+ }
+
+ void removeCall(Element caller, Spannable node) {
+ Set<Spannable> calls = callers[caller];
+ if (calls == null) return;
+ calls.remove(node);
+ if (calls.isEmpty) {
+ callers.remove(caller);
+ }
+ }
+
+ TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) {
+ if (abandonInferencing) {
+ return type;
+ }
+ if (element.isParameter()) {
+ Element enclosing = element.enclosingElement;
+ if (Elements.isLocal(enclosing)) {
+ // Do not infer types for parameters of closures.
+ giveUp(inferrer);
+ return type;
+ } else if (enclosing.isInstanceMember()
+ && (enclosing.name == Compiler.NO_SUCH_METHOD
+ || enclosing.name == Compiler.CALL_OPERATOR_NAME)) {
+ // Do not infer types for parameters of [noSuchMethod] and
+ // [call] instance methods.
+ giveUp(inferrer);
+ return type;
+ }
+ }
+ if (element.isField()
+ || element.isParameter()
+ || element.isFieldParameter()) {
+ if (!inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) {
+ // Do not infer types for fields and parameters being assigned
+ // by synthesized calls.
+ giveUp(inferrer);
+ return type;
+ }
+ }
+ if (inferrer.isNativeElement(element)) {
+ // Use the type annotation as the type for native elements. We
+ // also give up on inferring to make sure this element never
+ // goes in the work queue.
+ giveUp(inferrer);
+ if (element.isField()) {
+ InterfaceType rawType = element.computeType(inferrer.compiler).asRaw();
+ return rawType.treatAsDynamic
+ ? inferrer.types.dynamicType.type
+ : new TypeMask.subtype(rawType);
+ } else {
+ assert(element.isFunction()
+ || element.isGetter()
+ || element.isSetter());
+ var elementType = element.computeType(inferrer.compiler);
+ if (elementType.kind != TypeKind.FUNCTION) {
+ return type;
+ } else {
+ return inferrer.typeOfNativeBehavior(
+ native.NativeBehavior.ofMethod(element, inferrer.compiler)).type;
+ }
+ }
+ }
+ return null;
+ }
+
+ TypeMask refine(TypeGraphInferrerEngine inferrer) {
+ TypeMask special = handleSpecialCases(inferrer);
+ if (special != null) return special;
+ return inferrer.types.computeTypeMask(assignments);
+ }
+
+ TypeMask refineOptimistic(TypeGraphInferrerEngine inferrer) {
+ TypeMask special = handleSpecialCases(inferrer);
+ if (special != null) return special;
+ return inferrer.types.computeTypeMask(
+ assignments.where((e) => e.isConcrete));
+ }
+
+ String toString() => 'Element $element';
+}
+
+/**
+ * A [CallSiteTypeInformation] is a call found in the AST, or a
+ * synthesized call for implicit calls in Dart (such as forwarding
+ * factories). The [call] field is a [Node] for the former, and an
+ * [Element] for the latter.
+ *
+ * In the inferrer graph, [CallSiteTypeInformation] nodes do not have
+ * any assignment. They rely on the [caller] field for static calls,
+ * and [selector] and [receiver] fields for dynamic calls.
+ */
+abstract class CallSiteTypeInformation extends TypeInformation {
+ final Spannable call;
+ final Element caller;
+ final Selector selector;
+ final ArgumentsTypes arguments;
+
+ CallSiteTypeInformation(
+ this.call,
+ this.caller,
+ this.selector,
+ this.arguments,
+ TypeMask type) : super(type, null, const <TypeInformation>[]);
+
+ String toString() => 'Call site $call';
+
+ /// Add [this] to the graph being computed by [engine].
+ void addToGraph(TypeGraphInferrerEngine engine);
+
+ /// Return an iterable over the targets of this call.
+ Iterable<Element> get callees;
+}
+
+class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
+ final Element calledElement;
+
+ StaticCallSiteTypeInformation(
+ Spannable call,
+ Element enclosing,
+ this.calledElement,
+ Selector selector,
+ ArgumentsTypes arguments,
+ TypeMask type) : super(call, enclosing, selector, arguments, type);
+
+ void addToGraph(TypeGraphInferrerEngine inferrer) {
+ ElementTypeInformation callee =
+ inferrer.types.getInferredTypeOf(calledElement);
+ callee.addCall(caller, call);
+ callee.addUser(this);
+ if (arguments != null) {
+ arguments.forEach((info) => info.addUser(this));
+ }
+ inferrer.updateParameterAssignments(
+ this, calledElement, arguments, selector, remove: false, init: true);
+ }
+
+ TypeMask refine(TypeGraphInferrerEngine inferrer) {
+ return inferrer.types.getInferredTypeOf(calledElement).type;
+ }
+
+ Iterable<Element> get callees => [calledElement.implementation];
+}
+
+class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
+ final TypeInformation receiver;
+ /// Cached targets of this call.
+ Iterable<Element> targets;
+
+ DynamicCallSiteTypeInformation(
+ Spannable call,
+ Element enclosing,
+ Selector selector,
+ this.receiver,
+ ArgumentsTypes arguments,
+ TypeMask type) : super(call, enclosing, selector, arguments, type);
+
+ void addToGraph(TypeGraphInferrerEngine inferrer) {
+ assert(receiver != null);
+ Selector typedSelector = computeTypedSelector(inferrer);
+ targets = inferrer.compiler.world.allFunctions.filter(typedSelector);
+ receiver.addUser(this);
+ if (arguments != null) {
+ arguments.forEach((info) => info.addUser(this));
+ }
+ for (Element element in targets) {
+ ElementTypeInformation callee = inferrer.types.getInferredTypeOf(element);
+ callee.addCall(caller, call);
+ callee.addUser(this);
+ inferrer.updateParameterAssignments(
+ this, element, arguments, typedSelector, remove: false, init: true);
+ }
+ }
+
+ Iterable<Element> get callees => targets.map((e) => e.implementation);
+
+ Selector computeTypedSelector(TypeGraphInferrerEngine inferrer) {
+ TypeMask receiverType = receiver.type;
+ if (selector.mask != receiverType) {
+ return receiverType == inferrer.compiler.typesTask.dynamicType
+ ? selector.asUntyped
+ : new TypedSelector(receiverType, selector);
+ } else {
+ return selector;
+ }
+ }
+
+ bool hasOnePositionalArgumentWithType(TypeMask type) {
+ return arguments.named.isEmpty
+ && arguments.positional.length == 1
+ && arguments.positional[0].type == type;
+ }
+
+ /**
+ * We optimize certain operations on the [int] class because we know
+ * more about their return type than the actual Dart code. For
+ * example, we know int + int returns an int. The Dart code for
+ * [int.operator+] only says it returns a [num].
+ */
+ TypeInformation handleIntrisifiedSelector(Selector selector,
+ TypeGraphInferrerEngine inferrer) {
+ if (!inferrer.compiler.backend.intImplementation.isResolved) return null;
+ TypeMask intType = inferrer.compiler.typesTask.intType;
+ if (selector.mask != intType) return null;
+ if (!selector.isCall() && !selector.isOperator()) return null;
+ if (!arguments.named.isEmpty) return null;
+ if (arguments.positional.length > 1) return null;
+
+ SourceString name = selector.name;
+ if (name == const SourceString('*')
+ || name == const SourceString('+')
+ || name == const SourceString('%')
+ || name == const SourceString('remainder')) {
+ return hasOnePositionalArgumentWithType(intType)
+ ? inferrer.types.intType
+ : null;
+ } else if (name == const SourceString('-')) {
+ if (arguments.hasNoArguments()) return inferrer.types.intType;
+ if (hasOnePositionalArgumentWithType(intType)) {
+ return inferrer.types.intType;
+ }
+ return null;
+ } else if (name == const SourceString('abs')) {
+ return arguments.hasNoArguments() ? inferrer.types.intType : null;
+ }
+ return null;
+ }
+
+ TypeMask refine(TypeGraphInferrerEngine inferrer) {
+ Iterable<Element> oldTargets = targets;
+ Selector typedSelector = computeTypedSelector(inferrer);
+ inferrer.updateSelectorInTree(caller, call, typedSelector);
+ targets = inferrer.compiler.world.allFunctions.filter(typedSelector);
+
+ // Walk over the found targets, and compute the joined union type mask
+ // for all these targets.
+ TypeMask newType = inferrer.types.computeTypeMask(targets.map((element) {
+ if (!oldTargets.contains(element)) {
+ ElementTypeInformation callee =
+ inferrer.types.getInferredTypeOf(element);
+ callee.addCall(caller, call);
+ callee.addUser(this);
+ inferrer.updateParameterAssignments(
+ this, element, arguments, typedSelector, remove: false);
+ }
+
+ if (receiver.type.isContainer && selector.isIndex()) {
+ // Find the [ElementInContainerTypeInformation] node and tell
+ // that this node is a user of it. Later, when the element
+ // type changes, this node will be notified.
+ ContainerTypeMask mask = receiver.type;
+ ContainerTypeInformation container =
+ inferrer.types.allocatedContainers[mask.allocationNode];
+ ElementInContainerTypeInformation element = container.elementType;
+ if (!element.users.contains(element)) {
+ element.addUser(this);
+ }
+ return element;
+ } else {
+ TypeInformation info =
+ handleIntrisifiedSelector(typedSelector, inferrer);
+ if (info != null) return info;
+ return inferrer.typeOfElementWithSelector(element, typedSelector);
+ }
+ }));
+
+ // Walk over the old targets, and remove calls that cannot happen
+ // anymore.
+ oldTargets.forEach((element) {
+ if (!targets.contains(element)) {
+ ElementTypeInformation callee =
+ inferrer.types.getInferredTypeOf(element);
+ callee.removeCall(caller, call);
+ callee.removeUser(this);
+ inferrer.updateParameterAssignments(
+ this, element, arguments, typedSelector, remove: true);
+ }
+ });
+ return newType;
+ }
+
+ void giveUp(TypeGraphInferrerEngine inferrer) {
+ inferrer.updateSelectorInTree(caller, call, selector);
+ Iterable<Element> oldTargets = targets;
+ targets = inferrer.compiler.world.allFunctions.filter(selector);
+ for (Element element in targets) {
+ if (!oldTargets.contains(element)) {
+ ElementTypeInformation callee =
+ inferrer.types.getInferredTypeOf(element);
+ callee.addCall(caller, call);
+ inferrer.updateParameterAssignments(
+ this, element, arguments, selector, remove: false);
+ }
+ }
+ super.giveUp(inferrer);
+ }
+}
+
+/**
+ * A [ConcreteTypeInformation] represents a type that needed
+ * to be materialized during the creation of the graph. For example,
+ * literals, [:this:] or [:super:] need a [ConcreteTypeInformation].
+ *
+ * [ConcreteTypeInformation] nodes have no assignment. Also, to save
+ * on memory, we do not add users to [ConcreteTypeInformation] nodes,
+ * because we know such node will never be refined to a different
+ * type.
+ */
+class ConcreteTypeInformation extends TypeInformation {
+ ConcreteTypeInformation(TypeMask type)
+ : super(type, const <TypeInformation>[], const <TypeInformation>[]);
+
+ bool get isConcrete => true;
+
+ void addUser(TypeInformation user) {
+ // Nothing to do, a concrete type does not get updated so never
+ // needs to notify its users.
+ }
+
+ void removeUser(TypeInformation user) {
+ }
+
+ void addAssignment(TypeInformation assignment) {
+ }
+
+ void removeAssignment(TypeInformation assignment) {
+ assert(false);
+ }
+
+ String toString() => 'Type $type';
+}
+
+/**
+ * A [NarrowTypeInformation] narrows a [TypeInformation] to a type,
+ * represented in [typeAnnotation].
+ *
+ * A [NarrowTypeInformation] node has only one assignment: the
+ * [TypeInformation] it narrows.
+ *
+ * [NarrowTypeInformation] nodes are created for:
+ *
+ * - Code after `is` and `as` checks, where we have more information
+ * on the type of the right hand side of the expression.
+ *
+ * - Code after a dynamic call, where we have more information on the
+ * type of the receiver: it can only be of a class that holds a
+ * potential target of this dynamic call.
+ *
+ * - In checked mode, after a type annotation, we have more
+ * information on the type of an element (parameter, function,
+ * local). TODO(ngeoffray): Implement this.
+ */
+class NarrowTypeInformation extends TypeInformation {
+ final TypeMask typeAnnotation;
+
+ NarrowTypeInformation(narrowedType,
+ this.typeAnnotation,
+ TypeMask type) : super(type) {
+ addAssignment(narrowedType);
+ }
+
+ TypeMask refine(TypeGraphInferrerEngine inferrer) {
+ return assignments[0].type.intersection(typeAnnotation, inferrer.compiler);
+ }
+
+ String toString() => 'Narrow ${assignments.first} to $typeAnnotation';
+}
+
+/**
+ * A [ContainerTypeInformation] is a [ConcreteTypeInformation] created
+ * for each `List` instantiations.
+ */
+class ContainerTypeInformation extends ConcreteTypeInformation {
+ final TypeInformation elementType;
+
+ ContainerTypeInformation(containerType, this.elementType)
+ : super(containerType);
+
+ String toString() => 'Container type';
+}
+
+/**
+ * An [ElementInContainerTypeInformation] holds the common type of the
+ * elements in a [ContainerTypeInformation].
+ */
+class ElementInContainerTypeInformation extends TypeInformation {
+ final ContainerTypeMask container;
+
+ ElementInContainerTypeInformation(elementType, this.container, type)
+ : super(type) {
+ // [elementType] is not null for const lists.
+ if (elementType != null) addAssignment(elementType);
+ }
+
+ TypeMask refine(TypeGraphInferrerEngine inferrer) {
+ if (assignments.isEmpty) return type;
+ return container.elementType =
+ inferrer.types.computeTypeMask(assignments);
+ }
+
+ String toString() => 'Element in container';
+}
+
+/**
+ * A [PhiElementTypeInformation] is an union of
+ * [ElementTypeInformation], that is local to a method.
+ */
+class PhiElementTypeInformation extends TypeInformation {
+ final Node branchNode;
+ final bool isLoopPhi;
+ final Element element;
+
+ PhiElementTypeInformation(this.branchNode, this.isLoopPhi, this.element, type)
+ : super(type);
+
+ TypeMask refine(TypeGraphInferrerEngine inferrer) {
+ return inferrer.types.computeTypeMask(assignments);
+ }
+
+ TypeMask refineOptimistic(TypeGraphInferrerEngine inferrer) {
+ return isLoopPhi
+ ? assignments[0].type
+ : inferrer.types.computeTypeMask(assignments);
+ }
+
+ String toString() => 'Phi $element';
+}
+
+class TypeInformationSystem extends TypeSystem<TypeInformation> {
+ final Compiler compiler;
+
+ /// [ElementTypeInformation]s for elements.
+ final Map<Element, TypeInformation> typeInformations =
+ new Map<Element, TypeInformation>();
+
+ /// [ContainerTypeInformation] for allocated containers.
+ final Map<Node, TypeInformation> allocatedContainers =
+ new Map<Node, TypeInformation>();
+
+ /// Cache of [ConcreteTypeInformation].
+ final Map<TypeMask, TypeInformation> concreteTypes =
+ new Map<TypeMask, TypeInformation>();
+
+ /// List of [TypeInformation]s allocated inside method bodies (calls,
+ /// narrowing, phis, and containers).
+ final List<TypeInformation> allocatedTypes = <TypeInformation>[];
+
+ TypeInformationSystem(this.compiler) {
+ nonNullEmptyType = getConcreteTypeFor(const TypeMask.nonNullEmpty());
+ }
+
+ TypeInformation nullTypeCache;
+ TypeInformation get nullType {
+ if (nullTypeCache != null) return nullTypeCache;
+ return nullTypeCache = getConcreteTypeFor(compiler.typesTask.nullType);
+ }
+
+ TypeInformation intTypeCache;
+ TypeInformation get intType {
+ if (intTypeCache != null) return intTypeCache;
+ return intTypeCache = getConcreteTypeFor(compiler.typesTask.intType);
+ }
+
+ TypeInformation doubleTypeCache;
+ TypeInformation get doubleType {
+ if (doubleTypeCache != null) return doubleTypeCache;
+ return doubleTypeCache = getConcreteTypeFor(compiler.typesTask.doubleType);
+ }
+
+ TypeInformation numTypeCache;
+ TypeInformation get numType {
+ if (numTypeCache != null) return numTypeCache;
+ return numTypeCache = getConcreteTypeFor(compiler.typesTask.numType);
+ }
+
+ TypeInformation boolTypeCache;
+ TypeInformation get boolType {
+ if (boolTypeCache != null) return boolTypeCache;
+ return boolTypeCache = getConcreteTypeFor(compiler.typesTask.boolType);
+ }
+
+ TypeInformation functionTypeCache;
+ TypeInformation get functionType {
+ if (functionTypeCache != null) return functionTypeCache;
+ return functionTypeCache =
+ getConcreteTypeFor(compiler.typesTask.functionType);
+ }
+
+ TypeInformation listTypeCache;
+ TypeInformation get listType {
+ if (listTypeCache != null) return listTypeCache;
+ return listTypeCache = getConcreteTypeFor(compiler.typesTask.listType);
+ }
+
+ TypeInformation constListTypeCache;
+ TypeInformation get constListType {
+ if (constListTypeCache != null) return constListTypeCache;
+ return constListTypeCache =
+ getConcreteTypeFor(compiler.typesTask.constListType);
+ }
+
+ TypeInformation fixedListTypeCache;
+ TypeInformation get fixedListType {
+ if (fixedListTypeCache != null) return fixedListTypeCache;
+ return fixedListTypeCache =
+ getConcreteTypeFor(compiler.typesTask.fixedListType);
+ }
+
+ TypeInformation growableListTypeCache;
+ TypeInformation get growableListType {
+ if (growableListTypeCache != null) return growableListTypeCache;
+ return growableListTypeCache =
+ getConcreteTypeFor(compiler.typesTask.growableListType);
+ }
+
+ TypeInformation mapTypeCache;
+ TypeInformation get mapType {
+ if (mapTypeCache != null) return mapTypeCache;
+ return mapTypeCache = getConcreteTypeFor(compiler.typesTask.mapType);
+ }
+
+ TypeInformation constMapTypeCache;
+ TypeInformation get constMapType {
+ if (constMapTypeCache != null) return constMapTypeCache;
+ return constMapTypeCache =
+ getConcreteTypeFor(compiler.typesTask.constMapType);
+ }
+
+ TypeInformation stringTypeCache;
+ TypeInformation get stringType {
+ if (stringTypeCache != null) return stringTypeCache;
+ return stringTypeCache = getConcreteTypeFor(compiler.typesTask.stringType);
+ }
+
+ TypeInformation typeTypeCache;
+ TypeInformation get typeType {
+ if (typeTypeCache != null) return typeTypeCache;
+ return typeTypeCache = getConcreteTypeFor(compiler.typesTask.typeType);
+ }
+
+ TypeInformation dynamicTypeCache;
+ TypeInformation get dynamicType {
+ if (dynamicTypeCache != null) return dynamicTypeCache;
+ return dynamicTypeCache =
+ getConcreteTypeFor(compiler.typesTask.dynamicType);
+ }
+
+ TypeInformation nonNullEmptyType;
+
+ TypeInformation computeLUB(TypeInformation firstType,
+ TypeInformation secondType) {
+ if (firstType == null) return secondType;
+ if (firstType == secondType) return firstType;
+ if (firstType == nonNullEmptyType) return secondType;
+ if (secondType == nonNullEmptyType) return firstType;
+ if (firstType == dynamicType || secondType == dynamicType) {
+ return dynamicType;
+ }
+ return getConcreteTypeFor(
+ firstType.type.union(secondType.type, compiler));
+ }
+
+ TypeInformation refineReceiver(Selector selector, TypeInformation receiver) {
+ if (receiver.type.isExact) return receiver;
+ TypeMask otherType = compiler.world.allFunctions.receiverType(selector);
+ // If this is refining to nullable subtype of `Object` just return
+ // the receiver. We know the narrowing is useless.
+ if (otherType.isNullable && otherType.containsAll(compiler)) {
+ return receiver;
+ }
+ TypeInformation newType =
+ new NarrowTypeInformation(receiver, otherType, dynamicType.type);
+ allocatedTypes.add(newType);
+ return newType;
+ }
+
+ TypeInformation narrowType(TypeInformation type,
+ DartType annotation,
+ {bool isNullable: true}) {
+ if (annotation.treatAsDynamic) return type;
+ if (annotation.isVoid) return nullType;
+ if (annotation.element == compiler.objectClass) return type;
+ TypeMask otherType;
+ if (annotation.kind == TypeKind.TYPEDEF
+ || annotation.kind == TypeKind.FUNCTION) {
+ otherType = functionType.type;
+ } else if (annotation.kind == TypeKind.TYPE_VARIABLE) {
+ // TODO(ngeoffray): Narrow to bound.
+ return type;
+ } else {
+ assert(annotation.kind == TypeKind.INTERFACE);
+ otherType = new TypeMask.nonNullSubtype(annotation);
+ }
+ if (isNullable) otherType = otherType.nullable();
+ if (type.type.isExact) {
+ return type;
+ } else {
+ TypeInformation newType =
+ new NarrowTypeInformation(type, otherType, dynamicType.type);
+ allocatedTypes.add(newType);
+ return newType;
+ }
+ }
+
+ ElementTypeInformation getInferredTypeOf(Element element) {
+ element = element.implementation;
+ return typeInformations.putIfAbsent(element, () {
+ return new ElementTypeInformation(element, dynamicType.type);
+ });
+ }
+
+ ConcreteTypeInformation getConcreteTypeFor(TypeMask mask) {
+ return concreteTypes.putIfAbsent(mask, () {
+ return new ConcreteTypeInformation(mask);
+ });
+ }
+
+ TypeInformation nonNullSubtype(DartType type) {
+ return getConcreteTypeFor(new TypeMask.nonNullSubtype(type));
+ }
+
+ TypeInformation nonNullSubclass(DartType type) {
+ return getConcreteTypeFor(new TypeMask.nonNullSubclass(type));
+ }
+
+ TypeInformation nonNullExact(DartType type) {
+ return getConcreteTypeFor(new TypeMask.nonNullExact(type));
+ }
+
+ TypeInformation nonNullEmpty() {
+ return nonNullEmptyType;
+ }
+
+ TypeInformation allocateContainer(TypeInformation type,
+ Node node,
+ Element enclosing,
+ [TypeInformation elementType, int length]) {
+ ContainerTypeMask mask = new ContainerTypeMask(type.type, node, enclosing);
+ mask.elementType = elementType == null ? null : elementType.type;
+ mask.length = length;
+ TypeMask elementTypeMask = elementType == null
+ ? dynamicType.type
+ : elementType.type;
+ TypeInformation element = new ElementInContainerTypeInformation(
+ elementType, mask, elementTypeMask);
+ allocatedTypes.add(element);
+ return allocatedContainers[node] =
+ new ContainerTypeInformation(mask, element);
+ }
+
+ Selector newTypedSelector(TypeInformation info, Selector selector) {
+ return new TypedSelector(info.type, selector);
+ }
+
+ TypeInformation allocateDiamondPhi(TypeInformation firstInput,
+ TypeInformation secondInput) {
+ PhiElementTypeInformation result =
+ new PhiElementTypeInformation(null, false, null, dynamicType.type);
+ result.addAssignment(firstInput);
+ result.addAssignment(secondInput);
+ allocatedTypes.add(result);
+ return result;
+ }
+
+ PhiElementTypeInformation allocatePhi(Node node,
+ Element element,
+ inputType) {
+ // Check if [inputType] is a phi for a local updated in
+ // the try/catch block [node]. If it is, no need to allocate a new
+ // phi.
+ if (inputType is PhiElementTypeInformation
+ && inputType.branchNode == node) {
+ return inputType;
+ }
+ PhiElementTypeInformation result =
+ new PhiElementTypeInformation(node, true, element, dynamicType.type);
+ allocatedTypes.add(result);
+ result.addAssignment(inputType);
+ return result;
+ }
+
+ TypeInformation simplifyPhi(Node node,
+ Element element,
+ PhiElementTypeInformation phiType) {
+ if (phiType.assignments.length == 1) return phiType.assignments.first;
+ return phiType;
+ }
+
+ PhiElementTypeInformation addPhiInput(Element element,
+ PhiElementTypeInformation phiType,
+ TypeInformation newType) {
+ phiType.addAssignment(newType);
+ return phiType;
+ }
+
+ TypeMask computeTypeMask(Iterable<TypeInformation> assignments) {
+ TypeMask newType = const TypeMask.nonNullEmpty();
+ for (var info in assignments) {
+ newType = newType.union(info.type, compiler);
+ }
+ return newType.containsAll(compiler) ? dynamicType.type : newType;
+ }
+}
+
+/**
+ * A work queue for the inferrer. It filters out nodes on
+ * which we gave up on inferencing, as well as ensures through
+ * [TypeInformation.inQueue] that a node is in the queue only once at
+ * a time.
+ */
+class WorkQueue {
+ final Queue<TypeInformation> queue = new Queue<TypeInformation>();
+
+ void add(TypeInformation element) {
+ if (element.abandonInferencing) return;
+ if (element.inQueue) return;
+ queue.addLast(element);
+ element.inQueue = true;
+ }
+
+ void addAll(Iterable<TypeInformation> all) {
+ all.forEach(add);
+ }
+
+ TypeInformation remove() {
+ TypeInformation element = queue.removeFirst();
+ element.inQueue = false;
+ return element;
+ }
+
+ bool get isEmpty => queue.isEmpty;
+
+ int get length => queue.length;
+}
+
+/**
+ * An inferencing engine that computes a call graph of
+ * [TypeInformation] nodes by visiting the AST of the application, and
+ * then does the inferencing on the graph.
+ *
+ * The inferencing is currently done in three steps:
+ *
+ * 1) Compute the call graph.
+ * 2) Refine all nodes in a way that avoids cycles.
+ * 3) Refine all nodes.
+ *
+ */
+class TypeGraphInferrerEngine
+ extends InferrerEngine<TypeInformation, TypeInformationSystem> {
+ final Map<Element, ConcreteTypeInformation> defaultTypeOfParameter =
+ new Map<Element, ConcreteTypeInformation>();
+ final WorkQueue workQueue = new WorkQueue();
+
+ /// The maximum number of times we allow a node in the graph to
+ /// change types. If a node reaches that limit, we give up
+ /// inferencing on it and give it the dynamic type.
+ final int MAX_CHANGE_COUNT = 5;
+
+ int overallRefineCount = 0;
+
+ TypeGraphInferrerEngine(Compiler compiler)
+ : super(compiler, new TypeInformationSystem(compiler));
+
+ void runOverAllElements() {
+ if (compiler.disableTypeInference) return;
+ int addedInGraph = 0;
+ compiler.progress.reset();
+
+ sortResolvedElements().forEach((Element element) {
+ if (compiler.progress.elapsedMilliseconds > 500) {
+ compiler.log('Added $addedInGraph elements in inferencing graph.');
+ compiler.progress.reset();
+ }
+ SimpleTypeInferrerVisitor visitor =
+ new SimpleTypeInferrerVisitor(element, compiler, this);
+ TypeInformation type;
+ compiler.withCurrentElement(element, () {
+ type = visitor.run();
+ });
+ addedInGraph++;
+
+ if (element.isField()) {
+ Node node = element.parseNode(compiler);
+ if (element.modifiers.isFinal() || element.modifiers.isConst()) {
+ // If [element] is final and has an initializer, we record
+ // the inferred type.
+ if (node.asSendSet() != null) {
+ recordType(element, type);
+ } else if (!element.isInstanceMember()) {
+ recordType(element, types.nullType);
+ }
+ } else if (node.asSendSet() == null) {
+ // Only update types of static fields if there is no
+ // assignment. Instance fields are dealt with in the constructor.
+ if (Elements.isStaticOrTopLevelField(element)) {
+ recordTypeOfNonFinalField(node, element, type, null);
+ }
+ } else {
+ recordTypeOfNonFinalField(node, element, type, null);
+ }
+ } else {
+ recordReturnType(element, type);
+ }
+ });
+ compiler.log('Added $addedInGraph elements in inferencing graph.');
+
+ buildWorkQueue();
+ refineOptimistic();
+ buildWorkQueue();
+ refine();
+
+ compiler.log('Inferred $overallRefineCount types.');
+ }
+
+
+ void refineOptimistic() {
+ while (!workQueue.isEmpty) {
+ if (compiler.progress.elapsedMilliseconds > 500) {
+ compiler.log('Inferred $overallRefineCount types.');
+ compiler.progress.reset();
+ }
+ TypeInformation info = workQueue.remove();
+ TypeMask oldType = info.type;
+ TypeMask newType = info.refineOptimistic(this);
+ if ((info.type = newType) != oldType) {
+ overallRefineCount++;
+ workQueue.addAll(info.users);
+ }
+ }
+ }
+
+ void refine() {
+ while (!workQueue.isEmpty) {
+ if (compiler.progress.elapsedMilliseconds > 500) {
+ compiler.log('Inferred $overallRefineCount types.');
+ compiler.progress.reset();
+ }
+ TypeInformation info = workQueue.remove();
+ TypeMask oldType = info.type;
+ TypeMask newType = info.refine(this);
+ if ((info.type = newType) != oldType) {
+ overallRefineCount++;
+ info.refineCount++;
+ if (info.refineCount > MAX_CHANGE_COUNT) {
+ info.giveUp(this);
+ }
+ workQueue.addAll(info.users);
+ }
+ }
+ }
+
+ void buildWorkQueue() {
+ workQueue.addAll(types.typeInformations.values);
+ workQueue.addAll(types.allocatedTypes);
+ }
+
+ /**
+ * Update the assignments to parameters in the graph. [remove] tells
+ * wheter assignments must be added or removed. If [init] is true,
+ * parameters are added to the work queue.
+ */
+ void updateParameterAssignments(TypeInformation caller,
+ Element callee,
+ ArgumentsTypes arguments,
+ Selector selector,
+ {bool remove, bool init: false}) {
+ if (callee.name == Compiler.NO_SUCH_METHOD) return;
+ if (callee.isField()) {
+ if (selector.isSetter()) {
+ ElementTypeInformation info = types.getInferredTypeOf(callee);
+ if (remove) {
+ info.removeAssignment(arguments.positional[0]);
+ } else {
+ info.addAssignment(arguments.positional[0]);
+ }
+ if (!init) workQueue.add(info);
+ }
+ } else if (callee.isGetter()) {
+ return;
+ } else if (selector != null && selector.isGetter()) {
+ if (!remove) {
+ FunctionElement function = callee.implementation;
+ FunctionSignature signature = function.computeSignature(compiler);
+ signature.forEachParameter((Element parameter) {
+ ElementTypeInformation info = types.getInferredTypeOf(parameter);
+ info.giveUp(this);
+ });
+ }
+ } else {
+ FunctionElement function = callee.implementation;
+ FunctionSignature signature = function.computeSignature(compiler);
+ int parameterIndex = 0;
+ bool visitingRequiredParameter = true;
+ signature.forEachParameter((Element parameter) {
+ if (parameter == signature.firstOptionalParameter) {
+ visitingRequiredParameter = false;
+ }
+ TypeInformation type = visitingRequiredParameter
+ ? arguments.positional[parameterIndex]
+ : signature.optionalParametersAreNamed
+ ? arguments.named[parameter.name]
+ : parameterIndex < arguments.positional.length
+ ? arguments.positional[parameterIndex]
+ : null;
+ if (type == null) type = getDefaultTypeOfParameter(parameter);
+ TypeInformation info = types.getInferredTypeOf(parameter);
+ if (remove) {
+ info.removeAssignment(type);
+ } else {
+ info.addAssignment(type);
+ }
+ parameterIndex++;
+ if (!init) workQueue.add(info);
+ });
+ }
+ }
+
+ void updateAllParametersOf(FunctionElement function) {}
+ void onGenerativeConstructorAnalyzed(Element element) {}
+
+ void setDefaultTypeOfParameter(Element parameter, TypeInformation type) {
+ assert(parameter.enclosingElement.isImplementation);
+ getDefaultTypeOfParameter(parameter).type = type.type;
+ }
+
+ TypeInformation getDefaultTypeOfParameter(Element parameter) {
+ return defaultTypeOfParameter.putIfAbsent(parameter, () {
+ return new ConcreteTypeInformation(types.dynamicType.type);
+ });
+ }
+
+ TypeInformation typeOfElement(Element element) {
+ if (element is FunctionElement) return types.functionType;
+ return types.getInferredTypeOf(element);
+ }
+
+ TypeInformation returnTypeOfElement(Element element) {
+ if (element is !FunctionElement) return types.dynamicType;
+ return types.getInferredTypeOf(element);
+ }
+
+ void recordTypeOfFinalField(Spannable node,
+ Element analyzed,
+ Element element,
+ TypeInformation type,
+ CallSite constraint) {
+ types.getInferredTypeOf(element).addAssignment(type);
+ }
+
+ void recordTypeOfNonFinalField(Spannable node,
+ Element element,
+ TypeInformation type,
+ CallSite constraint) {
+ types.getInferredTypeOf(element).addAssignment(type);
+ }
+
+ bool recordType(Element element, TypeInformation type) {
+ types.getInferredTypeOf(element).addAssignment(type);
+ return false;
+ }
+
+ void recordReturnType(Element element, TypeInformation type) {
+ TypeInformation info = types.getInferredTypeOf(element);
+ if (element.name == const SourceString('==')) {
+ info.addAssignment(types.boolType);
+ }
+ // TODO(ngeoffray): Clean up. We do these checks because
+ // [SimpleTypesInferrer] deals with two different inferrers.
+ if (type == null) return;
+ if (info.assignments.isEmpty) info.addAssignment(type);
+ }
+
+ TypeInformation addReturnTypeFor(Element element,
+ TypeInformation unused,
+ TypeInformation newType) {
+ TypeInformation type = types.getInferredTypeOf(element);
+ // TODO(ngeoffray): Clean up. We do this check because
+ // [SimpleTypesInferrer] deals with two different inferrers.
+ if (element.isGenerativeConstructor()) return type;
+ type.addAssignment(newType);
+ return type;
+ }
+
+ TypeInformation registerCalledElement(Spannable node,
+ Selector selector,
+ Element caller,
+ Element callee,
+ ArgumentsTypes arguments,
+ CallSite constraint,
+ SideEffects sideEffects,
+ bool inLoop) {
+ CallSiteTypeInformation info = new StaticCallSiteTypeInformation(
+ node, caller, callee, selector, arguments, types.dynamicType.type);
+ if (inLoop) {
+ compiler.world.addFunctionCalledInLoop(callee);
+ }
+ info.addToGraph(this);
+ updateSideEffects(sideEffects, selector, callee);
+ return info;
+ }
+
+ TypeInformation registerCalledSelector(Node node,
+ Selector selector,
+ TypeInformation receiverType,
+ Element caller,
+ ArgumentsTypes arguments,
+ CallSite constraint,
+ SideEffects sideEffects,
+ bool inLoop) {
+ if (selector.isClosureCall()) return types.dynamicType;
+
+ if (inLoop && selector.mask != null) {
+ // For instance methods, we only register a selector called in a
+ // loop if it is a typed selector, to avoid marking too many
+ // methods as being called from within a loop. This cuts down
+ // on the code bloat.
+ // TODO(ngeoffray): We should move the filtering on the selector
+ // in the backend. It is not the inferrer role to do this kind
+ // of optimization.
+ compiler.world.allFunctions.filter(selector).forEach((callee) {
+ compiler.world.addFunctionCalledInLoop(callee);
+ });
+ }
+ compiler.world.allFunctions.filter(selector).forEach((callee) {
+ updateSideEffects(sideEffects, selector, callee);
+ });
+
+ DynamicCallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
+ node, caller, selector, receiverType, arguments,
+ types.dynamicType.type);
+ info.addToGraph(this);
+ return info;
+ }
+
+ // Sorts the resolved elements by size. We do this for this inferrer
+ // to get the same results for [ContainerTracer] compared to the
+ // [SimpleTypesInferrer].
+ Iterable<Element> sortResolvedElements() {
+ int max = 0;
+ Map<int, Set<Element>> methodSizes = new Map<int, Set<Element>>();
+ compiler.enqueuer.resolution.resolvedElements.forEach(
+ (Element element, TreeElementMapping mapping) {
+ element = element.implementation;
+ if (element.impliesType()) return;
+ assert(invariant(element,
+ element.isField() ||
+ element.isFunction() ||
+ element.isGenerativeConstructor() ||
+ element.isGetter() ||
+ element.isSetter(),
+ message: 'Unexpected element kind: ${element.kind}'));
+ // TODO(ngeoffray): Not sure why the resolver would put a null
+ // mapping.
+ if (mapping == null) return;
+ if (element.isAbstract(compiler)) return;
+ // Put the other operators in buckets by length, later to be added in
+ // length order.
+ int length = mapping.selectors.length;
+ max = length > max ? length : max;
+ Set<Element> set = methodSizes.putIfAbsent(
+ length, () => new LinkedHashSet<Element>());
+ set.add(element);
+ });
+
+ List<Element> result = <Element>[];
+
+ for (int i = 0; i <= max; i++) {
+ Set<Element> set = methodSizes[i];
+ if (set != null) {
+ result.addAll(set);
+ }
+ }
+ return result;
+ }
+
+ void clear() {
+ defaultTypeOfParameter.clear();
+ types.typeInformations.values.forEach((info) => info.clear());
+ types.allocatedTypes.clear();
+ types.concreteTypes.clear();
+ }
+
+ Iterable<Element> getCallersOf(Element element) {
+ if (compiler.disableTypeInference) {
+ throw new UnsupportedError(
+ "Cannot query the type inferrer when type inference is disabled.");
+ }
+ return types.getInferredTypeOf(element).callers.keys;
+ }
+}
+
+class TypeGraphInferrer implements TypesInferrer {
+ TypeGraphInferrerEngine inferrer;
+ final Compiler compiler;
+ TypeGraphInferrer(Compiler this.compiler);
+
+ String get name => 'Graph inferrer';
+
+ void analyzeMain(_) {
+ inferrer = new TypeGraphInferrerEngine(compiler);
+ inferrer.runOverAllElements();
+ }
+
+ TypeMask getReturnTypeOfElement(Element element) {
+ if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
+ return inferrer.types.getInferredTypeOf(element).type;
+ }
+
+ TypeMask getTypeOfElement(Element element) {
+ if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
+ return inferrer.types.getInferredTypeOf(element).type;
+ }
+
+ TypeMask getTypeOfNode(Element owner, Node node) {
+ if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
+ return inferrer.types.allocatedContainers[node].type;
+ }
+
+ TypeMask getTypeOfSelector(Selector selector) {
+ if (compiler.disableTypeInference) return compiler.typesTask.dynamicType;
+ // Bailout for closure calls. We're not tracking types of
+ // closures.
+ if (selector.isClosureCall()) return compiler.typesTask.dynamicType;
+ if (selector.isSetter() || selector.isIndexSet()) {
+ return compiler.typesTask.dynamicType;
+ }
+ if (selector.isIndex()
+ && selector.mask != null
+ && selector.mask.isContainer) {
+ ContainerTypeMask mask = selector.mask;
+ TypeMask elementType = mask.elementType;
+ return elementType == null ? compiler.typesTask.dynamicType : elementType;
+ }
+
+ TypeMask result = const TypeMask.nonNullEmpty();
+ Iterable<Element> elements = compiler.world.allFunctions.filter(selector);
+ for (Element element in elements) {
+ TypeMask type =
+ inferrer.typeOfElementWithSelector(element, selector).type;
+ result = result.union(type, compiler);
+ }
+ return result;
+ }
+
+ Iterable<TypeMask> get containerTypes {
+ if (compiler.disableTypeInference) {
+ throw new UnsupportedError(
+ "Cannot query the type inferrer when type inference is disabled.");
+ }
+ return inferrer.types.allocatedContainers.values.map((info) => info.type);
+ }
+
+ Iterable<Element> getCallersOf(Element element) {
+ if (compiler.disableTypeInference) {
+ throw new UnsupportedError(
+ "Cannot query the type inferrer when type inference is disabled.");
+ }
+ return inferrer.getCallersOf(element);
+ }
+
+ void clear() {
+ inferrer.clear();
+ }
+}
diff --git a/sdk/lib/_internal/compiler/implementation/types/types.dart b/sdk/lib/_internal/compiler/implementation/types/types.dart
index 201a697..a192b65 100644
--- a/sdk/lib/_internal/compiler/implementation/types/types.dart
+++ b/sdk/lib/_internal/compiler/implementation/types/types.dart
@@ -11,6 +11,7 @@
import '../util/util.dart';
import '../universe/universe.dart';
import 'simple_types_inferrer.dart' show SimpleTypesInferrer;
+import 'type_graph_inferrer.dart' show TypeGraphInferrer;
import 'concrete_types_inferrer.dart' show ConcreteTypesInferrer;
import '../dart_types.dart';
@@ -42,31 +43,151 @@
static final bool DUMP_SURPRISING_RESULTS = false;
final String name = 'Type inference';
- SimpleTypesInferrer typesInferrer;
+ TypesInferrer typesInferrer;
ConcreteTypesInferrer concreteTypesInferrer;
TypesTask(Compiler compiler) : super(compiler) {
- typesInferrer = new SimpleTypesInferrer(compiler);
+ typesInferrer = new TypeGraphInferrer(compiler);
if (compiler.enableConcreteTypeInference) {
concreteTypesInferrer = new ConcreteTypesInferrer(compiler);
}
}
- TypeMask dynamicType;
- TypeMask nullType;
- TypeMask intType;
- TypeMask doubleType;
- TypeMask numType;
- TypeMask boolType;
- TypeMask functionType;
- TypeMask listType;
- TypeMask constListType;
- TypeMask fixedListType;
- TypeMask growableListType;
- TypeMask mapType;
- TypeMask constMapType;
- TypeMask stringType;
- TypeMask typeType;
+ TypeMask dynamicTypeCache;
+ TypeMask nullTypeCache;
+ TypeMask intTypeCache;
+ TypeMask doubleTypeCache;
+ TypeMask numTypeCache;
+ TypeMask boolTypeCache;
+ TypeMask functionTypeCache;
+ TypeMask listTypeCache;
+ TypeMask constListTypeCache;
+ TypeMask fixedListTypeCache;
+ TypeMask growableListTypeCache;
+ TypeMask mapTypeCache;
+ TypeMask constMapTypeCache;
+ TypeMask stringTypeCache;
+ TypeMask typeTypeCache;
+
+ TypeMask get dynamicType {
+ if (dynamicTypeCache == null) {
+ dynamicTypeCache = new TypeMask.subclass(compiler.objectClass.rawType);
+ }
+ return dynamicTypeCache;
+ }
+
+ TypeMask get intType {
+ if (intTypeCache == null) {
+ intTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.intImplementation.rawType);
+ }
+ return intTypeCache;
+ }
+
+ TypeMask get doubleType {
+ if (doubleTypeCache == null) {
+ doubleTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.doubleImplementation.rawType);
+ }
+ return doubleTypeCache;
+ }
+
+ TypeMask get numType {
+ if (numTypeCache == null) {
+ numTypeCache = new TypeMask.nonNullSubclass(
+ compiler.backend.numImplementation.rawType);
+ }
+ return numTypeCache;
+ }
+
+ TypeMask get boolType {
+ if (boolTypeCache == null) {
+ boolTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.boolImplementation.rawType);
+ }
+ return boolTypeCache;
+ }
+
+ TypeMask get functionType {
+ if (functionTypeCache == null) {
+ functionTypeCache = new TypeMask.nonNullSubtype(
+ compiler.backend.functionImplementation.rawType);
+ }
+ return functionTypeCache;
+ }
+
+ TypeMask get listType {
+ if (listTypeCache == null) {
+ listTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.listImplementation.rawType);
+ }
+ return listTypeCache;
+ }
+
+ TypeMask get constListType {
+ if (constListTypeCache == null) {
+ constListTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.constListImplementation.rawType);
+ }
+ return constListTypeCache;
+ }
+
+ TypeMask get fixedListType {
+ if (fixedListTypeCache == null) {
+ fixedListTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.fixedListImplementation.rawType);
+ }
+ return fixedListTypeCache;
+ }
+
+ TypeMask get growableListType {
+ if (growableListTypeCache == null) {
+ growableListTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.growableListImplementation.rawType);
+ }
+ return growableListTypeCache;
+ }
+
+ TypeMask get mapType {
+ if (mapTypeCache == null) {
+ mapTypeCache = new TypeMask.nonNullSubtype(
+ compiler.backend.mapImplementation.rawType);
+ }
+ return mapTypeCache;
+ }
+
+ TypeMask get constMapType {
+ if (constMapTypeCache == null) {
+ constMapTypeCache = new TypeMask.nonNullSubtype(
+ compiler.backend.constMapImplementation.rawType);
+ }
+ return constMapTypeCache;
+ }
+
+ TypeMask get stringType {
+ if (stringTypeCache == null) {
+ stringTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.stringImplementation.rawType);
+ }
+ return stringTypeCache;
+ }
+
+ TypeMask get typeType {
+ if (typeTypeCache == null) {
+ typeTypeCache = new TypeMask.nonNullExact(
+ compiler.backend.typeImplementation.rawType);
+ }
+ return typeTypeCache;
+ }
+
+ TypeMask get nullType {
+ if (nullTypeCache == null) {
+ // TODO(johnniwinther): Assert that the null type has been resolved.
+ nullTypeCache = new TypeMask.empty();
+ }
+ return nullTypeCache;
+ }
+
/// Replaces native types by their backend implementation.
Element normalize(Element cls) {
@@ -181,56 +302,11 @@
}
}
- // TODO(ngeoffray): Get rid of this method. Unit tests don't always
- // ensure these classes are resolved.
- rawTypeOf(ClassElement cls) {
- cls.ensureResolved(compiler);
- assert(cls.rawType != null);
- return cls.rawType;
- }
-
- void initializeTypes() {
- nullType = new TypeMask.empty();
-
- Backend backend = compiler.backend;
- intType = new TypeMask.nonNullExact(
- rawTypeOf(backend.intImplementation));
- doubleType = new TypeMask.nonNullExact(
- rawTypeOf(backend.doubleImplementation));
- numType = new TypeMask.nonNullSubclass(
- rawTypeOf(backend.numImplementation));
- stringType = new TypeMask.nonNullExact(
- rawTypeOf(backend.stringImplementation));
- boolType = new TypeMask.nonNullExact(
- rawTypeOf(backend.boolImplementation));
-
- listType = new TypeMask.nonNullExact(
- rawTypeOf(backend.listImplementation));
- constListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.constListImplementation));
- fixedListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.fixedListImplementation));
- growableListType = new TypeMask.nonNullExact(
- rawTypeOf(backend.growableListImplementation));
-
- mapType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.mapImplementation));
- constMapType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.constMapImplementation));
- functionType = new TypeMask.nonNullSubtype(
- rawTypeOf(backend.functionImplementation));
- typeType = new TypeMask.nonNullExact(
- rawTypeOf(backend.typeImplementation));
-
- dynamicType = new TypeMask.subclass(rawTypeOf(compiler.objectClass));
- }
-
/**
* Called when resolution is complete.
*/
void onResolutionComplete(Element mainElement) {
measure(() {
- initializeTypes();
typesInferrer.analyzeMain(mainElement);
if (concreteTypesInferrer != null) {
bool success = concreteTypesInferrer.analyzeMain(mainElement);
diff --git a/sdk/lib/_internal/compiler/implementation/warnings.dart b/sdk/lib/_internal/compiler/implementation/warnings.dart
index c108cf8..f8248d4 100644
--- a/sdk/lib/_internal/compiler/implementation/warnings.dart
+++ b/sdk/lib/_internal/compiler/implementation/warnings.dart
@@ -378,6 +378,17 @@
static const MessageKind TYPE_VARIABLE_IN_CONSTANT = const MessageKind(
'Error: Cannot refer to type variable in constant.');
+ static const MessageKind INVALID_TYPE_VARIABLE_BOUND = const MessageKind(
+ "Warning: '#{typeArgument}' is not a subtype of bound '#{bound}' for "
+ "type variable '#{typeVariable}' of type '#{thisType}'.",
+ howToFix: "Try to change or remove the type argument.",
+ examples: const ["""
+class C<T extends num> {}
+
+// 'String' is not a valid instantiation of T with bound num.'.
+main() => new C<String>();
+"""]);
+
static const MessageKind INVALID_USE_OF_SUPER = const MessageKind(
'Error: "super" not allowed here.');
@@ -418,6 +429,21 @@
static const MessageKind CANNOT_INSTANTIATE_TYPEDEF = const MessageKind(
'Error: Cannot instantiate typedef "#{typedefName}".');
+ static const MessageKind TYPEDEF_FORMAL_WITH_DEFAULT = const MessageKind(
+ "Error: A parameter of a typedef can't specify a default value.",
+ howToFix: "Remove the default value.",
+ examples: const ["""
+typedef void F([int arg = 0]);
+
+main() {
+ F f;
+}""", """
+typedef void F({int arg: 0});
+
+main() {
+ F f;
+}"""]);
+
static const MessageKind CANNOT_INSTANTIATE_TYPE_VARIABLE = const MessageKind(
'Error: Cannot instantiate type variable "#{typeVariableName}".');
@@ -623,6 +649,21 @@
const MessageKind(
'Error: Top-level variable cannot be declared static.');
+ static const MessageKind REFERENCE_IN_INITIALIZATION = const MessageKind(
+ "Error: Variable '#{variableName}' is referenced during its "
+ "initialization.",
+ howToFix: "If you are trying to reference a shadowed variable, rename"
+ " one of the variables.",
+ examples: const ["""
+foo(t) {
+ var t = t;
+ return t;
+}
+
+main() => foo(1);
+"""]);
+
+
static const MessageKind WRONG_NUMBER_OF_ARGUMENTS_FOR_ASSERT =
const MessageKind(
'Error: Wrong number of arguments to assert. Should be 1, but given '
diff --git a/sdk/lib/_internal/lib/collection_patch.dart b/sdk/lib/_internal/lib/collection_patch.dart
index c5fa69c..670b4ab 100644
--- a/sdk/lib/_internal/lib/collection_patch.dart
+++ b/sdk/lib/_internal/lib/collection_patch.dart
@@ -6,23 +6,34 @@
import 'dart:_foreign_helper' show JS;
patch class HashMap<K, V> {
- patch factory HashMap({ bool equals(K key1, K key2), int hashCode(K key) }) {
- if (hashCode == null) {
+ patch factory HashMap({ bool equals(K key1, K key2),
+ int hashCode(K key),
+ bool isValidKey(potentialKey) }) {
+ if (isValidKey == null) {
+ if (hashCode == null) {
+ if (equals == null) {
+ return new _HashMap<K, V>();
+ }
+ if (identical(identical, equals)) {
+ return new _IdentityHashMap<K, V>();
+ }
+ hashCode = _defaultHashCode;
+ } else if (equals == null) {
+ equals = _defaultEquals;
+ }
+ } else {
+ if (hashCode == null) {
+ hashCode = _defaultHashCode;
+ }
if (equals == null) {
- return new _HashMapImpl<K, V>();
+ equals = _defaultEquals;
}
- if (identical(identical, equals)) {
- return new _IdentityHashMap<K, V>();
- }
- hashCode = _defaultHashCode;
- } else if (equals == null) {
- equals = _defaultEquals;
}
- return new _CustomHashMap<K, V>(equals, hashCode);
+ return new _CustomHashMap<K, V>(equals, hashCode, isValidKey);
}
}
-class _HashMapImpl<K, V> implements HashMap<K, V> {
+class _HashMap<K, V> implements HashMap<K, V> {
int _length = 0;
// The hash map contents are divided into three parts: one part for
@@ -43,7 +54,9 @@
// guard against concurrent modifications.
List _keys;
- _HashMapImpl();
+ _HashMap();
+
+ Type get runtimeType => HashMap;
int get length => _length;
bool get isEmpty => _length == 0;
@@ -234,7 +247,7 @@
_setTableEntry(table, key, value);
}
- V _removeHashTableEntry(var table, K key) {
+ V _removeHashTableEntry(var table, Object key) {
if (table != null && _hasTableEntry(table, key)) {
V value = _getTableEntry(table, key);
_deleteTableEntry(table, key);
@@ -325,7 +338,7 @@
}
}
-class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> {
+class _IdentityHashMap<K, V> extends _HashMap<K, V> {
int _findBucketIndex(var bucket, var key) {
if (bucket == null) return -1;
int length = JS('int', '#.length', bucket);
@@ -336,10 +349,27 @@
}
}
-class _CustomHashMap<K, V> extends _HashMapImpl<K, V> {
+class _CustomHashMap<K, V> extends _HashMap<K, V> {
final _Equality<K> _equals;
final _Hasher<K> _hashCode;
- _CustomHashMap(this._equals, this._hashCode);
+ final _Predicate _validKey;
+ _CustomHashMap(this._equals, this._hashCode, bool validKey(potentialKey))
+ : _validKey = (validKey != null) ? validKey : ((v) => v is K);
+
+ V operator[](Object key) {
+ if (!_validKey(key)) return null;
+ return super[key];
+ }
+
+ bool containsKey(Object key) {
+ if (!_validKey(key)) return false;
+ return super.containsKey(key);
+ }
+
+ V remove(Object key) {
+ if (!_validKey(key)) return null;
+ return super.remove(key);
+ }
int _computeHashCode(var key) {
// We force the hash codes to be unsigned 30-bit integers to avoid
@@ -416,6 +446,34 @@
}
patch class LinkedHashMap<K, V> {
+ patch factory LinkedHashMap({ bool equals(K key1, K key2),
+ int hashCode(K key),
+ bool isValidKey(potentialKey) }) {
+ if (isValidKey == null) {
+ if (hashCode == null) {
+ if (equals == null) {
+ return new _LinkedHashMap<K, V>();
+ }
+ if (identical(identical, equals)) {
+ return new _LinkedIdentityHashMap<K, V>();
+ }
+ hashCode = _defaultHashCode;
+ } else if (equals == null) {
+ equals = _defaultEquals;
+ }
+ } else {
+ if (hashCode == null) {
+ hashCode = _defaultHashCode;
+ }
+ if (equals == null) {
+ equals = _defaultEquals;
+ }
+ }
+ return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey);
+ }
+}
+
+class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
int _length = 0;
// The hash map contents are divided into three parts: one part for
@@ -440,22 +498,23 @@
// iterated over.
int _modifications = 0;
- patch LinkedHashMap();
+ _LinkedHash();
- patch int get length => _length;
- patch bool get isEmpty => _length == 0;
- patch bool get isNotEmpty => !isEmpty;
+ Type get runtimeType => LinkedHashMap;
+ int get length => _length;
+ bool get isEmpty => _length == 0;
+ bool get isNotEmpty => !isEmpty;
- patch Iterable<K> get keys {
+ Iterable<K> get keys {
return new LinkedHashMapKeyIterable<K>(this);
}
- patch Iterable<V> get values {
+ Iterable<V> get values {
return keys.map((each) => this[each]);
}
- patch bool containsKey(Object key) {
+ bool containsKey(Object key) {
if (_isStringKey(key)) {
var strings = _strings;
if (strings == null) return false;
@@ -474,17 +533,17 @@
}
}
- patch bool containsValue(Object value) {
+ bool containsValue(Object value) {
return keys.any((each) => this[each] == value);
}
- patch void addAll(Map<K, V> other) {
+ void addAll(Map<K, V> other) {
other.forEach((K key, V value) {
this[key] = value;
});
}
- patch V operator[](Object key) {
+ V operator[](Object key) {
if (_isStringKey(key)) {
var strings = _strings;
if (strings == null) return null;
@@ -506,7 +565,7 @@
}
}
- patch void operator[]=(K key, V value) {
+ void operator[]=(K key, V value) {
if (_isStringKey(key)) {
var strings = _strings;
if (strings == null) _strings = strings = _newHashTable();
@@ -536,14 +595,14 @@
}
}
- patch V putIfAbsent(K key, V ifAbsent()) {
+ V putIfAbsent(K key, V ifAbsent()) {
if (containsKey(key)) return this[key];
V value = ifAbsent();
this[key] = value;
return value;
}
- patch V remove(Object key) {
+ V remove(Object key) {
if (_isStringKey(key)) {
return _removeHashTableEntry(_strings, key);
} else if (_isNumericKey(key)) {
@@ -564,7 +623,7 @@
}
}
- patch void clear() {
+ void clear() {
if (_length > 0) {
_strings = _nums = _rest = _first = _last = null;
_length = 0;
@@ -572,7 +631,7 @@
}
}
- patch void forEach(void action(K key, V value)) {
+ void forEach(void action(K key, V value)) {
LinkedHashMapCell cell = _first;
int modifications = _modifications;
while (cell != null) {
@@ -593,7 +652,7 @@
}
}
- V _removeHashTableEntry(var table, K key) {
+ V _removeHashTableEntry(var table, Object key) {
if (table == null) return null;
LinkedHashMapCell cell = _getTableEntry(table, key);
if (cell == null) return null;
@@ -655,7 +714,7 @@
return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key);
}
- static int _computeHashCode(var key) {
+ int _computeHashCode(var key) {
// We force the hash codes to be unsigned 30-bit integers to avoid
// issues with problematic keys like '__proto__'. Another option
// would be to throw an exception if the hash code isn't a number.
@@ -675,12 +734,12 @@
JS('void', 'delete #[#]', table, key);
}
- static List _getBucket(var table, var key) {
+ List _getBucket(var table, var key) {
var hash = _computeHashCode(key);
return JS('var', '#[#]', table, hash);
}
- static int _findBucketIndex(var bucket, var key) {
+ int _findBucketIndex(var bucket, var key) {
if (bucket == null) return -1;
int length = JS('int', '#.length', bucket);
for (int i = 0; i < length; i++) {
@@ -702,6 +761,61 @@
_deleteTableEntry(table, temporaryKey);
return table;
}
+
+ String toString() => Maps.mapToString(this);
+}
+
+class _LinkedIdentityHashMap<K, V> extends _LinkedHashMap<K, V> {
+ int _findBucketIndex(var bucket, var key) {
+ if (bucket == null) return -1;
+ int length = JS('int', '#.length', bucket);
+ for (int i = 0; i < length; i++) {
+ LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
+ if (identical(cell._key, key)) return i;
+ }
+ return -1;
+ }
+}
+
+class _LinkedCustomHashMap<K, V> extends _LinkedHashMap<K, V> {
+ final _Equality<K> _equals;
+ final _Hasher<K> _hashCode;
+ final _Predicate _validKey;
+ _LinkedCustomHashMap(this._equals, this._hashCode,
+ bool validKey(potentialKey))
+ : _validKey = (validKey != null) ? validKey : ((v) => v is K);
+
+ V operator[](Object key) {
+ if (!_validKey(key)) return null;
+ return super[key];
+ }
+
+ bool containsKey(Object key) {
+ if (!_validKey(key)) return false;
+ return super.containsKey(key);
+ }
+
+ V remove(Object key) {
+ if (!_validKey(key)) return null;
+ return super.remove(key);
+ }
+
+ int _computeHashCode(var key) {
+ // We force the hash codes to be unsigned 30-bit integers to avoid
+ // issues with problematic keys like '__proto__'. Another option
+ // would be to throw an exception if the hash code isn't a number.
+ return JS('int', '# & 0x3ffffff', _hashCode(key));
+ }
+
+ int _findBucketIndex(var bucket, var key) {
+ if (bucket == null) return -1;
+ int length = JS('int', '#.length', bucket);
+ for (int i = 0; i < length; i++) {
+ LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
+ if (_equals(cell._key, key)) return i;
+ }
+ return -1;
+ }
}
class LinkedHashMapCell {
diff --git a/sdk/lib/_internal/lib/foreign_helper.dart b/sdk/lib/_internal/lib/foreign_helper.dart
index 165a0a4..ae91916 100644
--- a/sdk/lib/_internal/lib/foreign_helper.dart
+++ b/sdk/lib/_internal/lib/foreign_helper.dart
@@ -231,11 +231,6 @@
String JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG() {}
/**
- * Returns the global object, usually called encoded as [: $ :].
- */
-JS_GLOBAL_OBJECT() {}
-
-/**
* Obtain [name] from Namer.
*/
String JS_GET_NAME(String name) {}
diff --git a/sdk/lib/_internal/lib/io_patch.dart b/sdk/lib/_internal/lib/io_patch.dart
index 04ee3a9..1837aaf 100644
--- a/sdk/lib/_internal/lib/io_patch.dart
+++ b/sdk/lib/_internal/lib/io_patch.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
patch class _Directory {
- patch static String _current() {
+ patch static _current() {
throw new UnsupportedError("Directory._current");
}
patch static _setCurrent(path) {
diff --git a/sdk/lib/_internal/lib/isolate_helper.dart b/sdk/lib/_internal/lib/isolate_helper.dart
index 9fe12b0..d8fcff4a 100644
--- a/sdk/lib/_internal/lib/isolate_helper.dart
+++ b/sdk/lib/_internal/lib/isolate_helper.dart
@@ -577,13 +577,8 @@
JS("void", r"#.console.log(#)", globalThis, msg);
}
- /** Find a constructor given its name. */
- static dynamic _getJSConstructorFromName(String factoryName) {
- return JS("", "#[#]", JS_CURRENT_ISOLATE(), factoryName);
- }
-
- static dynamic _getJSFunctionFromName(String functionName) {
- return JS("", "#[#]", JS_CURRENT_ISOLATE(), functionName);
+ static _getJSFunctionFromName(String functionName) {
+ return JS("", "init.globalFunctions[#]", functionName);
}
/**
diff --git a/sdk/lib/_internal/lib/js_helper.dart b/sdk/lib/_internal/lib/js_helper.dart
index fe0fcc1..0ce4ec6 100644
--- a/sdk/lib/_internal/lib/js_helper.dart
+++ b/sdk/lib/_internal/lib/js_helper.dart
@@ -19,7 +19,6 @@
JS_FUNCTION_TYPE_TAG,
JS_FUNCTION_TYPE_VOID_RETURN_TAG,
JS_GET_NAME,
- JS_GLOBAL_OBJECT,
JS_HAS_EQUALS,
JS_IS_INDEXABLE_FIELD_NAME,
JS_OBJECT_CLASS_NAME,
@@ -729,7 +728,7 @@
if (JS('bool', '# == "num"', className)) return const JSNumber();
if (JS('bool', '# == "bool"', className)) return const JSBool();
if (JS('bool', '# == "List"', className)) return const JSArray();
- return JS('var', '#[#]', JS_CURRENT_ISOLATE(), className);
+ return JS('var', 'init.allClasses[#]', className);
}
static bool identicalImplementation(a, b) {
diff --git a/sdk/lib/_internal/lib/js_mirrors.dart b/sdk/lib/_internal/lib/js_mirrors.dart
index deb2faa..d16178b 100644
--- a/sdk/lib/_internal/lib/js_mirrors.dart
+++ b/sdk/lib/_internal/lib/js_mirrors.dart
@@ -91,12 +91,14 @@
var metadataFunction = data[4];
var fields = data[5];
bool isRoot = data[6];
+ var globalObject = data[7];
List metadata = (metadataFunction == null)
? const [] : JS('List', '#()', metadataFunction);
var libraries = result.putIfAbsent(name, () => <LibraryMirror>[]);
libraries.add(
new JsLibraryMirror(
- s(name), uri, classes, functions, metadata, fields, isRoot));
+ s(name), uri, classes, functions, metadata, fields, isRoot,
+ globalObject));
}
return result;
}
@@ -199,6 +201,7 @@
final List _metadata;
final String _compactFieldSpecification;
final bool _isRoot;
+ final _globalObject;
List<JsMethodMirror> _cachedFunctionMirrors;
List<JsVariableMirror> _cachedFields;
UnmodifiableMapView<Symbol, ClassMirror> _cachedClasses;
@@ -215,7 +218,8 @@
this._functions,
this._metadata,
this._compactFieldSpecification,
- this._isRoot)
+ this._isRoot,
+ this._globalObject)
: super(simpleName);
String get _prettyName => 'LibraryMirror';
@@ -282,11 +286,18 @@
}
_loadField(String name) {
+ // TODO(ahe): What about lazily initialized fields? See
+ // [JsClassMirror.getField].
+
+ // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so we
+ // shouldn't use [_globalObject] here.
assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE()));
return JS('', '#[#]', JS_CURRENT_ISOLATE(), name);
}
void _storeField(String name, Object arg) {
+ // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so we
+ // shouldn't use [_globalObject] here.
assert(JS('bool', '# in #', name, JS_CURRENT_ISOLATE()));
JS('void', '#[#] = #', JS_CURRENT_ISOLATE(), name, arg);
}
@@ -296,9 +307,7 @@
var result = new List<JsMethodMirror>();
for (int i = 0; i < _functions.length; i++) {
String name = _functions[i];
- // TODO(ahe): Create accessor for accessing $. It is also
- // used in js_helper.
- var jsFunction = JS('', '#[#]', JS_CURRENT_ISOLATE(), name);
+ var jsFunction = JS('', '#[#]', _globalObject, name);
String unmangledName = mangledGlobalNames[name];
if (unmangledName == null) {
// If there is no unmangledName, [jsFunction] is either a synthetic
@@ -794,9 +803,7 @@
String mangledName = keys[i];
if (mangledName == '') continue; // Skip static field descriptor.
String unmangledName = mangledName;
- // TODO(ahe): Create accessor for accessing $. It is also
- // used in js_helper.
- var jsFunction = JS('', '#[#]', JS_CURRENT_ISOLATE(), mangledName);
+ var jsFunction = JS('', '#[#]', owner._globalObject, mangledName);
bool isConstructor = false;
if (i + 1 < length) {
@@ -926,6 +933,8 @@
InstanceMirror setField(Symbol fieldName, Object arg) {
JsVariableMirror mirror = variables[fieldName];
if (mirror != null && mirror.isStatic && !mirror.isFinal) {
+ // '$' (JS_CURRENT_ISOLATE()) stores state which is stored directly, so
+ // we shouldn't use [JsLibraryMirror._globalObject] here.
String jsName = mirror._jsName;
if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) {
throw new RuntimeError('Cannot find "$jsName" in current isolate.');
@@ -941,6 +950,8 @@
JsVariableMirror mirror = variables[fieldName];
if (mirror != null && mirror.isStatic) {
String jsName = mirror._jsName;
+ // '$' (JS_CURRENT_ISOLATE()) stores state which is read directly, so
+ // we shouldn't use [JsLibraryMirror._globalObject] here.
if (!JS('bool', '# in #', jsName, JS_CURRENT_ISOLATE())) {
throw new RuntimeError('Cannot find "$jsName" in current isolate.');
}
@@ -969,7 +980,7 @@
orElse: () {
// TODO(ahe): What receiver to use?
throw new NoSuchMethodError(
- owner, constructorName, positionalArguments, namedArguments);
+ this, constructorName, positionalArguments, namedArguments);
});
JsCache.update(_jsConstructorCache, n(constructorName), mirror);
}
@@ -988,7 +999,7 @@
constructorName, positionalArguments, namedArguments));
}
- DeclarationMirror get owner {
+ JsLibraryMirror get owner {
if (_owner == null) {
if (_jsConstructorOrInterceptor is Interceptor) {
_owner = reflectType(Object).owner;
@@ -1363,6 +1374,10 @@
throw new NoSuchMethodError(
owner, simpleName, positionalArguments, namedArguments);
}
+ // Using JS_CURRENT_ISOLATE() ('$') here is actually correct, although
+ // _jsFunction may not be a property of '$', most static functions do not
+ // care who their receiver is. But to lazy getters, it is important that
+ // 'this' is '$'.
return JS('', r'#.apply(#, #)', _jsFunction, JS_CURRENT_ISOLATE(),
new List.from(positionalArguments));
}
diff --git a/sdk/lib/_internal/lib/js_number.dart b/sdk/lib/_internal/lib/js_number.dart
index 445e927..8dd89d8 100644
--- a/sdk/lib/_internal/lib/js_number.dart
+++ b/sdk/lib/_internal/lib/js_number.dart
@@ -278,6 +278,65 @@
bool get isOdd => (this & 1) == 1;
+ int toUnsigned(int width) {
+ return this & ((1 << width) - 1);
+ }
+
+ int toSigned(int width) {
+ int signMask = 1 << (width - 1);
+ return (this & (signMask - 1)) - (this & signMask);
+ }
+
+ int get bitLength {
+ int nonneg = this < 0 ? -this-1 : this;
+ if (nonneg >= 0x100000000) {
+ nonneg = nonneg ~/ 0x100000000;
+ return _bitCount(_spread(nonneg)) + 32;
+ }
+ return _bitCount(_spread(nonneg));
+ }
+
+ // Assumes i is <= 32-bit and unsigned.
+ static int _bitCount(int i) {
+ // See "Hacker's Delight", section 5-1, "Counting 1-Bits".
+
+ // The basic strategy is to use "divide and conquer" to
+ // add pairs (then quads, etc.) of bits together to obtain
+ // sub-counts.
+ //
+ // A straightforward approach would look like:
+ //
+ // i = (i & 0x55555555) + ((i >> 1) & 0x55555555);
+ // i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
+ // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F);
+ // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF);
+ // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF);
+ //
+ // The code below removes unnecessary &'s and uses a
+ // trick to remove one instruction in the first line.
+
+ i = _shru(i, 0) - (_shru(i, 1) & 0x55555555);
+ i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333);
+ i = 0x0F0F0F0F & (i + _shru(i, 4));
+ i += _shru(i, 8);
+ i += _shru(i, 16);
+ return (i & 0x0000003F);
+ }
+
+ static _shru(int value, int shift) => JS('int', '# >>> #', value, shift);
+ static _shrs(int value, int shift) => JS('int', '# >> #', value, shift);
+ static _ors(int a, int b) => JS('int', '# | #', a, b);
+
+ // Assumes i is <= 32-bit
+ static int _spread(int i) {
+ i = _ors(i, _shrs(i, 1));
+ i = _ors(i, _shrs(i, 2));
+ i = _ors(i, _shrs(i, 4));
+ i = _ors(i, _shrs(i, 8));
+ i = _shru(_ors(i, _shrs(i, 16)), 0);
+ return i;
+ }
+
Type get runtimeType => int;
int operator ~() => JS('int', r'(~#) >>> 0', this);
diff --git a/sdk/lib/_internal/lib/js_rti.dart b/sdk/lib/_internal/lib/js_rti.dart
index f582c1a..432d237 100644
--- a/sdk/lib/_internal/lib/js_rti.dart
+++ b/sdk/lib/_internal/lib/js_rti.dart
@@ -350,9 +350,9 @@
if (hasField(interceptor, '${JS_OPERATOR_IS_PREFIX()}_$signatureName')) {
return true;
}
- var signatureLocation = JS_GLOBAL_OBJECT();
+ var signatureLocation = JS_CURRENT_ISOLATE();
if (isNotNull(contextName)) {
- signatureLocation = getField(signatureLocation, contextName);
+ signatureLocation = getField(JS('=Object', 'init.allClasses'), contextName);
}
var typeSignature =
getField(signatureLocation, '${JS_SIGNATURE_NAME()}_$signatureName');
diff --git a/sdk/lib/_internal/lib/js_string.dart b/sdk/lib/_internal/lib/js_string.dart
index adc32e8..8a8ac16 100644
--- a/sdk/lib/_internal/lib/js_string.dart
+++ b/sdk/lib/_internal/lib/js_string.dart
@@ -44,8 +44,6 @@
return JS('String', r'# + #', this, other);
}
- String concat(String other) => this + other;
-
bool endsWith(String other) {
checkString(other);
int otherLength = other.length;
diff --git a/sdk/lib/_internal/lib/regexp_helper.dart b/sdk/lib/_internal/lib/regexp_helper.dart
index bec8253..09fe861 100644
--- a/sdk/lib/_internal/lib/regexp_helper.dart
+++ b/sdk/lib/_internal/lib/regexp_helper.dart
@@ -17,9 +17,9 @@
* when it's returned, with no user-provided code run in between.
*/
regExpGetGlobalNative(JSSyntaxRegExp regexp) {
- var regexp = regexp._nativeGlobalVersion;
- JS("void", "#.lastIndex = 0", regexp);
- return regexp;
+ var nativeRegexp = regexp._nativeGlobalVersion;
+ JS("void", "#.lastIndex = 0", nativeRegexp);
+ return nativeRegexp;
}
class JSSyntaxRegExp implements RegExp {
diff --git a/sdk/lib/_internal/pub/lib/src/barback.dart b/sdk/lib/_internal/pub/lib/src/barback.dart
index 36de8dc..3c14a16 100644
--- a/sdk/lib/_internal/pub/lib/src/barback.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback.dart
@@ -8,13 +8,45 @@
import 'package:barback/barback.dart';
-import 'barback/load_transformers.dart';
+import 'barback/load_all_transformers.dart';
import 'barback/pub_package_provider.dart';
import 'barback/rewrite_import_transformer.dart';
import 'barback/server.dart';
import 'barback/watch_sources.dart';
import 'utils.dart';
+/// An identifier for a transformer and the configuration that will be passed to
+/// it.
+///
+/// It's possible that [asset] defines multiple transformers. If so,
+/// [configuration] will be passed to all of them.
+class TransformerId {
+ /// The asset containing the transformer.
+ final AssetId asset;
+
+ /// The configuration to pass to the transformer.
+ ///
+ /// This will be null if no configuration was provided.
+ final Map configuration;
+
+ TransformerId(this.asset, this.configuration) {
+ if (configuration == null) return;
+ for (var reserved in ['include', 'exclude']) {
+ if (!configuration.containsKey(reserved)) continue;
+ throw new FormatException('Configuration for transformer '
+ '${idToLibraryIdentifier(asset)} may not include reserved key '
+ '"$reserved".');
+ }
+ }
+
+ // TODO(nweiz): support deep equality on [configuration] as well.
+ bool operator==(other) => other is TransformerId &&
+ other.asset == asset &&
+ other.configuration == configuration;
+
+ int get hashCode => asset.hashCode ^ configuration.hashCode;
+}
+
/// Creates a [BarbackServer] serving on [host] and [port].
///
/// This transforms and serves all library and asset files in all packages in
@@ -44,7 +76,7 @@
})
];
- _loadTransformers(server, graph).then((_) {
+ loadAllTransformers(server, graph).then((_) {
if (!completer.isCompleted) completer.complete(server);
}).catchError((error) {
if (!completer.isCompleted) completer.completeError(error);
@@ -58,49 +90,55 @@
});
}
-/// Loads all transformers depended on by packages in [graph].
+/// Parses a library identifier to an asset id.
///
-/// This uses [server] to serve the Dart files from which transformers are
-/// loaded, then adds the transformers to `server.barback`.
-Future _loadTransformers(BarbackServer server, PackageGraph graph) {
- // Add a rewrite transformer for each package, so that we can resolve
- // "package:" imports while loading transformers.
- var rewrite = new RewriteImportTransformer();
- for (var package in graph.packages.values) {
- server.barback.updateTransformers(package.name, [[rewrite]]);
+/// A library identifier is a string of the form "package_name" or
+/// "package_name/path/to/library". It does not have a trailing extension. If it
+/// just has a package name, it expands to lib/${package}.dart in that package.
+/// Otherwise, it expands to lib/${path}.dart in that package.
+AssetId libraryIdentifierToId(String identifier) {
+ if (identifier.isEmpty) {
+ throw new FormatError('Invalid library identifier: "".');
}
- // A map from each transformer id to the set of packages that use it.
- var idsToPackages = new Map<AssetId, Set<String>>();
- for (var package in graph.packages.values) {
- for (var id in unionAll(package.pubspec.transformers)) {
- idsToPackages.putIfAbsent(id, () => new Set<String>()).add(package.name);
- }
+ // Convert the concise asset name in the pubspec (of the form "package"
+ // or "package/library") to an AssetId that points to an actual dart
+ // file ("package/lib/package.dart" or "package/lib/library.dart",
+ // respectively).
+ var parts = split1(identifier, "/");
+ if (parts.length == 1) parts.add(parts.single);
+ return new AssetId(parts.first, 'lib/' + parts.last + '.dart');
+}
+
+final _libraryPathRegExp = new RegExp(r"^lib/(.*)\.dart$");
+
+/// Converts [id] to a library identifier.
+///
+/// A library identifier is a string of the form "package_name" or
+/// "package_name/path/to/library". It does not have a trailing extension. If it
+/// just has a package name, it expands to lib/${package}.dart in that package.
+/// Otherwise, it expands to lib/${path}.dart in that package.
+///
+/// This will throw an [ArgumentError] if [id] doesn't represent a library in
+/// `lib/`.
+String idToLibraryIdentifier(AssetId id) {
+ var match = _libraryPathRegExp.firstMatch(id.path);
+ if (match == null) {
+ throw new ArgumentError("Asset id $id doesn't identify a library.");
}
- // TODO(nweiz): support transformers that (possibly transitively)
- // depend on other transformers.
- var transformersForId = new Map<AssetId, Set<Transformer>>();
- return Future.wait(idsToPackages.keys.map((id) {
- return loadTransformers(server, id).then((transformers) {
- if (transformers.isEmpty) {
- var path = id.path.replaceFirst('lib/', '');
- // Ensure that packages are listed in a deterministic order.
- var packages = idsToPackages[id].toList();
- packages.sort();
- throw new ApplicationException(
- "No transformers were defined in package:${id.package}/$path,\n"
- "required by ${packages.join(', ')}.");
- }
+ if (match[1] == id.package) return id.package;
+ return '${id.package}/${match[1]}';
+}
- transformersForId[id] = transformers;
- });
- })).then((_) {
- for (var package in graph.packages.values) {
- var phases = package.pubspec.transformers.map((phase) {
- return unionAll(phase.map((id) => transformersForId[id]));
- });
- server.barback.updateTransformers(package.name, phases);
- }
- });
+/// Converts [id] to a "package:" URI.
+///
+/// This will throw an [ArgumentError] if [id] doesn't represent a library in
+/// `lib/`.
+Uri idToPackageUri(AssetId id) {
+ if (!id.path.startsWith('lib/')) {
+ throw new ArgumentError("Asset id $id doesn't identify a library.");
+ }
+
+ return new Uri(scheme: 'package', path: id.path.replaceFirst('lib/', ''));
}
diff --git a/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
new file mode 100644
index 0000000..e954da8
--- /dev/null
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
@@ -0,0 +1,252 @@
+// Copyright (c) 2013, 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.
+
+library pub.load_all_transformers;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+import 'load_transformers.dart';
+import 'rewrite_import_transformer.dart';
+import 'server.dart';
+import 'watch_sources.dart';
+import '../barback.dart';
+import '../utils.dart';
+
+/// Loads all transformers depended on by packages in [graph].
+///
+/// This uses [server] to serve the Dart files from which transformers are
+/// loaded, then adds the transformers to `server.barback`.
+Future loadAllTransformers(BarbackServer server, PackageGraph graph) {
+ // In order to determine in what order we should load transformers, we need to
+ // know which transformers depend on which others. This is different than
+ // normal package dependencies. Let's begin with some terminology:
+ //
+ // * If package A is transformed by package B, we say A has a "transformer
+ // dependency" on B.
+ // * If A imports B we say A has a "package dependency" on B.
+ // * If A needs B's transformers to be loaded in order to load A's
+ // transformers, we say A has an "ordering dependency" on B.
+ //
+ // In particular, an ordering dependency is defined as follows:
+ //
+ // * If A has a transformer dependency on B, A also has an ordering dependency
+ // on B.
+ // * If A has a transitive package dependency on B and B has a transformer
+ // dependency on C, A has an ordering dependency on C.
+ //
+ // The order that transformers are loaded is determined by each package's
+ // ordering dependencies. We treat the packages as a directed acyclic[1] graph
+ // where each package is a node and the ordering dependencies are the edges
+ // (that is, the packages form a partially ordered set). We then load[2]
+ // packages in a topological sort order of this graph.
+ //
+ // [1] TODO(nweiz): support cycles in some cases.
+ //
+ // [2] We use "loading a package" as a shorthand for loading that package's
+ // transformers.
+
+ // Add a rewrite transformer for each package, so that we can resolve
+ // "package:" imports while loading transformers.
+ var rewrite = new RewriteImportTransformer();
+ for (var package in graph.packages.values) {
+ server.barback.updateTransformers(package.name, [[rewrite]]);
+ }
+
+ var orderingDeps = _computeOrderingDeps(graph);
+ var packageTransformers = _computePackageTransformers(graph);
+
+ var loader = new _TransformerLoader(server, graph);
+
+ // The packages on which no packages have ordering dependencies -- that is,
+ // the packages that don't need to be loaded before any other packages. These
+ // packages will be loaded last, since all of their ordering dependencies need
+ // to be loaded before they're loaded. However, they'll be traversed by
+ // [loadPackage] first.
+ var rootPackages = graph.packages.keys.toSet()
+ .difference(unionAll(orderingDeps.values));
+
+ // The Futures for packages that have been loaded or are being actively loaded
+ // by [loadPackage]. Once one of these Futures is complete, the transformers
+ // for that package will all be available from [loader].
+ var loadingPackages = new Map<String, Future>();
+
+ // A helper function that loads all the transformers that [package] uses, then
+ // all the transformers that [package] defines.
+ Future loadPackage(String package) {
+ if (loadingPackages.containsKey(package)) return loadingPackages[package];
+
+ // First, load each package upon which [package] has an ordering dependency.
+ var future = Future.wait(orderingDeps[package].map(loadPackage)).then((_) {
+ // Go through the transformers used by [package] phase-by-phase. If any
+ // phase uses a transformer defined in [package] itself, that transform
+ // should be loaded after running all previous phases.
+ var transformers = [[rewrite]];
+ return Future.forEach(graph.packages[package].pubspec.transformers,
+ (phase) {
+ return Future.wait(phase.where((id) => id.asset.package == package)
+ .map(loader.load)).then((_) {
+ transformers.add(unionAll(phase.map(
+ (id) => loader.transformersFor(id))));
+ server.barback.updateTransformers(package, transformers);
+ });
+ }).then((_) {
+ // Now that we've applied all the transformers used by [package] via
+ // [Barback.updateTransformers], we load any transformers defined in
+ // [package] but used elsewhere.
+ return Future.wait(packageTransformers[package].map(loader.load));
+ });
+ });
+ loadingPackages[package] = future;
+ return future;
+ }
+
+ return Future.wait(rootPackages.map(loadPackage)).then((_) {
+ /// Reset the transformers for each package to get rid of [rewrite], which
+ /// is no longer needed.
+ for (var package in graph.packages.values) {
+ var phases = package.pubspec.transformers.map((phase) {
+ return unionAll(phase.map((id) => loader.transformersFor(id)));
+ });
+ server.barback.updateTransformers(package.name, phases);
+ }
+ });
+}
+
+/// Computes and returns the graph of ordering dependencies for [graph].
+///
+/// This graph is in the form of a map whose keys are packages and whose values
+/// are those packages' ordering dependencies.
+Map<String, Set<String>> _computeOrderingDeps(PackageGraph graph) {
+ var orderingDeps = new Map<String, Set<String>>();
+ // Iterate through the packages in a deterministic order so that if there are
+ // multiple cycles we choose which to print consistently.
+ var packages = ordered(graph.packages.values.map((package) => package.name));
+ for (var package in packages) {
+ // This package's transformer dependencies are also ordering dependencies.
+ var deps = _transformerDeps(graph, package);
+ deps.remove(package);
+ // The transformer dependencies of this package's transitive package
+ // dependencies are also ordering dependencies for this package.
+ var transitivePackageDeps = graph.transitiveDependencies(package)
+ .map((package) => package.name);
+ for (var packageDep in ordered(transitivePackageDeps)) {
+ var transformerDeps = _transformerDeps(graph, packageDep);
+ if (transformerDeps.contains(package)) {
+ throw _cycleError(graph, package, packageDep);
+ }
+ deps.addAll(transformerDeps);
+ }
+ orderingDeps[package] = deps;
+ }
+
+ return orderingDeps;
+}
+
+/// Returns the set of transformer dependencies for [package].
+Set<String> _transformerDeps(PackageGraph graph, String package) =>
+ unionAll(graph.packages[package].pubspec.transformers)
+ .map((id) => id.asset.package).toSet();
+
+/// Returns an [ApplicationException] describing an ordering dependency cycle
+/// detected in [graph].
+///
+/// [dependee] and [depender] should be the names of two packages known to be in
+/// the cycle. In addition, [depender] should have a transformer dependency on
+/// [dependee].
+ApplicationException _cycleError(PackageGraph graph, String dependee,
+ String depender) {
+ assert(_transformerDeps(graph, depender).contains(dependee));
+
+ var simpleGraph = mapMapValues(graph.packages,
+ (_, package) => package.dependencies.map((dep) => dep.name).toList());
+ var path = shortestPath(simpleGraph, dependee, depender);
+ path.add(dependee);
+ return new ApplicationException("Transformer cycle detected:\n" +
+ pairs(path).map((pair) {
+ var transformers = unionAll(graph.packages[pair.first].pubspec.transformers)
+ .where((id) => id.asset.package == pair.last)
+ .map(idToLibraryIdentifier).toList();
+ if (transformers.isEmpty) {
+ return " ${pair.first} depends on ${pair.last}";
+ } else {
+ return " ${pair.first} is transformed by ${toSentence(transformers)}";
+ }
+ }).join("\n"));
+}
+
+/// Returns a map from each package name in [graph] to the transformer ids of
+/// all transformers exposed by that package and used by other packages.
+Map<String, Set<TransformerId>> _computePackageTransformers(
+ PackageGraph graph) {
+ var packageTransformers = listToMap(graph.packages.values,
+ (package) => package.name, (_) => new Set<TransformerId>());
+ for (var package in graph.packages.values) {
+ for (var phase in package.pubspec.transformers) {
+ for (var id in phase) {
+ packageTransformers[id.asset.package].add(id);
+ }
+ }
+ }
+ return packageTransformers;
+}
+
+/// A class that loads transformers defined in specific files.
+class _TransformerLoader {
+ final BarbackServer _server;
+
+ /// The loaded transformers defined in the library identified by each
+ /// transformer id.
+ final _transformers = new Map<TransformerId, Set<Transformer>>();
+
+ /// The packages that use each transformer asset id.
+ ///
+ /// Used for error reporting.
+ final _transformerUsers = new Map<AssetId, Set<String>>();
+
+ _TransformerLoader(this._server, PackageGraph graph) {
+ for (var package in graph.packages.values) {
+ for (var id in unionAll(package.pubspec.transformers)) {
+ _transformerUsers.putIfAbsent(id.asset, () => new Set<String>())
+ .add(package.name);
+ }
+ }
+ }
+
+ /// Loads the transformer(s) defined in [id].
+ ///
+ /// Once the returned future completes, these transformers can be retrieved
+ /// using [transformersFor]. If [id] doesn't define any transformers, this
+ /// will complete to an error.
+ Future load(TransformerId id) {
+ if (_transformers.containsKey(id)) return new Future.value();
+
+ // TODO(nweiz): load multiple instances of the same transformer from the
+ // same isolate rather than spinning up a separate isolate for each one.
+ return loadTransformers(_server, id).then((transformers) {
+ if (!transformers.isEmpty) {
+ _transformers[id] = transformers;
+ return;
+ }
+
+ var message = "No transformers";
+ if (id.configuration != null) {
+ message += " that accept configuration";
+ }
+ throw new ApplicationException(
+ "$message were defined in ${idToPackageUri(id.asset)},\n"
+ "required by ${ordered(_transformerUsers[id.asset]).join(', ')}.");
+ });
+ }
+
+ /// Returns the set of transformers for [id].
+ ///
+ /// It's an error to call this before [load] is called with [id] and the
+ /// future it returns has completed.
+ Set<Transformers> transformersFor(TransformerId id) {
+ assert(_transformers.containsKey(id));
+ return _transformers[id];
+ }
+}
\ No newline at end of file
diff --git a/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart b/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
index 72634bc..b2c8464 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
@@ -2,9 +2,10 @@
// 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.
-library pub.serialize_transformer;
+library pub.load_transformers;
import 'dart:async';
+import 'dart:convert';
import 'dart:isolate';
import 'package:barback/barback.dart';
@@ -21,15 +22,19 @@
const _TRANSFORMER_ISOLATE = """
import 'dart:async';
import 'dart:isolate';
+import 'dart:convert';
import 'dart:mirrors';
import 'http://localhost:<<PORT>>/packages/barback/barback.dart';
/// Sets up the initial communication with the host isolate.
void main() {
- port.receive((uri, replyTo) {
+ port.receive((args, replyTo) {
_sendFuture(replyTo, new Future.sync(() {
- return initialize(Uri.parse(uri)).map(_serializeTransformer).toList();
+ var library = Uri.parse(args['library']);
+ var configuration = JSON.decode(args['configuration']);
+ return initialize(library, configuration).
+ map(_serializeTransformer).toList();
}));
});
}
@@ -37,8 +42,8 @@
/// Loads all the transformers defined in [uri] and adds them to [transformers].
///
/// We then load the library, find any Transformer subclasses in it, instantiate
-/// them, and return them.
-Iterable<Transformer> initialize(Uri uri) {
+/// them (with [configuration] if it's non-null), and return them.
+Iterable<Transformer> initialize(Uri uri, Map configuration) {
var mirrors = currentMirrorSystem();
// TODO(nweiz): look this up by name once issue 5897 is fixed.
var transformerUri = Uri.parse(
@@ -46,17 +51,30 @@
var transformerClass = mirrors.libraries[transformerUri]
.classes[const Symbol('Transformer')];
- return mirrors.libraries[uri].classes.values.where((classMirror) {
- if (classMirror.isPrivate) return false;
- if (isAbstract(classMirror)) return false;
- if (!classIsA(classMirror, transformerClass)) return false;
- var constructor = classMirror.constructors[classMirror.simpleName];
- if (constructor == null) return false;
- if (!constructor.parameters.isEmpty) return false;
- return true;
- }).map((classMirror) {
- return classMirror.newInstance(const Symbol(''), []).reflectee;
- });
+ // TODO(nweiz): if no valid transformers are found, throw an error message
+ // describing candidates and why they were rejected.
+ return mirrors.libraries[uri].classes.values.map((classMirror) {
+ if (classMirror.isPrivate) return null;
+ if (isAbstract(classMirror)) return null;
+ if (!classIsA(classMirror, transformerClass)) return null;
+
+ var constructor = getConstructor(classMirror, 'asPlugin');
+ if (constructor == null) return null;
+ if (constructor.parameters.isEmpty) {
+ if (configuration != null) return null;
+ return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee;
+ }
+ if (constructor.parameters.length != 1) return null;
+
+ // If the constructor expects configuration and none was passed, it defaults
+ // to an empty map.
+ if (configuration == null) configuration = {};
+
+ // TODO(nweiz): if the constructor accepts named parameters, automatically
+ // destructure the configuration map.
+ return classMirror.newInstance(const Symbol('asPlugin'), [configuration])
+ .reflectee;
+ }).where((classMirror) => classMirror != null);
}
/// A wrapper for a [Transform] that's in the host isolate.
@@ -107,6 +125,13 @@
}
ClassMirror _objectMirror;
+// TODO(nweiz): clean this up when issue 13248 is fixed.
+MethodMirror getConstructor(ClassMirror classMirror, String constructor) {
+ var name = new Symbol("\${MirrorSystem.getName(classMirror.simpleName)}"
+ ".\$constructor");
+ return classMirror.constructors[name];
+}
+
// TODO(nweiz): get rid of this when issue 12439 is fixed.
/// Returns whether or not [mirror] is a subtype of [superclass].
///
@@ -256,23 +281,28 @@
}
""";
-/// Load and return all transformers from the library identified by [library].
+/// Load and return all transformers from the library identified by [id].
///
-/// [server] is used to serve [library] and any Dart files it imports.
+/// [server] is used to serve any Dart files needed to load the transformer.
Future<Set<Transformer>> loadTransformers(BarbackServer server,
- AssetId library) {
- var path = library.path.replaceFirst('lib/', '');
+ TransformerId id) {
+ var path = id.asset.path.replaceFirst('lib/', '');
// TODO(nweiz): load from a "package:" URI when issue 12474 is fixed.
- var uri = 'http://localhost:${server.port}/packages/${library.package}/$path';
+ var uri = 'http://localhost:${server.port}/packages/${id.asset.package}/'
+ '$path';
var code = 'import "$uri";' +
_TRANSFORMER_ISOLATE.replaceAll('<<PORT>>', server.port.toString());
- log.fine("Loading transformers from $library");
+ log.fine("Loading transformers from ${id.asset}");
return dart.runInIsolate(code).then((sendPort) {
- return _receiveFuture(sendPort.call(uri)).then((transformers) {
+ return _receiveFuture(sendPort.call({
+ 'library': uri,
+ // TODO(nweiz): support non-JSON-encodable configuration maps.
+ 'configuration': JSON.encode(id.configuration)
+ })).then((transformers) {
transformers = transformers
.map((transformer) => new _ForeignTransformer(transformer))
.toSet();
- log.fine("Transformers from $library: $transformers");
+ log.fine("Transformers from ${id.asset}: $transformers");
return transformers;
});
}).catchError((error) {
@@ -287,7 +317,7 @@
// If there was an IsolateSpawnException and the import that actually failed
// was the one we were loading transformers from, throw an application
// exception with a more user-friendly message.
- fail('Transformer library "package:${library.package}/$path" not found.');
+ fail('Transformer library "package:${id.asset.package}/$path" not found.');
});
}
diff --git a/sdk/lib/_internal/pub/lib/src/directory_tree.dart b/sdk/lib/_internal/pub/lib/src/directory_tree.dart
index ad67e26..b0d0710 100644
--- a/sdk/lib/_internal/pub/lib/src/directory_tree.dart
+++ b/sdk/lib/_internal/pub/lib/src/directory_tree.dart
@@ -7,6 +7,8 @@
import 'package:path/path.dart' as path;
+import 'utils.dart';
+
/// Draws a directory tree for the given list of files. Given a list of files
/// like:
///
@@ -101,8 +103,7 @@
if (name != null) _drawLine(buffer, prefix, isLast, name);
// Recurse to the children.
- var childNames = new List.from(children.keys);
- childNames.sort();
+ var childNames = ordered(children.keys);
_drawChild(bool isLastChild, String child) {
var childPrefix = _getPrefix(name == null, isLast);
diff --git a/sdk/lib/_internal/pub/lib/src/package_graph.dart b/sdk/lib/_internal/pub/lib/src/package_graph.dart
index 687b720..634eb5c 100644
--- a/sdk/lib/_internal/pub/lib/src/package_graph.dart
+++ b/sdk/lib/_internal/pub/lib/src/package_graph.dart
@@ -18,4 +18,22 @@
final Map<String, Package> packages;
PackageGraph(this.entrypoint, this.packages);
+
+ /// Returns the set of transitive dependencies of the package named
+ /// [packageName].
+ Set<Package> transitiveDependencies(String packageName) {
+ var seen = new Set<Package>();
+ traverse(Package package) {
+ if (seen.contains(package)) return;
+ seen.add(package);
+ for (var dep in package.dependencies) {
+ traverse(packages[dep.name]);
+ }
+ }
+
+ var package = packages[packageName];
+ traverse(package);
+ seen.remove(package);
+ return seen;
+ }
}
diff --git a/sdk/lib/_internal/pub/lib/src/pubspec.dart b/sdk/lib/_internal/pub/lib/src/pubspec.dart
index ba0df4d..0813315 100644
--- a/sdk/lib/_internal/pub/lib/src/pubspec.dart
+++ b/sdk/lib/_internal/pub/lib/src/pubspec.dart
@@ -8,6 +8,7 @@
import 'package:yaml/yaml.dart';
import 'package:path/path.dart' as path;
+import 'barback.dart';
import 'io.dart';
import 'package.dart';
import 'source.dart';
@@ -29,9 +30,8 @@
/// The packages this package depends on when it is the root package.
final List<PackageDep> devDependencies;
- /// The ids of the libraries containing the transformers to use for this
- /// package.
- final List<Set<AssetId>> transformers;
+ /// The ids of the transformers to use for this package.
+ final List<Set<TransformerId>> transformers;
/// The environment-related metadata.
final PubspecEnvironment environment;
@@ -73,7 +73,7 @@
dependencies = <PackageDep>[],
devDependencies = <PackageDep>[],
environment = new PubspecEnvironment(),
- transformers = <Set<AssetId>>[],
+ transformers = <Set<TransformerId>>[],
fields = {};
/// Whether or not the pubspec has no contents.
@@ -157,8 +157,7 @@
if (collisions.length == 1) {
packageNames = 'Package "${collisions.first}"';
} else {
- var names = collisions.toList();
- names.sort();
+ var names = ordered(collisions);
var buffer = new StringBuffer();
buffer.write("Packages ");
for (var i = 0; i < names.length; i++) {
@@ -174,6 +173,7 @@
packageNames = buffer.toString();
}
+
throw new FormatException(
'$packageNames cannot appear in both "dependencies" and '
'"dev_dependencies".');
@@ -189,18 +189,28 @@
transformers = transformers.map((phase) {
if (phase is! List) phase = [phase];
return phase.map((transformer) {
- if (transformer is! String) {
+ if (transformer is! String && transformer is! Map) {
throw new FormatException(
- 'Transformer "$transformer" must be a string.');
+ 'Transformer "$transformer" must be a string or map.');
}
- // Convert the concise asset name in the pubspec (of the form "package"
- // or "package/library") to an AssetId that points to an actual dart
- // file ("package/lib/package.dart" or "package/lib/library.dart",
- // respectively).
- var parts = split1(transformer, "/");
- if (parts.length == 1) parts.add(parts.single);
- var id = new AssetId(parts.first, 'lib/' + parts.last + '.dart');
+ var id;
+ var configuration;
+ if (transformer is String) {
+ id = libraryIdentifierToId(transformer);
+ } else {
+ if (transformer.length != 1) {
+ throw new FormatException('Transformer map "$transformer" must '
+ 'have a single key: the library identifier.');
+ }
+
+ id = libraryIdentifierToId(transformer.keys.single);
+ configuration = transformer.values.single;
+ if (configuration is! Map) {
+ throw new FormatException('Configuration for transformer "$id" '
+ 'must be a map, but was "$configuration".');
+ }
+ }
if (id.package != name &&
!dependencies.any((ref) => ref.name == id.package)) {
@@ -208,7 +218,7 @@
'"$transformer".');
}
- return id;
+ return new TransformerId(id, configuration);
}).toSet();
}).toList();
} else {
diff --git a/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart b/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
index eb2fc63..09d5fbf 100644
--- a/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
+++ b/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
@@ -187,8 +187,7 @@
map[dep.depender] = dep.dep;
}
- var names = map.keys.toList();
- names.sort();
+ var names = ordered(map.keys);
buffer.writeAll(names.map(
(name) => "- '$name' ${_describeDependency(map[name])}"), '\n');
diff --git a/sdk/lib/_internal/pub/lib/src/utils.dart b/sdk/lib/_internal/pub/lib/src/utils.dart
index 44f530a..acca0bb 100644
--- a/sdk/lib/_internal/pub/lib/src/utils.dart
+++ b/sdk/lib/_internal/pub/lib/src/utils.dart
@@ -7,6 +7,7 @@
import 'dart:async';
import 'dart:io';
+import "dart:collection";
import "dart:convert";
import 'dart:mirrors';
@@ -91,6 +92,15 @@
return result.toString();
}
+/// Returns a sentence fragment listing the elements of [iter].
+///
+/// This converts each element of [iter] to a string and separates them with
+/// commas and/or "and" where appropriate.
+String toSentence(Iterable iter) {
+ if (iter.length == 1) return iter.first.toString();
+ return iter.take(iter.length - 1).join(", ") + " and ${iter.last}";
+}
+
/// Flattens nested lists inside an iterable into a single list containing only
/// non-list elements.
List flatten(Iterable nested) {
@@ -126,6 +136,115 @@
return minuendSet;
}
+/// Returns a list containing the sorted elements of [iter].
+List ordered(Iterable<Comparable> iter) {
+ var list = iter.toList();
+ list.sort();
+ return list;
+}
+
+/// Returns the element of [iter] for which [f] returns the minimum value.
+minBy(Iterable iter, Comparable f(element)) {
+ var min = null;
+ var minComparable = null;
+ for (var element in iter) {
+ var comparable = f(element);
+ if (minComparable == null ||
+ comparable.compareTo(minComparable) < 0) {
+ min = element;
+ minComparable = comparable;
+ }
+ }
+ return min;
+}
+
+/// Returns every pair of consecutive elements in [iter].
+///
+/// For example, if [iter] is `[1, 2, 3, 4]`, this will return `[(1, 2), (2, 3),
+/// (3, 4)]`.
+Iterable<Pair> pairs(Iterable iter) {
+ var previous = iter.first;
+ return iter.skip(1).map((element) {
+ var oldPrevious = previous;
+ previous = element;
+ return new Pair(oldPrevious, element);
+ });
+}
+
+/// Creates a map from [iter], using the return values of [keyFn] as the keys
+/// and the return values of [valueFn] as the values.
+Map listToMap(Iterable iter, keyFn(element), valueFn(element)) {
+ var map = new Map();
+ for (var element in iter) {
+ map[keyFn(element)] = valueFn(element);
+ }
+ return map;
+}
+
+/// Creates a new map from [map] with new keys and values.
+///
+/// The return values of [keyFn] are used as the keys and the return values of
+/// [valueFn] are used as the values for the new map.
+Map mapMap(Map map, keyFn(key, value), valueFn(key, value)) =>
+ listToMap(map.keys,
+ (key) => keyFn(key, map[key]),
+ (key) => valueFn(key, map[key]));
+
+/// Creates a new map from [map] with the same keys.
+///
+/// The return values of [valueFn] are used as the values for the new map.
+Map mapMapValues(Map map, fn(key, value)) => mapMap(map, (key, _) => key, fn);
+
+/// Returns the shortest path from [start] to [end] in [graph].
+///
+/// The graph is represented by a map whose keys are vertices and whose values
+/// are vertices reachable from the keys. [start] and [end] must be vertices in
+/// this graph.
+List shortestPath(Map<dynamic, Iterable> graph, start, end) {
+ assert(graph.containsKey(start));
+ assert(graph.containsKey(end));
+
+ // Dijkstra's algorithm.
+ var infinity = graph.length;
+ var distance = mapMapValues(graph, (_1, _2) => infinity);
+
+ // A map from each node to the node that came before it on the shortest path
+ // from it back to [start].
+ var previous = {};
+
+ distance[start] = 0;
+ var remaining = graph.keys.toSet();
+ while (!remaining.isEmpty) {
+ var current = minBy(remaining, (node) => distance[node]);
+ remaining.remove(current);
+
+ // If there's no remaining node that's reachable from [start], then there's
+ // no path from [start] to [end].
+ if (distance[current] == infinity) return null;
+
+ // If we've reached [end], we've found the shortest path to it and we just
+ // need to reconstruct that path.
+ if (current == end) break;
+
+ for (var neighbor in graph[current]) {
+ if (!remaining.contains(neighbor)) continue;
+ var newDistance = distance[current] + 1;
+ if (newDistance >= distance[neighbor]) continue;
+ distance[neighbor] = newDistance;
+ previous[neighbor] = current;
+ }
+ }
+
+ var path = new Queue();
+ var current = end;
+ while (current != null) {
+ path.addFirst(current);
+ current = previous[current];
+ }
+
+ return path.toList();
+}
+
/// Replace each instance of [matcher] in [source] with the return value of
/// [fn].
String replace(String source, Pattern matcher, String fn(Match)) {
diff --git a/sdk/lib/_internal/pub/test/pubspec_test.dart b/sdk/lib/_internal/pub/test/pubspec_test.dart
index 092611d..4404461 100644
--- a/sdk/lib/_internal/pub/test/pubspec_test.dart
+++ b/sdk/lib/_internal/pub/test/pubspec_test.dart
@@ -214,6 +214,15 @@
expectFormatError('{author: abe, authors: ted}');
});
+ test("throws if a transformer isn't a string or map", () {
+ expectFormatError('{transformers: 12}');
+ expectFormatError('{transformers: [12]}');
+ });
+
+ test("throws if a transformer's configuration isn't a map", () {
+ expectFormatError('{transformers: {pkg: 12}}');
+ });
+
test("allows comment-only files", () {
var pubspec = new Pubspec.parse(null, '''
# No external dependencies yet
diff --git a/sdk/lib/_internal/pub/test/serve/configuration_defaults_to_empty_map_test.dart b/sdk/lib/_internal/pub/test/serve/configuration_defaults_to_empty_map_test.dart
new file mode 100644
index 0000000..bad8e52
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/configuration_defaults_to_empty_map_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import 'dart:convert';
+
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+final transformer = """
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:barback/barback.dart';
+
+class ConfigTransformer extends Transformer {
+ final Map configuration;
+
+ ConfigTransformer.asPlugin(this.configuration);
+
+ String get allowedExtensions => '.txt';
+
+ Future apply(Transform transform) {
+ return transform.primaryInput.readAsString().then((contents) {
+ var id = transform.primaryInput.id.changeExtension(".json");
+ transform.addOutput(new Asset.fromString(id, JSON.encode(configuration)));
+ });
+ }
+}
+""";
+
+main() {
+ initConfig();
+ integration("configuration defaults to an empty map", () {
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["myapp/src/transformer"]
+ }),
+ d.dir("lib", [d.dir("src", [
+ d.file("transformer.dart", transformer)
+ ])]),
+ d.dir("web", [
+ d.file("foo.txt", "foo")
+ ])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ var server = startPubServe();
+ requestShouldSucceed("foo.json", JSON.encode({}));
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/detects_a_transformer_cycle.dart b/sdk/lib/_internal/pub/test/serve/detects_a_transformer_cycle.dart
new file mode 100644
index 0000000..1da3080
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/detects_a_transformer_cycle.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+ integration("detects a transformer cycle", () {
+ d.dir("foo", [
+ d.pubspec({
+ "name": "foo",
+ "version": "1.0.0",
+ "transformers": ["myapp/transformer"],
+ "dependencies": {'myapp': {'path': '../myapp'}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('foo')),
+ ])
+ ]).create();
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["foo/transformer"],
+ "dependencies": {'foo': {'path': '../foo'}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('myapp')),
+ ])
+ ]).create();
+
+ createLockFile('myapp', sandbox: ['foo'], pkg: ['barback']);
+
+ // Use port 0 to get an ephemeral port.
+ var process = startPub(args: ["serve", "--port=0"]);
+ process.shouldExit(1);
+ expect(process.remainingStderr(), completion(equals(
+ "Transformer cycle detected:\n"
+ " foo is transformed by myapp/transformer\n"
+ " myapp is transformed by foo/transformer")));
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/detects_an_ordering_dependency_cycle.dart b/sdk/lib/_internal/pub/test/serve/detects_an_ordering_dependency_cycle.dart
new file mode 100644
index 0000000..3316e8d
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/detects_an_ordering_dependency_cycle.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+ integration("detects an ordering dependency cycle", () {
+ d.dir("foo", [
+ d.pubspec({
+ "name": "foo",
+ "version": "1.0.0",
+ "transformers": ["myapp/transformer"],
+ "dependencies": {'myapp': {'path': '../myapp'}}
+ })
+ ]).create();
+
+ d.dir("bar", [
+ d.pubspec({
+ "name": "bar",
+ "version": "1.0.0",
+ "dependencies": {'foo': {'path': '../foo'}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('bar')),
+ ])
+ ]).create();
+
+ d.dir("baz", [
+ d.pubspec({
+ "name": "baz",
+ "version": "1.0.0",
+ "transformers": ["bar/transformer"],
+ "dependencies": {'bar': {'path': '../bar'}}
+ })
+ ]).create();
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "dependencies": {'baz': {'path': '../baz'}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('myapp')),
+ ])
+ ]).create();
+
+ createLockFile('myapp', sandbox: ['foo', 'bar', 'baz'], pkg: ['barback']);
+
+ // Use port 0 to get an ephemeral port.
+ var process = startPub(args: ["serve", "--port=0"]);
+ process.shouldExit(1);
+ expect(process.remainingStderr(), completion(equals(
+ "Transformer cycle detected:\n"
+ " bar depends on foo\n"
+ " foo is transformed by myapp/transformer\n"
+ " myapp depends on baz\n"
+ " baz is transformed by bar/transformer")));
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/does_not_run_a_transform_on_an_input_in_another_package_test.dart b/sdk/lib/_internal/pub/test/serve/does_not_run_a_transform_on_an_input_in_another_package_test.dart
index 3a39dd3..942a9e9 100644
--- a/sdk/lib/_internal/pub/test/serve/does_not_run_a_transform_on_an_input_in_another_package_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/does_not_run_a_transform_on_an_input_in_another_package_test.dart
@@ -32,9 +32,7 @@
])
]).create();
- createLockFile('myapp', {
- 'foo': '../foo'
- }, pkg: ['barback']);
+ createLockFile('myapp', sandbox: ['foo'], pkg: ['barback']);
startPubServe();
requestShould404("assets/myapp/bar.out");
diff --git a/sdk/lib/_internal/pub/test/serve/fails_to_load_a_file_that_defines_no_transforms_test.dart b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_file_that_defines_no_transforms_test.dart
index 3803c91..a636fcd 100644
--- a/sdk/lib/_internal/pub/test/serve/fails_to_load_a_file_that_defines_no_transforms_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_file_that_defines_no_transforms_test.dart
@@ -23,7 +23,7 @@
])
]).create();
- createLockFile('myapp', {}, pkg: ['barback']);
+ createLockFile('myapp', pkg: ['barback']);
var pub = startPub(args: ['serve', '--port=0']);
expect(pub.nextErrLine(), completion(startsWith('No transformers were '
diff --git a/sdk/lib/_internal/pub/test/serve/fails_to_load_a_pubspec_with_reserved_transformer_config_test.dart b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_pubspec_with_reserved_transformer_config_test.dart
new file mode 100644
index 0000000..5ba03d8
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_pubspec_with_reserved_transformer_config_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+
+ integration("fails to load a pubspec with reserved transformer config", () {
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": [{"myapp/src/transformer": {'include': 'something'}}]
+ }),
+ d.dir("lib", [d.dir("src", [
+ d.file("transformer.dart", REWRITE_TRANSFORMER)
+ ])])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ var pub = startPub(args: ['serve', '--port=0']);
+ expect(pub.nextErrLine(), completion(startsWith('Could not parse ')));
+ expect(pub.nextErrLine(), completion(equals('Configuration for '
+ 'transformer myapp/src/transformer may not include reserved key '
+ '"include".')));
+ pub.shouldExit(1);
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_a_syntax_error_test.dart b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_a_syntax_error_test.dart
index 38f96c1..b9b3073 100644
--- a/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_a_syntax_error_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_a_syntax_error_test.dart
@@ -25,7 +25,7 @@
])])
]).create();
- createLockFile('myapp', {}, pkg: ['barback']);
+ createLockFile('myapp', pkg: ['barback']);
var pub = startPub(args: ['serve', '--port=0']);
expect(pub.nextErrLine(), completion(startsWith('Error on line')));
diff --git a/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_an_import_error_test.dart b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_an_import_error_test.dart
index f663af0..0251159 100644
--- a/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_an_import_error_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/fails_to_load_a_transform_with_an_import_error_test.dart
@@ -25,7 +25,7 @@
])])
]).create();
- createLockFile('myapp', {}, pkg: ['barback']);
+ createLockFile('myapp', pkg: ['barback']);
var pub = startPub(args: ['serve', '--port=0']);
expect(pub.nextErrLine(), completion(matches(new RegExp(
diff --git a/sdk/lib/_internal/pub/test/serve/fails_to_load_an_unconfigurable_transformer_when_config_is_passed_test.dart b/sdk/lib/_internal/pub/test/serve/fails_to_load_an_unconfigurable_transformer_when_config_is_passed_test.dart
new file mode 100644
index 0000000..0403641
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/fails_to_load_an_unconfigurable_transformer_when_config_is_passed_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+
+ integration("fails to load an unconfigurable transformer when config is "
+ "passed", () {
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": [{"myapp/src/transformer": {'foo': 'bar'}}]
+ }),
+ d.dir("lib", [d.dir("src", [
+ d.file("transformer.dart", REWRITE_TRANSFORMER)
+ ])])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ var pub = startPub(args: ['serve', '--port=0']);
+ expect(pub.nextErrLine(), completion(startsWith('No transformers that '
+ 'accept configuration were defined in ')));
+ pub.shouldExit(1);
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/loads_a_diamond_transformer_dependency_graph_test.dart b/sdk/lib/_internal/pub/test/serve/loads_a_diamond_transformer_dependency_graph_test.dart
new file mode 100644
index 0000000..1a7330c
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/loads_a_diamond_transformer_dependency_graph_test.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+ integration("loads a diamond transformer dependency graph", () {
+ d.dir("top", [
+ d.pubspec({
+ "name": "top",
+ "version": "1.0.0"
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('top')),
+ ])
+ ]).create();
+
+ d.dir("left", [
+ d.pubspec({
+ "name": "left",
+ "version": "1.0.0",
+ "transformers": ["top/transformer"],
+ "dependencies": {"top": {"path": "../top"}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('left')),
+ ])
+ ]).create();
+
+ d.dir("right", [
+ d.pubspec({
+ "name": "right",
+ "version": "1.0.0",
+ "transformers": ["top/transformer"],
+ "dependencies": {"top": {"path": "../top"}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('right')),
+ ])
+ ]).create();
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": [
+ "left/transformer",
+ "right/transformer",
+ "myapp/transformer"
+ ],
+ "dependencies": {
+ 'left': {'path': '../left'},
+ 'right': {'path': '../right'},
+ }
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('myapp'))
+ ]),
+ d.dir("web", [
+ d.file("main.dart", 'const TOKEN = "main.dart";')
+ ])
+ ]).create();
+
+ createLockFile('myapp',
+ sandbox: ['top', 'left', 'right'],
+ pkg: ['barback']);
+
+ startPubServe();
+ requestShouldSucceed("main.dart",
+ 'const TOKEN = "(((main.dart, (left, top)), (right, top)), ((myapp, '
+ '(left, top)), (right, top)))";');
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/loads_ordering_dependencies_in_the_correct_order_test.dart b/sdk/lib/_internal/pub/test/serve/loads_ordering_dependencies_in_the_correct_order_test.dart
new file mode 100644
index 0000000..5695154
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/loads_ordering_dependencies_in_the_correct_order_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+ integration("loads ordering dependencies in the correct order", () {
+ d.dir("foo", [
+ d.libPubspec("foo", '1.0.0'),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('foo'))
+ ])
+ ]).create();
+
+ d.dir("bar", [
+ d.pubspec({
+ "name": "bar",
+ "version": "1.0.0",
+ "transformers": ["foo/transformer"],
+ "dependencies": {"foo": {"path": "../foo"}}
+ }),
+ d.dir("lib", [
+ d.file("bar.dart", 'const TOKEN = "bar";')
+ ])
+ ]).create();
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["myapp/transformer"],
+ "dependencies": {"bar": {"path": "../bar"}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('myapp', import: 'bar'))
+ ]),
+ d.dir("web", [
+ d.file("main.dart", 'const TOKEN = "main.dart";')
+ ])
+ ]).create();
+
+ createLockFile('myapp', sandbox: ['foo', 'bar'], pkg: ['barback']);
+
+ startPubServe();
+ requestShouldSucceed("main.dart",
+ 'const TOKEN = "(main.dart, myapp imports (bar, foo))";');
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/passes_configuration_to_a_transformer_test.dart b/sdk/lib/_internal/pub/test/serve/passes_configuration_to_a_transformer_test.dart
new file mode 100644
index 0000000..e3f68e1
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/passes_configuration_to_a_transformer_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import 'dart:convert';
+
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+final transformer = """
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:barback/barback.dart';
+
+class ConfigTransformer extends Transformer {
+ final Map configuration;
+
+ ConfigTransformer.asPlugin(this.configuration);
+
+ String get allowedExtensions => '.txt';
+
+ Future apply(Transform transform) {
+ return transform.primaryInput.readAsString().then((contents) {
+ var id = transform.primaryInput.id.changeExtension(".json");
+ transform.addOutput(new Asset.fromString(id, JSON.encode(configuration)));
+ });
+ }
+}
+""";
+
+main() {
+ initConfig();
+ integration("passes configuration to a transformer", () {
+ var configuration = {"param": ["list", "of", "values"]};
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": [{"myapp/src/transformer": configuration}]
+ }),
+ d.dir("lib", [d.dir("src", [
+ d.file("transformer.dart", transformer)
+ ])]),
+ d.dir("web", [
+ d.file("foo.txt", "foo")
+ ])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ var server = startPubServe();
+ requestShouldSucceed("foo.json", JSON.encode(configuration));
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/prints_a_transform_error_in_apply_test.dart b/sdk/lib/_internal/pub/test/serve/prints_a_transform_error_in_apply_test.dart
index e0b361d..2d90ee8 100644
--- a/sdk/lib/_internal/pub/test/serve/prints_a_transform_error_in_apply_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/prints_a_transform_error_in_apply_test.dart
@@ -16,7 +16,7 @@
import 'package:barback/barback.dart';
class RewriteTransformer extends Transformer {
- RewriteTransformer();
+ RewriteTransformer.asPlugin();
String get allowedExtensions => '.txt';
@@ -40,7 +40,7 @@
])
]).create();
- createLockFile('myapp', {}, pkg: ['barback']);
+ createLockFile('myapp', pkg: ['barback']);
var server = startPubServe();
expect(server.nextErrLine(),
diff --git a/sdk/lib/_internal/pub/test/serve/prints_a_transform_interface_error_test.dart b/sdk/lib/_internal/pub/test/serve/prints_a_transform_interface_error_test.dart
index 63aa5c1..9e35a46 100644
--- a/sdk/lib/_internal/pub/test/serve/prints_a_transform_interface_error_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/prints_a_transform_interface_error_test.dart
@@ -16,7 +16,7 @@
import 'package:barback/barback.dart';
class RewriteTransformer extends Transformer {
- RewriteTransformer();
+ RewriteTransformer.asPlugin();
String get allowedExtensions => '.txt';
}
@@ -38,7 +38,7 @@
])
]).create();
- createLockFile('myapp', {}, pkg: ['barback']);
+ createLockFile('myapp', pkg: ['barback']);
var server = startPubServe();
expect(server.nextErrLine(), completion(equals('Build error:')));
diff --git a/sdk/lib/_internal/pub/test/serve/runs_a_local_transform_on_the_application_package_test.dart b/sdk/lib/_internal/pub/test/serve/runs_a_local_transform_on_the_application_package_test.dart
index 0ae9397..e8fbd33 100644
--- a/sdk/lib/_internal/pub/test/serve/runs_a_local_transform_on_the_application_package_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/runs_a_local_transform_on_the_application_package_test.dart
@@ -24,7 +24,7 @@
])
]).create();
- createLockFile('myapp', {}, pkg: ['barback']);
+ createLockFile('myapp', pkg: ['barback']);
startPubServe();
requestShouldSucceed("foo.out", "foo.out");
diff --git a/sdk/lib/_internal/pub/test/serve/runs_a_third_party_transform_on_the_application_package_test.dart b/sdk/lib/_internal/pub/test/serve/runs_a_third_party_transform_on_the_application_package_test.dart
index 8185e56..ca88225 100644
--- a/sdk/lib/_internal/pub/test/serve/runs_a_third_party_transform_on_the_application_package_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/runs_a_third_party_transform_on_the_application_package_test.dart
@@ -29,9 +29,7 @@
])
]).create();
- createLockFile('myapp', {
- 'foo': '../foo'
- }, pkg: ['barback']);
+ createLockFile('myapp', sandbox: ['foo'], pkg: ['barback']);
startPubServe();
requestShouldSucceed("foo.out", "foo.out");
diff --git a/sdk/lib/_internal/pub/test/serve/runs_a_third_party_transformer_on_a_local_transformer_test.dart b/sdk/lib/_internal/pub/test/serve/runs_a_third_party_transformer_on_a_local_transformer_test.dart
new file mode 100644
index 0000000..b3073a8
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/runs_a_third_party_transformer_on_a_local_transformer_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+ integration("runs a third-party transformer on a local transformer", () {
+ d.dir("foo", [
+ d.libPubspec("foo", '1.0.0'),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('foo'))
+ ])
+ ]).create();
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["foo/transformer", "myapp/transformer"],
+ "dependencies": {"foo": {"path": "../foo"}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('myapp'))
+ ]),
+ d.dir("web", [
+ d.file("main.dart", 'const TOKEN = "main.dart";')
+ ])
+ ]).create();
+
+ createLockFile('myapp', sandbox: ['foo'], pkg: ['barback']);
+
+ startPubServe();
+ requestShouldSucceed("main.dart",
+ 'const TOKEN = "((main.dart, foo), (myapp, foo))";');
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/runs_a_transform_on_a_dependency_test.dart b/sdk/lib/_internal/pub/test/serve/runs_a_transform_on_a_dependency_test.dart
index 90f2990..80bff98 100644
--- a/sdk/lib/_internal/pub/test/serve/runs_a_transform_on_a_dependency_test.dart
+++ b/sdk/lib/_internal/pub/test/serve/runs_a_transform_on_a_dependency_test.dart
@@ -29,9 +29,7 @@
d.appPubspec({"foo": {"path": "../foo"}}),
]).create();
- createLockFile('myapp', {
- 'foo': '../foo'
- }, pkg: ['barback']);
+ createLockFile('myapp', sandbox: ['foo'], pkg: ['barback']);
startPubServe();
requestShouldSucceed("assets/foo/foo.out", "foo.out");
diff --git a/sdk/lib/_internal/pub/test/serve/runs_one_third_party_transformer_on_another_test.dart b/sdk/lib/_internal/pub/test/serve/runs_one_third_party_transformer_on_another_test.dart
new file mode 100644
index 0000000..96732e9
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/runs_one_third_party_transformer_on_another_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+ initConfig();
+ integration("runs one third-party transformer on another", () {
+ d.dir("foo", [
+ d.pubspec({
+ "name": "foo",
+ "version": "1.0.0"
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('foo')),
+ ])
+ ]).create();
+
+ d.dir("bar", [
+ d.pubspec({
+ "name": "bar",
+ "version": "1.0.0",
+ "transformers": ["foo/transformer"],
+ "dependencies": {"foo": {"path": "../foo"}}
+ }),
+ d.dir("lib", [
+ d.file("transformer.dart", dartTransformer('bar')),
+ ])
+ ]).create();
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": ["bar/transformer"],
+ "dependencies": {'bar': {'path': '../bar'}}
+ }),
+ d.dir("web", [
+ d.file("main.dart", 'const TOKEN = "main.dart";')
+ ])
+ ]).create();
+
+ createLockFile('myapp', sandbox: ['foo', 'bar'], pkg: ['barback']);
+
+ startPubServe();
+ requestShouldSucceed("main.dart",
+ 'const TOKEN = "(main.dart, (bar, foo))";');
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/utils.dart b/sdk/lib/_internal/pub/test/serve/utils.dart
index 5046638..46ace9d 100644
--- a/sdk/lib/_internal/pub/test/serve/utils.dart
+++ b/sdk/lib/_internal/pub/test/serve/utils.dart
@@ -26,7 +26,7 @@
import 'package:barback/barback.dart';
class RewriteTransformer extends Transformer {
- RewriteTransformer();
+ RewriteTransformer.asPlugin();
String get allowedExtensions => '.txt';
@@ -39,6 +39,52 @@
}
""";
+/// Returns the source code for a Dart library defining a Transformer that
+/// rewrites Dart files.
+///
+/// The transformer defines a constant named TOKEN whose value is [id]. When the
+/// transformer transforms another Dart file, it will look for a "TOKEN"
+/// constant definition there and modify it to include *this* transformer's
+/// TOKEN value as well.
+///
+/// If [import] is passed, it should be the name of a package that defines its
+/// own TOKEN constant. The primary library of that package will be imported
+/// here and its TOKEN value will be added to this library's.
+String dartTransformer(String id, {String import}) {
+ if (import != null) {
+ id = '$id imports \${$import.TOKEN}';
+ import = 'import "package:$import/$import.dart" as $import;';
+ } else {
+ import = '';
+ }
+
+ return """
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+$import
+
+const TOKEN = "$id";
+
+final _tokenRegExp = new RegExp(r'^const TOKEN = "(.*?)";\$', multiLine: true);
+
+class DartTransformer extends Transformer {
+ DartTransformer.asPlugin();
+
+ String get allowedExtensions => '.dart';
+
+ Future apply(Transform transform) {
+ return transform.primaryInput.readAsString().then((contents) {
+ transform.addOutput(new Asset.fromString(transform.primaryInput.id,
+ contents.replaceAllMapped(_tokenRegExp, (match) {
+ return 'const TOKEN = "(\${match[1]}, \$TOKEN)";';
+ })));
+ });
+ }
+}
+""";
+}
+
/// Schedules starting the "pub serve" process.
///
/// If [shouldInstallFirst] is `true`, validates that pub install is run first.
diff --git a/sdk/lib/_internal/pub/test/serve/with_configuration_only_instantiates_configurable_transformers_test.dart b/sdk/lib/_internal/pub/test/serve/with_configuration_only_instantiates_configurable_transformers_test.dart
new file mode 100644
index 0000000..6c010bf
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/with_configuration_only_instantiates_configurable_transformers_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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.
+
+library pub_tests;
+
+import 'dart:convert';
+
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+final transformer = """
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:barback/barback.dart';
+
+class ConfigTransformer extends Transformer {
+ final Map configuration;
+
+ ConfigTransformer.asPlugin(this.configuration);
+
+ String get allowedExtensions => '.txt';
+
+ Future apply(Transform transform) {
+ return transform.primaryInput.readAsString().then((contents) {
+ var id = transform.primaryInput.id.changeExtension(".json");
+ transform.addOutput(new Asset.fromString(id, JSON.encode(configuration)));
+ });
+ }
+}
+
+class RewriteTransformer extends Transformer {
+ RewriteTransformer.asPlugin();
+
+ String get allowedExtensions => '.txt';
+
+ Future apply(Transform transform) {
+ return transform.primaryInput.readAsString().then((contents) {
+ var id = transform.primaryInput.id.changeExtension(".out");
+ transform.addOutput(new Asset.fromString(id, "\$contents.out"));
+ });
+ }
+}
+""";
+
+main() {
+ initConfig();
+ integration("with configuration, only instantiates configurable transformers",
+ () {
+ var configuration = {"param": ["list", "of", "values"]};
+
+ d.dir(appPath, [
+ d.pubspec({
+ "name": "myapp",
+ "transformers": [{"myapp/src/transformer": configuration}]
+ }),
+ d.dir("lib", [d.dir("src", [
+ d.file("transformer.dart", transformer)
+ ])]),
+ d.dir("web", [
+ d.file("foo.txt", "foo")
+ ])
+ ]).create();
+
+ createLockFile('myapp', pkg: ['barback']);
+
+ var server = startPubServe();
+ requestShouldSucceed("foo.json", JSON.encode(configuration));
+ requestShould404("foo.out");
+ endPubServe();
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/test_pub.dart b/sdk/lib/_internal/pub/test/test_pub.dart
index 64c1682..897b69e 100644
--- a/sdk/lib/_internal/pub/test/test_pub.dart
+++ b/sdk/lib/_internal/pub/test/test_pub.dart
@@ -607,12 +607,20 @@
/// Create a lock file for [package] without running `pub install`.
///
-/// This creates a lock file with only path dependencies. [dependencies] is a
-/// map of dependency names to paths. [pkg] is a list of packages in the Dart
-/// repo's "pkg" directory; each package listed here and all its dependencies
-/// will be linked to the version in the Dart repo.
-void createLockFile(String package, Map<String, String> dependencies,
- {Iterable<String> pkg}) {
+/// This creates a lock file with only path dependencies. [sandbox] is a list of
+/// dependencies to be found in the sandbox directory. [pkg] is a list of
+/// packages in the Dart repo's "pkg" directory; each package listed here and
+/// all its dependencies will be linked to the version in the Dart repo.
+void createLockFile(String package, {Iterable<String> sandbox,
+ Iterable<String> pkg}) {
+ var dependencies = {};
+
+ if (sandbox != null) {
+ for (var package in sandbox) {
+ dependencies[package] = '../$package';
+ }
+ }
+
if (pkg != null) {
var pkgDir = path.absolute(path.join(
path.dirname(Platform.executable),
diff --git a/sdk/lib/async/broadcast_stream_controller.dart b/sdk/lib/async/broadcast_stream_controller.dart
index fb72872..aa5b111 100644
--- a/sdk/lib/async/broadcast_stream_controller.dart
+++ b/sdk/lib/async/broadcast_stream_controller.dart
@@ -107,7 +107,7 @@
* Any attempt to listen after calling [close] will throw, so there won't
* be any further listeners.
*/
- _FutureImpl _doneFuture;
+ _Future _doneFuture;
_BroadcastStreamController(this._onListen, this._onCancel)
: _state = _STATE_INITIAL {
@@ -140,9 +140,9 @@
bool get _mayAddEvent => (_state < _STATE_CLOSED);
- _FutureImpl _ensureDoneFuture() {
+ _Future _ensureDoneFuture() {
if (_doneFuture != null) return _doneFuture;
- return _doneFuture = new _FutureImpl();
+ return _doneFuture = new _Future();
}
// Linked list helpers
@@ -314,7 +314,7 @@
assert(_isEmpty);
if (isClosed && _doneFuture._mayComplete) {
// When closed, _doneFuture is not null.
- _doneFuture._asyncSetValue(null);
+ _doneFuture._asyncComplete(null);
}
_runGuarded(_onCancel);
}
@@ -348,7 +348,7 @@
} else {
assert(_doneFuture != null);
assert(_doneFuture._mayComplete);
- _doneFuture._asyncSetValue(null);
+ _doneFuture._asyncComplete(null);
}
}
}
@@ -388,7 +388,7 @@
} else {
assert(_doneFuture != null);
assert(_doneFuture._mayComplete);
- _doneFuture._asyncSetValue(null);
+ _doneFuture._asyncComplete(null);
}
}
}
@@ -480,5 +480,5 @@
}
void cancel() {}
bool get isPaused => _pauseCount > 0;
- Future asFuture([Object value]) => new _FutureImpl();
+ Future asFuture([Object value]) => new _Future();
}
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 4904415..ddeec59 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -100,18 +100,23 @@
* If a value is returned, it becomes the result of the created future.
*/
factory Future(computation()) {
- _ThenFuture<dynamic, T> future =
- new _ThenFuture<dynamic, T>((_) => computation());
- Timer.run(() => future._sendValue(null));
- return future;
+ _Future result = new _Future<T>();
+ Timer.run(() {
+ try {
+ result._complete(computation());
+ } catch (e, s) {
+ result._completeError(e, s);
+ }
+ });
+ return result;
}
/**
* Creates a future containing the result of immediately calling
* [computation].
*
- * if the result of executing [computation] throws, the returned future is
- * completed with the error. If a thrown value is an [AsyncError], it is used
+ * If calling [computation] throws, the returned future is completed with the
+ * error. If a thrown value is an [AsyncError], it is used
* directly, instead of wrapping this error again in another [AsyncError].
*
* If the returned value is itself a [Future], completion of
@@ -121,9 +126,9 @@
factory Future.sync(computation()) {
try {
var result = computation();
- return new _FutureImpl<T>().._setOrChainValue(result);
+ return new Future<T>.value(result);
} catch (error, stackTrace) {
- return new _FutureImpl<T>.immediateError(error, stackTrace);
+ return new Future<T>.error(error, stackTrace);
}
}
@@ -135,7 +140,9 @@
*
* See [Completer] to create a Future and complete it later.
*/
- factory Future.value([T value]) => new _FutureImpl<T>.immediate(value);
+ factory Future.value([T value]) {
+ return new _Future<T>.immediate(value);
+ }
/**
* A future that completes with an error in the next event-loop iteration.
@@ -143,7 +150,7 @@
* See [Completer] to create a Future and complete it later.
*/
factory Future.error(var error, [Object stackTrace]) {
- return new _FutureImpl<T>.immediateError(error, stackTrace);
+ return new _Future<T>.immediateError(error, stackTrace);
}
/**
@@ -163,13 +170,13 @@
* See [Completer]s, for futures with values that are computed asynchronously.
*/
factory Future.delayed(Duration duration, [T computation()]) {
- // TODO(floitsch): no need to allocate a ThenFuture when the computation is
- // null.
- if (computation == null) computation = (() => null);
- _ThenFuture<dynamic, T> future =
- new _ThenFuture<dynamic, T>((_) => computation());
- new Timer(duration, () => future._sendValue(null));
- return future;
+ Completer completer = new Completer.sync();
+ Future result = completer.future;
+ if (computation != null) {
+ result = result.then((ignored) => computation());
+ }
+ new Timer(duration, () { completer.complete(null); });
+ return result;
}
/**
@@ -181,7 +188,36 @@
* of the returned future will be a list of all the values that were produced.
*/
static Future<List> wait(Iterable<Future> futures) {
- return new _FutureImpl<List>.wait(futures);
+ Completer completer;
+ // List collecting values from the futures.
+ // Set to null if an error occurs.
+ List values;
+ void handleError(error) {
+ if (values != null) {
+ values = null;
+ completer.completeError(error);
+ }
+ }
+ // As each future completes, put its value into the corresponding
+ // position in the list of values.
+ int remaining = 0;
+ for (Future future in futures) {
+ int pos = remaining++;
+ future.catchError(handleError).then((Object value) {
+ if (values == null) return null;
+ values[pos] = value;
+ remaining--;
+ if (remaining == 0) {
+ completer.complete(values);
+ }
+ });
+ }
+ if (remaining == 0) {
+ return new Future.value(const []);
+ }
+ values = new List(remaining);
+ completer = new Completer<List>();
+ return completer.future;
}
/**
@@ -195,14 +231,14 @@
* iteration to stop and will be piped through the returned [Future].
*/
static Future forEach(Iterable input, Future f(element)) {
- _FutureImpl doneSignal = new _FutureImpl();
+ _Future doneSignal = new _Future();
Iterator iterator = input.iterator;
void nextElement(_) {
if (iterator.moveNext()) {
new Future.sync(() => f(iterator.current))
- .then(nextElement, onError: doneSignal._setError);
+ .then(nextElement, onError: doneSignal._completeError);
} else {
- doneSignal._setValue(null);
+ doneSignal._complete(null);
}
}
nextElement(null);
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 4c846c6..ea8f902 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -4,155 +4,53 @@
part of dart.async;
+/** The onValue and onError handlers return either a value or a future */
+typedef dynamic _FutureOnValue<T>(T value);
+typedef dynamic _FutureOnError(error);
+/** Test used by [Future.catchError] to handle skip some errors. */
+typedef bool _FutureErrorTest(var error);
+/** Used by [WhenFuture]. */
+typedef _FutureAction();
+
abstract class _Completer<T> implements Completer<T> {
- final Future<T> future;
- bool _isComplete = false;
+ final _Future<T> future = new _Future<T>();
- _Completer() : future = new _FutureImpl<T>() {
- _FutureImpl futureImpl = future;
- futureImpl._zone.expectCallback();
- }
+ void complete([T value]);
- void _setFutureValue(T value);
- void _setFutureError(error);
+ void completeError(Object error, [Object stackTrace = null]);
- void complete([T value]) {
- if (_isComplete) throw new StateError("Future already completed");
- _isComplete = true;
- _FutureImpl futureImpl = future;
- _setFutureValue(value);
- }
-
- void completeError(Object error, [Object stackTrace = null]) {
- if (_isComplete) throw new StateError("Future already completed");
- _isComplete = true;
- if (stackTrace != null) {
- // Force the stack trace onto the error, even if it already had one.
- _attachStackTrace(error, stackTrace);
- }
- _FutureImpl futureImpl = future;
- _setFutureError(error);
- }
-
- bool get isCompleted => _isComplete;
+ // The future's _isComplete doesn't take into account pending completions.
+ // We therefore use _mayComplete.
+ bool get isCompleted => !future._mayComplete;
}
class _AsyncCompleter<T> extends _Completer<T> {
- void _setFutureValue(T value) {
- _FutureImpl future = this.future;
- future._asyncSetValue(value);
- // The async-error will schedule another callback, so we can cancel
- // the expectation without shutting down the zone.
- future._zone.cancelCallbackExpectation();
+
+ void complete([T value]) {
+ if (!future._mayComplete) throw new StateError("Future already completed");
+ future._asyncComplete(value);
}
- void _setFutureError(error) {
- _FutureImpl future = this.future;
- future._asyncSetError(error);
- // The async-error will schedule another callback, so we can cancel
- // the expectation without shutting down the zone.
- future._zone.cancelCallbackExpectation();
+ void completeError(Object error, [Object stackTrace = null]) {
+ if (!future._mayComplete) throw new StateError("Future already completed");
+ future._asyncCompleteError(error, stackTrace);
}
}
class _SyncCompleter<T> extends _Completer<T> {
- void _setFutureValue(T value) {
- _FutureImpl future = this.future;
- future._setValue(value);
- future._zone.cancelCallbackExpectation();
+
+ void complete([T value]) {
+ if (!future._mayComplete) throw new StateError("Future already completed");
+ future._complete(value);
}
- void _setFutureError(error) {
- _FutureImpl future = this.future;
- future._setError(error);
- future._zone.cancelCallbackExpectation();
+ void completeError(Object error, [Object stackTrace = null]) {
+ if (!future._mayComplete) throw new StateError("Future already completed");
+ future._completeError(error, stackTrace);
}
}
-/**
- * A listener on a future.
- *
- * When the future completes, the [_sendValue] or [_sendError] method
- * is invoked with the result.
- *
- * Listeners are kept in a linked list.
- */
-abstract class _FutureListener<T> {
- _FutureListener _nextListener;
- factory _FutureListener.wrap(_FutureImpl future) {
- return new _FutureListenerWrapper(future);
- }
- void _sendValue(T value);
- void _sendError(error);
-
- bool _inSameErrorZone(_Zone otherZone);
-}
-
-/** Adapter for a [_FutureImpl] to be a future result listener. */
-class _FutureListenerWrapper<T> implements _FutureListener<T> {
- _FutureImpl future;
- _FutureListener _nextListener;
- _FutureListenerWrapper(this.future);
- _sendValue(T value) { future._setValueUnchecked(value); }
- _sendError(error) { future._setErrorUnchecked(error); }
- bool _inSameErrorZone(_Zone otherZone) => future._inSameErrorZone(otherZone);
-}
-
-/**
- * This listener is installed at error-zone boundaries. It signals an
- * uncaught error in the zone of origin when an error is sent from one error
- * zone to another.
- *
- * When a Future is listening to another Future and they have not been
- * instantiated in the same error-zone then Futures put an instance of this
- * class between them (see [_FutureImpl._addListener]).
- *
- * For example:
- *
- * var completer = new Completer();
- * var future = completer.future.then((x) => x);
- * catchErrors(() {
- * var future2 = future.catchError(print);
- * });
- * completer.completeError(499);
- *
- * In this example `future` and `future2` are in different error-zones. The
- * error (499) that originates outside `catchErrors` must not reach the
- * `catchError` future (`future2`) inside `catchErrors`.
- *
- * When invoking `catchError` on `future` the Future installs an
- * [_ErrorZoneBoundaryListener] between itself and the result, `future2`.
- *
- * Conceptually _ErrorZoneBoundaryListeners could be implemented as
- * `catchError`s on the origin future as well.
- */
-class _ErrorZoneBoundaryListener implements _FutureListener {
- _FutureListener _nextListener;
- final _FutureListener _listener;
-
- _ErrorZoneBoundaryListener(this._listener);
-
- bool _inSameErrorZone(_Zone otherZone) {
- // Should never be called. We use [_inSameErrorZone] to know if we have
- // to insert an instance of [_ErrorZoneBoundaryListener] (and in the
- // controller). Once we have inserted one we should never need to use it
- // anymore.
- throw new UnsupportedError(
- "A Zone boundary doesn't support the inSameErrorZone test.");
- }
-
- void _sendValue(value) {
- _listener._sendValue(value);
- }
-
- void _sendError(error) {
- // We are not allowed to send an error from one error-zone to another.
- // This is the whole purpose of this class.
- _Zone.current.handleUncaughtError(error);
- }
-}
-
-class _FutureImpl<T> implements Future<T> {
+class _Future<T> implements Future<T> {
// State of the future. The state determines the interpretation of the
// [resultOrListeners] field.
// TODO(lrn): rename field since it can also contain a chained future.
@@ -161,35 +59,40 @@
/// [resultOrListeners] field holds a single-linked list of
/// [FutureListener] listeners.
static const int _INCOMPLETE = 0;
- /// Pending completion. Set when completed using [_asyncSetValue] or
- /// [_asyncSetError]. It is an error to try to complete it again.
+ /// Pending completion. Set when completed using [_asyncComplete] or
+ /// [_asyncCompleteError]. It is an error to try to complete it again.
static const int _PENDING_COMPLETE = 1;
/// The future has been chained to another future. The result of that
/// other future becomes the result of this future as well.
- /// In this state, the [resultOrListeners] field holds the future that
- /// will give the result to this future. Both existing and new listeners are
- /// forwarded directly to the other future.
+ /// In this state, no callback should be executed anymore.
+ // TODO(floitsch): we don't really need a special "_CHAINED" state. We could
+ // just use the PENDING_COMPLETE state instead.
static const int _CHAINED = 2;
- /// The future has been chained to another future, but there hasn't been
- /// any listeners added to this future yet. If it is completed with an
- /// error, the error will be considered unhandled.
- static const int _CHAINED_UNLISTENED = 6;
/// The future has been completed with a value result.
- static const int _VALUE = 8;
+ static const int _VALUE = 4;
/// The future has been completed with an error result.
- static const int _ERROR = 12;
+ static const int _ERROR = 8;
/** Whether the future is complete, and as what. */
int _state = _INCOMPLETE;
final _Zone _zone = _Zone.current.fork();
- bool get _isChained => (_state & _CHAINED) != 0;
- bool get _hasChainedListener => _state == _CHAINED;
- bool get _isComplete => _state >= _VALUE;
bool get _mayComplete => _state == _INCOMPLETE;
+ bool get _isChained => _state == _CHAINED;
+ bool get _isComplete => _state >= _VALUE;
bool get _hasValue => _state == _VALUE;
- bool get _hasError => _state >= _ERROR;
+ bool get _hasError => _state == _ERROR;
+
+ set _isChained(bool value) {
+ if (value) {
+ assert(!_isComplete);
+ _state = _CHAINED;
+ } else {
+ assert(_isChained);
+ _state = _INCOMPLETE;
+ }
+ }
/**
* Either the result, a list of listeners or another future.
@@ -212,175 +115,132 @@
*/
var _resultOrListeners;
- _FutureImpl();
+ /**
+ * A [_Future] implements a linked list. If a future has more than one
+ * listener the [_nextListener] field of the first listener points to the
+ * remaining listeners.
+ */
+ // TODO(floitsch): since single listeners are the common case we should
+ // use a bit to indicate that the _resultOrListeners contains a container.
+ _Future _nextListener;
- _FutureImpl.immediate(T value) {
- _state = _VALUE;
- _resultOrListeners = value;
+ // TODO(floitsch): we only need two closure fields to store the callbacks.
+ // If we store the type of a closure in the state field (where there are
+ // still bits left), we can just store two closures instead of using 4
+ // fields of which 2 are always null.
+ final _FutureOnValue _onValueCallback;
+ final _FutureErrorTest _errorTestCallback;
+ final _FutureOnError _onErrorCallback;
+ final _FutureAction _whenCompleteActionCallback;
+
+ _FutureOnValue get _onValue => _isChained ? null : _onValueCallback;
+ _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback;
+ _FutureOnError get _onError => _isChained ? null : _onErrorCallback;
+ _FutureAction get _whenCompleteAction
+ => _isChained ? null : _whenCompleteActionCallback;
+
+ _Future()
+ : _onValueCallback = null, _errorTestCallback = null,
+ _onErrorCallback = null, _whenCompleteActionCallback = null;
+
+ _Future.immediate(T value)
+ : _onValueCallback = null, _errorTestCallback = null,
+ _onErrorCallback = null, _whenCompleteActionCallback = null {
+ _asyncComplete(value);
}
- _FutureImpl.immediateError(var error, [Object stackTrace]) {
- if (stackTrace != null) {
- // Force stack trace onto error, even if it had already one.
- _attachStackTrace(error, stackTrace);
- }
- _asyncSetError(error);
+ _Future.immediateError(var error, [Object stackTrace])
+ : _onValueCallback = null, _errorTestCallback = null,
+ _onErrorCallback = null, _whenCompleteActionCallback = null {
+ _asyncCompleteError(error, stackTrace);
}
- factory _FutureImpl.wait(Iterable<Future> futures) {
- Completer completer;
- // List collecting values from the futures.
- // Set to null if an error occurs.
- List values;
- void handleError(error) {
- if (values != null) {
- values = null;
- completer.completeError(error);
- }
- }
- // As each future completes, put its value into the corresponding
- // position in the list of values.
- int remaining = 0;
- for (Future future in futures) {
- int pos = remaining++;
- future.catchError(handleError).then((Object value) {
- if (values == null) return null;
- values[pos] = value;
- remaining--;
- if (remaining == 0) {
- completer.complete(values);
- }
- });
- }
- if (remaining == 0) {
- return new Future.value(const []);
- }
- values = new List(remaining);
- completer = new Completer<List>();
- return completer.future;
+ _Future._then(this._onValueCallback, this._onErrorCallback)
+ : _errorTestCallback = null, _whenCompleteActionCallback = null {
+ _zone.expectCallback();
+ }
+
+ _Future._catchError(this._onErrorCallback, this._errorTestCallback)
+ : _onValueCallback = null, _whenCompleteActionCallback = null {
+ _zone.expectCallback();
+ }
+
+ _Future._whenComplete(this._whenCompleteActionCallback)
+ : _onValueCallback = null, _errorTestCallback = null,
+ _onErrorCallback = null {
+ _zone.expectCallback();
}
Future then(f(T value), { onError(error) }) {
- if (onError == null) {
- return new _ThenFuture(f).._subscribeTo(this);
- }
- return new _SubscribeFuture(f, onError).._subscribeTo(this);
+ _Future result;
+ result = new _Future._then(f, onError);
+ _addListener(result);
+ return result;
}
Future catchError(f(error), { bool test(error) }) {
- return new _CatchErrorFuture(f, test).._subscribeTo(this);
+ _Future result = new _Future._catchError(f, test);
+ _addListener(result);
+ return result;
}
Future<T> whenComplete(action()) {
- return new _WhenFuture<T>(action).._subscribeTo(this);
+ _Future result = new _Future<T>._whenComplete(action);
+ _addListener(result);
+ return result;
}
Stream<T> asStream() => new Stream.fromFuture(this);
- bool _inSameErrorZone(_Zone otherZone) {
- return _zone.inSameErrorZone(otherZone);
+ void _markPendingCompletion() {
+ if (!_mayComplete) throw new StateError("Future already completed");
+ _state = _PENDING_COMPLETE;
+ }
+
+ T get _value {
+ assert(_isComplete && _hasValue);
+ return _resultOrListeners;
+ }
+
+ Object get _error {
+ assert(_isComplete && _hasError);
+ return _resultOrListeners;
}
void _setValue(T value) {
- if (!_mayComplete) throw new StateError("Future already completed");
- _setValueUnchecked(value);
- }
-
- void _setValueUnchecked(T value) {
- _FutureListener listeners = _isChained ? null : _removeListeners();
+ assert(!_isComplete); // But may have a completion pending.
_state = _VALUE;
_resultOrListeners = value;
- while (listeners != null) {
- _FutureListener listener = listeners;
- listeners = listener._nextListener;
- listener._nextListener = null;
- listener._sendValue(value);
- }
}
void _setError(Object error) {
- if (!_mayComplete) throw new StateError("Future already completed");
- _setErrorUnchecked(error);
- }
-
- void _setErrorUnchecked(Object error) {
- _FutureListener listeners;
- bool hasListeners;
- if (_isChained) {
- listeners = null;
- hasListeners = (_state == _CHAINED); // and not _CHAINED_UNLISTENED.
- } else {
- listeners = _removeListeners();
- hasListeners = (listeners != null);
- }
-
+ assert(!_isComplete); // But may have a completion pending.
_state = _ERROR;
_resultOrListeners = error;
-
- if (!hasListeners) {
- // TODO(floitsch): Hook this into unhandled error handling.
- var error = _resultOrListeners;
- _zone.handleUncaughtError(error);
- return;
- }
- while (listeners != null) {
- _FutureListener listener = listeners;
- listeners = listener._nextListener;
- listener._nextListener = null;
- listener._sendError(error);
- }
}
- void _asyncSetValue(T value) {
- if (!_mayComplete) throw new StateError("Future already completed");
- _state = _PENDING_COMPLETE;
- runAsync(() { _setValueUnchecked(value); });
- }
-
- void _asyncSetError(Object error) {
- if (!_mayComplete) throw new StateError("Future already completed");
- _state = _PENDING_COMPLETE;
- runAsync(() { _setErrorUnchecked(error); });
- }
-
- void _addListener(_FutureListener listener) {
+ void _addListener(_Future listener) {
assert(listener._nextListener == null);
- if (!listener._inSameErrorZone(_zone)) {
- listener = new _ErrorZoneBoundaryListener(listener);
- }
- if (_isChained) {
- _state = _CHAINED; // In case it was _CHAINED_UNLISTENED.
- _FutureImpl resultSource = _chainSource;
- resultSource._addListener(listener);
- return;
- }
if (_isComplete) {
// Handle late listeners asynchronously.
runAsync(() {
- if (_hasValue) {
- T value = _resultOrListeners;
- listener._sendValue(value);
- } else {
- assert(_hasError);
- listener._sendError(_resultOrListeners);
- }
+ _propagateToListeners(this, listener);
});
} else {
- assert(!_isComplete);
listener._nextListener = _resultOrListeners;
_resultOrListeners = listener;
}
}
- _FutureListener _removeListeners() {
+ _Future _removeListeners() {
// Reverse listeners before returning them, so the resulting list is in
// subscription order.
assert(!_isComplete);
- _FutureListener current = _resultOrListeners;
+ _Future current = _resultOrListeners;
_resultOrListeners = null;
- _FutureListener prev = null;
+ _Future prev = null;
while (current != null) {
- _FutureListener next = current._nextListener;
+ _Future next = current._nextListener;
current._nextListener = prev;
prev = current;
current = next;
@@ -388,270 +248,268 @@
return prev;
}
- /**
- * Make another [_FutureImpl] receive the result of this one.
- *
- * If this future is already complete, the [future] is notified
- * immediately. This function is only called during event resolution
- * where it's acceptable to send an event.
- */
- void _chain(_FutureImpl future) {
- if (!_isComplete) {
- future._chainFromFuture(this);
- } else if (_hasValue) {
- future._setValue(_resultOrListeners);
- } else {
- assert(_hasError);
- future._setError(_resultOrListeners);
- }
- }
+ static void _chainFutures(Future source, _Future target) {
+ assert(!target._isComplete);
- /**
- * Returns the future that this future is chained to.
- *
- * If that future is itself chained to something else,
- * get the [_chainSource] of that future instead, and make this
- * future chain directly to the earliest source.
- */
- _FutureImpl get _chainSource {
- assert(_isChained);
- _FutureImpl future = _resultOrListeners;
- if (future._isChained) {
- future = _resultOrListeners = future._chainSource;
- }
- return future;
- }
-
- /**
- * Make this incomplete future end up with the same result as [resultSource].
- *
- * This is done by moving all listeners to [resultSource] and forwarding all
- * future [_addListener] calls to [resultSource] directly.
- */
- void _chainFromFuture(_FutureImpl resultSource) {
- assert(!_isComplete);
- assert(!_isChained);
- if (resultSource._isChained) {
- resultSource = resultSource._chainSource;
- }
- assert(!resultSource._isChained);
- if (identical(this, resultSource)) {
- // The only unchained future in a future dependency tree (as defined
- // by the chain-relations) is the "root" that every other future depends
- // on. The future we are adding is unchained, so if it is already in the
- // tree, it must be the root, so that's the only one we need to check
- // against to detect a cycle.
- _setError(new StateError("Cyclic future dependency."));
- return;
- }
- _FutureListener cursor = _removeListeners();
- bool hadListeners = cursor != null;
- while (cursor != null) {
- _FutureListener listener = cursor;
- cursor = cursor._nextListener;
- listener._nextListener = null;
- resultSource._addListener(listener);
- }
- // Listen with this future as well, so that when the other future completes,
- // this future will be completed as well.
- resultSource._addListener(this._asListener());
- _resultOrListeners = resultSource;
- _state = hadListeners ? _CHAINED : _CHAINED_UNLISTENED;
- }
-
- /**
- * Helper function to handle the result of transforming an incoming event.
- *
- * If the result is itself a [Future], this future is linked to that
- * future's output. If not, this future is completed with the result.
- */
- void _setOrChainValue(var result) {
- assert(!_isChained);
- assert(!_isComplete);
- if (result is Future) {
- // Result should be a Future<T>.
- if (result is _FutureImpl) {
- _FutureImpl chainFuture = result;
- chainFuture._chain(this);
- return;
+ // Mark the target as chained (and as such half-completed).
+ target._isChained = true;
+ if (source is _Future) {
+ _Future internalFuture = source;
+ if (internalFuture._isComplete) {
+ _propagateToListeners(internalFuture, target);
} else {
- Future future = result;
- future.then(_setValue,
- onError: _setError);
- return;
+ internalFuture._addListener(target);
}
} else {
- // Result must be of type T.
- _setValue(result);
+ source.then((value) {
+ assert(target._isChained);
+ target._complete(value);
+ },
+ onError: (error) {
+ assert(target._isChained);
+ target._completeError(error);
+ });
}
}
- _FutureListener _asListener() => new _FutureListener.wrap(this);
-}
+ void _complete(value) {
+ assert(!_isComplete);
+ assert(_onValue == null);
+ assert(_onError == null);
+ assert(_whenCompleteAction == null);
+ assert(_errorTest == null);
-/**
- * Transforming future base class.
- *
- * A transforming future is itself a future and a future listener.
- * Subclasses override [_sendValue]/[_sendError] to intercept
- * the results of a previous future.
- */
-abstract class _TransformFuture<S, T> extends _FutureImpl<T>
- implements _FutureListener<S> {
- // _FutureListener implementation.
- _FutureListener _nextListener;
-
- _TransformFuture() {
- _zone.expectCallback();
- }
-
- void _sendValue(S value) {
- _zone.executeCallback(() => _zonedSendValue(value));
- }
-
- void _sendError(error) {
- _zone.executeCallback(() => _zonedSendError(error));
- }
-
- void _subscribeTo(_FutureImpl future) {
- future._addListener(this);
- }
-
- void _zonedSendValue(S value);
- void _zonedSendError(error);
-}
-
-/** The onValue and onError handlers return either a value or a future */
-typedef dynamic _FutureOnValue<T>(T value);
-typedef dynamic _FutureOnError(error);
-/** Test used by [Future.catchError] to handle skip some errors. */
-typedef bool _FutureErrorTest(var error);
-/** Used by [WhenFuture]. */
-typedef _FutureAction();
-
-/** Future returned by [Future.then] with no [:onError:] parameter. */
-class _ThenFuture<S, T> extends _TransformFuture<S, T> {
- // TODO(ahe): Restore type when feature is implemented in dart2js
- // checked mode.
- final /* _FutureOnValue<S> */ _onValue;
-
- _ThenFuture(this._onValue);
-
- _zonedSendValue(S value) {
- assert(_onValue != null);
- var result;
- try {
- result = _onValue(value);
- } catch (e, s) {
- _setError(_asyncError(e, s));
+ if (value is Future) {
+ _chainFutures(value, this);
return;
}
- _setOrChainValue(result);
- }
-
- void _zonedSendError(error) {
- _setError(error);
- }
-}
-
-/** Future returned by [Future.catchError]. */
-class _CatchErrorFuture<T> extends _TransformFuture<T,T> {
- final _FutureErrorTest _test;
- final _FutureOnError _onError;
-
- _CatchErrorFuture(this._onError, this._test);
-
- _zonedSendValue(T value) {
+ _Future listeners = _removeListeners();
_setValue(value);
+ _propagateToListeners(this, listeners);
}
- _zonedSendError(error) {
- assert(_onError != null);
- // if _test is supplied, check if it returns true, otherwise just
- // forward the error unmodified.
- if (_test != null) {
- bool matchesTest;
- try {
- matchesTest = _test(error);
- } catch (e, s) {
- _setError(_asyncError(e, s));
- return;
- }
- if (!matchesTest) {
- _setError(error);
- return;
- }
+ void _completeError(error, [StackTrace stackTrace]) {
+ assert(!_isComplete);
+ assert(_onValue == null);
+ assert(_onError == null);
+ assert(_whenCompleteAction == null);
+ assert(_errorTest == null);
+
+ if (stackTrace != null) {
+ // Force the stack trace onto the error, even if it already had one.
+ _attachStackTrace(error, stackTrace);
}
- // Act on the error, and use the result as this future's result.
- var result;
- try {
- result = _onError(error);
- } catch (e, s) {
- _setError(_asyncError(e, s));
- return;
- }
- _setOrChainValue(result);
- }
-}
-/** Future returned by [Future.then] with an [:onError:] parameter. */
-class _SubscribeFuture<S, T> extends _ThenFuture<S, T> {
- final _FutureOnError _onError;
-
- _SubscribeFuture(onValue(S value), this._onError) : super(onValue);
-
- // The _sendValue method is inherited from ThenFuture.
-
- void _zonedSendError(error) {
- assert(_onError != null);
- var result;
- try {
- result = _onError(error);
- } catch (e, s) {
- _setError(_asyncError(e, s));
- return;
- }
- _setOrChainValue(result);
- }
-}
-
-/** Future returned by [Future.whenComplete]. */
-class _WhenFuture<T> extends _TransformFuture<T, T> {
- final _FutureAction _action;
-
- _WhenFuture(this._action);
-
- void _zonedSendValue(T value) {
- try {
- var result = _action();
- if (result is Future) {
- Future resultFuture = result;
- resultFuture.then((_) {
- _setValue(value);
- }, onError: _setError);
- return;
- }
- } catch (e, s) {
- _setError(_asyncError(e, s));
- return;
- }
- _setValue(value);
- }
-
- void _zonedSendError(error) {
- try {
- var result = _action();
- if (result is Future) {
- Future resultFuture = result;
- // TODO(lrn): Find a way to combine [error] into [e].
- resultFuture.then((_) {
- _setError(error);
- }, onError: _setError);
- return;
- }
- } catch (e, s) {
- error = _asyncError(e, s);
- }
+ _Future listeners = _isChained ? null : _removeListeners();
_setError(error);
+ _propagateToListeners(this, listeners);
+ }
+
+ void _asyncComplete(value) {
+ assert(!_isComplete);
+ assert(_onValue == null);
+ assert(_onError == null);
+ assert(_whenCompleteAction == null);
+ assert(_errorTest == null);
+ // Two corner cases if the value is a future:
+ // 1. the future is already completed and an error.
+ // 2. the future is not yet completed but might become an error.
+ // The first case means that we must not immediately complete the Future,
+ // as our code would immediately start propagating the error without
+ // giving the time to install error-handlers.
+ // However the second case requires us to deal with the value immediately.
+ // Otherwise the value could complete with an error and report an
+ // unhandled error, even though we know we are already going to listen to
+ // it.
+ if (value is Future &&
+ (value is! _Future || !(value as _Future)._isComplete)) {
+ // Case 2 from above. We need to register.
+ // Note that we are still completing asynchronously: either we register
+ // through .then (in which case the completing is asynchronous), or we
+ // have a _Future which isn't complete yet.
+ _complete(value);
+ return;
+ }
+
+ _markPendingCompletion();
+ runAsync(() {
+ _complete(value);
+ });
+ }
+
+ void _asyncCompleteError(error, [StackTrace stackTrace]) {
+ assert(!_isComplete);
+ assert(_onValue == null);
+ assert(_onError == null);
+ assert(_whenCompleteAction == null);
+ assert(_errorTest == null);
+
+ _markPendingCompletion();
+ runAsync(() {
+ _completeError(error, stackTrace);
+ });
+ }
+
+ /**
+ * Propagates the value/error of [source] to its [listeners].
+ *
+ * Unlinks all listeners and propagates the source to each listener
+ * separately.
+ */
+ static void _propagateMultipleListeners(_Future source, _Future listeners) {
+ assert(listeners != null);
+ assert(listeners._nextListener != null);
+ do {
+ _Future listener = listeners;
+ listeners = listener._nextListener;
+ listener._nextListener = null;
+ _propagateToListeners(source, listener);
+ } while (listeners != null);
+ }
+
+ /**
+ * Propagates the value/error of [source] to its [listeners], executing the
+ * listeners' callbacks.
+ *
+ * If [runCallback] is true (which should be the default) it executes
+ * the registered action of listeners. If it is `false` then the callback is
+ * skipped. This is used to complete futures with chained futures.
+ */
+ static void _propagateToListeners(_Future source, _Future listeners) {
+ while (true) {
+ if (!source._isComplete) return; // Chained future.
+ bool hasError = source._hasError;
+ if (hasError && listeners == null) {
+ source._zone.handleUncaughtError(source._error);
+ return;
+ }
+ if (listeners == null) return;
+ _Future listener = listeners;
+ if (listener._nextListener != null) {
+ // Usually futures only have one listener. If they have several, we
+ // handle them specially.
+ _propagateMultipleListeners(source, listeners);
+ return;
+ }
+ if (hasError && !source._zone.inSameErrorZone(listener._zone)) {
+ // Don't cross zone boundaries with errors.
+ source._zone.handleUncaughtError(source._error);
+ return;
+ }
+ if (!identical(_Zone.current, listener._zone)) {
+ // Run the propagation in the listener's zone to avoid
+ // zone transitions. The idea is that many chained futures will
+ // be in the same zone.
+ listener._zone.executePeriodicCallback(() {
+ _propagateToListeners(source, listener);
+ });
+ return;
+ }
+
+ // Do the actual propagation.
+ // TODO(floitsch): Do we need to go through the zone even if we
+ // don't have a callback to execute?
+ bool listenerHasValue;
+ var listenerValueOrError;
+ // Set to true if a whenComplete needs to wait for a future.
+ // The whenComplete action will resume the propagation by itself.
+ bool isPropagationAborted = false;
+ // Even though we are already in the right zone (due to the optimization
+ // above), we still need to go through the zone. The overhead of
+ // executeCallback is however smaller when it is already in the correct
+ // zone.
+ // TODO(floitsch): only run callbacks in the zone, not the whole
+ // handling code.
+ listener._zone.executeCallback(() {
+ // TODO(floitsch): mark the listener as pending completion. Currently
+ // we can't do this, since the markPendingCompletion verifies that
+ // the future is not already marked (or chained).
+ try {
+ if (!hasError) {
+ var value = source._value;
+ if (listener._onValue != null) {
+ listenerValueOrError = listener._onValue(value);
+ listenerHasValue = true;
+ } else {
+ // Copy over the value from the source.
+ listenerValueOrError = value;
+ listenerHasValue = true;
+ }
+ } else {
+ Object error = source._error;
+ _FutureErrorTest test = listener._errorTest;
+ bool matchesTest = true;
+ if (test != null) {
+ matchesTest = test(error);
+ }
+ if (matchesTest && listener._onError != null) {
+ listenerValueOrError = listener._onError(error);
+ listenerHasValue = true;
+ } else {
+ // Copy over the error from the source.
+ listenerValueOrError = error;
+ listenerHasValue = false;
+ }
+ }
+
+ if (listener._whenCompleteAction != null) {
+ var completeResult = listener._whenCompleteAction();
+ if (completeResult is Future) {
+ listener._isChained = true;
+ completeResult.then((ignored) {
+ // Try again, but this time don't run the whenComplete callback.
+ _propagateToListeners(source, listener);
+ }, onError: (error) {
+ // When there is an error, we have to make the error the new
+ // result of the current listener.
+ if (completeResult is! _Future) {
+ // This should be a rare case.
+ completeResult = new _Future();
+ completeResult._setError(error);
+ }
+ _propagateToListeners(completeResult, listener);
+ });
+ isPropagationAborted = true;
+ // We will reenter the listener's zone.
+ listener._zone.expectCallback();
+ }
+ }
+ } catch (e, s) {
+ // Set the exception as error.
+ listenerValueOrError = _asyncError(e, s);
+ listenerHasValue = false;
+ }
+ if (listenerHasValue && listenerValueOrError is Future) {
+ // We are going to reenter the zone to finish what we started.
+ listener._zone.expectCallback();
+ }
+ });
+ if (isPropagationAborted) return;
+ // If the listener's value is a future we need to chain it.
+ if (listenerHasValue && listenerValueOrError is Future) {
+ Future chainSource = listenerValueOrError;
+ // Shortcut if the chain-source is already completed. Just continue the
+ // loop.
+ if (chainSource is _Future && (chainSource as _Future)._isComplete) {
+ // propagate the value (simulating a tail call).
+ listener._isChained = true;
+ source = chainSource;
+ listeners = listener;
+ continue;
+ }
+ _chainFutures(chainSource, listener);
+ return;
+ }
+
+ if (listenerHasValue) {
+ listeners = listener._removeListeners();
+ listener._setValue(listenerValueOrError);
+ } else {
+ listeners = listener._removeListeners();
+ listener._setError(listenerValueOrError);
+ }
+ // Prepare for next round.
+ source = listener;
+ }
}
}
diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart
index 4242e4f..495d047 100644
--- a/sdk/lib/async/stream.dart
+++ b/sdk/lib/async/stream.dart
@@ -272,7 +272,7 @@
* Reduces a sequence of values by repeatedly applying [combine].
*/
Future<T> reduce(T combine(T previous, T element)) {
- _FutureImpl<T> result = new _FutureImpl<T>();
+ _Future<T> result = new _Future<T>();
bool seenFirst = false;
T value;
StreamSubscription subscription;
@@ -287,12 +287,12 @@
seenFirst = true;
}
},
- onError: result._setError,
+ onError: result._completeError,
onDone: () {
if (!seenFirst) {
- result._setError(new StateError("No elements"));
+ result._completeError(new StateError("No elements"));
} else {
- result._setValue(value);
+ result._complete(value);
}
},
cancelOnError: true
@@ -302,7 +302,7 @@
/** Reduces a sequence of values by repeatedly applying [combine]. */
Future fold(var initialValue, combine(var previous, T element)) {
- _FutureImpl result = new _FutureImpl();
+ _Future result = new _Future();
var value = initialValue;
StreamSubscription subscription;
subscription = this.listen(
@@ -314,10 +314,10 @@
);
},
onError: (e) {
- result._setError(e);
+ result._completeError(e);
},
onDone: () {
- result._setValue(value);
+ result._complete(value);
},
cancelOnError: true);
return result;
@@ -334,7 +334,7 @@
* the "done" event arrives.
*/
Future<String> join([String separator = ""]) {
- _FutureImpl<String> result = new _FutureImpl<String>();
+ _Future<String> result = new _Future<String>();
StringBuffer buffer = new StringBuffer();
StreamSubscription subscription;
bool first = true;
@@ -348,14 +348,14 @@
buffer.write(element);
} catch (e, s) {
subscription.cancel();
- result._setError(_asyncError(e, s));
+ result._completeError(_asyncError(e, s));
}
},
onError: (e) {
- result._setError(e);
+ result._completeError(e);
},
onDone: () {
- result._setValue(buffer.toString());
+ result._complete(buffer.toString());
},
cancelOnError: true);
return result;
@@ -368,7 +368,7 @@
* If this stream reports an error, the [Future] will report that error.
*/
Future<bool> contains(Object needle) {
- _FutureImpl<bool> future = new _FutureImpl<bool>();
+ _Future<bool> future = new _Future<bool>();
StreamSubscription subscription;
subscription = this.listen(
(T element) {
@@ -377,15 +377,15 @@
(bool isMatch) {
if (isMatch) {
subscription.cancel();
- future._setValue(true);
+ future._complete(true);
}
},
_cancelAndError(subscription, future)
);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(false);
+ future._complete(false);
},
cancelOnError: true);
return future;
@@ -399,7 +399,7 @@
* stream has an error event, or if [action] throws.
*/
Future forEach(void action(T element)) {
- _FutureImpl future = new _FutureImpl();
+ _Future future = new _Future();
StreamSubscription subscription;
subscription = this.listen(
(T element) {
@@ -409,9 +409,9 @@
_cancelAndError(subscription, future)
);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(null);
+ future._complete(null);
},
cancelOnError: true);
return future;
@@ -424,7 +424,7 @@
* If this stream reports an error, the [Future] will report that error.
*/
Future<bool> every(bool test(T element)) {
- _FutureImpl<bool> future = new _FutureImpl<bool>();
+ _Future<bool> future = new _Future<bool>();
StreamSubscription subscription;
subscription = this.listen(
(T element) {
@@ -433,15 +433,15 @@
(bool isMatch) {
if (!isMatch) {
subscription.cancel();
- future._setValue(false);
+ future._complete(false);
}
},
_cancelAndError(subscription, future)
);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(true);
+ future._complete(true);
},
cancelOnError: true);
return future;
@@ -454,7 +454,7 @@
* If this stream reports an error, the [Future] will report that error.
*/
Future<bool> any(bool test(T element)) {
- _FutureImpl<bool> future = new _FutureImpl<bool>();
+ _Future<bool> future = new _Future<bool>();
StreamSubscription subscription;
subscription = this.listen(
(T element) {
@@ -463,15 +463,15 @@
(bool isMatch) {
if (isMatch) {
subscription.cancel();
- future._setValue(true);
+ future._complete(true);
}
},
_cancelAndError(subscription, future)
);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(false);
+ future._complete(false);
},
cancelOnError: true);
return future;
@@ -480,13 +480,13 @@
/** Counts the elements in the stream. */
Future<int> get length {
- _FutureImpl<int> future = new _FutureImpl<int>();
+ _Future<int> future = new _Future<int>();
int count = 0;
this.listen(
(_) { count++; },
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(count);
+ future._complete(count);
},
cancelOnError: true);
return future;
@@ -494,16 +494,16 @@
/** Reports whether this stream contains any elements. */
Future<bool> get isEmpty {
- _FutureImpl<bool> future = new _FutureImpl<bool>();
+ _Future<bool> future = new _Future<bool>();
StreamSubscription subscription;
subscription = this.listen(
(_) {
subscription.cancel();
- future._setValue(false);
+ future._complete(false);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(true);
+ future._complete(true);
},
cancelOnError: true);
return future;
@@ -512,14 +512,14 @@
/** Collects the data of this stream in a [List]. */
Future<List<T>> toList() {
List<T> result = <T>[];
- _FutureImpl<List<T>> future = new _FutureImpl<List<T>>();
+ _Future<List<T>> future = new _Future<List<T>>();
this.listen(
(T data) {
result.add(data);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(result);
+ future._complete(result);
},
cancelOnError: true);
return future;
@@ -528,14 +528,14 @@
/** Collects the data of this stream in a [Set]. */
Future<Set<T>> toSet() {
Set<T> result = new Set<T>();
- _FutureImpl<Set<T>> future = new _FutureImpl<Set<T>>();
+ _Future<Set<T>> future = new _Future<Set<T>>();
this.listen(
(T data) {
result.add(data);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setValue(result);
+ future._complete(result);
},
cancelOnError: true);
return future;
@@ -627,17 +627,17 @@
* [:this.elementAt(0):].
*/
Future<T> get first {
- _FutureImpl<T> future = new _FutureImpl<T>();
+ _Future<T> future = new _Future<T>();
StreamSubscription subscription;
subscription = this.listen(
(T value) {
subscription.cancel();
- future._setValue(value);
+ future._complete(value);
return;
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setError(new StateError("No elements"));
+ future._completeError(new StateError("No elements"));
},
cancelOnError: true);
return future;
@@ -653,7 +653,7 @@
* the resulting future completes with a [StateError].
*/
Future<T> get last {
- _FutureImpl<T> future = new _FutureImpl<T>();
+ _Future<T> future = new _Future<T>();
T result = null;
bool foundResult = false;
StreamSubscription subscription;
@@ -662,13 +662,13 @@
foundResult = true;
result = value;
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
if (foundResult) {
- future._setValue(result);
+ future._complete(result);
return;
}
- future._setError(new StateError("No elements"));
+ future._completeError(new StateError("No elements"));
},
cancelOnError: true);
return future;
@@ -680,7 +680,7 @@
* If [this] is empty or has more than one element throws a [StateError].
*/
Future<T> get single {
- _FutureImpl<T> future = new _FutureImpl<T>();
+ _Future<T> future = new _Future<T>();
T result = null;
bool foundResult = false;
StreamSubscription subscription;
@@ -690,19 +690,19 @@
subscription.cancel();
// This is the second element we get.
Error error = new StateError("More than one element");
- future._setError(error);
+ future._completeError(error);
return;
}
foundResult = true;
result = value;
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
if (foundResult) {
- future._setValue(result);
+ future._complete(result);
return;
}
- future._setError(new StateError("No elements"));
+ future._completeError(new StateError("No elements"));
},
cancelOnError: true);
return future;
@@ -723,7 +723,7 @@
* error.
*/
Future<dynamic> firstWhere(bool test(T element), {Object defaultValue()}) {
- _FutureImpl<dynamic> future = new _FutureImpl();
+ _Future<dynamic> future = new _Future();
StreamSubscription subscription;
subscription = this.listen(
(T value) {
@@ -732,19 +732,19 @@
(bool isMatch) {
if (isMatch) {
subscription.cancel();
- future._setValue(value);
+ future._complete(value);
}
},
_cancelAndError(subscription, future)
);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
if (defaultValue != null) {
- _runUserCode(defaultValue, future._setValue, future._setError);
+ _runUserCode(defaultValue, future._complete, future._completeError);
return;
}
- future._setError(new StateError("firstMatch ended without match"));
+ future._completeError(new StateError("firstMatch ended without match"));
},
cancelOnError: true);
return future;
@@ -758,7 +758,7 @@
* is done.
*/
Future<dynamic> lastWhere(bool test(T element), {Object defaultValue()}) {
- _FutureImpl<dynamic> future = new _FutureImpl();
+ _Future<dynamic> future = new _Future();
T result = null;
bool foundResult = false;
StreamSubscription subscription;
@@ -775,17 +775,17 @@
_cancelAndError(subscription, future)
);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
if (foundResult) {
- future._setValue(result);
+ future._complete(result);
return;
}
if (defaultValue != null) {
- _runUserCode(defaultValue, future._setValue, future._setError);
+ _runUserCode(defaultValue, future._complete, future._completeError);
return;
}
- future._setError(new StateError("lastMatch ended without match"));
+ future._completeError(new StateError("lastMatch ended without match"));
},
cancelOnError: true);
return future;
@@ -798,7 +798,7 @@
* matching element occurs in the stream.
*/
Future<T> singleWhere(bool test(T element)) {
- _FutureImpl<T> future = new _FutureImpl<T>();
+ _Future<T> future = new _Future<T>();
T result = null;
bool foundResult = false;
StreamSubscription subscription;
@@ -810,7 +810,7 @@
if (isMatch) {
if (foundResult) {
subscription.cancel();
- future._setError(
+ future._completeError(
new StateError('Multiple matches for "single"'));
return;
}
@@ -821,13 +821,13 @@
_cancelAndError(subscription, future)
);
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
if (foundResult) {
- future._setValue(result);
+ future._complete(result);
return;
}
- future._setError(new StateError("single ended without match"));
+ future._completeError(new StateError("single ended without match"));
},
cancelOnError: true);
return future;
@@ -846,20 +846,20 @@
*/
Future<T> elementAt(int index) {
if (index is! int || index < 0) throw new ArgumentError(index);
- _FutureImpl<T> future = new _FutureImpl<T>();
+ _Future<T> future = new _Future<T>();
StreamSubscription subscription;
subscription = this.listen(
(T value) {
if (index == 0) {
subscription.cancel();
- future._setValue(value);
+ future._complete(value);
return;
}
index -= 1;
},
- onError: future._setError,
+ onError: future._completeError,
onDone: () {
- future._setError(new RangeError.value(index));
+ future._completeError(new RangeError.value(index));
},
cancelOnError: true);
return future;
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index 5b3d760..194602e 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -250,7 +250,7 @@
// TODO(lrn): Could this be stored in the varData field too, if it's not
// accessed until the call to "close"? Then we need to special case if it's
// accessed earlier, or if close is called before subscribing.
- _FutureImpl _doneFuture;
+ _Future _doneFuture;
_StreamController();
@@ -347,7 +347,7 @@
// StreamSink interface.
Future addStream(Stream<T> source) {
if (!_mayAddEvent) throw _badEventState();
- if (_isCanceled) return new _FutureImpl.immediate(null);
+ if (_isCanceled) return new _Future.immediate(null);
_StreamControllerAddStreamState addState =
new _StreamControllerAddStreamState(this, _varData, source);
_varData = addState;
@@ -359,8 +359,8 @@
Future _ensureDoneFuture() {
if (_doneFuture == null) {
- _doneFuture = new _FutureImpl();
- if (_isCanceled) _doneFuture._setValue(null);
+ _doneFuture = new _Future();
+ if (_isCanceled) _doneFuture._complete(null);
}
return _doneFuture;
}
@@ -479,7 +479,7 @@
(_state & ~(_STATE_SUBSCRIBED | _STATE_ADDSTREAM)) | _STATE_CANCELED;
_runGuarded(_onCancel);
if (_doneFuture != null && _doneFuture._mayComplete) {
- _doneFuture._asyncSetValue(null);
+ _doneFuture._asyncComplete(null);
}
}
@@ -649,13 +649,13 @@
*/
class _AddStreamState<T> {
// [_FutureImpl] returned by call to addStream.
- _FutureImpl addStreamFuture;
+ _Future addStreamFuture;
// Subscription on stream argument to addStream.
StreamSubscription addSubscription;
_AddStreamState(_EventSink<T> controller, Stream source)
- : addStreamFuture = new _FutureImpl(),
+ : addStreamFuture = new _Future(),
addSubscription = source.listen(controller._add,
onError: controller._addError,
onDone: controller._close,
@@ -675,7 +675,7 @@
}
void complete() {
- addStreamFuture._asyncSetValue(null);
+ addStreamFuture._asyncComplete(null);
}
}
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index 7f26c4f..ffab312 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -190,13 +190,13 @@
}
Future asFuture([var futureValue]) {
- _FutureImpl<T> result = new _FutureImpl<T>();
+ _Future<T> result = new _Future<T>();
// Overwrite the onDone and onError handlers.
- _onDone = () { result._setValue(futureValue); };
+ _onDone = () { result._complete(futureValue); };
_onError = (error) {
cancel();
- result._setError(error);
+ result._completeError(error);
};
return result;
@@ -716,7 +716,7 @@
void cancel() {}
bool get isPaused => _pauseCounter > 0;
- Future asFuture([futureValue]) => new _FutureImpl();
+ Future asFuture([futureValue]) => new _Future();
}
class _AsBroadcastStream<T> extends Stream<T> {
@@ -917,14 +917,14 @@
Future<bool> moveNext() {
if (_state == _STATE_DONE) {
- return new _FutureImpl<bool>.immediate(false);
+ return new _Future<bool>.immediate(false);
}
if (_state == _STATE_MOVING) {
throw new StateError("Already waiting for next.");
}
if (_state == _STATE_FOUND) {
_state = _STATE_MOVING;
- _futureOrPrefetch = new _FutureImpl<bool>();
+ _futureOrPrefetch = new _Future<bool>();
return _futureOrPrefetch;
} else {
assert(_state >= _STATE_EXTRA_DATA);
@@ -934,14 +934,14 @@
_current = _futureOrPrefetch;
_futureOrPrefetch = null;
_subscription.resume();
- return new _FutureImpl<bool>.immediate(true);
+ return new _Future<bool>.immediate(true);
case _STATE_EXTRA_ERROR:
Object prefetch = _futureOrPrefetch;
_clear();
- return new _FutureImpl<bool>.immediateError(prefetch);
+ return new _Future<bool>.immediateError(prefetch);
case _STATE_EXTRA_DONE:
_clear();
- return new _FutureImpl<bool>.immediate(false);
+ return new _Future<bool>.immediate(false);
}
}
}
@@ -957,9 +957,9 @@
void cancel() {
StreamSubscription subscription = _subscription;
if (_state == _STATE_MOVING) {
- _FutureImpl<bool> hasNext = _futureOrPrefetch;
+ _Future<bool> hasNext = _futureOrPrefetch;
_clear();
- hasNext._setValue(false);
+ hasNext._complete(false);
} else {
_clear();
}
@@ -969,10 +969,10 @@
void _onData(T data) {
if (_state == _STATE_MOVING) {
_current = data;
- _FutureImpl<bool> hasNext = _futureOrPrefetch;
+ _Future<bool> hasNext = _futureOrPrefetch;
_futureOrPrefetch = null;
_state = _STATE_FOUND;
- hasNext._setValue(true);
+ hasNext._complete(true);
return;
}
_subscription.pause();
@@ -983,10 +983,10 @@
void _onError(Object error) {
if (_state == _STATE_MOVING) {
- _FutureImpl<bool> hasNext = _futureOrPrefetch;
+ _Future<bool> hasNext = _futureOrPrefetch;
// We have cancelOnError: true, so the subscription is canceled.
_clear();
- hasNext._setError(error);
+ hasNext._completeError(error);
return;
}
_subscription.pause();
@@ -997,9 +997,9 @@
void _onDone() {
if (_state == _STATE_MOVING) {
- _FutureImpl<bool> hasNext = _futureOrPrefetch;
+ _Future<bool> hasNext = _futureOrPrefetch;
_clear();
- hasNext._setValue(false);
+ hasNext._complete(false);
return;
}
_subscription.pause();
diff --git a/sdk/lib/async/stream_pipe.dart b/sdk/lib/async/stream_pipe.dart
index 7436b92..b126747 100644
--- a/sdk/lib/async/stream_pipe.dart
+++ b/sdk/lib/async/stream_pipe.dart
@@ -25,10 +25,10 @@
}
/** Helper function to make an onError argument to [_runUserCode]. */
-_cancelAndError(StreamSubscription subscription, _FutureImpl future) =>
+_cancelAndError(StreamSubscription subscription, _Future future) =>
(error) {
subscription.cancel();
- future._setError(error);
+ future._completeError(error);
};
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
index 9be85d1..bd1da92 100644
--- a/sdk/lib/async/zone.dart
+++ b/sdk/lib/async/zone.dart
@@ -87,13 +87,13 @@
*
* Returns the result of the invocation.
*/
- runFromChildZone(f());
+ dynamic runFromChildZone(f());
/**
* Same as [runFromChildZone] but catches uncaught errors and gives them to
* [handleUncaughtError].
*/
- runFromChildZoneGuarded(f());
+ dynamic runFromChildZoneGuarded(f());
/**
* Runs [f] asynchronously in [zone].
@@ -181,7 +181,7 @@
* A zone is done when its dynamic extent has finished executing and
* there are no outstanding asynchronous callbacks.
*/
- _dispose() {
+ void _dispose() {
if (_parentZone != null) {
_parentZone._removeChild(this);
}
@@ -218,10 +218,10 @@
this._runGuarded(f);
}
- runFromChildZone(f()) => this._runUnguarded(f);
- runFromChildZoneGuarded(f()) => this._runGuarded(f);
+ dynamic runFromChildZone(f()) => this._runUnguarded(f);
+ dynamic runFromChildZoneGuarded(f()) => this._runGuarded(f);
- _runInZone(f(), bool handleUncaught) {
+ dynamic _runInZone(f(), bool handleUncaught) {
if (identical(_Zone._current, this)
&& !handleUncaught
&& _isExecutingCallback) {
@@ -261,18 +261,18 @@
*
* Uncaught errors are given to [handleUncaughtError].
*/
- _runGuarded(void f()) {
+ dynamic _runGuarded(void f()) {
return _runInZone(f, true);
}
/**
* Runs the function but doesn't catch uncaught errors.
*/
- _runUnguarded(void f()) {
+ dynamic _runUnguarded(void f()) {
return _runInZone(f, false);
}
- runAsync(void f(), _Zone zone) => _parentZone.runAsync(f, zone);
+ void runAsync(void f(), _Zone zone) => _parentZone.runAsync(f, zone);
// TODO(floitsch): the zone should just forward to the parent zone. The
// default zone should then create the _ZoneTimer.
@@ -305,7 +305,7 @@
_Zone get _errorZone => this;
- handleUncaughtError(error) {
+ void handleUncaughtError(error) {
_scheduleAsyncCallback(() {
print("Uncaught Error: ${error}");
var trace = getAttachedStackTrace(error);
@@ -341,14 +341,15 @@
_WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone);
/**
- * Runs the given function asynchronously. Executes the [_onDone] callback
- * when the zone is done.
+ * Runs the given function.
+ *
+ * Executes the [_onDone] callback when the zone is done.
*/
- runWaitForCompletion(void f()) {
+ dynamic runWaitForCompletion(void f()) {
return this._runUnguarded(f);
}
- _dispose() {
+ void _dispose() {
super._dispose();
_onDone();
}
@@ -370,7 +371,7 @@
_Zone get _errorZone => this;
- handleUncaughtError(error) {
+ void handleUncaughtError(error) {
try {
_handleError(error);
} catch(e, s) {
@@ -386,7 +387,7 @@
* Runs the given function asynchronously. Executes the [_onDone] callback
* when the zone is done.
*/
- runWaitForCompletion(void f()) {
+ dynamic runWaitForCompletion(void f()) {
return this._runGuarded(f);
}
diff --git a/sdk/lib/collection/hash_map.dart b/sdk/lib/collection/hash_map.dart
index 3aadab2..bcca966 100644
--- a/sdk/lib/collection/hash_map.dart
+++ b/sdk/lib/collection/hash_map.dart
@@ -41,6 +41,14 @@
* for keys in order to place them in the hash table. If it is omitted, the
* key's own [Object.hashCode] is used.
*
+ * If using methods like [operator[]], [remove] and [containsKey] together
+ * with a custom equality and hashcode, an extra `isValidKey` function
+ * can be supplied. This function is called before calling [equals] or
+ * [hashCode] with an argument that may not be a [K] instance, and if the
+ * call returns false, the key is assumed to not be in the set.
+ * The [isValidKey] function defaults to just testing if the object is a
+ * [K] instance.
+ *
* The used `equals` and `hashCode` method should always be consistent,
* so that if `equals(a, b)` then `hashCode(a) == hashCode(b)`. The hash
* of an object, or what it compares equal to, should not change while the
@@ -50,7 +58,9 @@
* you also want to supply the other. The only common exception is to pass
* [identical] as the equality and use the default hash code.
*/
- external factory HashMap({bool equals(K key1, K key2), int hashCode(K key)});
+ external factory HashMap({bool equals(K key1, K key2),
+ int hashCode(K key),
+ bool isValidKey(potentialKey)});
/**
* Creates a [HashMap] that contains all key value pairs of [other].
diff --git a/sdk/lib/collection/linked_hash_map.dart b/sdk/lib/collection/linked_hash_map.dart
index ff6f045f..34744d0 100644
--- a/sdk/lib/collection/linked_hash_map.dart
+++ b/sdk/lib/collection/linked_hash_map.dart
@@ -18,8 +18,10 @@
*
* The map allows `null` as a key.
*/
-class LinkedHashMap<K, V> implements HashMap<K, V> {
- external LinkedHashMap();
+abstract class LinkedHashMap<K, V> implements HashMap<K, V> {
+ external factory LinkedHashMap({ bool equals(K key1, K key2),
+ int hashCode(K key),
+ bool isValidKey(potentialKey) });
/**
* Creates a [LinkedHashMap] that contains all key value pairs of [other].
@@ -64,35 +66,4 @@
Maps._fillMapWithIterables(map, keys, values);
return map;
}
-
- external bool containsKey(Object key);
-
- external bool containsValue(Object value);
-
- external void addAll(Map<K, V> other);
-
- external V operator [](Object key);
-
- external void operator []=(K key, V value);
-
- external V putIfAbsent(K key, V ifAbsent());
-
- external V remove(Object key);
-
- external void clear();
-
- external void forEach(void action (K key, V value));
-
- /** The keys of the map, in insertion order. */
- external Iterable<K> get keys;
- /** The values of the map, in the order of their corresponding [keys].*/
- external Iterable<V> get values;
-
- external int get length;
-
- external bool get isEmpty;
-
- external bool get isNotEmpty;
-
- String toString() => Maps.mapToString(this);
}
diff --git a/sdk/lib/collection/splay_tree.dart b/sdk/lib/collection/splay_tree.dart
index a0c3624..72251d9 100644
--- a/sdk/lib/collection/splay_tree.dart
+++ b/sdk/lib/collection/splay_tree.dart
@@ -4,6 +4,8 @@
part of dart.collection;
+typedef bool _Predicate<T>(T value);
+
/**
* A node in a splay tree. It holds the sorting key and the left
* and right children in the tree.
@@ -229,6 +231,10 @@
}
}
+class _TypeTest<T> {
+ bool test(v) => v is T;
+}
+
/*
* A [Map] of objects that can be ordered relative to each other.
*
@@ -238,19 +244,31 @@
* Keys of the map are compared using the `compare` function passed in
* the constructor. If that is omitted, the objects are assumed to be
* [Comparable], and are compared using their [Comparable.compareTo]
- * method. This also means that `null` is *not* allowed as a key.
+ * method. Non-comparable objects (including `null`) will not work as keys
+ * in that case.
+ *
+ * To allow calling [operator[]], [remove] or [containsKey] with objects
+ * that are not supported by the `compare` function, an extra `isValidKey`
+ * predicate function can be supplied. This function is tested before
+ * using the `compare` function on an argument value that may not be a [K]
+ * value. If omitted, the `isValidKey` function defaults to testing if the
+ * value is a [K].
*/
class SplayTreeMap<K, V> extends _SplayTree<K> implements Map<K, V> {
Comparator<K> _comparator;
+ _Predicate _validKey;
- SplayTreeMap([int compare(K key1, K key2)])
- : _comparator = (compare == null) ? Comparable.compare : compare;
+ SplayTreeMap([int compare(K key1, K key2), bool isValidKey(potentialKey)])
+ : _comparator = (compare == null) ? Comparable.compare : compare,
+ _validKey = (isValidKey != null) ? isValidKey : ((v) => v is K);
/**
* Creates a [SplayTreeMap] that contains all key value pairs of [other].
*/
- factory SplayTreeMap.from(Map<K, V> other, [int compare(K key1, K key2)]) =>
- new SplayTreeMap(compare)..addAll(other);
+ factory SplayTreeMap.from(Map<K, V> other,
+ [ int compare(K key1, K key2),
+ bool isValidKey(potentialKey)]) =>
+ new SplayTreeMap(compare, isValidKey)..addAll(other);
/**
* Creates a [SplayTreeMap] where the keys and values are computed from the
@@ -266,8 +284,9 @@
* identity function.
*/
factory SplayTreeMap.fromIterable(Iterable<K> iterable,
- {K key(element), V value(element), int compare(K key1, K key2)}) {
- SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare);
+ {K key(element), V value(element), int compare(K key1, K key2),
+ bool isValidKey(potentialKey) }) {
+ SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare, isValidKey);
Maps._fillMapWithMappedIterable(map, iterable, key, value);
return map;
}
@@ -284,8 +303,8 @@
* It is an error if the two [Iterable]s don't have the same length.
*/
factory SplayTreeMap.fromIterables(Iterable<K> keys, Iterable<V> values,
- [int compare(K key1, K key2)]) {
- SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare);
+ [int compare(K key1, K key2), bool isValidKey(potentialKey)]) {
+ SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare, isValidKey);
Maps._fillMapWithIterables(map, keys, values);
return map;
}
@@ -296,7 +315,7 @@
V operator [](Object key) {
if (key == null) throw new ArgumentError(key);
- if (key is! K) return null;
+ if (!_validKey(key)) return null;
if (_root != null) {
int comp = _splay(key);
if (comp == 0) {
@@ -308,7 +327,7 @@
}
V remove(Object key) {
- if (key is! K) return null;
+ if (!_validKey(key)) return null;
_SplayTreeMapNode mapRoot = _remove(key);
if (mapRoot != null) return mapRoot.value;
return null;
@@ -378,7 +397,7 @@
}
bool containsKey(Object key) {
- return key is K && _splay(key) == 0;
+ return _validKey(key) && _splay(key) == 0;
}
bool containsValue(Object value) {
diff --git a/sdk/lib/core/core.dart b/sdk/lib/core/core.dart
index a902226..9ac401b5 100644
--- a/sdk/lib/core/core.dart
+++ b/sdk/lib/core/core.dart
@@ -155,7 +155,7 @@
import "dart:collection";
import "dart:_collection-dev" hide Symbol;
import "dart:_collection-dev" as _collection_dev;
-import "dart:convert" show UTF8;
+import "dart:convert" show UTF8, Encoding;
part "bool.dart";
part "comparable.dart";
diff --git a/sdk/lib/core/double.dart b/sdk/lib/core/double.dart
index 409e294..4aa161b 100644
--- a/sdk/lib/core/double.dart
+++ b/sdk/lib/core/double.dart
@@ -9,6 +9,8 @@
// different platform implementations.
/**
+ * A double-precision floating point number.
+ *
* Representation of Dart doubles containing double specific constants
* and operations and specializations of operations inherited from
* [num]. Dart doubles are 64-bit floating-point numbers as specified in the
diff --git a/sdk/lib/core/int.dart b/sdk/lib/core/int.dart
index 96a6d40..2b1b70c 100644
--- a/sdk/lib/core/int.dart
+++ b/sdk/lib/core/int.dart
@@ -99,6 +99,78 @@
bool get isOdd;
/**
+ * Returns the minimum number of bits required to store this integer.
+ *
+ * The number of bits excludes the sign bit, which gives the natural length
+ * for non-negative (unsigned) values. Negative values are complemented to
+ * return the bit position of the first bit that differs from the sign bit.
+ *
+ * To find the the number of bits needed to store the value as a signed value,
+ * add one, i.e. use `x.bitLength + 1`.
+ *
+ * x.bitLength == (-x-1).bitLength
+ *
+ * 3.bitLength == 2; // 00000011
+ * 2.bitLength == 2; // 00000010
+ * 1.bitLength == 1; // 00000001
+ * 0.bitLength == 0; // 00000000
+ * (-1).bitLength == 0; // 11111111
+ * (-2).bitLength == 1; // 11111110
+ * (-3).bitLength == 2; // 11111101
+ * (-4).bitLength == 2; // 11111100
+ */
+ int get bitLength;
+
+ /**
+ * Returns the least significant [width] bits of this integer as a
+ * non-negative number (i.e. unsigned representation). The returned value has
+ * zeros in all bit positions higher than [width].
+ *
+ * (-1).toUnsigned(5) == 32 // 11111111 -> 00011111
+ *
+ * This operation can be used to simulate arithmetic from low level languages.
+ * For example, to increment an 8 bit quantity:
+ *
+ * q = (q + 1).toUnsigned(8);
+ *
+ * `q` will count from `0` up to `255` and then wrap around to `0`.
+ *
+ * If the input fits in [width] bits without truncation, the result is the
+ * same as the input. The minimum width needed to avoid truncation of `x` is
+ * given by `x.bitLength`, i.e.
+ *
+ * x == x.toUnsigned(x.bitLength);
+ */
+ int toUnsigned(int width);
+
+ /**
+ * Returns the least significant [width] bits of this integer, extending the
+ * highest retained bit to the sign. This is the same as truncating the value
+ * to fit in [width] bits using an signed 2-s complement representation. The
+ * returned value has the same bit value in all positions higher than [width].
+ *
+ * V--sign bit-V
+ * 16.toSigned(5) == -16 // 00010000 -> 11110000
+ * 239.toSigned(5) == 15 // 11101111 -> 00001111
+ * ^ ^
+ *
+ * This operation can be used to simulate arithmetic from low level languages.
+ * For example, to increment an 8 bit signed quantity:
+ *
+ * q = (q + 1).toSigned(8);
+ *
+ * `q` will count from `0` up to `127`, wrap to `-128` and count back up to
+ * `127`.
+ *
+ * If the input value fits in [width] bits without truncation, the result is
+ * the same as the input. The minimum width needed to avoid truncation of `x`
+ * is `x.bitLength + 1`, i.e.
+ *
+ * x == x.toSigned(x.bitLength + 1);
+ */
+ int toSigned(int width);
+
+ /**
* Return the negative value of this integer.
*
* The result of negating an integer always has the opposite sign, except
diff --git a/sdk/lib/core/stopwatch.dart b/sdk/lib/core/stopwatch.dart
index 9afc50b..c47c6d2 100644
--- a/sdk/lib/core/stopwatch.dart
+++ b/sdk/lib/core/stopwatch.dart
@@ -5,7 +5,7 @@
part of dart.core;
/**
- * A simple [Stopwatch] interface to measure elapsed time.
+ * A simple stopwatch interface to measure elapsed time.
*/
class Stopwatch {
// The _start and _stop fields capture the time when [start] and [stop]
diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart
index 30057f1..aa00f63 100644
--- a/sdk/lib/core/uri.dart
+++ b/sdk/lib/core/uri.dart
@@ -8,6 +8,7 @@
* A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986.
*/
class Uri {
+ final String _host;
int _port;
String _path;
@@ -46,15 +47,29 @@
*
* Returns the empty string if there is no authority component and
* hence no host.
+ *
+ * If the host is an IP version 6 address, the surrounding `[` and `]` is
+ * removed.
*/
- final String host;
+ String get host {
+ if (_host != null && _host.startsWith('[')) {
+ return _host.substring(1, _host.length - 1);
+ }
+ return _host;
+ }
/**
* Returns the port part of the authority component.
*
* Returns 0 if there is no port in the authority component.
*/
- int get port => _port;
+ int get port {
+ if (_port == 0) {
+ if (scheme == "http") return 80;
+ if (scheme == "https") return 443;
+ }
+ return _port;
+ }
/**
* Returns the path component.
@@ -98,7 +113,7 @@
static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri));
Uri._fromMatch(Match m) :
- this(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]),
+ this(scheme: _makeScheme(_emptyIfNull(m[_COMPONENT_SCHEME])),
userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]),
host: _eitherOf(
m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]),
@@ -123,7 +138,7 @@
* [userInfo].
*
* The host part of the authority component is set through
- * [host]. The host can either be a hostname, a IPv4 address or an
+ * [host]. The host can either be a hostname, an IPv4 address or an
* IPv6 address, contained in '[' and ']'. If the host contains a
* ':' character, the '[' and ']' are added if not already provided.
*
@@ -153,9 +168,9 @@
*
* The fragment component is set through [fragment].
*/
- Uri({scheme,
+ Uri({String scheme,
this.userInfo: "",
- this.host: "",
+ String host: "",
port: 0,
String path,
Iterable<String> pathSegments,
@@ -163,6 +178,7 @@
Map<String, String> queryParameters,
fragment: ""}) :
scheme = _makeScheme(scheme),
+ _host = _makeHost(host),
query = _makeQuery(query, queryParameters),
fragment = _makeFragment(fragment) {
// Perform scheme specific normalization.
@@ -237,22 +253,34 @@
break;
}
}
+ var hostEnd = hostStart;
+ if (hostStart < authority.length &&
+ authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
+ // IPv6 host.
+ for (; hostEnd < authority.length; hostEnd++) {
+ if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
+ }
+ if (hostEnd == authority.length) {
+ throw new FormatException("Invalid IPv6 host entry.");
+ }
+ parseIPv6Address(authority.substring(hostStart + 1, hostEnd));
+ hostEnd++; // Skip the closing bracket.
+ if (hostEnd != authority.length &&
+ authority.codeUnitAt(hostEnd) != _COLON) {
+ throw new FormatException("Invalid end of authority");
+ }
+ }
// Split host and port.
bool hasPort = false;
- for (int i = hostStart; i < authority.length; i++) {
- if (authority.codeUnitAt(i) == _COLON) {
- hasPort = true;
- host = authority.substring(hostStart, i);
- if (!host.isEmpty) {
- var portString = authority.substring(i + 1);
- if (portString.isNotEmpty) port = int.parse(portString);
- }
+ for (; hostEnd < authority.length; hostEnd++) {
+ if (authority.codeUnitAt(hostEnd) == _COLON) {
+ var portString = authority.substring(hostEnd + 1);
+ // We allow the empty port - falling back to initial value.
+ if (portString.isNotEmpty) port = int.parse(portString);
break;
}
}
- if (!hasPort) {
- host = hasUserInfo ? authority.substring(hostStart) : authority;
- }
+ host = authority.substring(hostStart, hostEnd);
return new Uri(scheme: scheme,
userInfo: userInfo,
@@ -474,6 +502,24 @@
return _queryParameters;
}
+ static String _makeHost(String host) {
+ if (host == null || host.isEmpty) return host;
+ if (host.codeUnitAt(0) == _LEFT_BRACKET) {
+ if (host.codeUnitAt(host.length - 1) != _RIGHT_BRACKET) {
+ throw new FormatException('Missing end `]` to match `[` in host');
+ }
+ parseIPv6Address(host.substring(1, host.length - 1));
+ return host;
+ }
+ for (int i = 0; i < host.length; i++) {
+ if (host.codeUnitAt(i) == _COLON) {
+ parseIPv6Address(host);
+ return '[$host]';
+ }
+ }
+ return host;
+ }
+
static String _makeScheme(String scheme) {
bool isSchemeLowerCharacter(int ch) {
return ch < 128 &&
@@ -489,6 +535,10 @@
int length = scheme.length;
for (int i = 0; i < length; i++) {
int codeUnit = scheme.codeUnitAt(i);
+ if (i == 0 && !_isAlphabeticCharacter(codeUnit)) {
+ // First code unit must be an alphabetic character.
+ throw new ArgumentError('Illegal scheme: $scheme');
+ }
if (!isSchemeLowerCharacter(codeUnit)) {
if (isSchemeCharacter(codeUnit)) {
allLowercase = false;
@@ -670,7 +720,7 @@
static final RegExp _splitRe = new RegExp(
'^'
'(?:'
- '([^:/?#.]+)' // scheme - ignore special characters
+ '([^:/?#]+)' // scheme - ignore special characters
// used by other URL parts such as :,
// ?, /, #, and .
':)?'
@@ -828,15 +878,15 @@
* See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
*/
String get origin {
- if (scheme == "" || host == null || host == "") {
+ if (scheme == "" || _host == null || _host == "") {
throw new StateError("Cannot use origin without a scheme: $this");
}
if (scheme != "http" && scheme != "https") {
throw new StateError(
"Origin is only applicable schemes http and https: $this");
}
- if (port == 0) return "$scheme://$host";
- return "$scheme://$host:$port";
+ if (_port == 0) return "$scheme://$_host";
+ return "$scheme://$_host:$_port";
}
/**
@@ -931,7 +981,7 @@
}
_checkNonWindowsPathReservedCharacters(pathSegments, false);
var result = new StringBuffer();
- if (isAbsolute) result.write("/");
+ if (_isPathAbsolute) result.write("/");
result.writeAll(pathSegments, "/");
return result.toString();
}
@@ -949,7 +999,7 @@
_checkWindowsPathReservedCharacters(segments, false);
}
var result = new StringBuffer();
- if (isAbsolute && !hasDriveLetter) result.write("\\");
+ if (_isPathAbsolute && !hasDriveLetter) result.write("\\");
if (host != "") {
result.write("\\");
result.write(host);
@@ -960,13 +1010,17 @@
return result.toString();
}
+ bool get _isPathAbsolute {
+ if (path == null || path.isEmpty) return false;
+ return path.startsWith('/');
+ }
+
void _writeAuthority(StringSink ss) {
_addIfNonEmpty(ss, userInfo, userInfo, "@");
- ss.write(host == null ? "null" :
- host.contains(':') ? '[$host]' : host);
- if (port != 0) {
+ ss.write(_host == null ? "null" : _host);
+ if (_port != 0) {
ss.write(":");
- ss.write(port.toString());
+ ss.write(_port.toString());
}
}
@@ -1017,7 +1071,7 @@
* safe for literal use as a URI component.
*
* All characters except uppercase and lowercase letters, digits and
- * the characters `!$&'()*+,;=:@` are percent-encoded. This is the
+ * the characters `-_.!~*'()` are percent-encoded. This is the
* set of characters specified in RFC 2396 and the which is
* specified for the encodeUriComponent in ECMA-262 version 5.1.
*
@@ -1083,12 +1137,13 @@
* pluses to spaces.
*
* It will create a byte-list of the decoded characters, and then use
- * [decode] to decode the byte-list to a String. Default is a UTF-8 decoder.
+ * [encoding] to decode the byte-list to a String. The default encoding is
+ * UTF-8.
*/
static String decodeQueryComponent(
String encodedComponent,
- {String decode(List<int> bytes): null}) {
- return _uriDecode(encodedComponent, plusToSpace: true, decode: decode);
+ {Encoding encoding: UTF8}) {
+ return _uriDecode(encodedComponent, plusToSpace: true, encoding: encoding);
}
/**
@@ -1128,26 +1183,156 @@
* Keys in the query string that have no value are mapped to the
* empty string.
*
- * Each query component will be decoded using [decode]. Default is a UTF-8
- * decoder.
+ * Each query component will be decoded using [encoding]. The default encoding
+ * is UTF-8.
*/
- static Map<String, String> splitQueryString(
- String query,
- {String decode(List<int> bytes): null}) {
+ static Map<String, String> splitQueryString(String query,
+ {Encoding encoding: UTF8}) {
return query.split("&").fold({}, (map, element) {
int index = element.indexOf("=");
if (index == -1) {
- if (element != "") map[decodeQueryComponent(element)] = "";
+ if (element != "") {
+ map[decodeQueryComponent(element, encoding: encoding)] = "";
+ }
} else if (index != 0) {
var key = element.substring(0, index);
var value = element.substring(index + 1);
- map[Uri.decodeQueryComponent(key, decode: decode)] =
- decodeQueryComponent(value, decode: decode);
+ map[Uri.decodeQueryComponent(key, encoding: encoding)] =
+ decodeQueryComponent(value, encoding: encoding);
}
return map;
});
}
+ /**
+ * Parse the [host] as an IP version 4 (IPv4) address, returning the address
+ * as a list of 4 bytes in network byte order (big endian).
+ *
+ * Throws a [FormatException] if [host] is not a valid IPv4 address
+ * representation.
+ */
+ static List<int> parseIPv4Address(String host) {
+ void error(String msg) {
+ throw new FormatException('Illegal IPv4 address, $msg');
+ }
+ var bytes = host.split('.');
+ if (bytes.length != 4) {
+ error('IPv4 address should contain exactly 4 parts');
+ }
+ // TODO(ajohnsen): Consider using Uint8List.
+ return bytes
+ .map((byteString) {
+ int byte = int.parse(byteString);
+ if (byte < 0 || byte > 255) {
+ error('each part must be in the range of `0..255`');
+ }
+ return byte;
+ })
+ .toList();
+ }
+
+ /**
+ * Parse the [host] as an IP version 6 (IPv6) address, returning the address
+ * as a list of 16 bytes in network byte order (big endian).
+ *
+ * Throws a [FormatException] if [host] is not a valid IPv6 address
+ * representation.
+ *
+ * Some examples of IPv6 addresses:
+ * * ::1
+ * * FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
+ * * 3ffe:2a00:100:7031::1
+ * * ::FFFF:129.144.52.38
+ * * 2010:836B:4179::836B:4179
+ */
+ static List<int> parseIPv6Address(String host) {
+ // An IPv6 address consists of exactly 8 parts of 1-4 hex digits, seperated
+ // by `:`'s, with the following exceptions:
+ //
+ // - One (and only one) wildcard (`::`) may be present, representing a fill
+ // of 0's. The IPv6 `::` is thus 16 bytes of `0`.
+ // - The last two parts may be replaced by an IPv4 address.
+ void error(String msg) {
+ throw new FormatException('Illegal IPv6 address, $msg');
+ }
+ int parseHex(int start, int end) {
+ if (end - start > 4) {
+ error('an IPv6 part can only contain a maximum of 4 hex digits');
+ }
+ int value = int.parse(host.substring(start, end), radix: 16);
+ if (value < 0 || value > (1 << 16) - 1) {
+ error('each part must be in the range of `0x0..0xFFFF`');
+ }
+ return value;
+ }
+ if (host.length < 2) error('address is too short');
+ List<int> parts = [];
+ bool wildcardSeen = false;
+ int partStart = 0;
+ // Parse all parts, except a potential last one.
+ for (int i = 0; i < host.length; i++) {
+ if (host.codeUnitAt(i) == _COLON) {
+ if (i == 0) {
+ // If we see a `:` in the beginning, expect wildcard.
+ i++;
+ if (host.codeUnitAt(i) != _COLON) {
+ error('invalid start colon.');
+ }
+ partStart = i;
+ }
+ if (i == partStart) {
+ // Wildcard. We only allow one.
+ if (wildcardSeen) {
+ error('only one wildcard `::` is allowed');
+ }
+ wildcardSeen = true;
+ parts.add(-1);
+ } else {
+ // Found a single colon. Parse [partStart..i] as a hex entry.
+ parts.add(parseHex(partStart, i));
+ }
+ partStart = i + 1;
+ }
+ }
+ if (parts.length == 0) error('too few parts');
+ bool atEnd = partStart == host.length;
+ bool isLastWildcard = parts.last == -1;
+ if (atEnd && !isLastWildcard) {
+ error('expected a part after last `:`');
+ }
+ if (!atEnd) {
+ try {
+ parts.add(parseHex(partStart, host.length));
+ } catch (e) {
+ // Failed to parse the last chunk as hex. Try IPv4.
+ try {
+ List<int> last = parseIPv4Address(host.substring(partStart));
+ parts.add(last[0] << 8 | last[1]);
+ parts.add(last[2] << 8 | last[3]);
+ } catch (e) {
+ error('invalid end of IPv6 address.');
+ }
+ }
+ }
+ if (wildcardSeen) {
+ if (parts.length > 7) {
+ error('an address with a wildcard must have less than 7 parts');
+ }
+ } else if (parts.length != 8) {
+ error('an address without a wildcard must contain exactly 8 parts');
+ }
+ // TODO(ajohnsen): Consider using Uint8List.
+ return parts
+ .expand((value) {
+ if (value == -1) {
+ return new List.filled((9 - parts.length) * 2, 0);
+ } else {
+ return [(value >> 8) & 0xFF, value & 0xFF];
+ }
+ })
+ .toList();
+ }
+
// Frequently used character codes.
static const int _DOUBLE_QUOTE = 0x22;
static const int _PERCENT = 0x25;
@@ -1164,7 +1349,9 @@
static const int _UPPER_CASE_A = 0x41;
static const int _UPPER_CASE_F = 0x46;
static const int _UPPER_CASE_Z = 0x5A;
+ static const int _LEFT_BRACKET = 0x5B;
static const int _BACKSLASH = 0x5C;
+ static const int _RIGHT_BRACKET = 0x5D;
static const int _LOWER_CASE_A = 0x61;
static const int _LOWER_CASE_F = 0x66;
static const int _LOWER_CASE_Z = 0x7A;
@@ -1244,11 +1431,11 @@
* If [plusToSpace] is `true`, plus characters will be converted to spaces.
*
* The decoder will create a byte-list of the percent-encoded parts, and then
- * decode the byte-list using [decode]. Default is a UTF-8 decoder.
+ * decode the byte-list using [encoding]. The default encodingis UTF-8.
*/
static String _uriDecode(String text,
{bool plusToSpace: false,
- String decode(List<int> bytes): null}) {
+ Encoding encoding: UTF8}) {
StringBuffer result = new StringBuffer();
List<int> codepoints = new List<int>();
for (int i = 0; i < text.length;) {
@@ -1271,13 +1458,16 @@
if (i == text.length) break;
ch = text.codeUnitAt(i);
}
- result.write(
- decode == null ? UTF8.decode(codepoints) : decode(codepoints));
+ result.write(encoding.decode(codepoints));
}
}
return result.toString();
}
+ static bool _isAlphabeticCharacter(int codeUnit)
+ => (codeUnit >= _LOWER_CASE_A && codeUnit <= _LOWER_CASE_Z) ||
+ (codeUnit >= _UPPER_CASE_A && codeUnit <= _UPPER_CASE_Z);
+
// Tables of char-codes organized as a bit vector of 128 bits where
// each bit indicate whether a character code on the 0-127 needs to
// be escaped or not.
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 129d2d9..d5bf1dd 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -12,7 +12,7 @@
*
* * If you've never written a web app before, try our
* tutorials—[A Game of Darts](http://dartlang.org/docs/tutorials).
- *
+ *
* * To see some web-based Dart apps in action and to play with the code,
* download
* [Dart Editor](http://www.dartlang.org/#get-started)
@@ -54,7 +54,8 @@
JSName, Null, Returns,
findDispatchTagForInterceptorClass, setNativeSubclassDispatchRecord;
import 'dart:_interceptors' show
- Interceptor, JSExtendableArray, findInterceptorConstructorForType;
+ Interceptor, JSExtendableArray, findInterceptorConstructorForType,
+ getNativeInterceptor;
@@ -184,7 +185,7 @@
@DomName('HTMLAnchorElement.HTMLAnchorElement')
@DocsEditable()
factory AnchorElement({String href}) {
- var e = document.$dom_createElement("a");
+ var e = document.createElement("a");
if (href != null) e.href = href;
return e;
}
@@ -432,7 +433,7 @@
@DomName('HTMLAreaElement.HTMLAreaElement')
@DocsEditable()
- factory AreaElement() => document.$dom_createElement("area");
+ factory AreaElement() => document.createElement("area");
@DomName('HTMLAreaElement.alt')
@DocsEditable()
@@ -540,7 +541,7 @@
@DomName('HTMLBRElement.HTMLBRElement')
@DocsEditable()
- factory BRElement() => document.$dom_createElement("br");
+ factory BRElement() => document.createElement("br");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -570,7 +571,7 @@
@DomName('HTMLBaseElement.HTMLBaseElement')
@DocsEditable()
- factory BaseElement() => document.$dom_createElement("base");
+ factory BaseElement() => document.createElement("base");
@DomName('HTMLBaseElement.href')
@DocsEditable()
@@ -697,7 +698,7 @@
@DomName('HTMLBodyElement.HTMLBodyElement')
@DocsEditable()
- factory BodyElement() => document.$dom_createElement("body");
+ factory BodyElement() => document.createElement("body");
@DomName('HTMLBodyElement.onblur')
@DocsEditable()
@@ -760,7 +761,7 @@
@DomName('HTMLButtonElement.HTMLButtonElement')
@DocsEditable()
- factory ButtonElement() => document.$dom_createElement("button");
+ factory ButtonElement() => document.createElement("button");
@DomName('HTMLButtonElement.autofocus')
@DocsEditable()
@@ -882,7 +883,7 @@
@DomName('HTMLCanvasElement.HTMLCanvasElement')
@DocsEditable()
factory CanvasElement({int width, int height}) {
- var e = document.$dom_createElement("canvas");
+ var e = document.createElement("canvas");
if (width != null) e.width = width;
if (height != null) e.height = height;
return e;
@@ -1975,7 +1976,7 @@
@DomName('HTMLContentElement.HTMLContentElement')
@DocsEditable()
- factory ContentElement() => document.$dom_createElement("content");
+ factory ContentElement() => document.createElement("content");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('content');
@@ -6317,7 +6318,7 @@
@DomName('HTMLDListElement.HTMLDListElement')
@DocsEditable()
- factory DListElement() => document.$dom_createElement("dl");
+ factory DListElement() => document.createElement("dl");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -6336,7 +6337,7 @@
@DomName('HTMLDataListElement.HTMLDataListElement')
@DocsEditable()
- factory DataListElement() => document.$dom_createElement("datalist");
+ factory DataListElement() => document.createElement("datalist");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('datalist');
@@ -6551,7 +6552,7 @@
@DomName('HTMLDetailsElement.HTMLDetailsElement')
@DocsEditable()
- factory DetailsElement() => document.$dom_createElement("details");
+ factory DetailsElement() => document.createElement("details");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('details');
@@ -6914,10 +6915,11 @@
@DocsEditable()
/**
- * Represents an HTML <div> element.
+ * A generic container for content on an HTML page;
+ * corresponds to the <div> tag.
*
- * The [DivElement] is a generic container for content and does not have any
- * special significance. It is functionally similar to [SpanElement].
+ * The [DivElement] is a generic container and does not have any semantic
+ * significance. It is functionally similar to [SpanElement].
*
* The [DivElement] is a block-level element, as opposed to [SpanElement],
* which is an inline-level element.
@@ -6941,7 +6943,7 @@
@DomName('HTMLDivElement.HTMLDivElement')
@DocsEditable()
- factory DivElement() => document.$dom_createElement("div");
+ factory DivElement() => document.createElement("div");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -7169,16 +7171,14 @@
@DocsEditable()
DocumentFragment createDocumentFragment() native;
- @JSName('createElement')
/// Deprecated: use new Element.tag(tagName) instead.
@DomName('Document.createElement')
@DocsEditable()
- Element $dom_createElement(String localName_OR_tagName, [String typeExtension]) native;
+ Element createElement(String localName_OR_tagName, [String typeExtension]) native;
- @JSName('createElementNS')
@DomName('Document.createElementNS')
@DocsEditable()
- Element $dom_createElementNS(String namespaceURI, String qualifiedName, [String typeExtension]) native;
+ Element createElementNS(String namespaceURI, String qualifiedName, [String typeExtension]) native;
@JSName('createEvent')
@DomName('Document.createEvent')
@@ -8819,8 +8819,8 @@
*
* * [isTagSupported]
*/
- factory Element.tag(String tag) =>
- _ElementFactoryProvider.createElement_tag(tag);
+ factory Element.tag(String tag, [String typeExtention]) =>
+ _ElementFactoryProvider.createElement_tag(tag, typeExtention);
/// Creates a new `<a>` element.
///
@@ -9137,7 +9137,7 @@
* The tag should be a valid HTML tag name.
*/
static bool isTagSupported(String tag) {
- var e = _ElementFactoryProvider.createElement_tag(tag);
+ var e = _ElementFactoryProvider.createElement_tag(tag, null);
return e is Element && !(e is UnknownElement);
}
@@ -9397,7 +9397,7 @@
@SupportedBrowser(SupportedBrowser.CHROME, '25')
@Experimental()
ShadowRoot get shadowRoot =>
- JS('ShadowRoot', '#.shadowRoot || #.webkitShadowRoot', this, this);
+ JS('ShadowRoot|Null', '#.shadowRoot || #.webkitShadowRoot', this, this);
/**
@@ -9688,12 +9688,17 @@
if (_parseDocument == null) {
_parseDocument = document.implementation.createHtmlDocument('');
_parseRange = _parseDocument.createRange();
+
+ // Workaround for Chrome bug 229142- URIs are not resolved in new doc.
+ var base = _parseDocument.createElement('base');
+ base.href = document._baseUri;
+ _parseDocument.head.append(base);
}
var contextElement;
if (this is BodyElement) {
contextElement = _parseDocument.body;
} else {
- contextElement = _parseDocument.$dom_createElement(tagName);
+ contextElement = _parseDocument.createElement(tagName);
_parseDocument.body.append(contextElement);
}
var fragment;
@@ -10586,9 +10591,19 @@
@DomName('Document.createElement')
// Optimization to improve performance until the dart2js compiler inlines this
// method.
- static dynamic createElement_tag(String tag) =>
- // Firefox may return a JS function for some types (Embed, Object).
- JS('Element|=Object', 'document.createElement(#)', tag);
+ static dynamic createElement_tag(String tag, String typeExtension) {
+ // Firefox may return a JS function for some types (Embed, Object).
+ if (typeExtension != null) {
+ return JS('Element|=Object', 'document.createElement(#, #)',
+ tag, typeExtension);
+ }
+ // Should be able to eliminate this and just call the two-arg version above
+ // with null typeExtension, but Chrome treats the tag as case-sensitive if
+ // typeExtension is null.
+ // https://code.google.com/p/chromium/issues/detail?id=282467
+ return JS('Element|=Object', 'document.createElement(#)', tag);
+ }
+
}
@@ -10624,7 +10639,7 @@
@DomName('HTMLEmbedElement.HTMLEmbedElement')
@DocsEditable()
- factory EmbedElement() => document.$dom_createElement("embed");
+ factory EmbedElement() => document.createElement("embed");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('embed');
@@ -11263,7 +11278,7 @@
@DomName('HTMLFieldSetElement.HTMLFieldSetElement')
@DocsEditable()
- factory FieldSetElement() => document.$dom_createElement("fieldset");
+ factory FieldSetElement() => document.createElement("fieldset");
@DomName('HTMLFieldSetElement.disabled')
@DocsEditable()
@@ -11957,7 +11972,7 @@
@DomName('HTMLFormElement.HTMLFormElement')
@DocsEditable()
- factory FormElement() => document.$dom_createElement("form");
+ factory FormElement() => document.createElement("form");
@DomName('HTMLFormElement.acceptCharset')
@DocsEditable()
@@ -12217,7 +12232,7 @@
@DomName('HTMLHRElement.HTMLHRElement')
@DocsEditable()
- factory HRElement() => document.$dom_createElement("hr");
+ factory HRElement() => document.createElement("hr");
}
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -12273,7 +12288,7 @@
@DomName('HTMLHeadElement.HTMLHeadElement')
@DocsEditable()
- factory HeadElement() => document.$dom_createElement("head");
+ factory HeadElement() => document.createElement("head");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -12288,27 +12303,27 @@
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h1() => document.$dom_createElement("h1");
+ factory HeadingElement.h1() => document.createElement("h1");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h2() => document.$dom_createElement("h2");
+ factory HeadingElement.h2() => document.createElement("h2");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h3() => document.$dom_createElement("h3");
+ factory HeadingElement.h3() => document.createElement("h3");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h4() => document.$dom_createElement("h4");
+ factory HeadingElement.h4() => document.createElement("h4");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h5() => document.$dom_createElement("h5");
+ factory HeadingElement.h5() => document.createElement("h5");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h6() => document.$dom_createElement("h6");
+ factory HeadingElement.h6() => document.createElement("h6");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -12732,8 +12747,76 @@
'#.webkitVisibilityState)', this, this, this, this);
@Experimental
- void register(String tag, Type customElementClass) {
- _registerCustomElement(JS('', 'window'), this, tag, customElementClass);
+ /**
+ * Register a custom subclass of Element to be instantiatable by the DOM.
+ *
+ * This is necessary to allow the construction of any custom elements.
+ *
+ * The class being registered must either subclass HtmlElement or SvgElement.
+ * If they subclass these directly then they can be used as:
+ *
+ * class FooElement extends HtmlElement{
+ * void created() {
+ * print('FooElement created!');
+ * }
+ * }
+ *
+ * main() {
+ * document.register('x-foo', FooElement);
+ * var myFoo = new Element.tag('x-foo');
+ * // prints 'FooElement created!' to the console.
+ * }
+ *
+ * The custom element can also be instantiated via HTML using the syntax
+ * `<x-foo></x-foo>`
+ *
+ * Other elements can be subclassed as well:
+ *
+ * class BarElement extends InputElement{
+ * void created() {
+ * print('BarElement created!');
+ * }
+ * }
+ *
+ * main() {
+ * document.register('x-bar', BarElement);
+ * var myBar = new Element.tag('input', 'x-bar');
+ * // prints 'BarElement created!' to the console.
+ * }
+ *
+ * This custom element can also be instantiated via HTML using the syntax
+ * `<input is="x-bar"></input>`
+ *
+ * The [nativeTagName] parameter is needed by platforms without native support
+ * when subclassing a native type other than:
+ *
+ * * HtmlElement
+ * * SvgElement
+ * * AnchorElement
+ * * AudioElement
+ * * ButtonElement
+ * * CanvasElement
+ * * DivElement
+ * * ImageElement
+ * * InputElement
+ * * LIElement
+ * * LabelElement
+ * * MenuElement
+ * * MeterElement
+ * * OListElement
+ * * OptionElement
+ * * OutputElement
+ * * ParagraphElement
+ * * PreElement
+ * * ProgressElement
+ * * SelectElement
+ * * SpanElement
+ * * UListElement
+ * * VideoElement
+ */
+ void register(String tag, Type customElementClass, {String nativeTagName}) {
+ _registerCustomElement(JS('', 'window'), this, tag, customElementClass,
+ nativeTagName);
}
@Creates('Null') // Set from Dart code; does not instantiate a native type.
@@ -12798,7 +12881,7 @@
@DomName('HTMLHtmlElement.HTMLHtmlElement')
@DocsEditable()
- factory HtmlHtmlElement() => document.$dom_createElement("html");
+ factory HtmlHtmlElement() => document.createElement("html");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -13394,7 +13477,7 @@
@DomName('HTMLIFrameElement.HTMLIFrameElement')
@DocsEditable()
- factory IFrameElement() => document.$dom_createElement("iframe");
+ factory IFrameElement() => document.createElement("iframe");
@DomName('HTMLIFrameElement.contentWindow')
@DocsEditable()
@@ -13488,7 +13571,7 @@
@DomName('HTMLImageElement.HTMLImageElement')
@DocsEditable()
factory ImageElement({String src, int width, int height}) {
- var e = document.$dom_createElement("img");
+ var e = document.createElement("img");
if (src != null) e.src = src;
if (width != null) e.width = width;
if (height != null) e.height = height;
@@ -13587,7 +13670,7 @@
native "HTMLInputElement" {
factory InputElement({String type}) {
- var e = document.$dom_createElement("input");
+ var e = document.createElement("input");
if (type != null) {
try {
// IE throws an exception for unknown types.
@@ -14627,7 +14710,7 @@
@DomName('HTMLKeygenElement.HTMLKeygenElement')
@DocsEditable()
- factory KeygenElement() => document.$dom_createElement("keygen");
+ factory KeygenElement() => document.createElement("keygen");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('keygen') && (new Element.tag('keygen') is KeygenElement);
@@ -14700,7 +14783,7 @@
@DomName('HTMLLIElement.HTMLLIElement')
@DocsEditable()
- factory LIElement() => document.$dom_createElement("li");
+ factory LIElement() => document.createElement("li");
@DomName('HTMLLIElement.type')
@DocsEditable()
@@ -14725,7 +14808,7 @@
@DomName('HTMLLabelElement.HTMLLabelElement')
@DocsEditable()
- factory LabelElement() => document.$dom_createElement("label");
+ factory LabelElement() => document.createElement("label");
@DomName('HTMLLabelElement.control')
@DocsEditable()
@@ -14752,7 +14835,7 @@
@DomName('HTMLLegendElement.HTMLLegendElement')
@DocsEditable()
- factory LegendElement() => document.$dom_createElement("legend");
+ factory LegendElement() => document.createElement("legend");
@DomName('HTMLLegendElement.form')
@DocsEditable()
@@ -14771,7 +14854,7 @@
@DomName('HTMLLinkElement.HTMLLinkElement')
@DocsEditable()
- factory LinkElement() => document.$dom_createElement("link");
+ factory LinkElement() => document.createElement("link");
@DomName('HTMLLinkElement.disabled')
@DocsEditable()
@@ -14918,7 +15001,7 @@
@DomName('HTMLMapElement.HTMLMapElement')
@DocsEditable()
- factory MapElement() => document.$dom_createElement("map");
+ factory MapElement() => document.createElement("map");
@DomName('HTMLMapElement.areas')
@DocsEditable()
@@ -16135,7 +16218,7 @@
@DomName('HTMLMenuElement.HTMLMenuElement')
@DocsEditable()
- factory MenuElement() => document.$dom_createElement("menu");
+ factory MenuElement() => document.createElement("menu");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -16282,7 +16365,7 @@
@DomName('HTMLMetaElement.HTMLMetaElement')
@DocsEditable()
- factory MetaElement() => document.$dom_createElement("meta");
+ factory MetaElement() => document.createElement("meta");
@DomName('HTMLMetaElement.content')
@DocsEditable()
@@ -16348,7 +16431,7 @@
@DomName('HTMLMeterElement.HTMLMeterElement')
@DocsEditable()
- factory MeterElement() => document.$dom_createElement("meter");
+ factory MeterElement() => document.createElement("meter");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('meter');
@@ -17785,6 +17868,11 @@
@DocsEditable()
static const int TEXT_NODE = 3;
+ @JSName('baseURI')
+ @DomName('Node.baseURI')
+ @DocsEditable()
+ final String _baseUri;
+
@DomName('Node.childNodes')
@DocsEditable()
@deprecated
@@ -18291,7 +18379,7 @@
@DomName('HTMLOListElement.HTMLOListElement')
@DocsEditable()
- factory OListElement() => document.$dom_createElement("ol");
+ factory OListElement() => document.createElement("ol");
@DomName('HTMLOListElement.reversed')
@DocsEditable()
@@ -18322,7 +18410,7 @@
@DomName('HTMLObjectElement.HTMLObjectElement')
@DocsEditable()
- factory ObjectElement() => document.$dom_createElement("object");
+ factory ObjectElement() => document.createElement("object");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('object');
@@ -18402,7 +18490,7 @@
@DomName('HTMLOptGroupElement.HTMLOptGroupElement')
@DocsEditable()
- factory OptGroupElement() => document.$dom_createElement("optgroup");
+ factory OptGroupElement() => document.createElement("optgroup");
@DomName('HTMLOptGroupElement.disabled')
@DocsEditable()
@@ -18490,7 +18578,7 @@
@DomName('HTMLOutputElement.HTMLOutputElement')
@DocsEditable()
- factory OutputElement() => document.$dom_createElement("output");
+ factory OutputElement() => document.createElement("output");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('output');
@@ -18612,7 +18700,7 @@
@DomName('HTMLParagraphElement.HTMLParagraphElement')
@DocsEditable()
- factory ParagraphElement() => document.$dom_createElement("p");
+ factory ParagraphElement() => document.createElement("p");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -18628,7 +18716,7 @@
@DomName('HTMLParamElement.HTMLParamElement')
@DocsEditable()
- factory ParamElement() => document.$dom_createElement("param");
+ factory ParamElement() => document.createElement("param");
@DomName('HTMLParamElement.name')
@DocsEditable()
@@ -19270,7 +19358,7 @@
@DomName('HTMLPreElement.HTMLPreElement')
@DocsEditable()
- factory PreElement() => document.$dom_createElement("pre");
+ factory PreElement() => document.createElement("pre");
@DomName('HTMLPreElement.wrap')
@DocsEditable()
@@ -19315,7 +19403,7 @@
@DomName('HTMLProgressElement.HTMLProgressElement')
@DocsEditable()
- factory ProgressElement() => document.$dom_createElement("progress");
+ factory ProgressElement() => document.createElement("progress");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('progress');
@@ -19440,7 +19528,7 @@
@DomName('HTMLQuoteElement.HTMLQuoteElement')
@DocsEditable()
- factory QuoteElement() => document.$dom_createElement("q");
+ factory QuoteElement() => document.createElement("q");
@DomName('HTMLQuoteElement.cite')
@DocsEditable()
@@ -20462,7 +20550,7 @@
@DomName('HTMLScriptElement.HTMLScriptElement')
@DocsEditable()
- factory ScriptElement() => document.$dom_createElement("script");
+ factory ScriptElement() => document.createElement("script");
@DomName('HTMLScriptElement.async')
@DocsEditable()
@@ -20649,7 +20737,7 @@
@DomName('HTMLSelectElement.HTMLSelectElement')
@DocsEditable()
- factory SelectElement() => document.$dom_createElement("select");
+ factory SelectElement() => document.createElement("select");
@DomName('HTMLSelectElement.autofocus')
@DocsEditable()
@@ -20892,7 +20980,7 @@
@DomName('HTMLShadowElement.HTMLShadowElement')
@DocsEditable()
- factory ShadowElement() => document.$dom_createElement("shadow");
+ factory ShadowElement() => document.createElement("shadow");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('shadow');
@@ -21135,7 +21223,7 @@
@DomName('HTMLSourceElement.HTMLSourceElement')
@DocsEditable()
- factory SourceElement() => document.$dom_createElement("source");
+ factory SourceElement() => document.createElement("source");
@DomName('HTMLSourceElement.media')
@DocsEditable()
@@ -21192,7 +21280,7 @@
@DomName('HTMLSpanElement.HTMLSpanElement')
@DocsEditable()
- factory SpanElement() => document.$dom_createElement("span");
+ factory SpanElement() => document.createElement("span");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -22091,7 +22179,7 @@
@DomName('HTMLStyleElement.HTMLStyleElement')
@DocsEditable()
- factory StyleElement() => document.$dom_createElement("style");
+ factory StyleElement() => document.createElement("style");
@DomName('HTMLStyleElement.disabled')
@DocsEditable()
@@ -22182,7 +22270,7 @@
@DomName('HTMLTableCaptionElement.HTMLTableCaptionElement')
@DocsEditable()
- factory TableCaptionElement() => document.$dom_createElement("caption");
+ factory TableCaptionElement() => document.createElement("caption");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -22197,7 +22285,7 @@
@DomName('HTMLTableCellElement.HTMLTableCellElement')
@DocsEditable()
- factory TableCellElement() => document.$dom_createElement("td");
+ factory TableCellElement() => document.createElement("td");
@DomName('HTMLTableCellElement.cellIndex')
@DocsEditable()
@@ -22228,7 +22316,7 @@
@DomName('HTMLTableColElement.HTMLTableColElement')
@DocsEditable()
- factory TableColElement() => document.$dom_createElement("col");
+ factory TableColElement() => document.createElement("col");
@DomName('HTMLTableColElement.span')
@DocsEditable()
@@ -22294,7 +22382,7 @@
@DomName('HTMLTableElement.HTMLTableElement')
@DocsEditable()
- factory TableElement() => document.$dom_createElement("table");
+ factory TableElement() => document.createElement("table");
@DomName('HTMLTableElement.border')
@DocsEditable()
@@ -22399,7 +22487,7 @@
@DomName('HTMLTableRowElement.HTMLTableRowElement')
@DocsEditable()
- factory TableRowElement() => document.$dom_createElement("tr");
+ factory TableRowElement() => document.createElement("tr");
@JSName('cells')
@DomName('HTMLTableRowElement.cells')
@@ -22573,7 +22661,7 @@
@DomName('HTMLTemplateElement.HTMLTemplateElement')
@DocsEditable()
- factory TemplateElement() => document.$dom_createElement("template");
+ factory TemplateElement() => document.createElement("template");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('template');
@@ -22693,7 +22781,7 @@
// + <td>Bar</td>
//
static Element _extractTemplateFromAttributeTemplate(Element el) {
- var template = el.document.$dom_createElement('template');
+ var template = el.document.createElement('template');
el.parentNode.insertBefore(template, el);
for (var name in el.attributes.keys.toList()) {
@@ -22846,7 +22934,7 @@
@DomName('HTMLTextAreaElement.HTMLTextAreaElement')
@DocsEditable()
- factory TextAreaElement() => document.$dom_createElement("textarea");
+ factory TextAreaElement() => document.createElement("textarea");
@DomName('HTMLTextAreaElement.autofocus')
@DocsEditable()
@@ -23351,7 +23439,7 @@
@DomName('HTMLTitleElement.HTMLTitleElement')
@DocsEditable()
- factory TitleElement() => document.$dom_createElement("title");
+ factory TitleElement() => document.createElement("title");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -23617,7 +23705,7 @@
@DomName('HTMLTrackElement.HTMLTrackElement')
@DocsEditable()
- factory TrackElement() => document.$dom_createElement("track");
+ factory TrackElement() => document.createElement("track");
/// Checks if this type is supported on the current platform.
static bool get supported => Element.isTagSupported('track');
@@ -23894,7 +23982,7 @@
@DomName('HTMLUListElement.HTMLUListElement')
@DocsEditable()
- factory UListElement() => document.$dom_createElement("ul");
+ factory UListElement() => document.createElement("ul");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -24000,7 +24088,7 @@
@DomName('HTMLVideoElement.HTMLVideoElement')
@DocsEditable()
- factory VideoElement() => document.$dom_createElement("video");
+ factory VideoElement() => document.createElement("video");
@DomName('HTMLVideoElement.height')
@DocsEditable()
@@ -31959,7 +32047,32 @@
convertDartClosureToJS(_callCreated, 1));
}
-void _registerCustomElement(context, document, String tag, Type type) {
+const _typeNameToTag = const {
+ 'HTMLAnchorElement': 'a',
+ 'HTMLAudioElement': 'audio',
+ 'HTMLButtonElement': 'button',
+ 'HTMLCanvasElement': 'canvas',
+ 'HTMLDivElement': 'div',
+ 'HTMLImageElement': 'img',
+ 'HTMLInputElement': 'input',
+ 'HTMLLIElement': 'li',
+ 'HTMLLabelElement': 'label',
+ 'HTMLMenuElement': 'menu',
+ 'HTMLMeterElement': 'meter',
+ 'HTMLOListElement': 'ol',
+ 'HTMLOptionElement': 'option',
+ 'HTMLOutputElement': 'output',
+ 'HTMLParagraphElement': 'p',
+ 'HTMLPreElement': 'pre',
+ 'HTMLProgressElement': 'progress',
+ 'HTMLSelectElement': 'select',
+ 'HTMLSpanElement': 'span',
+ 'HTMLUListElement': 'ul',
+ 'HTMLVideoElement': 'video',
+};
+
+void _registerCustomElement(context, document, String tag, Type type,
+ String extendsTagName) {
// Function follows the same pattern as the following JavaScript code for
// registering a custom element.
//
@@ -31979,6 +32092,10 @@
throw new ArgumentError(type);
}
+ // Workaround for 13190- use an article element to ensure that HTMLElement's
+ // interceptor is resolved correctly.
+ getNativeInterceptor(new Element.tag('article'));
+
String baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
if (baseClassName == null) {
throw new ArgumentError(type);
@@ -32001,8 +32118,17 @@
setNativeSubclassDispatchRecord(proto, interceptor);
- JS('void', '#.register(#, #)',
- document, tag, JS('', '{prototype: #}', proto));
+ var options = JS('=Object', '{prototype: #}', proto);
+
+ if (baseClassName != 'HTMLElement') {
+ if (extendsTagName != null) {
+ JS('=Object', '#.extends = #', options, extendsTagName);
+ } else if (_typeNameToTag.containsKey(baseClassName)) {
+ JS('=Object', '#.extends = #', options, _typeNameToTag[baseClassName]);
+ }
+ }
+
+ JS('void', '#.register(#, #)', document, tag, options);
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 3bce62d..4682b6c 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -204,7 +204,7 @@
@DomName('HTMLAnchorElement.HTMLAnchorElement')
@DocsEditable()
factory AnchorElement({String href}) {
- var e = document.$dom_createElement("a");
+ var e = document.createElement("a");
if (href != null) e.href = href;
return e;
}
@@ -537,7 +537,7 @@
@DomName('HTMLAreaElement.HTMLAreaElement')
@DocsEditable()
- factory AreaElement() => document.$dom_createElement("area");
+ factory AreaElement() => document.createElement("area");
@DomName('HTMLAreaElement.alt')
@DocsEditable()
@@ -678,7 +678,7 @@
@DomName('HTMLBRElement.HTMLBRElement')
@DocsEditable()
- factory BRElement() => document.$dom_createElement("br");
+ factory BRElement() => document.createElement("br");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -714,7 +714,7 @@
@DomName('HTMLBaseElement.HTMLBaseElement')
@DocsEditable()
- factory BaseElement() => document.$dom_createElement("base");
+ factory BaseElement() => document.createElement("base");
@DomName('HTMLBaseElement.href')
@DocsEditable()
@@ -861,7 +861,7 @@
@DomName('HTMLBodyElement.HTMLBodyElement')
@DocsEditable()
- factory BodyElement() => document.$dom_createElement("body");
+ factory BodyElement() => document.createElement("body");
@DomName('HTMLBodyElement.onblur')
@DocsEditable()
@@ -927,7 +927,7 @@
@DomName('HTMLButtonElement.HTMLButtonElement')
@DocsEditable()
- factory ButtonElement() => document.$dom_createElement("button");
+ factory ButtonElement() => document.createElement("button");
@DomName('HTMLButtonElement.autofocus')
@DocsEditable()
@@ -1098,7 +1098,7 @@
@DomName('HTMLCanvasElement.HTMLCanvasElement')
@DocsEditable()
factory CanvasElement({int width, int height}) {
- var e = document.$dom_createElement("canvas");
+ var e = document.createElement("canvas");
if (width != null) e.width = width;
if (height != null) e.height = height;
return e;
@@ -2369,7 +2369,7 @@
@DomName('HTMLContentElement.HTMLContentElement')
@DocsEditable()
- factory ContentElement() => document.$dom_createElement("content");
+ factory ContentElement() => document.createElement("content");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -6884,7 +6884,7 @@
@DomName('HTMLDListElement.HTMLDListElement')
@DocsEditable()
- factory DListElement() => document.$dom_createElement("dl");
+ factory DListElement() => document.createElement("dl");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -6906,7 +6906,7 @@
@DomName('HTMLDataListElement.HTMLDataListElement')
@DocsEditable()
- factory DataListElement() => document.$dom_createElement("datalist");
+ factory DataListElement() => document.createElement("datalist");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -7145,7 +7145,7 @@
@DomName('HTMLDetailsElement.HTMLDetailsElement')
@DocsEditable()
- factory DetailsElement() => document.$dom_createElement("details");
+ factory DetailsElement() => document.createElement("details");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -7456,10 +7456,11 @@
@DocsEditable()
/**
- * Represents an HTML <div> element.
+ * A generic container for content on an HTML page;
+ * corresponds to the <div> tag.
*
- * The [DivElement] is a generic container for content and does not have any
- * special significance. It is functionally similar to [SpanElement].
+ * The [DivElement] is a generic container and does not have any semantic
+ * significance. It is functionally similar to [SpanElement].
*
* The [DivElement] is a block-level element, as opposed to [SpanElement],
* which is an inline-level element.
@@ -7483,7 +7484,7 @@
@DomName('HTMLDivElement.HTMLDivElement')
@DocsEditable()
- factory DivElement() => document.$dom_createElement("div");
+ factory DivElement() => document.createElement("div");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -7705,7 +7706,7 @@
@DocsEditable()
DocumentFragment createDocumentFragment() native "Document_createDocumentFragment_Callback";
- Element $dom_createElement(String localName_OR_tagName, [String typeExtension]) {
+ Element createElement(String localName_OR_tagName, [String typeExtension]) {
if ((localName_OR_tagName is String || localName_OR_tagName == null) && typeExtension == null) {
return _createElement_1(localName_OR_tagName);
}
@@ -7719,7 +7720,7 @@
Element _createElement_2(localName_OR_tagName, typeExtension) native "Document__createElement_2_Callback";
- Element $dom_createElementNS(String namespaceURI, String qualifiedName, [String typeExtension]) {
+ Element createElementNS(String namespaceURI, String qualifiedName, [String typeExtension]) {
if ((qualifiedName is String || qualifiedName == null) && (namespaceURI is String || namespaceURI == null) && typeExtension == null) {
return _createElementNS_1(namespaceURI, qualifiedName);
}
@@ -8491,7 +8492,7 @@
String get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -8499,7 +8500,7 @@
String get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -8507,7 +8508,7 @@
String get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -9398,8 +9399,8 @@
*
* * [isTagSupported]
*/
- factory Element.tag(String tag) =>
- _ElementFactoryProvider.createElement_tag(tag);
+ factory Element.tag(String tag, [String typeExtention]) =>
+ _ElementFactoryProvider.createElement_tag(tag, typeExtention);
/// Creates a new `<a>` element.
///
@@ -9716,7 +9717,7 @@
* The tag should be a valid HTML tag name.
*/
static bool isTagSupported(String tag) {
- var e = _ElementFactoryProvider.createElement_tag(tag);
+ var e = _ElementFactoryProvider.createElement_tag(tag, null);
return e is Element && !(e is UnknownElement);
}
@@ -10103,12 +10104,17 @@
if (_parseDocument == null) {
_parseDocument = document.implementation.createHtmlDocument('');
_parseRange = _parseDocument.createRange();
+
+ // Workaround for Chrome bug 229142- URIs are not resolved in new doc.
+ var base = _parseDocument.createElement('base');
+ base.href = document._baseUri;
+ _parseDocument.head.append(base);
}
var contextElement;
if (this is BodyElement) {
contextElement = _parseDocument.body;
} else {
- contextElement = _parseDocument.$dom_createElement(tagName);
+ contextElement = _parseDocument.createElement(tagName);
_parseDocument.body.append(contextElement);
}
var fragment;
@@ -11004,8 +11010,8 @@
class _ElementFactoryProvider {
@DomName('Document.createElement')
- static Element createElement_tag(String tag) =>
- document.$dom_createElement(tag);
+ static Element createElement_tag(String tag, String typeExtension) =>
+ document.createElement(tag, typeExtension);
}
@@ -11043,7 +11049,7 @@
@DomName('HTMLEmbedElement.HTMLEmbedElement')
@DocsEditable()
- factory EmbedElement() => document.$dom_createElement("embed");
+ factory EmbedElement() => document.createElement("embed");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -11712,7 +11718,7 @@
@DomName('HTMLFieldSetElement.HTMLFieldSetElement')
@DocsEditable()
- factory FieldSetElement() => document.$dom_createElement("fieldset");
+ factory FieldSetElement() => document.createElement("fieldset");
@DomName('HTMLFieldSetElement.disabled')
@DocsEditable()
@@ -11947,7 +11953,7 @@
File get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -11955,7 +11961,7 @@
File get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -11963,7 +11969,7 @@
File get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -12464,7 +12470,7 @@
@DomName('HTMLFormElement.HTMLFormElement')
@DocsEditable()
- factory FormElement() => document.$dom_createElement("form");
+ factory FormElement() => document.createElement("form");
@DomName('HTMLFormElement.acceptCharset')
@DocsEditable()
@@ -12750,7 +12756,7 @@
@DomName('HTMLHRElement.HTMLHRElement')
@DocsEditable()
- factory HRElement() => document.$dom_createElement("hr");
+ factory HRElement() => document.createElement("hr");
}
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
@@ -12806,7 +12812,7 @@
@DomName('HTMLHeadElement.HTMLHeadElement')
@DocsEditable()
- factory HeadElement() => document.$dom_createElement("head");
+ factory HeadElement() => document.createElement("head");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12824,27 +12830,27 @@
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h1() => document.$dom_createElement("h1");
+ factory HeadingElement.h1() => document.createElement("h1");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h2() => document.$dom_createElement("h2");
+ factory HeadingElement.h2() => document.createElement("h2");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h3() => document.$dom_createElement("h3");
+ factory HeadingElement.h3() => document.createElement("h3");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h4() => document.$dom_createElement("h4");
+ factory HeadingElement.h4() => document.createElement("h4");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h5() => document.$dom_createElement("h5");
+ factory HeadingElement.h5() => document.createElement("h5");
@DomName('HTMLHeadingElement.HTMLHeadingElement')
@DocsEditable()
- factory HeadingElement.h6() => document.$dom_createElement("h6");
+ factory HeadingElement.h6() => document.createElement("h6");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12984,7 +12990,7 @@
Node get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -12992,7 +12998,7 @@
Node get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -13000,7 +13006,7 @@
Node get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -13061,7 +13067,7 @@
Node get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -13069,7 +13075,7 @@
Node get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -13077,7 +13083,7 @@
Node get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -13277,8 +13283,75 @@
String get visibilityState => _webkitVisibilityState;
@Experimental
- void register(String tag, Type custom) {
- _Utils.register(tag, custom);
+ /**
+ * Register a custom subclass of Element to be instantiatable by the DOM.
+ *
+ * This is necessary to allow the construction of any custom elements.
+ *
+ * The class being registered must either subclass HtmlElement or SvgElement.
+ * If they subclass these directly then they can be used as:
+ *
+ * class FooElement extends HtmlElement{
+ * void created() {
+ * print('FooElement created!');
+ * }
+ * }
+ *
+ * main() {
+ * document.register('x-foo', FooElement);
+ * var myFoo = new Element.tag('x-foo');
+ * // prints 'FooElement created!' to the console.
+ * }
+ *
+ * The custom element can also be instantiated via HTML using the syntax
+ * `<x-foo></x-foo>`
+ *
+ * Other elements can be subclassed as well:
+ *
+ * class BarElement extends InputElement{
+ * void created() {
+ * print('BarElement created!');
+ * }
+ * }
+ *
+ * main() {
+ * document.register('x-bar', BarElement);
+ * var myBar = new Element.tag('input', 'x-bar');
+ * // prints 'BarElement created!' to the console.
+ * }
+ *
+ * This custom element can also be instantiated via HTML using the syntax
+ * `<input is="x-bar"></input>`
+ *
+ * The [nativeTagName] parameter is needed by platforms without native support
+ * when subclassing a native type other than:
+ *
+ * * HtmlElement
+ * * SvgElement
+ * * AnchorElement
+ * * AudioElement
+ * * ButtonElement
+ * * CanvasElement
+ * * DivElement
+ * * ImageElement
+ * * InputElement
+ * * LIElement
+ * * LabelElement
+ * * MenuElement
+ * * MeterElement
+ * * OListElement
+ * * OptionElement
+ * * OutputElement
+ * * ParagraphElement
+ * * PreElement
+ * * ProgressElement
+ * * SelectElement
+ * * SpanElement
+ * * UListElement
+ * * VideoElement
+ */
+ void register(String tag, Type customElementClass, {String nativeTagName}) {
+ _Utils.register(tag, customElementClass);
}
// Note: used to polyfill <template>
@@ -13486,7 +13559,7 @@
@DomName('HTMLHtmlElement.HTMLHtmlElement')
@DocsEditable()
- factory HtmlHtmlElement() => document.$dom_createElement("html");
+ factory HtmlHtmlElement() => document.createElement("html");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -14074,7 +14147,7 @@
@DomName('HTMLIFrameElement.HTMLIFrameElement')
@DocsEditable()
- factory IFrameElement() => document.$dom_createElement("iframe");
+ factory IFrameElement() => document.createElement("iframe");
@DomName('HTMLIFrameElement.contentWindow')
@DocsEditable()
@@ -14202,7 +14275,7 @@
@DomName('HTMLImageElement.HTMLImageElement')
@DocsEditable()
factory ImageElement({String src, int width, int height}) {
- var e = document.$dom_createElement("img");
+ var e = document.createElement("img");
if (src != null) e.src = src;
if (width != null) e.width = width;
if (height != null) e.height = height;
@@ -14339,7 +14412,7 @@
{
factory InputElement({String type}) {
- var e = document.$dom_createElement("input");
+ var e = document.createElement("input");
if (type != null) {
try {
// IE throws an exception for unknown types.
@@ -15554,7 +15627,7 @@
@DomName('HTMLKeygenElement.HTMLKeygenElement')
@DocsEditable()
- factory KeygenElement() => document.$dom_createElement("keygen");
+ factory KeygenElement() => document.createElement("keygen");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -15648,7 +15721,7 @@
@DomName('HTMLLIElement.HTMLLIElement')
@DocsEditable()
- factory LIElement() => document.$dom_createElement("li");
+ factory LIElement() => document.createElement("li");
@DomName('HTMLLIElement.type')
@DocsEditable()
@@ -15686,7 +15759,7 @@
@DomName('HTMLLabelElement.HTMLLabelElement')
@DocsEditable()
- factory LabelElement() => document.$dom_createElement("label");
+ factory LabelElement() => document.createElement("label");
@DomName('HTMLLabelElement.control')
@DocsEditable()
@@ -15720,7 +15793,7 @@
@DomName('HTMLLegendElement.HTMLLegendElement')
@DocsEditable()
- factory LegendElement() => document.$dom_createElement("legend");
+ factory LegendElement() => document.createElement("legend");
@DomName('HTMLLegendElement.form')
@DocsEditable()
@@ -15742,7 +15815,7 @@
@DomName('HTMLLinkElement.HTMLLinkElement')
@DocsEditable()
- factory LinkElement() => document.$dom_createElement("link");
+ factory LinkElement() => document.createElement("link");
@DomName('HTMLLinkElement.disabled')
@DocsEditable()
@@ -15949,7 +16022,7 @@
@DomName('HTMLMapElement.HTMLMapElement')
@DocsEditable()
- factory MapElement() => document.$dom_createElement("map");
+ factory MapElement() => document.createElement("map");
@DomName('HTMLMapElement.areas')
@DocsEditable()
@@ -17408,7 +17481,7 @@
@DomName('HTMLMenuElement.HTMLMenuElement')
@DocsEditable()
- factory MenuElement() => document.$dom_createElement("menu");
+ factory MenuElement() => document.createElement("menu");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -17545,7 +17618,7 @@
@DomName('HTMLMetaElement.HTMLMetaElement')
@DocsEditable()
- factory MetaElement() => document.$dom_createElement("meta");
+ factory MetaElement() => document.createElement("meta");
@DomName('HTMLMetaElement.content')
@DocsEditable()
@@ -17624,7 +17697,7 @@
@DomName('HTMLMeterElement.HTMLMeterElement')
@DocsEditable()
- factory MeterElement() => document.$dom_createElement("meter");
+ factory MeterElement() => document.createElement("meter");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -17971,7 +18044,7 @@
MimeType get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -17979,7 +18052,7 @@
MimeType get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -17987,7 +18060,7 @@
MimeType get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -19126,6 +19199,10 @@
@DocsEditable()
static const int TEXT_NODE = 3;
+ @DomName('Node.baseURI')
+ @DocsEditable()
+ String get _baseUri native "Node_baseURI_Getter";
+
@DomName('Node.childNodes')
@DocsEditable()
@deprecated
@@ -19399,7 +19476,7 @@
Node get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -19407,7 +19484,7 @@
Node get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -19415,7 +19492,7 @@
Node get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -19666,7 +19743,7 @@
@DomName('HTMLOListElement.HTMLOListElement')
@DocsEditable()
- factory OListElement() => document.$dom_createElement("ol");
+ factory OListElement() => document.createElement("ol");
@DomName('HTMLOListElement.reversed')
@DocsEditable()
@@ -19712,7 +19789,7 @@
@DomName('HTMLObjectElement.HTMLObjectElement')
@DocsEditable()
- factory ObjectElement() => document.$dom_createElement("object");
+ factory ObjectElement() => document.createElement("object");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -19825,7 +19902,7 @@
@DomName('HTMLOptGroupElement.HTMLOptGroupElement')
@DocsEditable()
- factory OptGroupElement() => document.$dom_createElement("optgroup");
+ factory OptGroupElement() => document.createElement("optgroup");
@DomName('HTMLOptGroupElement.disabled')
@DocsEditable()
@@ -19933,7 +20010,7 @@
@DomName('HTMLOutputElement.HTMLOutputElement')
@DocsEditable()
- factory OutputElement() => document.$dom_createElement("output");
+ factory OutputElement() => document.createElement("output");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -20074,7 +20151,7 @@
@DomName('HTMLParagraphElement.HTMLParagraphElement')
@DocsEditable()
- factory ParagraphElement() => document.$dom_createElement("p");
+ factory ParagraphElement() => document.createElement("p");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -20093,7 +20170,7 @@
@DomName('HTMLParamElement.HTMLParamElement')
@DocsEditable()
- factory ParamElement() => document.$dom_createElement("param");
+ factory ParamElement() => document.createElement("param");
@DomName('HTMLParamElement.name')
@DocsEditable()
@@ -20679,7 +20756,7 @@
Plugin get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -20687,7 +20764,7 @@
Plugin get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -20695,7 +20772,7 @@
Plugin get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -20811,7 +20888,7 @@
@DomName('HTMLPreElement.HTMLPreElement')
@DocsEditable()
- factory PreElement() => document.$dom_createElement("pre");
+ factory PreElement() => document.createElement("pre");
@DomName('HTMLPreElement.wrap')
@DocsEditable()
@@ -20867,7 +20944,7 @@
@DomName('HTMLProgressElement.HTMLProgressElement')
@DocsEditable()
- factory ProgressElement() => document.$dom_createElement("progress");
+ factory ProgressElement() => document.createElement("progress");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -21010,7 +21087,7 @@
@DomName('HTMLQuoteElement.HTMLQuoteElement')
@DocsEditable()
- factory QuoteElement() => document.$dom_createElement("q");
+ factory QuoteElement() => document.createElement("q");
@DomName('HTMLQuoteElement.cite')
@DocsEditable()
@@ -22030,7 +22107,7 @@
@DomName('HTMLScriptElement.HTMLScriptElement')
@DocsEditable()
- factory ScriptElement() => document.$dom_createElement("script");
+ factory ScriptElement() => document.createElement("script");
@DomName('HTMLScriptElement.async')
@DocsEditable()
@@ -22264,7 +22341,7 @@
@DomName('HTMLSelectElement.HTMLSelectElement')
@DocsEditable()
- factory SelectElement() => document.$dom_createElement("select");
+ factory SelectElement() => document.createElement("select");
@DomName('HTMLSelectElement.autofocus')
@DocsEditable()
@@ -22546,7 +22623,7 @@
@DomName('HTMLShadowElement.HTMLShadowElement')
@DocsEditable()
- factory ShadowElement() => document.$dom_createElement("shadow");
+ factory ShadowElement() => document.createElement("shadow");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -22794,7 +22871,7 @@
SourceBuffer get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -22802,7 +22879,7 @@
SourceBuffer get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -22810,7 +22887,7 @@
SourceBuffer get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -22851,7 +22928,7 @@
@DomName('HTMLSourceElement.HTMLSourceElement')
@DocsEditable()
- factory SourceElement() => document.$dom_createElement("source");
+ factory SourceElement() => document.createElement("source");
@DomName('HTMLSourceElement.media')
@DocsEditable()
@@ -22926,7 +23003,7 @@
@DomName('HTMLSpanElement.HTMLSpanElement')
@DocsEditable()
- factory SpanElement() => document.$dom_createElement("span");
+ factory SpanElement() => document.createElement("span");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -23014,7 +23091,7 @@
SpeechGrammar get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -23022,7 +23099,7 @@
SpeechGrammar get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -23030,7 +23107,7 @@
SpeechGrammar get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -23989,7 +24066,7 @@
@DomName('HTMLStyleElement.HTMLStyleElement')
@DocsEditable()
- factory StyleElement() => document.$dom_createElement("style");
+ factory StyleElement() => document.createElement("style");
@DomName('HTMLStyleElement.disabled')
@DocsEditable()
@@ -24109,7 +24186,7 @@
@DomName('HTMLTableCaptionElement.HTMLTableCaptionElement')
@DocsEditable()
- factory TableCaptionElement() => document.$dom_createElement("caption");
+ factory TableCaptionElement() => document.createElement("caption");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -24127,7 +24204,7 @@
@DomName('HTMLTableCellElement.HTMLTableCellElement')
@DocsEditable()
- factory TableCellElement() => document.$dom_createElement("td");
+ factory TableCellElement() => document.createElement("td");
@DomName('HTMLTableCellElement.cellIndex')
@DocsEditable()
@@ -24173,7 +24250,7 @@
@DomName('HTMLTableColElement.HTMLTableColElement')
@DocsEditable()
- factory TableColElement() => document.$dom_createElement("col");
+ factory TableColElement() => document.createElement("col");
@DomName('HTMLTableColElement.span')
@DocsEditable()
@@ -24217,7 +24294,7 @@
@DomName('HTMLTableElement.HTMLTableElement')
@DocsEditable()
- factory TableElement() => document.$dom_createElement("table");
+ factory TableElement() => document.createElement("table");
@DomName('HTMLTableElement.border')
@DocsEditable()
@@ -24324,7 +24401,7 @@
@DomName('HTMLTableRowElement.HTMLTableRowElement')
@DocsEditable()
- factory TableRowElement() => document.$dom_createElement("tr");
+ factory TableRowElement() => document.createElement("tr");
@DomName('HTMLTableRowElement.cells')
@DocsEditable()
@@ -24481,7 +24558,7 @@
@DomName('HTMLTemplateElement.HTMLTemplateElement')
@DocsEditable()
- factory TemplateElement() => document.$dom_createElement("template");
+ factory TemplateElement() => document.createElement("template");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -24600,7 +24677,7 @@
// + <td>Bar</td>
//
static Element _extractTemplateFromAttributeTemplate(Element el) {
- var template = el.document.$dom_createElement('template');
+ var template = el.document.createElement('template');
el.parentNode.insertBefore(template, el);
for (var name in el.attributes.keys.toList()) {
@@ -24753,7 +24830,7 @@
@DomName('HTMLTextAreaElement.HTMLTextAreaElement')
@DocsEditable()
- factory TextAreaElement() => document.$dom_createElement("textarea");
+ factory TextAreaElement() => document.createElement("textarea");
@DomName('HTMLTextAreaElement.autofocus')
@DocsEditable()
@@ -25288,7 +25365,7 @@
TextTrackCue get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -25296,7 +25373,7 @@
TextTrackCue get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -25304,7 +25381,7 @@
TextTrackCue get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -25365,7 +25442,7 @@
TextTrack get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -25373,7 +25450,7 @@
TextTrack get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -25381,7 +25458,7 @@
TextTrack get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -25460,7 +25537,7 @@
@DomName('HTMLTitleElement.HTMLTitleElement')
@DocsEditable()
- factory TitleElement() => document.$dom_createElement("title");
+ factory TitleElement() => document.createElement("title");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -25663,7 +25740,7 @@
Touch get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -25671,7 +25748,7 @@
Touch get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -25679,7 +25756,7 @@
Touch get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -25713,7 +25790,7 @@
@DomName('HTMLTrackElement.HTMLTrackElement')
@DocsEditable()
- factory TrackElement() => document.$dom_createElement("track");
+ factory TrackElement() => document.createElement("track");
/// Checks if this type is supported on the current platform.
static bool get supported => true;
@@ -26008,7 +26085,7 @@
@DomName('HTMLUListElement.HTMLUListElement')
@DocsEditable()
- factory UListElement() => document.$dom_createElement("ul");
+ factory UListElement() => document.createElement("ul");
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -26146,7 +26223,7 @@
@DomName('HTMLVideoElement.HTMLVideoElement')
@DocsEditable()
- factory VideoElement() => document.$dom_createElement("video");
+ factory VideoElement() => document.createElement("video");
@DomName('HTMLVideoElement.height')
@DocsEditable()
@@ -28566,7 +28643,7 @@
Rect get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -28574,7 +28651,7 @@
Rect get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -28582,7 +28659,7 @@
Rect get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -28645,7 +28722,7 @@
CssRule get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -28653,7 +28730,7 @@
CssRule get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -28661,7 +28738,7 @@
CssRule get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -28714,7 +28791,7 @@
_CSSValue get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -28722,7 +28799,7 @@
_CSSValue get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -28730,7 +28807,7 @@
_CSSValue get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -28980,7 +29057,7 @@
Gamepad get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -28988,7 +29065,7 @@
Gamepad get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -28996,7 +29073,7 @@
Gamepad get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -29159,7 +29236,7 @@
Node get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -29167,7 +29244,7 @@
Node get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -29175,7 +29252,7 @@
Node get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -29331,7 +29408,7 @@
SpeechInputResult get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -29339,7 +29416,7 @@
SpeechInputResult get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -29347,7 +29424,7 @@
SpeechInputResult get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -29398,7 +29475,7 @@
SpeechRecognitionResult get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -29406,7 +29483,7 @@
SpeechRecognitionResult get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -29414,7 +29491,7 @@
SpeechRecognitionResult get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -29463,7 +29540,7 @@
StyleSheet get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -29471,7 +29548,7 @@
StyleSheet get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -29479,7 +29556,7 @@
StyleSheet get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -34406,6 +34483,31 @@
// BSD-style license that can be found in the LICENSE file.
+class _ConsoleVariables {
+ Map<String, Object> _data = new Map<String, Object>();
+
+ /**
+ * Forward member accesses to the backing JavaScript object.
+ */
+ noSuchMethod(Invocation invocation) {
+ String member = MirrorSystem.getName(invocation.memberName);
+ if (invocation.isGetter) {
+ return _data[member];
+ } else if (invocation.isSetter) {
+ _data[member] = invocation.positionalArguments[0];
+ } else {
+ return Function.apply(_data[member], invocation.positionalArguments, invocation.namedArguments);
+ }
+ }
+
+ void clear() => _data.clear();
+
+ /**
+ * List all variables currently defined.
+ */
+ List variables() => _data.keys.toList(growable: false);
+}
+
class _Utils {
static double dateTimeToDouble(DateTime dateTime) =>
dateTime.millisecondsSinceEpoch.toDouble();
@@ -34504,6 +34606,41 @@
return map;
}
+ static _ConsoleVariables _consoleTempVariables = new _ConsoleVariables();
+ /**
+ * Takes an [expression] and a list of [local] variable and returns an
+ * expression for a closure with a body matching the original expression
+ * where locals are passed in as arguments. Returns a list containing the
+ * String expression for the closure and the list of arguments that should
+ * be passed to it.
+ *
+ * For example:
+ * <code>wrapExpressionAsClosure("foo + bar", ["bar", 40, "foo", 2])</code>
+ * will return:
+ * <code>["(final $var, final bar, final foo) => foo + bar", [40, 2]]</code>
+ */
+ static List wrapExpressionAsClosure(String expression, List locals) {
+ var args = {};
+ var sb = new StringBuffer("(");
+ addArg(arg, value) {
+ arg = stripMemberName(arg);
+ if (args.containsKey(arg)) return;
+ if (args.isNotEmpty) {
+ sb.write(", ");
+ }
+ sb.write("final $arg");
+ args[arg] = value;
+ }
+
+ addArg("\$var", _consoleTempVariables);
+
+ for (int i = 0; i < locals.length; i+= 2) {
+ addArg(locals[i], locals[i+1]);
+ }
+ sb..write(')=>\n$expression');
+ return [sb.toString(), args.values.toList(growable: false)];
+ }
+
/**
* Convenience helper to get the keys of a [Map] as a [List].
*/
diff --git a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
index b1eea89..7ca4735 100644
--- a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
+++ b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
@@ -308,6 +308,10 @@
@DocsEditable()
+/**
+ * An indexed database object for storing client-side data
+ * in web apps.
+ */
@DomName('IDBDatabase')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX, '15')
diff --git a/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart b/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
index 9071f38..94c23a0 100644
--- a/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
+++ b/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
@@ -119,6 +119,10 @@
@DocsEditable()
+/**
+ * An indexed database object for storing client-side data
+ * in web apps.
+ */
@DomName('IDBDatabase')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX, '15')
diff --git a/sdk/lib/io/directory.dart b/sdk/lib/io/directory.dart
index a7fe520..3cbf4d5 100644
--- a/sdk/lib/io/directory.dart
+++ b/sdk/lib/io/directory.dart
@@ -109,6 +109,15 @@
Directory renameSync(String newPath);
/**
+ * Returns a [Directory] instance whose path is the absolute path to [this].
+ *
+ * The absolute path is computed by prefixing
+ * a relative path with the current working directory, and returning
+ * an absolute path unchanged.
+ */
+ Directory get absolute;
+
+ /**
* Lists the sub-directories and files of this [Directory].
* Optionally recurses into sub-directories.
*
diff --git a/sdk/lib/io/directory_impl.dart b/sdk/lib/io/directory_impl.dart
index f54a641..61a226a 100644
--- a/sdk/lib/io/directory_impl.dart
+++ b/sdk/lib/io/directory_impl.dart
@@ -24,7 +24,7 @@
}
}
- external static String _current();
+ external static _current();
external static _setCurrent(path);
external static _createTemp(String template);
external static int _exists(String path);
@@ -34,7 +34,14 @@
external static List _list(String path, bool recursive, bool followLinks);
external static SendPort _newServicePort();
- static Directory get current => new _Directory(_current());
+ static Directory get current {
+ var result = _current();
+ if (result is OSError) {
+ throw new DirectoryException(
+ "Getting current working directory failed", "", result);
+ }
+ return new _Directory(result);
+ }
static void set current(path) {
if (path is Directory) path = path.path;
@@ -67,6 +74,8 @@
return (result == 1);
}
+ Directory get absolute => new Directory(_absolutePath);
+
Future<FileStat> stat() => FileStat.stat(path);
FileStat statSync() => FileStat.statSync(path);
diff --git a/sdk/lib/io/file.dart b/sdk/lib/io/file.dart
index d5579ca..4f9d3f5 100644
--- a/sdk/lib/io/file.dart
+++ b/sdk/lib/io/file.dart
@@ -99,6 +99,15 @@
int lengthSync();
/**
+ * Returns a [File] instance whose path is the absolute path to [this].
+ *
+ * The absolute path is computed by prefixing
+ * a relative path with the current working directory, and returning
+ * an absolute path unchanged.
+ */
+ File get absolute;
+
+ /**
* Get the last-modified time of the file. Returns a
* [:Future<DateTime>:] that completes with a [DateTime] object for the
* modification date.
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index ba6b642..86acf77 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -267,6 +267,8 @@
return result;
}
+ File get absolute => new File(_absolutePath);
+
Future<FileStat> stat() => FileStat.stat(path);
FileStat statSync() => FileStat.statSync(path);
@@ -509,28 +511,42 @@
return builder.takeBytes();
}
+ String _tryDecode(List<int> bytes, Encoding encoding) {
+ try {
+ return encoding.decode(bytes);
+ } catch (_) {
+ throw new FileException(
+ "Failed to decode data using encoding '${encoding.name}'", path);
+ }
+ }
+
Future<String> readAsString({Encoding encoding: UTF8}) {
_ensureFileService();
return readAsBytes().then((bytes) {
- return encoding.decode(bytes);
+ return _tryDecode(bytes, encoding);
});
}
String readAsStringSync({Encoding encoding: UTF8}) {
List<int> bytes = readAsBytesSync();
- return encoding.decode(bytes);
+ return _tryDecode(bytes, encoding);
}
- static List<String> _decodeLines(List<int> bytes, Encoding encoding) {
+ List<String> _decodeLines(List<int> bytes, Encoding encoding) {
if (bytes.length == 0) return [];
var list = [];
var controller = new StreamController(sync: true);
+ var error = null;
controller.stream
.transform(encoding.decoder)
.transform(new LineSplitter())
- .listen((line) => list.add(line));
+ .listen((line) => list.add(line), onError: (e) => error = e);
controller.add(bytes);
controller.close();
+ if (error != null) {
+ throw new FileException(
+ "Failed to decode data using encoding '${encoding.name}'", path);
+ }
return list;
}
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
index 0887175..ab2b7d3 100644
--- a/sdk/lib/io/file_system_entity.dart
+++ b/sdk/lib/io/file_system_entity.dart
@@ -343,6 +343,45 @@
});
}
+ static final RegExp _absoluteWindowsPathPattern =
+ new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])');
+
+ /**
+ * Returns a [bool] indicating whether this object's path is absolute.
+ *
+ * On Windows, a path is absolute if it starts with \\ or a drive letter
+ * between a and z (upper or lower case) followed by :\ or :/.
+ * On non-Windows, a path is absolute if it starts with /.
+ */
+ bool get isAbsolute {
+ if (Platform.isWindows) {
+ return path.startsWith(_absoluteWindowsPathPattern);
+ } else {
+ return path.startsWith('/');
+ }
+ }
+
+ /**
+ * Returns a [FileSystemEntity] whose path is the absolute path to [this].
+ * The type of the returned instance is the type of [this].
+ *
+ * The absolute path is computed by prefixing
+ * a relative path with the current working directory, and returning
+ * an absolute path unchanged.
+ */
+ FileSystemEntity get absolute;
+
+ String get _absolutePath {
+ if (isAbsolute) return path;
+ String current = Directory.current.path;
+ if (current.endsWith('/') ||
+ (Platform.isWindows && current.endsWith('\\'))) {
+ return '$current$path';
+ } else {
+ return '$current${Platform.pathSeparator}$path';
+ }
+ }
+
/**
* Synchronously checks whether two paths refer to the same object in the
diff --git a/sdk/lib/io/http.dart b/sdk/lib/io/http.dart
index 1cf3387..31f6b97 100644
--- a/sdk/lib/io/http.dart
+++ b/sdk/lib/io/http.dart
@@ -55,8 +55,10 @@
/**
- * The [HttpServer] class implements the server side of the HTTP
- * protocol. The [HttpServer] is a [Stream] of [HttpRequest]s. Each
+ * A server that delivers content, such as web pages, using
+ * the HTTP protocol.
+ *
+ * The [HttpServer] is a [Stream] of [HttpRequest]s. Each
* [HttpRequest] has an associated [HttpResponse] object as its
* [HttpRequest.response] member, and the server responds to a request by
* writing to that [HttpResponse] object.
@@ -950,8 +952,10 @@
/**
- * The [HttpClient] class implements the client side of the HTTP
- * protocol. It contains a number of methods to send a HTTP request
+ * A client that receives content, such as web pages, from
+ * a server using the HTTP protocol.
+ *
+ * HttpClient contains a number of methods to send a HTTP request
* to a HTTP server and receive a HTTP response back.
*
* This is a two-step process, triggered by two futures. When the
diff --git a/sdk/lib/io/link.dart b/sdk/lib/io/link.dart
index 3df3fc9..83cd223 100644
--- a/sdk/lib/io/link.dart
+++ b/sdk/lib/io/link.dart
@@ -85,6 +85,15 @@
Link renameSync(String newPath);
/**
+ * Returns a [Link] instance whose path is the absolute path to [this].
+ *
+ * The absolute path is computed by prefixing
+ * a relative path with the current working directory, and returning
+ * an absolute path unchanged.
+ */
+ Link get absolute;
+
+ /**
* Gets the target of the link. Returns a future that completes with
* the path to the target.
*
@@ -127,6 +136,8 @@
bool existsSync() => FileSystemEntity.isLinkSync(path);
+ Link get absolute => new Link(_absolutePath);
+
Future<FileStat> stat() => FileStat.stat(path);
FileStat statSync() => FileStat.statSync(path);
diff --git a/sdk/lib/svg/dart2js/svg_dart2js.dart b/sdk/lib/svg/dart2js/svg_dart2js.dart
index 75f2c18..be59cd9 100644
--- a/sdk/lib/svg/dart2js/svg_dart2js.dart
+++ b/sdk/lib/svg/dart2js/svg_dart2js.dart
@@ -32,7 +32,7 @@
class _SvgElementFactoryProvider {
static SvgElement createSvgElement_tag(String tag) {
final Element temp =
- document.$dom_createElementNS("http://www.w3.org/2000/svg", tag);
+ document.createElementNS("http://www.w3.org/2000/svg", tag);
return temp;
}
}
@@ -4740,7 +4740,7 @@
static final _START_TAG_REGEXP = new RegExp('<(\\w+)');
factory SvgElement.tag(String tag) =>
- document.$dom_createElementNS("http://www.w3.org/2000/svg", tag);
+ document.createElementNS("http://www.w3.org/2000/svg", tag);
factory SvgElement.svg(String svg,
{NodeValidator validator, NodeTreeSanitizer treeSanitizer}) {
diff --git a/sdk/lib/svg/dartium/svg_dartium.dart b/sdk/lib/svg/dartium/svg_dartium.dart
index 228c9a8..2201493 100644
--- a/sdk/lib/svg/dartium/svg_dartium.dart
+++ b/sdk/lib/svg/dartium/svg_dartium.dart
@@ -21,7 +21,7 @@
class _SvgElementFactoryProvider {
static SvgElement createSvgElement_tag(String tag) {
final Element temp =
- document.$dom_createElementNS("http://www.w3.org/2000/svg", tag);
+ document.createElementNS("http://www.w3.org/2000/svg", tag);
return temp;
}
}
@@ -2970,7 +2970,7 @@
Length get first {
if (this.length > 0) {
- return this[0];
+ return getItem(0);
}
throw new StateError("No elements");
}
@@ -2978,7 +2978,7 @@
Length get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return getItem(len - 1);
}
throw new StateError("No elements");
}
@@ -2986,7 +2986,7 @@
Length get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return getItem(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -3430,7 +3430,7 @@
Number get first {
if (this.length > 0) {
- return this[0];
+ return getItem(0);
}
throw new StateError("No elements");
}
@@ -3438,7 +3438,7 @@
Number get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return getItem(len - 1);
}
throw new StateError("No elements");
}
@@ -3446,7 +3446,7 @@
Number get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return getItem(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -4440,7 +4440,7 @@
PathSeg get first {
if (this.length > 0) {
- return this[0];
+ return getItem(0);
}
throw new StateError("No elements");
}
@@ -4448,7 +4448,7 @@
PathSeg get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return getItem(len - 1);
}
throw new StateError("No elements");
}
@@ -4456,7 +4456,7 @@
PathSeg get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return getItem(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -5152,7 +5152,7 @@
String get first {
if (this.length > 0) {
- return this[0];
+ return getItem(0);
}
throw new StateError("No elements");
}
@@ -5160,7 +5160,7 @@
String get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return getItem(len - 1);
}
throw new StateError("No elements");
}
@@ -5168,7 +5168,7 @@
String get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return getItem(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -5318,7 +5318,7 @@
static final _START_TAG_REGEXP = new RegExp('<(\\w+)');
factory SvgElement.tag(String tag) =>
- document.$dom_createElementNS("http://www.w3.org/2000/svg", tag);
+ document.createElementNS("http://www.w3.org/2000/svg", tag);
factory SvgElement.svg(String svg,
{NodeValidator validator, NodeTreeSanitizer treeSanitizer}) {
@@ -6104,7 +6104,7 @@
Transform get first {
if (this.length > 0) {
- return this[0];
+ return getItem(0);
}
throw new StateError("No elements");
}
@@ -6112,7 +6112,7 @@
Transform get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return getItem(len - 1);
}
throw new StateError("No elements");
}
@@ -6120,7 +6120,7 @@
Transform get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return getItem(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
@@ -6479,7 +6479,7 @@
ElementInstance get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -6487,7 +6487,7 @@
ElementInstance get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -6495,7 +6495,7 @@
ElementInstance get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
diff --git a/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart b/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart
index 5933b2e..f04e918 100644
--- a/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart
+++ b/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart
@@ -563,7 +563,8 @@
}
-class Uint8ClampedList extends Uint8List
+class Uint8ClampedList extends TypedData with ListMixin<int>,
+ FixedLengthListMixin<int> implements JavaScriptIndexingBehavior, List<int>
native "Uint8ClampedArray,CanvasPixelArray" {
factory Uint8ClampedList(int length) => _create1(length);
@@ -578,8 +579,7 @@
static const int BYTES_PER_ELEMENT = 1;
- // Use implementation from Uint8List
- // final int length;
+ int get length => JS("int", '#(#)', fetchLength, this);
int operator[](int index) {
_checkIndex(index, length);
@@ -615,7 +615,11 @@
class Uint8List
extends TypedData with ListMixin<int>, FixedLengthListMixin<int>
implements JavaScriptIndexingBehavior, List<int>
- native "Uint8Array" {
+ // On some browsers Uint8ClampedArray is a subtype of Uint8Array. Marking
+ // Uint8List as !nonleaf ensures that the native dispatch correctly handles
+ // the potential for Uint8ClampedArray to 'accidentally' pick up the
+ // dispatch record for Uint8List.
+ native "Uint8Array,!nonleaf" {
factory Uint8List(int length) => _create1(length);
factory Uint8List.fromList(List<num> list) =>
diff --git a/sdk/lib/typed_data/typed_data.dart b/sdk/lib/typed_data/typed_data.dart
index 71d6a8d..61577b2 100644
--- a/sdk/lib/typed_data/typed_data.dart
+++ b/sdk/lib/typed_data/typed_data.dart
@@ -430,7 +430,7 @@
* more space- and time-efficient than the default [List] implementation.
* Indexed store clamps the value to range 0..0xFF.
*/
-abstract class Uint8ClampedList implements Uint8List {
+abstract class Uint8ClampedList implements List<int>, TypedData {
/**
* Creates a [Uint8ClampedList] of the specified length (in elements), all of
* whose elements are initially zero.
diff --git a/sdk/lib/web_sql/dartium/web_sql_dartium.dart b/sdk/lib/web_sql/dartium/web_sql_dartium.dart
index 828bcf8..4dfaac3 100644
--- a/sdk/lib/web_sql/dartium/web_sql_dartium.dart
+++ b/sdk/lib/web_sql/dartium/web_sql_dartium.dart
@@ -232,7 +232,7 @@
Map get first {
if (this.length > 0) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
throw new StateError("No elements");
}
@@ -240,7 +240,7 @@
Map get last {
int len = this.length;
if (len > 0) {
- return this[len - 1];
+ return _nativeIndexedGetter(len - 1);
}
throw new StateError("No elements");
}
@@ -248,7 +248,7 @@
Map get single {
int len = this.length;
if (len == 1) {
- return this[0];
+ return _nativeIndexedGetter(0);
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
diff --git a/tests/co19/co19-analyzer.status b/tests/co19/co19-analyzer.status
index bb87b73..8befe3d 100644
--- a/tests/co19/co19-analyzer.status
+++ b/tests/co19/co19-analyzer.status
@@ -29,8 +29,15 @@
Language/15_Types/4_Interface_Types_A11_t01: Skip
Language/15_Types/4_Interface_Types_A11_t02: Skip
+# TBF: Hence, a static warning will not be issued if f has no declared return type, since the return type would be dynamic and dynamic may be assigned to void.
+Language/13_Statements/11_Return_A07_t01: fail
+
+# TBF: typedef int func2(int); - "int is not a type"
+Language/15_Types/6_Type_dynamic_A03_t01: fail
+Language/15_Types/6_Type_dynamic_A04_t01: fail
+LibTest/core/Uri/decodeQueryComponent_A02_t01: fail # co19 Issue 591
# co19 issue #380, Strings class has been removed
LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A03_t01: fail, OK
@@ -80,14 +87,11 @@
# co19 issue #513, rules for finals were loosened, contradiction in spec was fixed
Language/07_Classes/6_Constructors/1_Generative_Constructors_A21_t01: fail, OK
-# co19 issue #514, it is still an error to have both a n initializing formal and an initializer in the constructor's initializer list
-Language/05_Variables/05_Variables_A05_t03: fail, OK
-
# co19 issue #515, it is a compile-time error if there is more than one entity with the same name declared in the same scope
-Language/07_Classes/3_Setters_A08_t01: fail, OK
-Language/07_Classes/3_Setters_A08_t02: fail, OK
Language/07_Classes/3_Setters_A08_t03: fail, OK
-Language/07_Classes/3_Setters_A08_t04: fail, OK
+
+# co19 issue #593: Conditional expressions are now allowed as constant expressions
+Language/12_Expressions/01_Constants_A15_t16: fail, OK
# co19 issue #438, Static variables are initialized lazily, need not be constants
Language/12_Expressions/01_Constants_A16_t01: fail, OK
@@ -106,9 +110,6 @@
Language/12_Expressions/12_Instance_Creation_A01_t05: fail, OK
Language/12_Expressions/12_Instance_Creation_A01_t06: fail, OK
-# co19 issue #388 (wrongly closed), StringBuffer methods changed
-Language/12_Expressions/15_Method_Invocation/4_Super_Invocation_A02_t04: fail, OK
-
# co19 issue #433 (wrongly closed), missing @static-warning annotation
Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t01: fail, OK
Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t07: fail, OK
@@ -120,98 +121,130 @@
# co19 issue #543: invocation of a non-function
Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A03_t02: fail, OK
-LibTest/core/NoSuchMethodError/NoSuchMethodError_A01_t01: Fail, OK # co19 issue 556
+# co19 issue #562: malformed superclass
+Language/14_Libraries_and_Scripts/1_Imports_A03_t27: fail, OK
-LibTest/async/Stream/listen_A04_t01: Fail, OK # co19 issue 557
-LibTest/typed_data/ByteData/buffer_A01_t02: Fail, OK # co19 issue 557
+# co19 issue #563: implicitly named libraries are allowed
+Language/14_Libraries_and_Scripts/2_Exports_A05_t01: fail, OK
-Language/13_Statements/11_Return_A07_t01: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A02_t03: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A03_t03: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A04_t03: fail # co19-roll r546: Please triage this failure
-Language/14_Libraries_and_Scripts/1_Imports_A03_t27: fail # co19-roll r546: Please triage this failure
-Language/14_Libraries_and_Scripts/1_Imports_A03_t47: fail # co19-roll r546: Please triage this failure
-Language/14_Libraries_and_Scripts/1_Imports_A03_t67: fail # co19-roll r546: Please triage this failure
-Language/14_Libraries_and_Scripts/2_Exports_A05_t01: fail # co19-roll r546: Please triage this failure
-Language/14_Libraries_and_Scripts/5_URIs_A01_t24: fail # co19-roll r546: Please triage this failure
-Language/14_Libraries_and_Scripts/5_URIs_A01_t25: fail # co19-roll r546: Please triage this failure
-Language/15_Types/6_Type_dynamic_A03_t01: fail # co19-roll r546: Please triage this failure
-Language/15_Types/6_Type_dynamic_A04_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/asBroadcastStream_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/distinct_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/drain_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/drain_A02_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/EventTransformStream_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/expand_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/first_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/handleError_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/EventTransformStream/listen_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Future/asStream_A01_t02: pass # co19-roll r546: Please triage this failure
-LibTest/async/Future/asStream_A02_t01: pass # co19-roll r546: Please triage this failure
-LibTest/async/Future/whenComplete_A03_t01: pass # co19-roll r546: Please triage this failure
-LibTest/async/Stream/asBroadcastStream_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/asBroadcastStream_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/contains_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/hasListener_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/hasListener_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/isClosed_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/isClosed_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/isPaused_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/isPaused_A01_t03: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/sink_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/StreamController.broadcast_A04_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/distinct_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/distinct_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/drain_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/drain_A02_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamEventTransformer/handleData_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamEventTransformer/handleDone_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamEventTransformer/handleError_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/expand_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/handleError_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/isBroadcast_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/isBroadcast_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamIterator/cancel_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/listen_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/Stream_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/Stream.fromFuture_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/Stream.fromFuture_A02_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/Stream.fromIterable_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/Timer/cancel_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/DateTime/compareTo_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/DateTime/subtract_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_div_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_eq_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_gt_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_lt_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_lte_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_minus_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_mult_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/Duration/operator_plus_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/asMap_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/every_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/forEach_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/lastWhere_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/List_A01_t03: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/List.filled_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/List.from_A01_t03: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/List.generate_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/List.generate_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/List.generate_A01_t03: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/map_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/replaceRange_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/replaceRange_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/skipWhile_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/takeWhile_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/toList_A01_t03: fail # co19-roll r546: Please triage this failure
-LibTest/core/StringBuffer/writeAll_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/isolate_api/streamSpawnFunction_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateSink/add_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateSink/addError_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateSink/addError_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateSink/operator_equality_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateStream/any_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateStream/asBroadcastStream_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateStream/contains_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateStream/contains_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateStream/isBroadcast_A01_t02: fail # co19-roll r546: Please triage this failure
+# co19 issue #564: URI can be any number adjacent string literals
+Language/14_Libraries_and_Scripts/5_URIs_A01_t24: fail, OK
+Language/14_Libraries_and_Scripts/5_URIs_A01_t25: fail, OK
+
+# co19 issue #565: not implemented members in non-abstract class
+LibTest/async/EventTransformStream/asBroadcastStream_A01_t02: fail, OK
+LibTest/async/Stream/asBroadcastStream_A01_t02: fail, OK
+LibTest/async/Stream/asBroadcastStream_A02_t01: fail, OK
+LibTest/async/Stream/isBroadcast_A01_t01: fail, OK
+LibTest/async/Stream/isBroadcast_A01_t02: fail, OK
+
+# co19 issue #566: Undefined class 'T'
+LibTest/async/EventTransformStream/distinct_A01_t02: fail, OK
+LibTest/async/EventTransformStream/EventTransformStream_A01_t02: fail, OK
+LibTest/async/EventTransformStream/expand_A01_t01: fail, OK
+LibTest/async/Stream/contains_A03_t01: fail, OK
+LibTest/async/Stream/distinct_A01_t01: fail, OK
+LibTest/async/Stream/distinct_A01_t02: fail, OK
+LibTest/async/Stream/expand_A01_t01: fail, OK
+LibTest/core/List/List.filled_A01_t01: fail, OK
+LibTest/core/List/List.generate_A01_t01: fail, OK
+LibTest/core/List/List.generate_A01_t02: fail, OK
+LibTest/core/List/List.generate_A01_t03: fail, OK
+LibTest/core/List/replaceRange_A01_t01: fail, OK
+LibTest/core/List/replaceRange_A01_t02: fail, OK
+LibTest/core/List/skipWhile_A02_t01: fail, OK
+LibTest/core/List/takeWhile_A02_t01: fail, OK
+
+# co19 issue #567: Undefined name 'value'
+LibTest/async/EventTransformStream/drain_A02_t01: fail, OK
+LibTest/async/EventTransformStream/drain_A02_t02: fail, OK
+LibTest/async/Stream/drain_A02_t01: fail, OK
+LibTest/async/Stream/drain_A02_t02: fail, OK
+
+# co19 issue #568: Undefined name 'Expec'
+LibTest/async/EventTransformStream/first_A02_t01: fail, OK
+
+# co19 issue #569: There is no such getter 'message' in 'Object'
+LibTest/async/EventTransformStream/handleError_A03_t01: fail, OK
+LibTest/async/EventTransformStream/listen_A03_t01: fail, OK
+LibTest/async/Stream/handleError_A03_t01: fail, OK
+LibTest/async/Stream/listen_A03_t01: fail, OK
+
+# co19 issue #570: undefined name 'events1'
+LibTest/async/StreamController/hasListener_A01_t01: fail, OK
+LibTest/async/StreamController/hasListener_A01_t02: fail, OK
+LibTest/async/StreamController/isPaused_A01_t01: fail, OK
+LibTest/async/StreamController/isPaused_A01_t03: fail, OK
+
+# co19 issue #571: There is no such getter 'hasListener' in 'EventSink'
+LibTest/async/StreamController/sink_A01_t01: fail, OK
+
+# co19 issue #572: Undefined class 'boolean'; did you mean 'bool'?
+LibTest/async/StreamController/StreamController.broadcast_A04_t01: fail, OK
+LibTest/async/StreamIterator/cancel_A01_t01: fail, OK
+LibTest/core/List/map_A02_t01: fail, OK
+
+# co19 issue #573: There is no such operator '<' in 'Object'
+LibTest/async/StreamEventTransformer/handleData_A01_t01: fail, OK
+LibTest/async/StreamEventTransformer/handleDone_A01_t01: fail, OK
+LibTest/async/StreamEventTransformer/handleError_A01_t01: fail, OK
+
+# co19 issue #574: Abstract classes cannot be created with a 'new' expression
+LibTest/async/Stream/Stream_A01_t01: fail, OK
+
+# co19 issue #575: Error has not constructor parameters
+LibTest/async/Stream/Stream.fromFuture_A02_t01: fail, OK
+LibTest/async/Stream/Stream.fromFuture_A02_t02: fail, OK
+LibTest/async/Stream/Stream.fromIterable_A01_t02: fail, OK
+
+# co19 issue #576: The name 'timer' cannot be referenced in the initializer of a variable with the same name
+LibTest/async/Timer/cancel_A01_t01: fail, OK
+
+# co19 issue #577: The argument type 'int' cannot be assigned to the parameter type 'Duration'
+LibTest/core/DateTime/subtract_A02_t01: fail, OK
+
+# co19 issue #578: Undefined class 'long'
+LibTest/core/Duration/operator_div_A01_t01: fail, OK
+LibTest/core/Duration/operator_eq_A01_t01: fail, OK
+LibTest/core/Duration/operator_gt_A01_t01: fail, OK
+LibTest/core/Duration/operator_lt_A01_t01: fail, OK
+LibTest/core/Duration/operator_lte_A01_t01: fail, OK
+LibTest/core/Duration/operator_minus_A01_t01: fail, OK
+LibTest/core/Duration/operator_mult_A01_t01: fail, OK
+LibTest/core/Duration/operator_plus_A01_t01: fail, OK
+
+# co19 issue #579: The argument type 'bool' cannot be assigned to the parameter type '(String) -> bool'
+LibTest/core/List/every_A03_t01: fail, OK
+LibTest/core/List/forEach_A01_t02: fail, OK
+
+# co19 issue #580: Undefined name 'faled'
+LibTest/core/List/lastWhere_A03_t01: fail, OK
+
+# co19 issue #581: Undefined name 'Expect'
+LibTest/core/List/List_A01_t03: fail, OK
+LibTest/core/List/List.from_A01_t03: fail, OK
+LibTest/core/List/toList_A01_t03: fail, OK
+
+# co19 issue #582: The method 'addAll' is not defined for the class 'StringBuffer'
+LibTest/core/StringBuffer/writeAll_A03_t01: fail, OK
+
+# co19 issue #583: The method 'close' is not defined for the class 'SendPort'
+LibTest/isolate/isolate_api/streamSpawnFunction_A01_t01: fail, OK
+LibTest/isolate/IsolateSink/add_A01_t02: fail, OK
+LibTest/isolate/IsolateSink/operator_equality_A01_t01: fail, OK
+
+# co19 issue #584: Undefined name 'received
+LibTest/isolate/isolate_api/streamSpawnFunction_A02_t01: fail, OK
+
+# co19 issue #585: Undefined name 'messagesList'
+LibTest/isolate/IsolateSink/addError_A01_t01: fail, OK
+
+# co19 issue #586: Undefined name 'message'
+LibTest/isolate/IsolateSink/addError_A01_t02: fail, OK
+
+# co19 issue #587: Missing dart:async import and other mistypes
+LibTest/isolate/IsolateStream/any_A02_t01: fail, OK
+LibTest/isolate/IsolateStream/asBroadcastStream_A01_t01: fail, OK
+LibTest/isolate/IsolateStream/contains_A01_t01: fail, OK
+LibTest/isolate/IsolateStream/contains_A02_t01: fail, OK
+LibTest/isolate/IsolateStream/isBroadcast_A01_t02: fail, OK
diff --git a/tests/co19/co19-analyzer2.status b/tests/co19/co19-analyzer2.status
index 7b4a78b..cdcaa67 100644
--- a/tests/co19/co19-analyzer2.status
+++ b/tests/co19/co19-analyzer2.status
@@ -30,7 +30,7 @@
Language/15_Types/4_Interface_Types_A11_t02: Skip
-
+LibTest/core/Uri/decodeQueryComponent_A02_t01: fail # co19 Issue 591
# co19 issue #380, Strings class has been removed
LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A03_t01: fail, OK
@@ -77,14 +77,6 @@
# co19 issue #513, rules for finals were loosened, contradiction in spec was fixed
Language/07_Classes/6_Constructors/1_Generative_Constructors_A21_t01: fail, OK
-# co19 issue #514, it is still an error to have both a n initializing formal and an initializer in the constructor's initializer list
-Language/05_Variables/05_Variables_A05_t03: fail, OK
-
-# co19 issue #515, it is a compile-time error if there is more than one entity with the same name declared in the same scope
-Language/07_Classes/3_Setters_A08_t01: fail, OK
-Language/07_Classes/3_Setters_A08_t02: fail, OK
-Language/07_Classes/3_Setters_A08_t04: fail, OK
-
# co19 issue #438, Static variables are initialized lazily, need not be constants
Language/12_Expressions/01_Constants_A16_t01: fail, OK
Language/12_Expressions/01_Constants_A16_t02: fail, OK
@@ -97,9 +89,6 @@
Language/12_Expressions/12_Instance_Creation_A01_t05: fail, OK
Language/12_Expressions/12_Instance_Creation_A01_t06: fail, OK
-# co19 issue #388 (wrongly closed), StringBuffer methods changed
-Language/12_Expressions/15_Method_Invocation/4_Super_Invocation_A02_t04: fail, OK
-
# co19 issue #433 (wrongly closed), missing @static-warning annotation
Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t01: fail, OK
Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t07: fail, OK
@@ -111,15 +100,7 @@
# co19 issue #543: invocation of a non-function
Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A03_t02: fail, OK
-LibTest/core/NoSuchMethodError/NoSuchMethodError_A01_t01: Fail, OK # co19 issue 556
-
-LibTest/async/Stream/listen_A04_t01: Fail, OK # co19 issue 557
-LibTest/typed_data/ByteData/buffer_A01_t02: Fail, OK # co19 issue 557
-
Language/13_Statements/11_Return_A07_t01: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A02_t03: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A03_t03: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A04_t03: fail # co19-roll r546: Please triage this failure
Language/14_Libraries_and_Scripts/1_Imports_A03_t27: fail # co19-roll r546: Please triage this failure
Language/14_Libraries_and_Scripts/1_Imports_A03_t47: fail # co19-roll r546: Please triage this failure
Language/14_Libraries_and_Scripts/1_Imports_A03_t67: fail # co19-roll r546: Please triage this failure
@@ -145,8 +126,6 @@
LibTest/async/Stream/contains_A03_t01: fail # co19-roll r546: Please triage this failure
LibTest/async/StreamController/hasListener_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/async/StreamController/hasListener_A01_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/isClosed_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/StreamController/isClosed_A01_t02: fail # co19-roll r546: Please triage this failure
LibTest/async/StreamController/isPaused_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/async/StreamController/isPaused_A01_t03: fail # co19-roll r546: Please triage this failure
LibTest/async/StreamController/sink_A01_t01: fail # co19-roll r546: Please triage this failure
@@ -178,7 +157,6 @@
LibTest/core/Duration/operator_minus_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/core/Duration/operator_mult_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/core/Duration/operator_plus_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/core/List/asMap_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/core/List/every_A03_t01: fail # co19-roll r546: Please triage this failure
LibTest/core/List/forEach_A01_t02: fail # co19-roll r546: Please triage this failure
LibTest/core/List/lastWhere_A03_t01: fail # co19-roll r546: Please triage this failure
@@ -196,6 +174,7 @@
LibTest/core/List/toList_A01_t03: fail # co19-roll r546: Please triage this failure
LibTest/core/StringBuffer/writeAll_A03_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/streamSpawnFunction_A01_t01: fail # co19-roll r546: Please triage this failure
+LibTest/isolate/isolate_api/streamSpawnFunction_A02_t01: fail # co19-roll r576: Please triage this failure
LibTest/isolate/IsolateSink/add_A01_t02: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/addError_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/addError_A01_t02: fail # co19-roll r546: Please triage this failure
@@ -204,4 +183,4 @@
LibTest/isolate/IsolateStream/asBroadcastStream_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateStream/contains_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateStream/contains_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/IsolateStream/isBroadcast_A01_t02: fail # co19-roll r546: Please triage this failure
\ No newline at end of file
+LibTest/isolate/IsolateStream/isBroadcast_A01_t02: fail # co19-roll r546: Please triage this failure
diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status
index e470bb0..2cdbc44 100644
--- a/tests/co19/co19-co19.status
+++ b/tests/co19/co19-co19.status
@@ -8,20 +8,26 @@
### GENERAL FAILURES ###
+
+[ $runtime == vm || $compiler == dart2dart || $compiler == dart2js ]
+LibTest/core/Uri/decodeQueryComponent_A02_t01: fail # co19 Issue 591
+LibTest/core/Uri/encodeComponent_A01_t02: fail # Issue 13251, co19 Issues 588, 589
+LibTest/core/Uri/toFilePath_A01_t01: pass, fail, ok # co19 Issue 592
+LibTest/core/Uri/Uri_A03_t01: fail # Issue 13250, co19 Issue 588
+LibTest/core/Uri/Uri.http_A02_t01: fail # Issue 13250, co19 Issue 588
+LibTest/core/Uri/Uri.https_A02_t01: fail # Issue 13250, co19 Issue 588
+# Maybe we should wait until isolate library is sealed before triaging these.
+LibTest/isolate/isolate_api/spawnFunction_A04_t01: fail, timeout # co19-roll r546: Please triage this failure
+LibTest/isolate/isolate_api/streamSpawnFunction_A02_t01: fail, timeout # co19-roll r546: Please triage this failure
+
[ $runtime == vm || $compiler == dart2dart || $compiler == dart2js ]
LibTest/typed_data/ByteData/elementSizeInBytes_A01_t01: fail # co19-roll r569: Please triage this failure
[ $runtime == vm || $compiler == dart2js ]
+Language/07_Classes/6_Constructors_A02_t01: SKIP # co19 issue 415.
Language/12_Expressions/01_Constants_A16_t01: FAIL, OK # co19 issue 525
Language/12_Expressions/01_Constants_A16_t02: FAIL, OK # co19 issue 525
-
-Language/13_Statements/11_Try_A02_t03: FAIL, OK # co19 issue 552
-Language/13_Statements/11_Try_A03_t03: FAIL, OK # co19 issue 552
-Language/13_Statements/11_Try_A04_t03: FAIL, OK # co19 issue 552
-Language/13_Statements/11_Try_A07_t03: FAIL, OK # co19 issue 553
-
-Language/07_Classes/6_Constructors_A02_t01: SKIP # co19 issue 415.
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A04_t15: FAIL, OK # co19 issue 547
+Language/14_Libraries_and_Scripts/1_Imports_A03_t65: PASS, FAIL, OK # co19 issue 560
LibTest/math/acos_A01_t01: PASS, FAIL, OK # co19 issue 44
LibTest/math/asin_A01_t01: PASS, FAIL, OK # co19 issue 44
@@ -45,8 +51,6 @@
LibTest/core/double/truncate_A01_t03: FAIL, OK # co19 issue 389
LibTest/core/double/truncate_A01_t04: FAIL, OK # co19 issue 389
-LibTest/core/int/toStringAsExponential_A02_t01: FAIL, OK # co19 issue 477
-
LibTest/async/Stream/Stream.periodic_A01_t01: TIMEOUT, PASS, FAIL, OK # co19 issue 538
LibTest/async/Stream/Stream.periodic_A03_t01: PASS, FAIL, OK # co19 issue 538
LibTest/async/Timer/run_A01_t01: PASS, FAIL, OK # co19 issue 538
@@ -65,9 +69,6 @@
LibTest/async/EventTransformStream/listen_A04_t01: Pass, Timeout # co19 issue 542
-LibTest/async/EventTransformStream/elementAt_A03_t01: FAIL # co19 issue 545
-LibTest/async/Stream/elementAt_A03_t01: FAIL # co19 issue 545
-
LibTest/typed_data/Int8List/Int8List.fromList_A01_t02: Fail # co19-roll r559: Please triage this failure
LibTest/typed_data/Float32List/removeAll_A01_t01: Fail # co19 issue 548
@@ -95,11 +96,19 @@
LibTest/typed_data/Uint8List/removeAll_A01_t01: Fail # co19 issue 548
LibTest/typed_data/Uint8List/retainAll_A01_t01: Fail # co19 issue 548
+Language/12_Expressions/06_Lists_A08_t01: Fail # co19 issue 561
+LibTest/core/String/String_class_A01_t01: Fail # co19 issue 561
+LibTest/core/String/concat_A01_t01: Fail # co19 issue 561
+LibTest/core/String/concat_A02_t01: Fail # co19 issue 561
+LibTest/core/String/hashCode_A01_t01: Fail # co19 issue 561
+
### CHECKED MODE FAILURES ###
[ ($runtime == vm || $compiler == dart2js) && $checked]
Language/13_Statements/09_Switch_A05_t01: FAIL, OK # co19 issue 498
Language/14_Libraries_and_Scripts/1_Imports_A03_t26: FAIL, OK # co19 issue 498
+Language/14_Libraries_and_Scripts/1_Imports_A03_t46: PASS, FAIL, OK # co19 issue 560
+Language/14_Libraries_and_Scripts/1_Imports_A03_t66: PASS, FAIL, OK # co19 issue 560
Language/15_Types/1_Static_Types_A03_t01: Fail # co19-roll r559: Please triage this failure
LibTest/async/EventTransformStream/contains_A01_t01: FAIL, OK # co19 issue 498
LibTest/core/DateTime/compareTo_A02_t01: FAIL, OK # co19 issue 498
@@ -111,4 +120,5 @@
LibTest/core/TypeError/line_A01_t01: FAIL, OK # co19 issue 510
LibTest/core/TypeError/srcType_A01_t01: FAIL, OK # co19 issue 510
LibTest/core/TypeError/url_A01_t01: FAIL, OK # co19 issue 510
-LibTest/core/NoSuchMethodError/NoSuchMethodError_A01_t01: Fail # co19 issue 550
+LibTest/core/Uri/encodeFull_A01_t02: fail # co19 Issue 589
+
diff --git a/tests/co19/co19-dart2dart.status b/tests/co19/co19-dart2dart.status
index a926046..91b835e 100644
--- a/tests/co19/co19-dart2dart.status
+++ b/tests/co19/co19-dart2dart.status
@@ -6,7 +6,6 @@
LibTest/async/Stream/Stream.periodic_A01_t01: Pass, Fail # Issue 12562.
Language/07_Classes/9_Superclasses/1_Inheritance_and_Overriding_A01_t03: Fail # TODO(dart2dart-team): Please triage this failure.
-LibTest/core/int/toStringAsExponential_A02_t01: Fail # co19 issue 477
Language/03_Overview/1_Scoping_A01_t39: Fail # http://dartbug.com/7202
@@ -122,7 +121,6 @@
Language/03_Overview/1_Scoping_A02_t28: Fail # co19-roll r559: Please triage this failure
Language/05_Variables/05_Variables_A05_t01: fail # co19-roll r546: Please triage this failure
Language/05_Variables/05_Variables_A05_t02: fail # co19-roll r546: Please triage this failure
-Language/05_Variables/05_Variables_A05_t03: fail # co19-roll r546: Please triage this failure
Language/05_Variables/05_Variables_A06_t01: fail # co19-roll r546: Please triage this failure
Language/05_Variables/05_Variables_A06_t02: fail # co19-roll r546: Please triage this failure
Language/05_Variables/05_Variables_A06_t03: fail # co19-roll r546: Please triage this failure
@@ -137,9 +135,6 @@
Language/07_Classes/1_Instance_Methods_A01_t04: Fail # co19-roll r559: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t05: Fail # co19-roll r559: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t06: Fail # co19-roll r559: Please triage this failure
-Language/07_Classes/3_Setters_A08_t01: fail # co19-roll r546: Please triage this failure
-Language/07_Classes/3_Setters_A08_t02: fail # co19-roll r546: Please triage this failure
-Language/07_Classes/3_Setters_A08_t04: fail # co19-roll r546: Please triage this failure
Language/07_Classes/4_Abstract_Instance_Members_A03_t01: Fail # co19-roll r559: Please triage this failure
Language/07_Classes/4_Abstract_Instance_Members_A03_t02: Fail # co19-roll r559: Please triage this failure
Language/07_Classes/4_Abstract_Instance_Members_A03_t03: Fail # co19-roll r559: Please triage this failure
@@ -225,7 +220,6 @@
Language/12_Expressions/12_Instance_Creation/2_Const_A10_t01: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A05_t01: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A04_t09: fail # co19-roll r546: Please triage this failure
-Language/12_Expressions/15_Method_Invocation/4_Super_Invocation_A02_t04: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/22_Equality_A01_t01: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/22_Equality_A01_t15: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/22_Equality_A01_t16: fail # co19-roll r546: Please triage this failure
@@ -248,20 +242,9 @@
Language/12_Expressions/33_Type_Cast_A03_t02: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/33_Type_Cast_A03_t03: fail # co19-roll r546: Please triage this failure
Language/13_Statements/02_Expression_Statements_A01_t08: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t01: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t02: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t03: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t04: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t05: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t06: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t07: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/03_Variable_Declaration_A04_t08: fail # co19-roll r546: Please triage this failure
Language/13_Statements/06_For_A01_t11: fail # co19-roll r546: Please triage this failure
Language/13_Statements/09_Switch_A01_t02: fail # co19-roll r546: Please triage this failure
Language/13_Statements/09_Switch_A04_t01: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A02_t03: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A03_t03: fail # co19-roll r546: Please triage this failure
-Language/13_Statements/11_Try_A04_t03: fail # co19-roll r546: Please triage this failure
Language/13_Statements/11_Try_A07_t03: fail # co19-roll r546: Please triage this failure
Language/13_Statements/12_Labels_A01_t03: fail # co19-roll r546: Please triage this failure
Language/14_Libraries_and_Scripts/1_Imports_A02_t28: fail # co19-roll r546: Please triage this failure
@@ -289,11 +272,7 @@
Language/14_Libraries_and_Scripts/5_URIs_A01_t21: fail # co19-roll r546: Please triage this failure
Language/14_Libraries_and_Scripts/5_URIs_A01_t24: fail # co19-roll r546: Please triage this failure
Language/14_Libraries_and_Scripts/5_URIs_A01_t25: fail # co19-roll r546: Please triage this failure
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t01: fail # co19-roll r546: Please triage this failure
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t02: fail # co19-roll r546: Please triage this failure
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t03: fail # co19-roll r546: Please triage this failure
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t04: fail # co19-roll r546: Please triage this failure
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t05: fail # co19-roll r546: Please triage this failure
+Language/14_Libraries_and_Scripts/1_Imports_A03_t65: pass, fail, ok # co19 issue 560
Language/15_Types/3_Type_Declarations/1_Typedef_A07_t01: fail # co19-roll r546: Please triage this failure
Language/15_Types/3_Type_Declarations/1_Typedef_A07_t02: fail # co19-roll r546: Please triage this failure
Language/15_Types/3_Type_Declarations/1_Typedef_A07_t03: fail # co19-roll r546: Please triage this failure
@@ -306,10 +285,8 @@
LibTest/async/Stream/Stream.periodic_A03_t01: fail, pass # co19-roll r546: Please triage this failure
LibTest/async/Timer/Timer.periodic_A02_t01: fail, pass # co19-roll r546: Please triage this failure
LibTest/core/DateTime/parse_A03_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/isolate_api/spawnFunction_A04_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/spawnUri_A02_t02: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/spawnUri_A02_t03: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/isolate_api/streamSpawnFunction_A02_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/addError_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/addError_A01_t02: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateStream/any_A02_t01: fail # co19-roll r546: Please triage this failure
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 0f0d2e3..e794031 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -12,6 +12,7 @@
LibTest/core/List/sort_A01_t04: Fail, Pass, Timeout # Must be a bug in jsshell, test sometimes times out.
[ $compiler == dart2js && $checked && $runtime == ie9 ]
+LibTest/core/Uri/encodeQueryComponent_A01_t02: Pass, Timeout # co19-roll r576: Please triage this failure
LibTest/isolate/isolate_api/spawnUri_A02_t03: Pass, Fail # Issue 8920
@@ -20,10 +21,12 @@
[ $compiler == dart2js ]
Language/03_Overview/1_Scoping_A02_t05: Fail # TODO(ahe): Please triage this failure.
Language/03_Overview/1_Scoping_A02_t06: Fail # TODO(ahe): Please triage this failure.
+Language/05_Variables/1_Evaluation_of_Implicit_Variable_Getters_A01_t02: fail # co19-roll r576: Please triage this failure
Language/07_Classes/6_Constructors/2_Factories_A07_t01: Fail # TODO(ahe): Please triage this failure.
-LibTest/core/List/List_A03_t01: Fail # TODO(kasperl): Please triage this failure.
+Language/13_Statements/11_Try_A07_t03: fail # co19-roll r576: Please triage this failure
LibTest/core/double/INFINITY_A01_t04: Fail # TODO(ahe): Please triage this failure.
LibTest/core/double/NEGATIVE_INFINITY_A01_t04: Fail # TODO(ahe): Please triage this failure.
+LibTest/core/List/List_A03_t01: Fail # TODO(kasperl): Please triage this failure.
LibTest/core/List/sort_A01_t04: Pass, Slow # http://dartbug.com/11846
LibTest/math/pow_A13_t01: FAIL, OK # co19 issue 507
LibTest/math/pow_A18_t01: FAIL, OK # co19 issue 507
@@ -44,6 +47,19 @@
LibTest/typed_data/ByteData/setUint16_A02_t02: fail # co19-roll r569: Please triage this failure
LibTest/typed_data/ByteData/setUint32_A02_t02: fail # co19-roll r569: Please triage this failure
LibTest/typed_data/ByteData/setUint64_A02_t02: fail # co19-roll r569: Please triage this failure
+LibTest/typed_data/Float32List/Float32List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Float32x4List/Float32x4List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Float64List/Float64List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Int16List/Int16List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Int32List/Int32List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Int8List/Int8List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Uint16List/Uint16List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Uint32List/Uint32List_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Uint8ClampedList/Uint8ClampedList_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/typed_data/Uint8List/Uint8List_A02_t01: fail # co19-roll r576: Please triage this failure
+
+[ $compiler == dart2js && $runtime != ie9 ]
+LibTest/typed_data/ByteData/ByteData_A02_t01: fail # co19-roll r576: Please triage this failure
[ $compiler == dart2js && $runtime == jsshell ]
LibTest/isolate/SendPort/send_A02_t05: Fail, Pass # TODO(ahe): Please triage this failure.
@@ -314,21 +330,8 @@
Language/12_Expressions/30_Identifier_Reference_A04_t09: Fail # Checks that it is a compile-time error when a built-in identifier dynamic is used as the declared name of a type variable.
Language/12_Expressions/30_Identifier_Reference_A05_t01: Fail # Checks that it is a compile-time error when a built-in identifier "abstract" is used as a type annotation of a local variable.
Language/12_Expressions/30_Identifier_Reference_A05_t12: Fail # Checks that it is a compile-time error when a built-in identifier "static" is used as a type annotation of a local variable.
-Language/13_Statements/03_Variable_Declaration_A04_t01: Fail # Checks that a variable declaration statement var e = e; causes a compile-time error.
-Language/13_Statements/03_Variable_Declaration_A04_t02: Fail # Checks that a variable declaration statement T e = e; causes a compile-time error.
-Language/13_Statements/03_Variable_Declaration_A04_t03: Fail # Checks that a variable declaration statement const e = e; causes a compile-time error.
-Language/13_Statements/03_Variable_Declaration_A04_t04: Fail # Checks that a variable declaration statement const T e = e; causes a compile-time error.
-Language/13_Statements/03_Variable_Declaration_A04_t05: Fail # Checks that a variable declaration statement final e = e; causes a compile-time error.
-Language/13_Statements/03_Variable_Declaration_A04_t06: Fail # Checks that a variable declaration statement final T e = e; causes a compile-time error.
-Language/13_Statements/03_Variable_Declaration_A04_t07: Fail # Checks that a variable declaration statement var e = e; causes a compile-time error. A top-level variable with the same name is declared.
-Language/13_Statements/03_Variable_Declaration_A04_t08: Fail # Checks that it is a compile-time error if the initializing expression of a local variable v refers to the name v= even when a top-level variable with the same name is declared.
Language/14_Libraries_and_Scripts/5_URIs_A01_t24: Fail # Checks that it is a compile-time error when the URI in a part directive consists of two adjacent string literals instead of one.
Language/14_Libraries_and_Scripts/5_URIs_A01_t25: Fail # Checks that it is a compile-time error when the URI in a part directive consists of two adjacent multi-line string literals instead of one.
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t01: Fail # Checks that a compile error is produced when a null default value is specified for a required argument in a function type alias.
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t02: Fail # Checks that a compile error is produced when a default value is specified for an optional positional argument in a function type alias.
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t03: Fail # Checks that a compile error is produced when a default value is specified for an optional named argument in a function type alias.
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t04: Fail # Checks that a compile error is produced when a default value is specified for one of the required arguments in a function type alias.
-Language/15_Types/3_Type_Declarations/1_Typedef_A06_t05: Fail # Checks that a compile error is produced when default values are specified for several parameters in a function type alias.
Language/15_Types/3_Type_Declarations/1_Typedef_A07_t01: Fail # Checks that self-referencing typedef is not allowed (return value type annotation has the same name as the type alias).
Language/15_Types/3_Type_Declarations/1_Typedef_A07_t02: Fail # Checks that self-referencing typedef is not allowed (positional formal parameter type annotation has the same name as the type alias).
Language/15_Types/3_Type_Declarations/1_Typedef_A07_t03: Fail # Checks that self-referencing typedef is not allowed (positional optional parameter type annotation has the same name as the type alias).
@@ -423,16 +426,12 @@
Language/03_Overview/1_Scoping_A02_t28: fail # co19-roll r559: Please triage this failure
Language/05_Variables/05_Variables_A05_t01: fail # co19-roll r546: Please triage this failure
Language/05_Variables/05_Variables_A05_t02: fail # co19-roll r546: Please triage this failure
-Language/05_Variables/05_Variables_A05_t03: fail # co19-roll r546: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t01: fail # co19-roll r559: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t02: fail # co19-roll r559: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t03: fail # co19-roll r559: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t04: fail # co19-roll r559: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t05: fail # co19-roll r559: Please triage this failure
Language/07_Classes/1_Instance_Methods_A01_t06: fail # co19-roll r559: Please triage this failure
-Language/07_Classes/3_Setters_A08_t01: fail # co19-roll r546: Please triage this failure
-Language/07_Classes/3_Setters_A08_t02: fail # co19-roll r546: Please triage this failure
-Language/07_Classes/3_Setters_A08_t04: fail # co19-roll r546: Please triage this failure
Language/07_Classes/4_Abstract_Instance_Members_A03_t01: fail # co19-roll r559: Please triage this failure
Language/07_Classes/4_Abstract_Instance_Members_A03_t02: fail # co19-roll r559: Please triage this failure
Language/07_Classes/4_Abstract_Instance_Members_A03_t03: fail # co19-roll r559: Please triage this failure
@@ -461,7 +460,6 @@
Language/12_Expressions/12_Instance_Creation/2_Const_A11_t01: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/12_Instance_Creation/2_Const_A11_t03: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A04_t09: fail # co19-roll r546: Please triage this failure
-Language/12_Expressions/15_Method_Invocation/4_Super_Invocation_A02_t04: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/22_Equality_A01_t01: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/22_Equality_A05_t01: fail # co19-roll r546: Please triage this failure
Language/12_Expressions/30_Identifier_Reference_A02_t01: fail # co19-roll r546: Please triage this failure
@@ -511,13 +509,11 @@
LibTest/core/Match/pattern_A01_t01: fail # co19-roll r559: Please triage this failure
LibTest/core/RegExp/allMatches_A01_t01: fail # co19-roll r559: Please triage this failure
LibTest/isolate/isolate_api/spawnFunction_A02_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/isolate_api/spawnFunction_A04_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/spawnFunction_A04_t02: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/spawnFunction_A04_t03: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/spawnUri_A02_t02: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/spawnUri_A02_t03: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/streamSpawnFunction_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/isolate_api/streamSpawnFunction_A02_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/isolate_api/streamSpawnFunction_A02_t02: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/add_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/add_A01_t02: fail # co19-roll r546: Please triage this failure
@@ -666,5 +662,7 @@
[ $compiler == dart2js || $compiler == dart2dart ]
Language/07_Classes/6_Constructors/1_Generative_Constructors_A15_t08: fail # co19-roll r569: Please triage this failure
-
+LibTest/core/NoSuchMethodError/NoSuchMethodError_A01_t01: fail # co19-roll r576: Please triage this failure
+LibTest/core/Uri/Uri_A06_t02: fail # co19-roll r576: Please triage this failure
+LibTest/core/Uri/Uri_A06_t03: fail # co19-roll r576: Please triage this failure
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index 950b3b7..972dd6a 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -5,6 +5,40 @@
[ $runtime == vm ]
Language/12_Expressions/01_Constants_A16_t04: fail # Issue 392
Language/12_Expressions/01_Constants_A16_t05: fail # Issue 392
+LibTest/typed_data/Float32List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Float32List/Float32List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Float32List/Float32List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Float32List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Float32x4List/Float32x4List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Float32x4List/Float32x4List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Float32x4List/Float32x4List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Float64List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Float64List/Float64List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Float64List/Float64List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Float64List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Int16List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Int16List/Int16List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Int16List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Int32List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Int32List/Int32List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Int32List/Int32List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Int32List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Int64List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Int64List/Int64List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Int64List/Int64List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Int64List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint16List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint16List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint16List/Uint16List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint16List/Uint16List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Uint32List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint32List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint32List/Uint32List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint32List/Uint32List.view_A01_t02: fail # Issue 549
+LibTest/typed_data/Uint64List/buffer_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint64List/offsetInBytes_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint64List/Uint64List.view_A01_t01: fail # Issue 549
+LibTest/typed_data/Uint64List/Uint64List.view_A01_t02: fail # Issue 549
[ $runtime == vm && $system == windows ]
LibTest/core/Stopwatch/elapsed_A01_t01: Pass, Fail # Issue 11382.
@@ -60,10 +94,12 @@
LibTest/typed_data/Float32List/Float32List.view_A01_t02: Crash # Issue 12868
LibTest/typed_data/Float64List/Float64List.view_A01_t02: Crash # Issue 12868
+[ $compiler == none && $runtime == vm && ($arch == simarm || $arch == simmips) ]
+LibTest/core/Uri/Uri_A06_t03: Pass, Timeout # co19-roll r576: Please triage this failure
+
[ $compiler == none && $runtime == vm ]
Language/05_Variables/05_Variables_A05_t01: fail # Dart issue 12539
Language/05_Variables/05_Variables_A05_t02: fail # Dart issue 12539
-Language/05_Variables/05_Variables_A05_t03: fail # Dart issue 12539
Language/05_Variables/05_Variables_A06_t01: fail # Dart issue 12543
Language/05_Variables/05_Variables_A06_t02: fail # Dart issue 12543
Language/05_Variables/05_Variables_A06_t03: fail # Dart issue 12543
@@ -71,11 +107,6 @@
Language/05_Variables/05_Variables_A06_t05: fail # Dart issue 12543
Language/05_Variables/05_Variables_A06_t06: fail # Dart issue 12543
Language/05_Variables/05_Variables_A16_t02: fail # Dart issue 12544
-Language/07_Classes/07_Classes_A11_t02: fail # Dart issue 12545
-Language/07_Classes/07_Classes_A11_t04: fail # Dart issue 12545
-Language/07_Classes/3_Setters_A08_t01: fail # co19 issue 520
-Language/07_Classes/3_Setters_A08_t02: fail # co19 issue 520
-Language/07_Classes/3_Setters_A08_t04: fail # co19 issue 520
Language/07_Classes/6_Constructors/1_Generative_Constructors_A09_t01: fail # Dart issue 12543
Language/12_Expressions/05_Strings_A02_t46: fail # Dart issue 12547
Language/12_Expressions/05_Strings_A02_t48: fail # Dart issue 12547
@@ -85,8 +116,6 @@
Language/12_Expressions/12_Instance_Creation/2_Const_A10_t01: fail # co19 issue 525
Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A05_t01: fail # Dart issue 12550
Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A04_t09: fail # Dart issue 12549
-Language/12_Expressions/15_Method_Invocation/4_Super_Invocation_A02_t04: fail # Dart issue 12549
-Language/12_Expressions/30_Identifier_Reference_A05_t02: fail # Dart issue 12551
Language/12_Expressions/30_Identifier_Reference_A08_t02: fail # Dart issue 12593
Language/12_Expressions/32_Type_Test_A04_t02: fail # co19 issue 503
Language/12_Expressions/32_Type_Test_A04_t03: fail # co19 issue 534
@@ -102,18 +131,7 @@
Language/13_Statements/09_Switch_A03_t02: fail # Dart issue 7307
Language/13_Statements/09_Switch_A04_t01: fail # Dart issue 11233
Language/13_Statements/12_Labels_A01_t03: fail # Dart issue 2238
-Language/14_Libraries_and_Scripts/13_Libraries_and_Scripts_A05_t04: fail # Dart issue 12913
-Language/14_Libraries_and_Scripts/1_Imports_A03_t09: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t10: fail # Dart issue 12915
Language/14_Libraries_and_Scripts/1_Imports_A03_t27: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t29: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t30: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t47: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t49: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t50: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t67: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t69: fail # Dart issue 12915
-Language/14_Libraries_and_Scripts/1_Imports_A03_t70: fail # Dart issue 12915
Language/14_Libraries_and_Scripts/2_Exports_A04_t02: fail # Dart issue 12916
Language/14_Libraries_and_Scripts/2_Exports_A04_t03: fail # Dart issue 12916
Language/14_Libraries_and_Scripts/2_Exports_A05_t01: fail # Dart issue 12918
@@ -128,8 +146,6 @@
LibTest/core/DateTime/parse_A03_t01: fail # Issue 12514
# All isolate are being ignored at the moment as the library will go through some changes.
-LibTest/isolate/isolate_api/spawnFunction_A04_t01: fail # co19-roll r546: Please triage this failure
-LibTest/isolate/isolate_api/streamSpawnFunction_A02_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/addError_A01_t01: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateSink/addError_A01_t02: fail # co19-roll r546: Please triage this failure
LibTest/isolate/IsolateStream/any_A02_t01: fail # co19-roll r546: Please triage this failure
@@ -163,3 +179,10 @@
LibTest/typed_data/Float32x4List/skip_A01_t01: Fail # Dart issue 12861
LibTest/typed_data/Float32x4List/take_A01_t01: Fail # Dart issue 12861
LibTest/typed_data/Float32x4List/take_A02_t01: Fail # Dart issue 12861
+
+[ $compiler == none && $runtime == vm && $system == windows ]
+Language/15_Types/4_Interface_Types_A11_t01: pass, timeout # co19-roll r576: Please triage this failure
+Language/15_Types/4_Interface_Types_A11_t02: pass, timeout # co19-roll r576: Please triage this failure
+LibTest/core/Uri/toFilePath_A02_t01: fail # co19-roll r576: Please triage this failure
+LibTest/core/Uri/Uri.file_A01_t01: fail # co19-roll r576: Please triage this failure
+
diff --git a/tests/compiler/dart2js/analyze_all_test.dart b/tests/compiler/dart2js/analyze_all_test.dart
index 58a313c..5f6a8ab 100644
--- a/tests/compiler/dart2js/analyze_all_test.dart
+++ b/tests/compiler/dart2js/analyze_all_test.dart
@@ -29,18 +29,16 @@
main() {
Uri uri = Uri.parse('test:code');
- asyncStart();
var compiler1 = compilerFor(SOURCE, uri, analyzeAll: false);
- compiler1.runCompiler(uri).then((_) {
+ asyncTest(() => compiler1.runCompiler(uri).then((_) {
Expect.isFalse(compiler1.compilationFailed);
print(compiler1.warnings);
Expect.isTrue(compiler1.warnings.isEmpty, 'unexpected warnings');
Expect.isTrue(compiler1.errors.isEmpty, 'unexpected errors');
- }).whenComplete(() => asyncEnd());
+ }));
- asyncStart();
var compiler2 = compilerFor(SOURCE, uri, analyzeAll: true);
- compiler2.runCompiler(uri).then((_) {
+ asyncTest(() => compiler2.runCompiler(uri).then((_) {
Expect.isTrue(compiler2.compilationFailed);
Expect.isTrue(compiler2.warnings.isEmpty, 'unexpected warnings');
Expect.equals(2, compiler2.errors.length,
@@ -53,5 +51,5 @@
Expect.equals(MessageKind.CONSTRUCTOR_IS_NOT_CONST,
compiler2.errors[1].message.kind);
Expect.equals("Foo", compiler2.errors[1].node.toString());
- }).whenComplete(() => asyncEnd());
+ }));
}
diff --git a/tests/compiler/dart2js/analyze_only_test.dart b/tests/compiler/dart2js/analyze_only_test.dart
index 67bbeb8..818149a 100644
--- a/tests/compiler/dart2js/analyze_only_test.dart
+++ b/tests/compiler/dart2js/analyze_only_test.dart
@@ -45,7 +45,7 @@
onValue(code, errors, warnings);
}, onError: (e) {
throw 'Compilation failed';
- }).whenComplete(() => asyncEnd());
+ }).then(asyncSuccess);
}
main() {
diff --git a/tests/compiler/dart2js/async_compiler_input_provider_test.dart b/tests/compiler/dart2js/async_compiler_input_provider_test.dart
index 9b8a826..d329e3a 100644
--- a/tests/compiler/dart2js/async_compiler_input_provider_test.dart
+++ b/tests/compiler/dart2js/async_compiler_input_provider_test.dart
@@ -46,11 +46,10 @@
Uri libraryRoot = script.resolve('../../../sdk/');
Uri packageRoot = script.resolve('./packages/');
- asyncStart();
- compiler.compile(entrypoint, libraryRoot, packageRoot,
+ asyncTest(() => compiler.compile(entrypoint, libraryRoot, packageRoot,
provideInput, handleDiagnostic, []).then((code) {
Expect.isNotNull(code);
- }).whenComplete(() => asyncEnd());
+ }));
}
void handleDiagnostic(Uri uri, int begin, int end, String message,
diff --git a/tests/compiler/dart2js/bad_loop_test.dart b/tests/compiler/dart2js/bad_loop_test.dart
index 65629a4..8a86fd6 100644
--- a/tests/compiler/dart2js/bad_loop_test.dart
+++ b/tests/compiler/dart2js/bad_loop_test.dart
@@ -37,12 +37,11 @@
libraryRoot,
packageRoot,
['--analyze-only']);
- asyncStart();
- compiler.run(Uri.parse('memory:main.dart')).then((_) {
+ asyncTest(() => compiler.run(Uri.parse('memory:main.dart')).then((_) {
Expect.isTrue(compiler.compilationFailed);
Expect.equals(5, errorCount);
Expect.equals(1, warningCount);
- }).whenComplete(() => asyncEnd());
+ }));
}
const Map MEMORY_SOURCE_FILES = const {
diff --git a/tests/compiler/dart2js/call_site_simple_type_inferer_test.dart b/tests/compiler/dart2js/call_site_simple_type_inferer_test.dart
index b568b19..541face 100644
--- a/tests/compiler/dart2js/call_site_simple_type_inferer_test.dart
+++ b/tests/compiler/dart2js/call_site_simple_type_inferer_test.dart
@@ -16,14 +16,13 @@
bool disableInlining,
check(compiler, element)) {
Uri uri = new Uri(scheme: 'source');
- asyncStart();
var compiler = compilerFor(code, uri);
compiler.disableInlining = disableInlining;
- compiler.runCompiler(uri).then((_) {
+ asyncTest(() => compiler.runCompiler(uri).then((_) {
var cls = findElement(compiler, className);
var member = cls.lookupLocalMember(buildSourceString(memberName));
return check(compiler, member);
- }).whenComplete(() => asyncEnd());
+ }));
}
const String TEST_1 = r"""
@@ -244,7 +243,7 @@
void test() {
runTest(TEST_1, (compiler) => [compiler.typesTask.stringType]);
runTest(TEST_2, (compiler) => [compiler.typesTask.intType]);
- runTest(TEST_3, (compiler) => [compiler.typesTask.numType]);
+ runTest(TEST_3, (compiler) => [compiler.typesTask.intType]);
runTest(TEST_4, (compiler) => [compiler.typesTask.numType]);
runTest(TEST_5, (compiler) => [compiler.typesTask.numType]);
runTest(TEST_6, (compiler) => [compiler.typesTask.numType]);
@@ -257,8 +256,8 @@
compiler.typesTask.dynamicType.nonNullable()]);
runTest(TEST_9, (compiler) => [compiler.typesTask.intType,
compiler.typesTask.intType]);
- runTest(TEST_10, (compiler) => [subclassOfInterceptor(compiler),
- subclassOfInterceptor(compiler)]);
+ runTest(TEST_10, (compiler) => [compiler.typesTask.intType,
+ compiler.typesTask.intType]);
runTest(TEST_11, (compiler) => [subclassOfInterceptor(compiler),
subclassOfInterceptor(compiler)]);
diff --git a/tests/compiler/dart2js/class_codegen2_test.dart b/tests/compiler/dart2js/class_codegen2_test.dart
index 458e1d3..0169e4e 100644
--- a/tests/compiler/dart2js/class_codegen2_test.dart
+++ b/tests/compiler/dart2js/class_codegen2_test.dart
@@ -90,10 +90,10 @@
// { a: true, }. Make sure this doesn't happen again.
RegExp danglingComma = new RegExp(r',[ \n]*}');
- asyncStart();
- Future.forEach([TEST_ONE, TEST_TWO, TEST_THREE, TEST_FOUR], (test) {
+ asyncTest(() => Future.forEach([TEST_ONE, TEST_TWO, TEST_THREE, TEST_FOUR],
+ (test) {
return compileAll(test).then((generated) {
Expect.isFalse(danglingComma.hasMatch(generated));
});
- }).whenComplete(() => asyncEnd());
+ }));
}
diff --git a/tests/compiler/dart2js/class_codegen_test.dart b/tests/compiler/dart2js/class_codegen_test.dart
index ed7c90c..9a57c80 100644
--- a/tests/compiler/dart2js/class_codegen_test.dart
+++ b/tests/compiler/dart2js/class_codegen_test.dart
@@ -90,7 +90,7 @@
constructor1() {
asyncTest(() => compileAll(TEST_FIVE).then((generated) {
- Expect.isTrue(generated.contains(new RegExp(r"new [$a-z]+\.A\(a\);")));
+ Expect.isTrue(generated.contains(new RegExp(r"new [$A-Z]+\.A\(a\);")));
}));
}
diff --git a/tests/compiler/dart2js/compiler_helper.dart b/tests/compiler/dart2js/compiler_helper.dart
index 98aeb8a..8af47af 100644
--- a/tests/compiler/dart2js/compiler_helper.dart
+++ b/tests/compiler/dart2js/compiler_helper.dart
@@ -29,6 +29,8 @@
import '../../../sdk/lib/_internal/compiler/implementation/types/types.dart'
as types;
+export '../../../sdk/lib/_internal/compiler/implementation/types/types.dart'
+ show TypeMask;
import '../../../sdk/lib/_internal/compiler/implementation/util/util.dart';
export '../../../sdk/lib/_internal/compiler/implementation/util/util.dart';
diff --git a/tests/compiler/dart2js/interceptor_test.dart b/tests/compiler/dart2js/interceptor_test.dart
index 9b9da69..a87ffe0 100644
--- a/tests/compiler/dart2js/interceptor_test.dart
+++ b/tests/compiler/dart2js/interceptor_test.dart
@@ -42,7 +42,7 @@
// Check that one-shot interceptors preserve variable names, see
// https://code.google.com/p/dart/issues/detail?id=8106.
generated = compile(TEST_TWO, entry: 'foo');
- Expect.isTrue(generated.contains(new RegExp(r'[$a-z]+\.toString\$0\(a\)')));
+ Expect.isTrue(generated.contains(new RegExp(r'[$A-Z]+\.toString\$0\(a\)')));
Expect.isTrue(generated.contains('myVariableName'));
// Check that an intercepted getter that does not need to be
@@ -50,7 +50,7 @@
// access.
generated = compile(TEST_THREE, entry: 'foo');
Expect.isFalse(generated.contains(r'a.get$length()'));
- Expect.isTrue(generated.contains(new RegExp(r'[$a-z]+\.A\$\(\)\.length')));
+ Expect.isTrue(generated.contains(new RegExp(r'[$A-Z]+\.A\$\(\)\.length')));
Expect.isTrue(
- generated.contains(new RegExp(r'[$a-z]+\.get\$length\$a\(a\)')));
+ generated.contains(new RegExp(r'[$A-Z]+\.get\$length\$a\(a\)')));
}
diff --git a/tests/compiler/dart2js/mirrors_test.dart b/tests/compiler/dart2js/mirrors_test.dart
index 7051a3b..0d6119b 100644
--- a/tests/compiler/dart2js/mirrors_test.dart
+++ b/tests/compiler/dart2js/mirrors_test.dart
@@ -55,7 +55,7 @@
<String>['--preserve-comments']);
result.then((MirrorSystem mirrors) {
test(mirrors);
- }).whenComplete(() => asyncEnd());
+ }).then(asyncSuccess);
}
void test(MirrorSystem mirrors) {
diff --git a/tests/compiler/dart2js/simple_inferrer_callers_test.dart b/tests/compiler/dart2js/simple_inferrer_callers_test.dart
new file mode 100644
index 0000000..616c819
--- /dev/null
+++ b/tests/compiler/dart2js/simple_inferrer_callers_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2013, 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 that computation of callers of an element works when two
+// elements of the same name are being invoked in the same method.
+
+import 'package:expect/expect.dart';
+import "package:async_helper/async_helper.dart";
+import '../../../sdk/lib/_internal/compiler/implementation/types/types.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/types/simple_types_inferrer.dart';
+
+import 'compiler_helper.dart';
+import 'parser_helper.dart';
+
+const String TEST = """
+class A {
+ var field;
+}
+
+class B {
+ var field;
+}
+
+main() {
+ new A().field;
+ new B().field;
+}
+""";
+
+// Create our own type inferrer to avoid clearing out the internal
+// data structures.
+class MyInferrer extends SimpleTypesInferrer {
+ MyInferrer(compiler) : super(compiler);
+ clear() {}
+}
+
+void main() {
+ Uri uri = new Uri(scheme: 'source');
+ var compiler = compilerFor(TEST, uri);
+ var inferrer = new MyInferrer(compiler);
+ compiler.typesTask.typesInferrer = inferrer;
+ asyncTest(() => compiler.runCompiler(uri).then((_) {
+ var mainElement = findElement(compiler, 'main');
+ var classA = findElement(compiler, 'A');
+ var fieldA = classA.lookupLocalMember(buildSourceString('field'));
+ var classB = findElement(compiler, 'B');
+ var fieldB = classB.lookupLocalMember(buildSourceString('field'));;
+
+ Expect.isTrue(inferrer.getCallersOf(fieldA).contains(mainElement));
+ Expect.isTrue(inferrer.getCallersOf(fieldB).contains(mainElement));
+ }));
+}
diff --git a/tests/compiler/dart2js/simple_inferrer_closure_test.dart b/tests/compiler/dart2js/simple_inferrer_closure_test.dart
index 2ea06ba..ef06d96 100644
--- a/tests/compiler/dart2js/simple_inferrer_closure_test.dart
+++ b/tests/compiler/dart2js/simple_inferrer_closure_test.dart
@@ -108,13 +108,13 @@
}
checkReturn('returnInt1', compiler.typesTask.intType);
- checkReturn('returnInt2', compiler.typesTask.intType.nullable());
+ checkReturn('returnInt2', compiler.typesTask.intType);
checkReturn('returnInt3', compiler.typesTask.intType);
checkReturn('returnInt4', compiler.typesTask.intType);
- checkReturn('returnDyn1', compiler.typesTask.dynamicType);
- checkReturn('returnDyn2', compiler.typesTask.dynamicType);
- checkReturn('returnDyn3', compiler.typesTask.dynamicType);
+ checkReturn('returnDyn1', compiler.typesTask.dynamicType.nonNullable());
+ checkReturn('returnDyn2', compiler.typesTask.dynamicType.nonNullable());
+ checkReturn('returnDyn3', compiler.typesTask.dynamicType.nonNullable());
checkReturn('returnNum1', compiler.typesTask.numType);
}));
}
diff --git a/tests/compiler/dart2js/simple_inferrer_no_such_method_test.dart b/tests/compiler/dart2js/simple_inferrer_no_such_method_test.dart
index 45b3878..af06626 100644
--- a/tests/compiler/dart2js/simple_inferrer_no_such_method_test.dart
+++ b/tests/compiler/dart2js/simple_inferrer_no_such_method_test.dart
@@ -21,7 +21,7 @@
foo() => {};
}
-var a;
+var a = [new B(), new C()][0];
test1() => new A().foo();
test2() => a.foo();
test3() => new B().foo();
@@ -57,7 +57,7 @@
noSuchMethod(im) => 42.5;
}
-var a;
+var a = [new B(), new C(), new D()][0];
test1() => a.foo();
test2() => new B().foo();
test3() => new C().foo();
diff --git a/tests/compiler/dart2js/simple_inferrer_postfix_prefix_test.dart b/tests/compiler/dart2js/simple_inferrer_postfix_prefix_test.dart
index 427e64d..7a1e29b 100644
--- a/tests/compiler/dart2js/simple_inferrer_postfix_prefix_test.dart
+++ b/tests/compiler/dart2js/simple_inferrer_postfix_prefix_test.dart
@@ -90,9 +90,9 @@
checkReturnInClass('B', 'returnString1', typesTask.stringType);
checkReturnInClass('B', 'returnString2', typesTask.stringType);
- checkReturnInClass('B', 'returnDynamic1', typesTask.dynamicType);
- checkReturnInClass('B', 'returnDynamic2', typesTask.dynamicType);
- checkReturnInClass('B', 'returnDynamic3', typesTask.dynamicType);
- checkReturnInClass('B', 'returnDynamic4', typesTask.dynamicType);
+ checkReturnInClass('B', 'returnDynamic1', const TypeMask.nonNullEmpty());
+ checkReturnInClass('B', 'returnDynamic2', const TypeMask.nonNullEmpty());
+ checkReturnInClass('B', 'returnDynamic3', const TypeMask.nonNullEmpty());
+ checkReturnInClass('B', 'returnDynamic4', const TypeMask.nonNullEmpty());
}));
}
diff --git a/tests/compiler/dart2js/simple_inferrer_test.dart b/tests/compiler/dart2js/simple_inferrer_test.dart
index cfc2a07..9af3822 100644
--- a/tests/compiler/dart2js/simple_inferrer_test.dart
+++ b/tests/compiler/dart2js/simple_inferrer_test.dart
@@ -246,6 +246,49 @@
return (a is int || a is List) ? a : 42;
}
+testIsCheck23(a) {
+ if (a is! int) throw 'foo';
+ return a;
+}
+
+testIsCheck24(a) {
+ if (a is! int) return 42;
+ return a;
+}
+
+testIsCheck25(a) {
+ if (a is int) throw 'foo';
+ return a;
+}
+
+testIsCheck26(a) {
+ if (a is int) {
+ } else {
+ throw 42;
+ }
+ return a;
+}
+
+testIsCheck27(a) {
+ if (a is int) {
+ } else {
+ return 42;
+ }
+ return a;
+}
+
+testIsCheck28(a) {
+ if (a is int) {
+ } else {
+ }
+ return a;
+}
+
+testIsCheck29(a) {
+ if (a is int) {}
+ return a;
+}
+
testIf1(a) {
var c = null;
if (a) {
@@ -559,6 +602,13 @@
testIsCheck20();
testIsCheck21(topLevelGetter());
testIsCheck22(topLevelGetter());
+ testIsCheck23(topLevelGetter());
+ testIsCheck24(topLevelGetter());
+ testIsCheck25(topLevelGetter());
+ testIsCheck26(topLevelGetter());
+ testIsCheck27(topLevelGetter());
+ testIsCheck28(topLevelGetter());
+ testIsCheck29(topLevelGetter());
testIf1(topLevelGetter());
testIf2(topLevelGetter());
returnAsString();
@@ -665,6 +715,13 @@
checkReturn('testIsCheck20', typesTask.dynamicType.nonNullable());
checkReturn('testIsCheck21', typesTask.dynamicType);
checkReturn('testIsCheck22', typesTask.dynamicType);
+ checkReturn('testIsCheck23', intType);
+ checkReturn('testIsCheck24', intType);
+ checkReturn('testIsCheck25', typesTask.dynamicType);
+ checkReturn('testIsCheck26', intType);
+ checkReturn('testIsCheck27', intType);
+ checkReturn('testIsCheck28', typesTask.dynamicType);
+ checkReturn('testIsCheck29', typesTask.dynamicType);
checkReturn('testIf1', typesTask.intType.nullable());
checkReturn('testIf2', typesTask.intType.nullable());
checkReturn('returnAsString',
@@ -730,7 +787,7 @@
checkReturn('testCascade1', typesTask.growableListType);
checkReturn('testCascade2', new TypeMask.nonNullExact(
- typesTask.rawTypeOf(findElement(compiler, 'CascadeHelper'))));
+ findElement(compiler, 'CascadeHelper').rawType));
checkReturn('testSpecialization1', typesTask.numType);
checkReturn('testSpecialization2', typesTask.dynamicType);
checkReturn('testSpecialization3', typesTask.intType.nullable());
diff --git a/tests/compiler/dart2js/static_closure_test.dart b/tests/compiler/dart2js/static_closure_test.dart
index 622def2..d08c9f0 100644
--- a/tests/compiler/dart2js/static_closure_test.dart
+++ b/tests/compiler/dart2js/static_closure_test.dart
@@ -18,6 +18,6 @@
// If this test fail, please take a look at the use of
// toStringWrapper in captureStackTrace in js_helper.dart.
Expect.isTrue(code.contains(
- new RegExp(r'print\([$a-z]+\.main\$closure\);')));
+ new RegExp(r'print\([$A-Z]+\.main\$closure\);')));
}));
}
diff --git a/tests/compiler/dart2js/string_add_test.dart b/tests/compiler/dart2js/string_add_test.dart
new file mode 100644
index 0000000..55adbc6
--- /dev/null
+++ b/tests/compiler/dart2js/string_add_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+import 'compiler_helper.dart';
+
+main() {
+ asyncTest(() => compileAll(
+ r'''main() { return "foo" + "bar"; }''').then((code) {
+ Expect.isTrue(!code.contains(r'$add'));
+ }));
+}
diff --git a/tests/compiler/dart2js/string_interpolation_test.dart b/tests/compiler/dart2js/string_interpolation_test.dart
index 01ea1bb..4cb5f60 100644
--- a/tests/compiler/dart2js/string_interpolation_test.dart
+++ b/tests/compiler/dart2js/string_interpolation_test.dart
@@ -14,6 +14,6 @@
asyncTest(() => compileAll(
r'''main() { return "foo ${new Object()}"; }''').then((code) {
- Expect.isFalse(code.contains(r'concat'));
+ Expect.isFalse(code.contains(r'$add'));
}));
}
diff --git a/tests/compiler/dart2js/subtype_test.dart b/tests/compiler/dart2js/subtype_test.dart
index 2322ee0..a947ced 100644
--- a/tests/compiler/dart2js/subtype_test.dart
+++ b/tests/compiler/dart2js/subtype_test.dart
@@ -730,4 +730,4 @@
expect(true, J_U, J_U);
expect(false, J_U, A_T);
}));
-}
\ No newline at end of file
+}
diff --git a/tests/compiler/dart2js/type_guard_unuser_test.dart b/tests/compiler/dart2js/type_guard_unuser_test.dart
index 4cad5fd..7df302b 100644
--- a/tests/compiler/dart2js/type_guard_unuser_test.dart
+++ b/tests/compiler/dart2js/type_guard_unuser_test.dart
@@ -47,7 +47,7 @@
checkNumberOfMatches(matches, 0);
Expect.isTrue(
generated.contains(
- new RegExp(r'return a === true \? [$a-z]+\.foo\(2\) : b;')));
+ new RegExp(r'return a === true \? [$A-Z]+\.foo\(2\) : b;')));
generated = compile(TEST_TWO, entry: 'foo');
regexp = new RegExp("foo\\(1\\)");
diff --git a/tests/compiler/dart2js/type_variable_bound_test.dart b/tests/compiler/dart2js/type_variable_bound_test.dart
index 5c9b720..6844abb 100644
--- a/tests/compiler/dart2js/type_variable_bound_test.dart
+++ b/tests/compiler/dart2js/type_variable_bound_test.dart
@@ -9,12 +9,36 @@
Future compile(String source) {
Uri uri = Uri.parse('test:code');
- var compiler = compilerFor(source, uri);
+ var compiler = compilerFor(source, uri, analyzeOnly: true);
+ compiler.diagnosticHandler = createHandler(compiler, source);
return compiler.runCompiler(uri).then((_) {
return compiler;
});
}
+test(String source, {var errors, var warnings}) {
+ if (errors == null) errors = [];
+ if (errors is! List) errors = [errors];
+ if (warnings == null) warnings = [];
+ if (warnings is! List) warnings = [warnings];
+ asyncTest(() => compile(source).then((compiler) {
+ Expect.equals(!errors.isEmpty, compiler.compilationFailed);
+ Expect.equals(errors.length, compiler.errors.length,
+ 'unexpected error count: ${compiler.errors.length} '
+ 'expected ${errors.length}');
+ Expect.equals(warnings.length, compiler.warnings.length,
+ 'unexpected warning count: ${compiler.warnings.length} '
+ 'expected ${warnings.length}');
+
+ for (int i = 0 ; i < errors.length ; i++) {
+ Expect.equals(errors[i], compiler.errors[i].message.kind);
+ }
+ for (int i = 0 ; i < warnings.length ; i++) {
+ Expect.equals(warnings[i], compiler.warnings[i].message.kind);
+ }
+ }));
+}
+
test1() {
asyncTest(() => compile(r"""
class A<T extends T> {}
@@ -111,9 +135,93 @@
}));
}
+test5() {
+ test(r"""
+class A<T extends num> {}
+
+void main() {
+ new A();
+ new A<num>();
+ new A<dynamic>();
+ new A<int>();
+ new A<double>();
+}
+""");
+}
+
+test6() {
+ test(r"""
+class A<T extends num> {}
+
+void main() {
+ new A<String>();
+}
+""", warnings: MessageKind.INVALID_TYPE_VARIABLE_BOUND);
+}
+
+test7() {
+ test(r"""
+class A<T extends num> {}
+class B<T> extends A<T> {} // Warning produced here.
+
+void main() {
+ new B(); // No warning produced here.
+ new B<String>(); // No warning produced here.
+}
+""", warnings: MessageKind.INVALID_TYPE_VARIABLE_BOUND);
+}
+
+test8() {
+ test(r"""
+class B<T extends B<T>> {}
+class C<T extends B<T>> extends B<T> {}
+class D<T extends C<T>> extends C<T> {}
+class E<T extends E<T>> extends D<T> {}
+class F extends E<F> {}
+
+void main() {
+ new B();
+ new B<dynamic>();
+ new B<F>();
+ new B<B<F>>();
+ new C();
+ new C<dynamic>();
+ new C<B<F>>();
+ new D();
+ new D<dynamic>();
+ new D<C<F>>();
+ new E();
+ new E<dynamic>();
+ new E<E<F>>();
+ new F();
+}
+""");
+}
+
+test9() {
+ test(r"""
+class B<T extends B<T>> {}
+class C<T extends B<T>> extends B<T> {}
+class D<T extends C<T>> extends C<T> {}
+class E<T extends E<T>> extends D<T> {}
+class F extends E<F> {}
+
+void main() {
+ new D<B<F>>(); // Warning: B<F> is not a subtype of C<T>.
+ new E<D<F>>(); // Warning: E<F> is not a subtype of E<T>.
+}
+""", warnings: [MessageKind.INVALID_TYPE_VARIABLE_BOUND,
+ MessageKind.INVALID_TYPE_VARIABLE_BOUND]);
+}
+
main() {
test1();
test2();
test3();
test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ test9();
}
diff --git a/tests/compiler/dart2js/value_range_test.dart b/tests/compiler/dart2js/value_range_test.dart
index eff5ad8..f14c1de 100644
--- a/tests/compiler/dart2js/value_range_test.dart
+++ b/tests/compiler/dart2js/value_range_test.dart
@@ -11,6 +11,7 @@
const int KEPT = 3;
const int ONE_CHECK = 4;
const int ONE_ZERO_CHECK = 5;
+const int BELOW_ZERO_CHECK = 6;
final List TESTS = [
"""
@@ -197,6 +198,65 @@
}
""",
REMOVED,
+"""
+main(value) {
+ var a = new List(4);
+ var sum = 0;
+ for (int i = 0; i < a.length; i++) {
+ sum += a[i];
+ if (sum == 0) i++;
+ }
+ return sum;
+}
+""",
+REMOVED,
+"""
+main(value) {
+ var a = new List(5);
+ var sum = 0;
+ for (int i = a.length - 1; i >= 0; i--) {
+ sum += a[i];
+ if (sum == 0) i--;
+ }
+ return sum;
+}
+""",
+REMOVED,
+"""
+main(value) {
+ var a = new List(6);
+ var sum = 0;
+ for (int i = 0; i < a.length; i++) {
+ sum += a[i];
+ if (sum == 0) i--;
+ }
+ return sum;
+}
+""",
+BELOW_ZERO_CHECK,
+"""
+main(value) {
+ var a = new List(7);
+ var sum = 0;
+ for (int i = 0; i < a.length;) {
+ sum += a[i];
+ sum == 0 ? i-- : i++;
+ }
+ return sum;
+}
+""",
+BELOW_ZERO_CHECK,
+"""
+main(value) {
+ var a = new List(7);
+ var sum = 0;
+ for (int i = -2; i < a.length; i = 0) {
+ sum += a[i];
+ }
+ return sum;
+}
+""",
+BELOW_ZERO_CHECK,
];
// TODO(ahe): It would probably be better if this test used the real
@@ -290,6 +350,11 @@
case ABOVE_ZERO:
Expect.isTrue(!generated.contains('< 0'));
Expect.isTrue(generated.contains('ioore'));
+ break;
+
+ case BELOW_ZERO_CHECK:
+ Expect.isTrue(generated.contains('< 0'));
+ Expect.isTrue(!generated.contains('||'));
Expect.isTrue(generated.contains('ioore'));
break;
diff --git a/tests/compiler/dart2js_native/abstract_class_test.dart b/tests/compiler/dart2js_native/abstract_class_test.dart
new file mode 100644
index 0000000..6a5989c
--- /dev/null
+++ b/tests/compiler/dart2js_native/abstract_class_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2013, 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:expect/expect.dart";
+
+// Native classes can have subclasses that are not declared to the program. The
+// subclasses are indistinguishable from the base class. This means that
+// abstract native classes can appear to have instances.
+
+abstract class A native "A" {
+}
+
+abstract class B native "B" {
+ foo() native;
+}
+
+class C {}
+
+makeA() native;
+makeB() native;
+
+void setup() native """
+// This code is all inside 'setup' and so not accesible from the global scope.
+function A(){}
+function B(){}
+B.prototype.foo = function() { return 'B.foo'; };
+makeA = function(){return new A};
+makeB = function(){return new B};
+""";
+
+var inscrutable;
+main() {
+ setup();
+ inscrutable = (x) => x;
+ inscrutable = inscrutable(inscrutable);
+
+ var a = makeA();
+ var b = makeB();
+ var c = inscrutable(new C());
+
+ Expect.isTrue(a is A);
+ Expect.isFalse(b is A);
+ Expect.isFalse(c is A);
+
+ Expect.isFalse(a is B);
+ Expect.isTrue(b is B);
+ Expect.isFalse(c is B);
+
+ Expect.isFalse(a is C);
+ Expect.isFalse(b is C);
+ Expect.isTrue(c is C);
+
+ Expect.equals('B.foo', b.foo());
+}
diff --git a/tests/compiler/dart2js_native/native_class_avoids_hidden_name_frog_test.dart b/tests/compiler/dart2js_native/native_class_avoids_hidden_name_frog_test.dart
index 3090172..5e4e0c3 100644
--- a/tests/compiler/dart2js_native/native_class_avoids_hidden_name_frog_test.dart
+++ b/tests/compiler/dart2js_native/native_class_avoids_hidden_name_frog_test.dart
@@ -6,14 +6,14 @@
// Test that hidden native class names are not used by generated code.
-class A native "B" {
- get name => 'A';
- static A create() => makeA();
+class AA native "BB" {
+ get name => 'AA';
+ static AA create() => makeA();
}
-class B native "C" {
- get name => 'B';
- static B create() => makeB();
+class BB native "C" {
+ get name => 'BB';
+ static BB create() => makeB();
}
class C { // Ordinary class with name clashing with native class.
@@ -25,18 +25,18 @@
makeB() native;
void setup1() native """
-// Poison hidden native names 'B' and 'C' to prove the compiler didn't place
+// Poison hidden native names 'BB' and 'C' to prove the compiler didn't place
// anthing on the hidden native class.
-B = null;
+BB = null;
C = null;
""";
void setup2() native """
// This code is all inside 'setup' and so not accesible from the global scope.
-function B(){}
+function BB(){}
function C(){}
-makeA = function(){return new B}; // A is "*B"
-makeB = function(){return new C}; // B is "*C"
+makeA = function(){return new BB}; // AA is "*BB"
+makeB = function(){return new C}; // BB is "*C"
""";
int inscrutable(int x) => x == 0 ? 0 : x | inscrutable(x & (x - 1));
@@ -45,12 +45,12 @@
setup1();
setup2();
- var things = [A.create(), B.create(), C.create()];
+ var things = [AA.create(), BB.create(), C.create()];
var a = things[inscrutable(0)];
var b = things[inscrutable(1)];
var c = things[inscrutable(2)];
- Expect.equals('A', a.name);
- Expect.equals('B', b.name);
+ Expect.equals('AA', a.name);
+ Expect.equals('BB', b.name);
Expect.equals('C', c.name);
}
diff --git a/tests/compiler/dart2js_native/subclassing_type_test.dart b/tests/compiler/dart2js_native/subclassing_type_test.dart
new file mode 100644
index 0000000..34748bf
--- /dev/null
+++ b/tests/compiler/dart2js_native/subclassing_type_test.dart
@@ -0,0 +1,145 @@
+// Copyright (c) 2013, 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:expect/expect.dart";
+import 'dart:_js_helper' show Creates, setNativeSubclassDispatchRecord;
+import 'dart:_interceptors' show Interceptor, findInterceptorForType;
+
+// Test that type checks and casts work for subclasses of native classes and
+// mixins on native classes.
+
+class M {}
+
+class N native "N" {}
+
+class A extends N {}
+
+class B extends A with M { // native mixin application.
+}
+
+class C extends Object with M { // non-native mixin application.
+}
+
+B makeB() native;
+
+@Creates('=Object')
+getBPrototype() native;
+
+void setup() native r"""
+function B() {}
+makeB = function(){return new B;};
+
+getBPrototype = function(){return B.prototype;};
+""";
+
+A gA;
+B gB;
+C gC;
+M gM;
+
+isA(x) => x is A;
+asA(x) => x as A;
+setA(x) => gA = x;
+
+isB(x) => x is B;
+asB(x) => x as B;
+setB(x) => gB = x;
+
+isC(x) => x is C;
+asC(x) => x as C;
+setC(x) => gC = x;
+
+isM(x) => x is M;
+asM(x) => x as M;
+setM(x) => gM = x;
+
+checkTrue(f) => (x) { Expect.isTrue(f(x)); };
+checkFalse(f) => (x) { Expect.isFalse(f(x)); };
+checkId(f) => (x) { Expect.identical(x, f(x)); };
+checkThrows(f) => (x) { Expect.throws(() => f(x)); };
+
+bool get checkedMode {
+ try {
+ setA(1);
+ gA = null;
+ return false;
+ } catch (e) {
+ return true;
+ }
+}
+
+main() {
+ setup();
+
+ setNativeSubclassDispatchRecord(getBPrototype(), findInterceptorForType(B));
+
+ B b = makeB();
+ C c = new C();
+
+
+ checkFalse(isA)(1);
+ checkFalse(isB)(1);
+ checkFalse(isC)(1);
+ checkFalse(isM)(1);
+
+ checkTrue(isA)(b);
+ checkTrue(isB)(b);
+ checkTrue(isM)(b);
+ checkFalse(isC)(b);
+
+ checkTrue(isC)(c);
+ checkTrue(isM)(c);
+ checkFalse(isA)(c);
+ checkFalse(isB)(c);
+
+
+ checkThrows(asA)(1);
+ checkThrows(asB)(1);
+ checkThrows(asC)(1);
+ checkThrows(asM)(1);
+
+ checkId(asA)(b);
+ checkId(asB)(b);
+ checkId(asM)(b);
+ checkThrows(asC)(b);
+
+ checkId(asC)(c);
+ checkId(asM)(c);
+ checkThrows(asA)(c);
+ checkThrows(asB)(c);
+
+
+ if (checkedMode) {
+ checkThrows(setA)(1);
+ checkThrows(setB)(1);
+ checkThrows(setC)(1);
+ checkThrows(setM)(1);
+
+ checkId(setA)(b);
+ checkId(setB)(b);
+ checkId(setM)(b);
+ checkThrows(setC)(b);
+
+ checkId(setC)(c);
+ checkId(setM)(c);
+ checkThrows(setA)(c);
+ checkThrows(setB)(c);
+
+ // One of the above assignments had a value of the correct type.
+ Expect.isNotNull(gA);
+ Expect.isNotNull(gB);
+ Expect.isNotNull(gC);
+ Expect.isNotNull(gM);
+
+ checkId(setA)(null);
+ checkId(setB)(null);
+ checkId(setC)(null);
+ checkId(setM)(null);
+
+ Expect.isNull(gA);
+ Expect.isNull(gB);
+ Expect.isNull(gC);
+ Expect.isNull(gM);
+ }
+}
diff --git a/tests/corelib/bit_twiddling_bigint_test.dart b/tests/corelib/bit_twiddling_bigint_test.dart
new file mode 100644
index 0000000..042f7b8
--- /dev/null
+++ b/tests/corelib/bit_twiddling_bigint_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2013, 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.
+// Testing Bigints.
+
+library bit_twiddling_test;
+import "package:expect/expect.dart";
+
+// See bit_twiddling_test.dart first. This file contains only the tests that
+// need Bigint or would fail in dart2js compatibility mode.
+
+testBitLength() {
+ check(int i, width) {
+ Expect.equals(width, i.bitLength, '$i.bitLength == $width');
+ // (~i) written as (-i-1) to avoid issues with limited range of dart2js ops.
+ Expect.equals(width, (-i-1).bitLength, '(~$i).bitLength == $width');
+ }
+
+ check(0xffffffffffffff, 56);
+ check(0xffffffffffffffff, 64);
+ check(0xffffffffffffffffff, 72);
+ check(0x1000000000000000000, 73);
+ check(0x1000000000000000001, 73);
+
+
+ check(0xfffffffffffffffffffffffffffffffffffffe, 152);
+ check(0xffffffffffffffffffffffffffffffffffffff, 152);
+ check(0x100000000000000000000000000000000000000, 153);
+ check(0x100000000000000000000000000000000000001, 153);
+}
+
+testToUnsigned() {
+ checkU(src, width, expected) {
+ Expect.equals(expected, src.toUnsigned(width));
+ }
+
+ checkU(0x100000100000000000001, 2, 1);
+ checkU(0x100000200000000000001, 60, 0x200000000000001);
+ checkU(0x100000200000000000001, 59, 0x200000000000001);
+ checkU(0x100000200000000000001, 58, 0x200000000000001);
+ checkU(0x100000200000000000001, 57, 1);
+}
+
+testToSigned() {
+ checkS(src, width, expected) {
+ Expect.equals(expected, src.toSigned(width),
+ '$src.toSigned($width) == $expected');
+ }
+
+ checkS(0x100000100000000000001, 2, 1);
+ checkS(0x100000200000000000001, 60, 0x200000000000001);
+ checkS(0x100000200000000000001, 59, 0x200000000000001);
+ checkS(0x100000200000000000001, 58, -0x200000000000000 + 1);
+ checkS(0x100000200000000000001, 57, 1);
+}
+
+main() {
+ testBitLength();
+ testToUnsigned();
+ testToSigned();
+}
diff --git a/tests/corelib/bit_twiddling_test.dart b/tests/corelib/bit_twiddling_test.dart
new file mode 100644
index 0000000..92dafac
--- /dev/null
+++ b/tests/corelib/bit_twiddling_test.dart
@@ -0,0 +1,175 @@
+// Copyright (c) 2013, 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.
+// Testing Bigints.
+
+library bit_twiddling_test;
+import "package:expect/expect.dart";
+
+
+bool haveBigints() {
+ return 100000000000000000000 + 1 != 100000000000000000000;
+}
+
+testBitLength() {
+ check(int i, width) {
+ Expect.equals(width, i.bitLength, '$i.bitLength == $width');
+ // (~i) written as (-i-1) to avoid issues with limited range of dart2js ops.
+ Expect.equals(width, (-i-1).bitLength, '(~$i).bitLength == $width');
+ }
+
+ check(0, 0);
+ check(1, 1);
+ check(2, 2);
+ check(3, 2);
+ check(4, 3);
+ check(5, 3);
+ check(6, 3);
+ check(7, 3);
+ check(8, 4);
+ check(127, 7);
+ check(128, 8);
+ check(129, 8);
+ check(2147483646, 31);
+ check(2147483647, 31);
+ check(2147483648, 32);
+ check(2147483649, 32);
+ check(4294967295, 32);
+ check(4294967296, 33);
+ check(0xffffffffff, 40);
+ check(0xfffffffffff, 44);
+ check(0xffffffffffff, 48);
+ check(0x1000000000000, 49);
+ check(0x1000000000001, 49);
+ check(0x1ffffffffffff, 49);
+ check(0x2000000000000, 50);
+ check(0x2000000000001, 50);
+
+ if (haveBigints()) {
+ check(0xffffffffffffff, 56);
+ check(0xffffffffffffffff, 64);
+ check(0xffffffffffffffffff, 72);
+ check(0x1000000000000000000, 73);
+ check(0x1000000000000000001, 73);
+
+
+ check(0xfffffffffffffffffffffffffffffffffffffe, 152);
+ check(0xffffffffffffffffffffffffffffffffffffff, 152);
+ check(0x100000000000000000000000000000000000000, 153);
+ check(0x100000000000000000000000000000000000001, 153);
+
+ }
+}
+
+testToUnsigned() {
+ checkU(src, width, expected) {
+ Expect.equals(expected, src.toUnsigned(width));
+ }
+
+ checkU(1, 8, 1);
+ checkU(0xff, 8, 0xff);
+ checkU(0xffff, 8, 0xff);
+ checkU(-1, 8, 0xff);
+ checkU(0xffffffff, 32, 0xffffffff);
+
+ checkU(0x7fffffff, 30, 0x3fffffff);
+ checkU(0x7fffffff, 31, 0x7fffffff);
+ checkU(0x7fffffff, 32, 0x7fffffff);
+ checkU(0x80000000, 30, 0);
+ checkU(0x80000000, 31, 0);
+ checkU(0x80000000, 32, 0x80000000);
+ checkU(0xffffffff, 30, 0x3fffffff);
+ checkU(0xffffffff, 31, 0x7fffffff);
+ checkU(0xffffffff, 32, 0xffffffff);
+ checkU(0x100000000, 30, 0);
+ checkU(0x100000000, 31, 0);
+ checkU(0x100000000, 32, 0);
+ checkU(0x1ffffffff, 30, 0x3fffffff);
+ checkU(0x1ffffffff, 31, 0x7fffffff);
+ checkU(0x1ffffffff, 32, 0xffffffff);
+
+ checkU(-1, 0, 0);
+ checkU( 0, 0, 0);
+ checkU( 1, 0, 0);
+ checkU( 2, 0, 0);
+ checkU( 3, 0, 0);
+
+ checkU(-1, 1, 1);
+ checkU( 0, 1, 0);
+ checkU( 1, 1, 1);
+ checkU( 2, 1, 0);
+ checkU( 3, 1, 1);
+ checkU( 4, 1, 0);
+
+ checkU(-1, 2, 3);
+ checkU( 0, 2, 0);
+ checkU( 1, 2, 1);
+ checkU( 2, 2, 2);
+ checkU( 3, 2, 3);
+ checkU( 4, 2, 0);
+
+ checkU(-1, 3, 7);
+ checkU( 0, 3, 0);
+ checkU( 1, 3, 1);
+ checkU( 2, 3, 2);
+ checkU( 3, 3, 3);
+ checkU( 4, 3, 4);
+}
+
+testToSigned() {
+ checkS(src, width, expected) {
+ Expect.equals(expected, src.toSigned(width),
+ '$src.toSigned($width) == $expected');
+ }
+
+ checkS(1, 8, 1);
+ checkS(0xff, 8, -1);
+ checkS(0xffff, 8, -1);
+ checkS(-1, 8, -1);
+ checkS(128, 8, -128);
+ checkS(0xffffffff, 32, -1);
+
+ checkS(0x7fffffff, 30, -1);
+ checkS(0x7fffffff, 31, -1);
+ checkS(0x7fffffff, 32, 0x7fffffff);
+ checkS(0x80000000, 30, 0);
+ checkS(0x80000000, 31, 0);
+ checkS(0x80000000, 32, -2147483648);
+ checkS(0xffffffff, 30, -1);
+ checkS(0xffffffff, 31, -1);
+ checkS(0xffffffff, 32, -1);
+
+ checkS(0x100000000, 30, 0);
+ checkS(0x100000000, 31, 0);
+ checkS(0x100000000, 32, 0);
+ checkS(0x1ffffffff, 30, -1);
+ checkS(0x1ffffffff, 31, -1);
+ checkS(0x1ffffffff, 32, -1);
+
+ checkS(-1, 1, -1);
+ checkS( 0, 1, 0);
+ checkS( 1, 1, -1); // The only bit is the sign bit.
+ checkS( 2, 1, 0);
+ checkS( 3, 1, -1);
+ checkS( 4, 1, 0);
+
+ checkS(-1, 2, -1);
+ checkS( 0, 2, 0);
+ checkS( 1, 2, 1);
+ checkS( 2, 2, -2);
+ checkS( 3, 2, -1);
+ checkS( 4, 2, 0);
+
+ checkS(-1, 3, -1);
+ checkS( 0, 3, 0);
+ checkS( 1, 3, 1);
+ checkS( 2, 3, 2);
+ checkS( 3, 3, 3);
+ checkS( 4, 3, -4);
+}
+
+main() {
+ testBitLength();
+ testToUnsigned();
+ testToSigned();
+}
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 186f01e..e46b59f 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -41,6 +41,7 @@
error_stack_trace1_test: Fail # Issue 12399
big_integer_vm_test: Fail, OK # VM specific test.
+bit_twiddling_bigint_test: Fail # Requires bigint support.
compare_to2_test: Fail, OK # Requires bigint support.
string_base_vm_test: Fail, OK # VM specific test.
@@ -102,4 +103,4 @@
collection_length_test: Pass, Timeout
[ $arch == simmips && $mode == debug ]
-collection_to_string_test: Pass, Crash # Issue: 11207
\ No newline at end of file
+collection_to_string_test: Pass, Crash # Issue: 11207
diff --git a/tests/corelib/map_test.dart b/tests/corelib/map_test.dart
index c9ba29f..a700027 100644
--- a/tests/corelib/map_test.dart
+++ b/tests/corelib/map_test.dart
@@ -33,6 +33,8 @@
testNumericKeys(new HashMap<num, String>(equals: identical));
testNumericKeys(new LinkedHashMap());
testNumericKeys(new LinkedHashMap<num, String>());
+ testNumericKeys(new LinkedHashMap(equals: identical));
+ testNumericKeys(new LinkedHashMap<num, String>(equals: identical));
testNaNKeys(new Map());
testNaNKeys(new Map<num, String>());
@@ -44,10 +46,40 @@
// NaN is not equal to NaN.
testIdentityMap(new HashMap(equals: identical));
+ testIdentityMap(new LinkedHashMap(equals: identical));
- testCustomMap(new HashMap(equals: myEquals, hashCode: myHashCode));
+ testCustomMap(new HashMap(equals: myEquals, hashCode: myHashCode,
+ isValidKey: (v) => v is Customer));
+ testCustomMap(new LinkedHashMap(equals: myEquals, hashCode: myHashCode,
+ isValidKey: (v) => v is Customer));
+ testCustomMap(new HashMap<Customer,dynamic>(equals: myEquals,
+ hashCode: myHashCode));
+
+ testCustomMap(new LinkedHashMap<Customer,dynamic>(equals: myEquals,
+ hashCode: myHashCode));
testIterationOrder(new LinkedHashMap());
+ testIterationOrder(new LinkedHashMap(equals: identical));
+
+ testOtherKeys(new SplayTreeMap<int, int>());
+ testOtherKeys(new SplayTreeMap<int, int>((int a, int b) => a - b,
+ (v) => v is int));
+ testOtherKeys(new SplayTreeMap((int a, int b) => a - b,
+ (v) => v is int));
+ testOtherKeys(new HashMap<int, int>());
+ testOtherKeys(new HashMap<int, int>(equals: identical));
+ testOtherKeys(new HashMap<int, int>(hashCode: (v) => v.hashCode,
+ isValidKey: (v) => v is int));
+ testOtherKeys(new HashMap(equals: (int x, int y) => x == y,
+ hashCode: (int v) => v.hashCode,
+ isValidKey: (v) => v is int));
+ testOtherKeys(new LinkedHashMap<int, int>());
+ testOtherKeys(new LinkedHashMap<int, int>(equals: identical));
+ testOtherKeys(new LinkedHashMap<int, int>(hashCode: (v) => v.hashCode,
+ isValidKey: (v) => v is int));
+ testOtherKeys(new LinkedHashMap(equals: (int x, int y) => x == y,
+ hashCode: (int v) => v.hashCode,
+ isValidKey: (v) => v is int));
}
@@ -642,9 +674,26 @@
int myHashCode(Customer c) => c.secondId;
bool myEquals(Customer a, Customer b) => a.secondId == b.secondId;
-testIterationOrder(Map map) {
+void testIterationOrder(Map map) {
var order = [0, 6, 4, 2, 7, 9, 7, 1, 2, 5, 3];
for (int i = 0; i < order.length; i++) map[order[i]] = i;
Expect.listEquals(map.keys.toList(), [0, 6, 4, 2, 7, 9, 1, 5, 3]);
Expect.listEquals(map.values.toList(), [0, 1, 2, 8, 6, 5, 7, 9, 10]);
}
+
+void testOtherKeys(Map<int, int> map) {
+ // Test that non-int keys are allowed in containsKey/remove/lookup.
+ // Custom hash sets and tree sets must be constructed so they don't
+ // use the equality/comparator on incompatible objects.
+
+ // This should not throw in either checked or unchecked mode.
+ map[0] = 0;
+ map[1] = 1;
+ map[2] = 2;
+ Expect.isFalse(map.containsKey("not an int"));
+ Expect.isFalse(map.containsKey(1.5));
+ Expect.isNull(map.remove("not an int"));
+ Expect.isNull(map.remove(1.5));
+ Expect.isNull(map["not an int"]);
+ Expect.isNull(map[1.5]);
+}
diff --git a/tests/corelib/uri_http_test.dart b/tests/corelib/uri_http_test.dart
index 354e052..95614f7 100644
--- a/tests/corelib/uri_http_test.dart
+++ b/tests/corelib/uri_http_test.dart
@@ -31,6 +31,8 @@
check(new Uri.http("host", "/a/b", { "c": "d" }), "http://host/a/b?c=d");
check(new Uri.http("host",
"/a/b", { "c=": "&d" }), "http://host/a/b?c%3D=%26d");
+ check(new Uri.http("[::]", "a"), "http://[::]/a");
+ check(new Uri.http("[::127.0.0.1]", "a"), "http://[::127.0.0.1]/a");
}
testHttpsUri() {
@@ -60,6 +62,8 @@
check(new Uri.https("host", "/a/b", { "c": "d" }), "https://host/a/b?c=d");
check(new Uri.https("host",
"/a/b", { "c=": "&d" }), "https://host/a/b?c%3D=%26d");
+ check(new Uri.https("[::]", "a"), "https://[::]/a");
+ check(new Uri.https("[::127.0.0.1]", "a"), "https://[::127.0.0.1]/a");
}
main() {
diff --git a/tests/corelib/uri_ipv4_test.dart b/tests/corelib/uri_ipv4_test.dart
new file mode 100644
index 0000000..e04b675
--- /dev/null
+++ b/tests/corelib/uri_ipv4_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2012, 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:expect/expect.dart';
+
+void testParseIPv4Address() {
+ void pass(String host, List<int> out) {
+ Expect.listEquals(Uri.parseIPv4Address(host), out);
+ }
+ void fail(String host) {
+ Expect.throws(() => Uri.parseIPv4Address(host),
+ (e) => e is FormatException);
+ }
+
+ pass('127.0.0.1', [127, 0, 0, 1]);
+ pass('128.0.0.1', [128, 0, 0, 1]);
+ pass('255.255.255.255', [255, 255, 255, 255]);
+ pass('0.0.0.0', [0, 0, 0, 0]);
+ fail('127.0.0.-1');
+ fail('255.255.255.256');
+ fail('0.0.0.0.');
+ fail('0.0.0.0.0');
+ fail('a.0.0.0');
+ fail('0.0..0');
+}
+
+void main() {
+ testParseIPv4Address();
+}
diff --git a/tests/corelib/uri_ipv6_test.dart b/tests/corelib/uri_ipv6_test.dart
index 1ec9d57..0f3ba03 100644
--- a/tests/corelib/uri_ipv6_test.dart
+++ b/tests/corelib/uri_ipv6_test.dart
@@ -28,7 +28,7 @@
uri = Uri.parse(path);
Expect.equals('http', uri.scheme);
Expect.equals('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(80, uri.port);
Expect.equals('/index.html', uri.path);
Expect.equals('http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html',
uri.toString());
@@ -37,7 +37,7 @@
uri = Uri.parse(path);
Expect.equals('https', uri.scheme);
Expect.equals('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(443, uri.port);
Expect.equals('/index.html', uri.path);
Expect.equals('https://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html',
uri.toString());
@@ -46,7 +46,7 @@
uri = Uri.parse(path);
Expect.equals('http', uri.scheme);
Expect.equals('1080:0:0:0:8:800:200C:417A', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(80, uri.port);
Expect.equals('/index.html', uri.path);
Expect.equals(path, uri.toString());
@@ -54,7 +54,7 @@
uri = Uri.parse(path);
Expect.equals('http', uri.scheme);
Expect.equals('3ffe:2a00:100:7031::1', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(80, uri.port);
Expect.equals('', uri.path);
Expect.equals(path, uri.toString());
@@ -62,7 +62,7 @@
uri = Uri.parse(path);
Expect.equals('http', uri.scheme);
Expect.equals('1080::8:800:200C:417A', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(80, uri.port);
Expect.equals('/foo', uri.path);
Expect.equals(path, uri.toString());
@@ -70,7 +70,7 @@
uri = Uri.parse(path);
Expect.equals('http', uri.scheme);
Expect.equals('::192.9.5.5', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(80, uri.port);
Expect.equals('/ipng', uri.path);
Expect.equals(path, uri.toString());
@@ -86,7 +86,7 @@
uri = Uri.parse(path);
Expect.equals('http', uri.scheme);
Expect.equals('::FFFF:129.144.52.38', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(80, uri.port);
Expect.equals('/index.html', uri.path);
Expect.equals('http://[::FFFF:129.144.52.38]/index.html', uri.toString());
@@ -94,7 +94,7 @@
uri = Uri.parse(path);
Expect.equals('https', uri.scheme);
Expect.equals('::FFFF:129.144.52.38', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(443, uri.port);
Expect.equals('/index.html', uri.path);
Expect.equals('https://[::FFFF:129.144.52.38]/index.html', uri.toString());
@@ -102,12 +102,50 @@
uri = Uri.parse(path);
Expect.equals('http', uri.scheme);
Expect.equals('2010:836B:4179::836B:4179', uri.host);
- Expect.equals(0, uri.port);
+ Expect.equals(80, uri.port);
Expect.equals('', uri.path);
Expect.equals(path, uri.toString());
}
+
+void testParseIPv6Address() {
+ void pass(String host, List<int> expected) {
+ Expect.listEquals(expected, Uri.parseIPv6Address(host));
+ }
+ void fail(String host) {
+ Expect.throws(() => Uri.parseIPv6Address(host),
+ (e) => e is FormatException);
+ }
+
+ pass('::127.0.0.1', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1]);
+ pass('0::127.0.0.1', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1]);
+ pass('::', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+ pass('0::', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+ fail(':0::127.0.0.1');
+ fail('0:::');
+ fail(':::');
+ fail('::0:');
+ fail('::0::');
+ fail('::0::0');
+ fail('00000::0');
+ fail('-1::0');
+ fail('-AAA::0');
+ fail('0::127.0.0.1:0');
+ fail('0::127.0.0');
+ pass('0::1111', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17]);
+ pass('2010:836B:4179::836B:4179',
+ [32, 16, 131, 107, 65, 121, 0, 0, 0, 0, 0, 0, 131, 107, 65, 121] );
+ fail('2010:836B:4179:0000:127.0.0.1');
+ fail('2010:836B:4179:0000:0000:127.0.0.1');
+ fail('2010:836B:4179:0000:0000:0000::127.0.0.1');
+ fail('2010:836B:4179:0000:0000:0000:0000:127.0.0.1');
+ pass('2010:836B:4179:0000:0000:0000:127.0.0.1',
+ [32, 16, 131, 107, 65, 121, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1] );
+}
+
+
void main() {
testValidIpv6Uri();
+ testParseIPv6Address();
}
diff --git a/tests/corelib/uri_path_test.dart b/tests/corelib/uri_path_test.dart
index d1804f9..6274e8b 100644
--- a/tests/corelib/uri_path_test.dart
+++ b/tests/corelib/uri_path_test.dart
@@ -47,12 +47,9 @@
var uri1 = new Uri(pathSegments: segments);
var uri2 = new Uri(path: path);
- var uri3 = Uri.parse(path);
check(uri1);
check(uri2);
- check(uri3);
- Expect.equals(uri1, uri3);
- Expect.equals(uri2, uri3);
+ Expect.equals(uri1, uri2);
}
test("", []);
diff --git a/tests/corelib/uri_scheme_test.dart b/tests/corelib/uri_scheme_test.dart
index 2a7fd76..2565e70 100644
--- a/tests/corelib/uri_scheme_test.dart
+++ b/tests/corelib/uri_scheme_test.dart
@@ -7,6 +7,8 @@
void testInvalidArguments() {
Expect.throws(() => new Uri(scheme: "_"), (e) => e is ArgumentError);
Expect.throws(() => new Uri(scheme: "http_s"), (e) => e is ArgumentError);
+ Expect.throws(() => new Uri(scheme: "127.0.0.1:80"),
+ (e) => e is ArgumentError);
}
void testScheme() {
@@ -21,6 +23,7 @@
test("http+ssl", "http+ssl:", "HTTP+ssl");
test("urn", "urn:", "urn");
test("urn", "urn:", "UrN");
+ test("a123.432", "a123.432:", "a123.432");
}
main() {
diff --git a/tests/html/custom/constructor_calls_created_synchronously_test.dart b/tests/html/custom/constructor_calls_created_synchronously_test.dart
index 77b5446..f383a7a 100644
--- a/tests/html/custom/constructor_calls_created_synchronously_test.dart
+++ b/tests/html/custom/constructor_calls_created_synchronously_test.dart
@@ -18,12 +18,26 @@
}
}
+loadPolyfills() {
+ if (!document.supportsRegister) {
+ // Cache blocker is a workaround for:
+ // https://code.google.com/p/dart/issues/detail?id=11834
+ var cacheBlocker = new DateTime.now().millisecondsSinceEpoch;
+ return HttpRequest.getString('/root_dart/pkg/custom_element/lib/'
+ 'custom-elements.debug.js?cacheBlock=$cacheBlocker').then((code) {
+ document.head.children.add(new ScriptElement()..text = code);
+ });
+ }
+}
+
main() {
useHtmlConfiguration();
// Adapted from Blink's
// fast/dom/custom/constructor-calls-created-synchronously test.
+ setUp(loadPolyfills);
+
test('createdCallback', () {
document.register(A.tag, A);
var x = new A();
diff --git a/tests/html/custom/created_callback_test.dart b/tests/html/custom/created_callback_test.dart
index 1265b51..00848c4 100644
--- a/tests/html/custom/created_callback_test.dart
+++ b/tests/html/custom/created_callback_test.dart
@@ -54,12 +54,26 @@
}
}
+loadPolyfills() {
+ if (!document.supportsRegister) {
+ // Cache blocker is a workaround for:
+ // https://code.google.com/p/dart/issues/detail?id=11834
+ var cacheBlocker = new DateTime.now().millisecondsSinceEpoch;
+ return HttpRequest.getString('/root_dart/pkg/custom_element/lib/'
+ 'custom-elements.debug.js?cacheBlock=$cacheBlocker').then((code) {
+ document.head.children.add(new ScriptElement()..text = code);
+ });
+ }
+}
+
main() {
useHtmlConfiguration();
// Adapted from Blink's
// fast/dom/custom/created-callback test.
+ setUp(loadPolyfills);
+
test('transfer created callback', () {
document.register(A.tag, A);
var x = new A();
@@ -79,6 +93,8 @@
<x-b id="w"></x-b>
""", treeSanitizer: new NullTreeSanitizer());
+ Platform.upgradeCustomElements(div);
+
expect(C.createdInvocations, 2);
expect(div.query('#w') is B, isTrue);
});
diff --git a/tests/html/custom/document_register_basic_test.dart b/tests/html/custom/document_register_basic_test.dart
index eb91447..6934a47 100644
--- a/tests/html/custom/document_register_basic_test.dart
+++ b/tests/html/custom/document_register_basic_test.dart
@@ -37,11 +37,25 @@
factory BadE() => new Element.tag(tag);
}
+loadPolyfills() {
+ if (!document.supportsRegister) {
+ // Cache blocker is a workaround for:
+ // https://code.google.com/p/dart/issues/detail?id=11834
+ var cacheBlocker = new DateTime.now().millisecondsSinceEpoch;
+ return HttpRequest.getString('/root_dart/pkg/custom_element/lib/'
+ 'custom-elements.debug.js?cacheBlock=$cacheBlocker').then((code) {
+ document.head.children.add(new ScriptElement()..text = code);
+ });
+ }
+}
+
main() {
useHtmlConfiguration();
// Adapted from Blink's fast/dom/custom/document-register-basic test.
+ setUp(loadPolyfills);
+
test('Testing document.register() basic behaviors', () {
document.register(Foo.tag, Foo);
@@ -86,6 +100,7 @@
document.body.append(container);
container.setInnerHtml("<x-foo></x-foo>",
treeSanitizer: new NullTreeSanitizer());
+ Platform.upgradeCustomElements(container);
var parsedFoo = container.firstChild;
expect(parsedFoo is Foo, isTrue);
@@ -121,6 +136,7 @@
container.setInnerHtml("<X-BAR></X-BAR><X-Bar></X-Bar>",
treeSanitizer: new NullTreeSanitizer());
+ Platform.upgradeCustomElements(container);
expect(container.firstChild is Bar, isTrue);
expect(container.firstChild.tagName, "X-BAR");
expect(container.lastChild is Bar, isTrue);
diff --git a/tests/html/custom/document_register_type_extensions_test.dart b/tests/html/custom/document_register_type_extensions_test.dart
index ec4bd17..cae07a1 100644
--- a/tests/html/custom/document_register_type_extensions_test.dart
+++ b/tests/html/custom/document_register_type_extensions_test.dart
@@ -4,37 +4,56 @@
library document_register_type_extensions_test;
import 'package:unittest/unittest.dart';
-import 'package:unittest/html_config.dart';
+import 'package:unittest/html_individual_config.dart';
import 'dart:html';
import '../utils.dart';
class Foo extends HtmlElement {
- static final tag = 'x-foo';
+ static const tag = 'x-foo';
+ static final List outerHtmlStrings = [
+ '<x-foo></x-foo>',
+ '<?XML:NAMESPACE PREFIX = PUBLIC NS = "URN:COMPONENT" /><x-foo></x-foo>'];
factory Foo() => new Element.tag(tag);
}
class Bar extends InputElement {
- static final tag = 'x-bar';
- factory Bar() => document.$dom_createElement('input', tag);
+ static const tag = 'x-bar';
+ static const outerHtmlString = '<input is="x-bar">';
+ factory Bar() => new Element.tag('input', tag);
}
class Baz extends Foo {
- static final tag = 'x-baz';
+ static const tag = 'x-baz';
+ static final List outerHtmlStrings = [
+ '<x-baz></x-baz>',
+ '<?XML:NAMESPACE PREFIX = PUBLIC NS = "URN:COMPONENT" /><x-baz></x-baz>'];
factory Baz() => new Element.tag(tag);
}
class Qux extends Bar {
- static final tag = 'x-qux';
- factory Qux() => document.$dom_createElement('input', tag);
+ static const tag = 'x-qux';
+ factory Qux() => new Element.tag('input', tag);
}
class FooBad extends DivElement {
- static final tag = 'x-foo';
- factory FooBad() => document.$dom_createElement('div', tag);
+ static const tag = 'x-foo';
+ factory FooBad() => new Element.tag('div', tag);
+}
+
+loadPolyfills() {
+ if (!document.supportsRegister) {
+ // Cache blocker is a workaround for:
+ // https://code.google.com/p/dart/issues/detail?id=11834
+ var cacheBlocker = new DateTime.now().millisecondsSinceEpoch;
+ return HttpRequest.getString('/root_dart/pkg/custom_element/lib/'
+ 'custom-elements.debug.js?cacheBlock=$cacheBlocker').then((code) {
+ document.head.children.add(new ScriptElement()..text = code);
+ });
+ }
}
main() {
- useHtmlConfiguration();
+ useHtmlIndividualConfiguration();
// Adapted from Blink's fast/dom/custom/document-register-type-extension test.
@@ -46,154 +65,203 @@
return element.form == testForm;
};
- test('construction', () {
+ var registeredTypes = false;
+ void registerTypes() {
+ if (registeredTypes) {
+ return;
+ }
+ registeredTypes = true;
document.register(Foo.tag, Foo);
document.register(Bar.tag, Bar);
document.register(Baz.tag, Baz);
document.register(Qux.tag, Qux);
+ }
- expect(() => document.register(FooBad.tag, Foo), throws);
+ setUp(loadPolyfills);
- // Constructors
+ group('construction', () {
+ setUp(registerTypes);
- var fooNewed = new Foo();
- var fooOuterHtml = "<x-foo></x-foo>";
- expect(fooNewed.outerHtml, fooOuterHtml);
- expect(fooNewed is Foo, isTrue);
- expect(fooNewed is HtmlElement, isTrue);
- expect(fooNewed is UnknownElement, isFalse);
+ test('cannot register twice', () {
+ expect(() => document.register(FooBad.tag, Foo), throws);
+ });
- var barNewed = new Bar();
- var barOuterHtml = '<input is="x-bar">';
- expect(barNewed.outerHtml, barOuterHtml);
- expect(barNewed is Bar, isTrue);
- expect(barNewed is InputElement, isTrue);
- expect(isFormControl(barNewed), isTrue);
+ group('constructors', () {
- var bazNewed = new Baz();
- var bazOuterHtml = "<x-baz></x-baz>";
- expect(bazNewed.outerHtml, bazOuterHtml);
- expect(bazNewed is Baz, isTrue);
- expect(bazNewed is HtmlElement, isTrue);
- expect(bazNewed is UnknownElement, isFalse);
+ test('custom tag', () {
+ var fooNewed = new Foo();
+ expect(fooNewed.outerHtml, anyOf(Foo.outerHtmlStrings));
+ expect(fooNewed is Foo, isTrue);
+ expect(fooNewed is HtmlElement, isTrue);
+ expect(fooNewed is UnknownElement, isFalse);
+ });
- var quxNewed = new Qux();
- var quxOuterHtml = '<input is="x-qux">';
- expect(quxNewed.outerHtml, quxOuterHtml);
- expect(quxNewed is Qux, isTrue);
- expect(quxNewed is InputElement, isTrue);
- expect(isFormControl(quxNewed), isTrue);
+ test('type extension', () {
+ var barNewed = new Bar();
+ expect(barNewed.outerHtml, Bar.outerHtmlString);
+ expect(barNewed is Bar, isTrue);
+ expect(barNewed is InputElement, isTrue);
+ expect(isFormControl(barNewed), isTrue);
+ });
- // new Element.tag
+ test('custom tag deriving from custom tag', () {
+ var bazNewed = new Baz();
+ expect(bazNewed.outerHtml, anyOf(Baz.outerHtmlStrings));
+ expect(bazNewed is Baz, isTrue);
+ expect(bazNewed is HtmlElement, isTrue);
+ expect(bazNewed is UnknownElement, isFalse);
+ });
- var fooCreated = new Element.tag('x-foo');
- expect(fooCreated.outerHtml, fooOuterHtml);
- expect(fooCreated is Foo, isTrue);
+ test('type extension deriving from custom tag', () {
+ var quxNewed = new Qux();
+ var quxOuterHtml = '<input is="x-qux">';
+ expect(quxNewed.outerHtml, quxOuterHtml);
+ expect(quxNewed is Qux, isTrue);
+ expect(quxNewed is InputElement, isTrue);
+ expect(isFormControl(quxNewed), isTrue);
+ });
+ });
- var barCreated = new Element.tag('x-bar');
- expect(barCreated.outerHtml, "<x-bar></x-bar>");
- expect(barCreated is Bar, isFalse);
- expect(barCreated is UnknownElement, isFalse);
- expect(barCreated is HtmlElement, isTrue);
+ group('single-parameter createElement', () {
+ test('custom tag', () {
+ var fooCreated = new Element.tag('x-foo');
+ expect(fooCreated.outerHtml, anyOf(Foo.outerHtmlStrings));
+ expect(fooCreated is Foo, isTrue);
+ });
- var bazCreated = new Element.tag('x-baz');
- expect(bazCreated.outerHtml, bazOuterHtml);
- expect(bazCreated is Baz, isTrue);
- expect(bazCreated is UnknownElement, isFalse);
+ test('does not upgrade type extension', () {
+ var barCreated = new Element.tag('x-bar');
+ expect(barCreated is Bar, isFalse);
+ expect(barCreated.outerHtml, "<x-bar></x-bar>");
+ expect(barCreated is UnknownElement, isFalse);
+ expect(barCreated is HtmlElement, isTrue);
+ });
- var quxCreated = new Element.tag('x-qux');
- expect(quxCreated.outerHtml, "<x-qux></x-qux>");
- expect(quxCreated is Qux, isFalse);
- expect(quxCreated is UnknownElement, isFalse);
- expect(quxCreated is HtmlElement, isTrue);
+ test('custom tag deriving from custom tag', () {
+ var bazCreated = new Element.tag('x-baz');
+ expect(bazCreated.outerHtml, anyOf(Baz.outerHtmlStrings));
+ expect(bazCreated is Baz, isTrue);
+ expect(bazCreated is UnknownElement, isFalse);
+ });
- // create with type extensions
- // TODO(vsm): How should we expose this?
+ test('type extension deriving from custom tag', () {
+ var quxCreated = new Element.tag('x-qux');
+ expect(quxCreated.outerHtml, "<x-qux></x-qux>");
+ expect(quxCreated is Qux, isFalse);
+ expect(quxCreated is UnknownElement, isFalse);
+ expect(quxCreated is HtmlElement, isTrue);
+ });
+ });
- var divFooCreated = document.$dom_createElement("div", Foo.tag);
- expect(divFooCreated.outerHtml, '<div is="x-foo"></div>');
- expect(divFooCreated is Foo, isFalse);
- expect(divFooCreated is DivElement, isTrue);
+ group('createElement with type extension', () {
+ test('does not upgrade extension of custom tag', () {
+ var divFooCreated = new Element.tag("div", Foo.tag);
+ expect(divFooCreated.outerHtml, '<div is="x-foo"></div>');
+ expect(divFooCreated is Foo, isFalse);
+ expect(divFooCreated is DivElement, isTrue);
+ });
- var inputBarCreated =
- document.$dom_createElement("input", Bar.tag);
- expect(inputBarCreated.outerHtml, barOuterHtml);
- expect(inputBarCreated is Bar, isTrue);
- expect(inputBarCreated is UnknownElement, isFalse);
- expect(isFormControl(inputBarCreated), isTrue);
+ test('upgrades valid extension', () {
+ var inputBarCreated = new Element.tag("input", Bar.tag);
+ expect(inputBarCreated.outerHtml, Bar.outerHtmlString);
+ expect(inputBarCreated is Bar, isTrue);
+ expect(inputBarCreated is UnknownElement, isFalse);
+ expect(isFormControl(inputBarCreated), isTrue);
+ });
- var divBarCreated = document.$dom_createElement("div", Bar.tag);
- expect(divBarCreated.outerHtml, '<div is="x-bar"></div>');
- expect(divBarCreated is Bar, isFalse);
- expect(divBarCreated is DivElement, isTrue);
+ test('type extension of incorrect tag', () {
+ var divBarCreated = new Element.tag("div", Bar.tag);
+ expect(divBarCreated.outerHtml, '<div is="x-bar"></div>');
+ expect(divBarCreated is Bar, isFalse);
+ expect(divBarCreated is DivElement, isTrue);
+ });
- var fooBarCreated =
- document.$dom_createElement(Foo.tag, Bar.tag);
- expect(fooBarCreated.outerHtml, '<x-foo is="x-bar"></x-foo>');
- expect(fooBarCreated is Foo, isTrue);
+ test('incorrect extension of custom tag', () {
+ var fooBarCreated = new Element.tag(Foo.tag, Bar.tag);
+ expect(fooBarCreated.outerHtml, anyOf(
+ '<x-foo is="x-bar"></x-foo>',
+ '<?XML:NAMESPACE PREFIX = PUBLIC NS = "URN:COMPONENT" />'
+ '<x-foo is="x-bar"></x-foo>'));
+ expect(fooBarCreated is Foo, isTrue);
+ });
- var barFooCreated = document.$dom_createElement(Bar.tag,
- Foo.tag);
- expect(barFooCreated.outerHtml, '<x-bar is="x-foo"></x-bar>');
- expect(barFooCreated is UnknownElement, isFalse);
- expect(barFooCreated is HtmlElement, isTrue);
+ test('incorrect extension of type extension', () {
+ var barFooCreated = new Element.tag(Bar.tag, Foo.tag);
+ expect(barFooCreated.outerHtml, '<x-bar is="x-foo"></x-bar>');
+ expect(barFooCreated is UnknownElement, isFalse);
+ expect(barFooCreated is HtmlElement, isTrue);
+ });
- var fooCreatedNull = document.$dom_createElement(Foo.tag, null);
- expect(fooCreatedNull.outerHtml, fooOuterHtml);
- expect(fooCreatedNull is Foo, isTrue);
+ test('null type extension', () {
+ var fooCreatedNull = new Element.tag(Foo.tag, null);
+ expect(fooCreatedNull.outerHtml, anyOf(Foo.outerHtmlStrings));
+ expect(fooCreatedNull is Foo, isTrue);
+ });
- var fooCreatedEmpty = document.$dom_createElement(Foo.tag, "");
- expect(fooCreatedEmpty.outerHtml, fooOuterHtml);
- expect(fooCreatedEmpty is Foo, isTrue);
+ test('empty type extension', () {
+ var fooCreatedEmpty = new Element.tag(Foo.tag, "");
+ expect(fooCreatedEmpty.outerHtml, anyOf(Foo.outerHtmlStrings));
+ expect(fooCreatedEmpty is Foo, isTrue);
+ });
+ });
+ });
- expect(() => document.$dom_createElement('@invalid', 'x-bar'), throws);
+ group('namespaces', () {
+ setUp(registerTypes);
- // Create NS with type extensions
+ test('createElementNS', () {
+ var fooCreatedNS =
+ document.createElementNS("http://www.w3.org/1999/xhtml",
+ Foo.tag, null);
+ expect(fooCreatedNS.outerHtml, anyOf(Foo.outerHtmlStrings));
+ expect(fooCreatedNS is Foo, isTrue);
- var fooCreatedNS =
- document.$dom_createElementNS("http://www.w3.org/1999/xhtml",
- Foo.tag, null);
- expect(fooCreatedNS.outerHtml, fooOuterHtml);
- expect(fooCreatedNS is Foo, isTrue);
+ var barCreatedNS =
+ document.createElementNS("http://www.w3.org/1999/xhtml", "input",
+ Bar.tag);
+ expect(barCreatedNS.outerHtml, Bar.outerHtmlString);
+ expect(barCreatedNS is Bar, isTrue);
+ expect(isFormControl(barCreatedNS), isTrue);
- var barCreatedNS =
- document.$dom_createElementNS("http://www.w3.org/1999/xhtml", "input",
- Bar.tag);
- expect(barCreatedNS.outerHtml, barOuterHtml);
- expect(barCreatedNS is Bar, isTrue);
- expect(isFormControl(barCreatedNS), isTrue);
+ expect(() =>
+ document.createElementNS(
+ 'http://example.com/2013/no-such-namespace',
+ 'xml:lang', 'x-bar'), throws);
+ });
+ });
- expect(() =>
- document.$dom_createElementNS(
- 'http://example.com/2013/no-such-namespace',
- 'xml:lang', 'x-bar'), throws);
+ group('parsing', () {
+ setUp(registerTypes);
- // Parser
+ test('parsing', () {
+ createElementFromHtml(html) {
+ var container = new DivElement()..setInnerHtml(html,
+ treeSanitizer: new NullTreeSanitizer());
+ Platform.upgradeCustomElements(container);
+ return container.firstChild;
+ }
- createElementFromHtml(html) {
- var container = new DivElement()..setInnerHtml(html,
- treeSanitizer: new NullTreeSanitizer());
- return container.firstChild;
- }
+ var fooParsed = createElementFromHtml('<x-foo>');
+ expect(fooParsed is Foo, isTrue);
- var fooParsed = createElementFromHtml('<x-foo>');
- expect(fooParsed is Foo, isTrue);
+ var barParsed = createElementFromHtml('<input is=x-bar>');
+ expect(barParsed is Bar, isTrue);
+ expect(isFormControl(barParsed), isTrue);
- var barParsed = createElementFromHtml('<input is=x-bar>');
- expect(barParsed is Bar, isTrue);
- expect(isFormControl(barParsed), isTrue);
+ var divFooParsed = createElementFromHtml('<div is=x-foo>');
+ expect(divFooParsed is Foo, isFalse);
+ expect(divFooParsed is DivElement, isTrue);
- var divFooParsed = createElementFromHtml('<div is=x-foo>');
- expect(divFooParsed is Foo, isFalse);
- expect(divFooParsed is DivElement, isTrue);
+ var namedBarParsed = createElementFromHtml('<x-bar>');
+ expect(namedBarParsed is Bar, isFalse);
+ // Polyfill does not convert parsed unregistered custom elements to
+ // HtmlElement.
+ // expect(namedBarParsed is UnknownElement, isFalse);
+ expect(namedBarParsed is HtmlElement, isTrue);
- var namedBarParsed = createElementFromHtml('<x-bar>');
- expect(namedBarParsed is Bar, isFalse);
- expect(namedBarParsed is UnknownElement, isFalse);
- expect(namedBarParsed is HtmlElement, isTrue);
-
- var divBarParsed = createElementFromHtml('<div is=x-bar>');
- expect(divBarParsed is Bar, isFalse);
- expect(divBarParsed is DivElement, isTrue);
+ var divBarParsed = createElementFromHtml('<div is=x-bar>');
+ expect(divBarParsed is Bar, isFalse);
+ expect(divBarParsed is DivElement, isTrue);
+ });
});
}
diff --git a/tests/html/html.status b/tests/html/html.status
index 0c6ee7c..812076a 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -6,8 +6,9 @@
event_test: Skip # Issue 1996
interactive_test: Skip # Must be run manually.
-[ $compiler == dart2js && $runtime != drt ]
-custom/*: Skip
+[ $compiler == dart2js && $runtime != drt && $browser ]
+custom/document_register_type_extensions_test/namespaces: Fail # Polyfill does not support createElementNS
+custom/document_register_basic_test: Fail # Polyfill is not case-sensitive
[ $compiler == dart2js && $browser ]
# Issue 9325 failures
@@ -24,7 +25,6 @@
postmessage_structured_test/typed_arrays: Fail
xhr_test: Pass, Fail # Issue 12648
custom/attribute_changed_callback_test: Fail # 12643
-custom/created_callback_test: Fail # Issue 12642
xhr_test/json: Fail # Issue 13069
[ $compiler == none && $runtime == drt && $system == windows ]
@@ -35,9 +35,6 @@
[ $compiler == dart2js && $runtime == ie10 ]
async_test: Pass, Fail # timers test fails on ie10.
-indexeddb_2_test: Fail # Issue 12893
-indexeddb_3_test: Fail # Issue 12893
-indexeddb_4_test: Fail # Issue 12893
indexeddb_5_test: Fail # Issue 12893
[ $compiler == dart2js && ( $runtime == ie9 || $runtime == ie10 ) ]
@@ -50,11 +47,6 @@
[ $compiler == dart2js && $browser && $checked ]
postmessage_structured_test/typed_arrays: Fail # Issue 10097
postmessage_structured_test/primitives: Fail # Issue 10097
-# Issue 9325 failures
-custom/constructor_calls_created_synchronously_test: Fail
-custom/created_callback_test: Fail
-custom/document_register_basic_test: Fail
-custom/document_register_type_extensions_test: Fail
[ $runtime == chrome ]
canvasrenderingcontext2d_test/drawImage_video_element: Pass,Fail # Issue 11836
@@ -90,7 +82,6 @@
dromaeo_smoke_test: Skip #TODO(efortuna): investigating.
element_test/click: Fail # IE does not support firing this event.
history_test/history: Pass, Fail # issue 8183
-indexeddb_1_test/functional: Fail # Issue 12893
microtask_test: Fail, Pass # Appears to be flaky
native_gc_test: Fail, Pass # BUG(7774): Untriaged.
serialized_script_value_test: Fail
@@ -146,9 +137,13 @@
xsltprocessor_test/supported: Fail
+[ $runtime == ie9 || $runtime == ie10 ]
+custom/document_register_type_extensions_test/construction: Fail # Issue 13193
+
[ $runtime == ie9 ]
isolates_test: Timeout # Issue 13027
blob_constructor_test: Fail
+custom/document_register_type_extensions_test/namespaces: Fail # Issue 13193
document_test/document: Pass, Fail # BUG(9654) need per-instance patching
dom_constructors_test: Fail
dromaeo_smoke_test: Skip #TODO(efortuna): investigating.
@@ -251,6 +246,9 @@
xsltprocessor_test/supported: Fail
[ $runtime == safari ]
+worker_test: Skip # Issue 13221
+worker_api_test: Skip # Issue 13221
+notifications_test/supported: Pass, Fail # Issue 13210
element_types_test/supported_track: Pass, Fail
input_element_test/supported_month: Fail, Crash
input_element_test/supported_time: Fail, Crash
diff --git a/tests/html/safe_dom_test.dart b/tests/html/safe_dom_test.dart
index 7407d76..ee2ddaa 100644
--- a/tests/html/safe_dom_test.dart
+++ b/tests/html/safe_dom_test.dart
@@ -64,7 +64,7 @@
var contextElement;
if (contextTag != null) {
- contextElement = doc.$dom_createElement(contextTag);
+ contextElement = doc.createElement(contextTag);
} else {
contextElement = doc.body;
}
diff --git a/tests/language/duplicate_implements_test.dart b/tests/language/duplicate_implements_test.dart
index 5baf45f..e9ff103 100644
--- a/tests/language/duplicate_implements_test.dart
+++ b/tests/language/duplicate_implements_test.dart
@@ -15,5 +15,8 @@
abstract class Z implements K<int>, K<int> { } /// 04: compile-time error
main() {
- return null;
+ X x = new X(); /// 01: continued
+ X x = new X(); /// 02: continued
+ Z z = new Z(); /// 03: continued
+ Z z = new Z(); /// 04: continued
}
diff --git a/tests/language/first_class_types_literals_test.dart b/tests/language/first_class_types_literals_test.dart
index 896ba5d..8ba570a 100644
--- a/tests/language/first_class_types_literals_test.dart
+++ b/tests/language/first_class_types_literals_test.dart
@@ -19,9 +19,11 @@
Expect.equals(int, int);
Expect.notEquals(int, num);
Expect.equals(Foo, Foo);
+ Expect.equals(dynamic, dynamic);
// Test that class literals return instances of Type.
Expect.isTrue((D).runtimeType is Type);
+ Expect.isTrue((dynamic).runtimeType is Type);
// Test that types from runtimeType and literals agree.
Expect.equals(int, 1.runtimeType);
@@ -40,4 +42,11 @@
Expect.throws(() => C + 1, (e) => e is NoSuchMethodError);
Expect.throws(() => C[2], (e) => e is NoSuchMethodError);
Expect.throws(() => C[2] = 'hest', (e) => e is NoSuchMethodError);
+ Expect.throws(() => dynamic = 1, (e) => e is NoSuchMethodError);
+ Expect.throws(() => dynamic++, (e) => e is NoSuchMethodError);
+ Expect.throws(() => dynamic + 1, (e) => e is NoSuchMethodError);
+ Expect.throws(() => dynamic[2], (e) => e is NoSuchMethodError);
+ Expect.throws(() => dynamic[2] = 'hest', (e) => e is NoSuchMethodError);
+
+ Expect.equals((dynamic).toString(), 'dynamic');
}
diff --git a/tests/language/function_type_alias7_test.dart b/tests/language/function_type_alias7_test.dart
index 03c5e03..41712f2 100644
--- a/tests/language/function_type_alias7_test.dart
+++ b/tests/language/function_type_alias7_test.dart
@@ -7,6 +7,8 @@
typedef void badFuncType([int arg = 0]); /// 00: compile-time error
+typedef void badFuncType({int arg: 0}); /// 02: compile-time error
+
class A
extends funcType /// 01: compile-time error
{
@@ -14,4 +16,6 @@
main() {
new A();
+ badFuncType f; /// 00: continued
+ badFuncType f; /// 02: continued
}
diff --git a/tests/language/inference_super_constructor_call_test.dart b/tests/language/inference_super_constructor_call_test.dart
new file mode 100644
index 0000000..0dc55a6
--- /dev/null
+++ b/tests/language/inference_super_constructor_call_test.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2013, 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.
+
+// Regression test for dart2js's inferrer that used to not propagate
+// types given to generative constructors in super constructor calls.
+
+import "package:expect/expect.dart";
+
+class A {
+ final field;
+ A.full(this.field);
+}
+
+class B extends A {
+ // The following super call used to not be analyzed properly.
+ B.full(field) : super.full(field);
+}
+
+main() {
+ // Call [A.full] with an int to have the inferrer think [field] is
+ // always an int.
+ Expect.equals(84, new A.full(42).field + 42);
+ Expect.throws(() => new B.full(null).field + 42,
+ (e) => e is NoSuchMethodError);
+}
diff --git a/tests/language/issue10561_test.dart b/tests/language/issue10561_test.dart
index 37a6cef..7ff80be 100644
--- a/tests/language/issue10561_test.dart
+++ b/tests/language/issue10561_test.dart
@@ -9,7 +9,7 @@
import 'dart:collection';
-class Foo extends LinkedHashMap {
+class Foo extends HashSet {
}
main() {
diff --git a/tests/language/issue9949_test.dart b/tests/language/issue9949_test.dart
index e161d5a..d5df0ca 100644
--- a/tests/language/issue9949_test.dart
+++ b/tests/language/issue9949_test.dart
@@ -8,11 +8,11 @@
import "package:expect/expect.dart";
import 'dart:collection';
-class Crash extends LinkedHashMap<String,String> {
+class Crash extends HashSet<String> {
Crash(): super();
}
void main() {
Crash map = new Crash();
- Expect.isTrue(map is LinkedHashMap);
+ Expect.isTrue(map is HashSet);
}
diff --git a/tests/language/language.status b/tests/language/language.status
index f1293db..7cd6bc0 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -33,7 +33,6 @@
mixin_forwarding_constructor2_test: Fail # Issue 11888
mixin_typedef_constructor_test: Fail # Issue 11888
mixin_type_parameter2_test: Fail # Issue 11888
-built_in_identifier_illegal_test/19: Fail # Issue 13021
[ $compiler == none && $unchecked ]
# Only checked mode reports an error on type assignment
@@ -60,3 +59,6 @@
[ $compiler == none && $runtime == drt ]
mixin_illegal_object_test/01: pass # Issue 10952.
mixin_illegal_object_test/02: pass # Issue 10952.
+
+[ $compiler == none && $runtime == drt && $system == macos ]
+field_type_check_test/none: Pass, Fail # Issue 13266
diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status
index e360479..4dc9997 100644
--- a/tests/language/language_analyzer.status
+++ b/tests/language/language_analyzer.status
@@ -128,10 +128,6 @@
instantiate_type_variable_test/01: fail # Issue 11595
-mixin_type_parameters_errors_test/01: fail # Issue 11598
-mixin_type_parameters_errors_test/02: fail # Issue 11598
-mixin_type_parameters_errors_test/05: fail # Issue 11598
-
# test issue 11698, no setter, so warning, not error
assign_instance_method_negative_test: fail
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index c0c31c7..6b182de 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -3,6 +3,7 @@
# BSD-style license that can be found in the LICENSE file.
[ $compiler == dart2js || $compiler == dart2dart ]
+block_scope_test: Fail # Issue 13204.
compile_time_constant_c_test/none: Fail # Issue 11583
null_test/03: Fail # Issue 12445.
black_listed_test/none: Fail # Issue 12446.
@@ -108,8 +109,6 @@
not_enough_positional_arguments_test/01: Fail # Issue 12838
not_enough_positional_arguments_test/02: Fail # Issue 12838
not_enough_positional_arguments_test/05: Fail # Issue 12838
-dynamic_test: Fail # Issue 12398
-dynamic_prefix_core_test: Fail # Issue 12398
metadata_test: Fail # Issue 5841
infinity_test: Fail # Issue 4984
positive_bit_operations_test: Fail # Issue 12795
@@ -121,17 +120,12 @@
function_type_alias5_test/00: Fail # Issue 12754
function_type_alias5_test/01: Fail # Issue 12754
function_type_alias5_test/02: Fail # Issue 12754
-function_type_alias7_test/00: Fail # Issue 12801
method_binding_test: Fail # Issue 12807
method_override_test: Fail # Issue 12808
method_override5_test: Fail # Issue 12809
parameter_initializer6_negative_test: Fail # Issue 3502
-named_parameters_aggregated_test/01: Fail # Issue 12801
named_parameters_aggregated_test/03: Fail # Issue 12812
super_implicit_closure_test: Fail # Issue 12884
-mixin_type_parameters_errors_test/01: Fail # Issue 12885
-mixin_type_parameters_errors_test/02: Fail # Issue 12885
-mixin_type_parameters_errors_test/05: Fail # Issue 12885
external_test/11: Fail # Issue 12887
external_test/12: Fail # Issue 12887
external_test/13: Fail # Issue 12887
@@ -150,13 +144,8 @@
const_syntax_test/03: Fail # Issue 12932
const_syntax_test/04: Fail # Issue 12932
constructor9_test/01: Fail # Issue 12934
-duplicate_implements_test/01: Fail # Issue 12991
-duplicate_implements_test/02: Fail # Issue 12991
-duplicate_implements_test/03: Fail # Issue 12991
-duplicate_implements_test/04: Fail # Issue 12991
list_literal1_test/01: Fail # Issue 12993
map_literal1_test/01: Fail # Issue 12993
-number_identifier_test/05: Fail # Issue 13070
scope_variable_test/01: Fail # Issue 13016
static_final_field2_test/02: Fail # Issue 13017
throw7_test/01: Fail # Issue 13019
@@ -219,10 +208,6 @@
on_catch_malformed_type_test: Fail # Issue 8601
-mixin_type_parameters_errors_test/01: Fail # Issue 12886
-mixin_type_parameters_errors_test/02: Fail # Issue 12886
-mixin_type_parameters_errors_test/05: Fail # Issue 12886
-
# Mixins fail on the VM.
mixin_mixin_test: Fail # Issue 9683
mixin_mixin2_test: Fail # Issue 9683
@@ -283,7 +268,6 @@
final_syntax_test/03: Fail # Issue 13020
final_syntax_test/04: Fail # Issue 13020
getter_no_setter_test/01: Fail # Issue 5519
-named_parameters_aggregated_test/01: Fail # Issue 12802
named_parameters_aggregated_test/03: Fail # Issue 12813
not_enough_positional_arguments_test/01: Fail # Issue 12839
not_enough_positional_arguments_test/02: Fail # Issue 12839
@@ -302,10 +286,6 @@
external_test/31: Fail # Issue 12888
built_in_identifier_test/01: Fail # Issue 13022
lazy_static3_test: Fail # Issue 12593
-duplicate_implements_test/01: Fail # Issue 12992
-duplicate_implements_test/02: Fail # Issue 12992
-duplicate_implements_test/03: Fail # Issue 12992
-duplicate_implements_test/04: Fail # Issue 12992
list_literal1_test/01: Fail # Issue 12993
map_literal1_test/01: Fail # Issue 12993
method_override4_test: Fail # Issue 12810
@@ -322,7 +302,6 @@
function_type_alias5_test/00: Fail # Issue 12755
function_type_alias5_test/01: Fail # Issue 12755
function_type_alias5_test/02: Fail # Issue 12755
-function_type_alias7_test/00: Fail # Issue 12802
parameter_initializer6_negative_test: Fail # Issue 3502
# DartVM problem.
@@ -357,9 +336,9 @@
super_getter_setter_test: Fail # Issue 11065.
f_bounded_quantification4_test: Fail # Issue 12605.
f_bounded_quantification5_test: Fail # Issue 12605.
-many_overridden_no_such_method_test: Fail # Issue 13078
-overridden_no_such_method_test: Fail # Issue 13078
-no_such_method_test: Fail # Issue 13078
+many_overridden_no_such_method_test: Pass, Fail # Issue 13078
+overridden_no_such_method_test: Pass, Fail # Issue 13078
+no_such_method_test: Pass, Fail # Issue 13078
# TODO(tball): Assign proper bug numbers.
class_literal_test: Fail
diff --git a/tests/language/mixin_type_parameters_errors_test.dart b/tests/language/mixin_type_parameters_errors_test.dart
index be459bd..c5889e5 100644
--- a/tests/language/mixin_type_parameters_errors_test.dart
+++ b/tests/language/mixin_type_parameters_errors_test.dart
@@ -6,11 +6,11 @@
class M<U> { }
class A<X> extends S<int> with M<double> { }
-class B<U, V> extends S with M<U, V> { } /// 01: compile-time error
-class C<A, B> extends S<A, int> with M { } /// 02: compile-time error
+class B<U, V> extends S with M<U, V> { } /// 01: static type warning
+class C<A, B> extends S<A, int> with M { } /// 02: static type warning
typedef F<X> = S<X> with M<X>;
-typedef G = S<int> with M<double, double>; /// 05: compile-time error
+typedef G = S<int> with M<double, double>; /// 05: static type warning
main() {
var a;
diff --git a/tests/language/number_identifier_test.dart b/tests/language/number_identifier_test.dart
index 9ef17d8..ab5ac20 100644
--- a/tests/language/number_identifier_test.dart
+++ b/tests/language/number_identifier_test.dart
@@ -31,6 +31,14 @@
Expect.equals(1e+2, 1e+2as double);
Expect.throws(() => 1.e+2, /// 05: ok
(e) => e is NoSuchMethodError); /// 05: continued
+ 1d; /// 06: compile-time error
+ 1D; /// 07: compile-time error
+ Expect.throws(() => 1.d+2, /// 08: ok
+ (e) => e is NoSuchMethodError); /// 08: continued
+ Expect.throws(() => 1.D+2, /// 09: ok
+ (e) => e is NoSuchMethodError); /// 09: continued
+ 1.1d; /// 10: compile-time error
+ 1.1D; /// 11: compile-time error
1e; /// 02: compile-time error
1x; /// 03: compile-time error
}
diff --git a/tests/lib/async/future_test.dart b/tests/lib/async/future_test.dart
index 2c81f05..1594832 100644
--- a/tests/lib/async/future_test.dart
+++ b/tests/lib/async/future_test.dart
@@ -617,22 +617,6 @@
completer.complete(21);
}
-void testChainedFutureCycle() {
- asyncStart();
- var completer = new Completer();
- var future, future2;
- future = completer.future.then((_) => future2);
- future2 = completer.future.then((_) => future);
- completer.complete(42);
- int ctr = 2;
- void receiveError(e) {
- Expect.isTrue(e is StateError);
- if (--ctr == 0) asyncEnd();
- }
- future.catchError(receiveError);
- future.catchError(receiveError);
-}
-
main() {
testValue();
testSync();
@@ -674,5 +658,4 @@
testChainedFutureValue();
testChainedFutureValueDelay();
testChainedFutureError();
- testChainedFutureCycle();
}
diff --git a/tests/lib/async/future_value_chain2_test.dart b/tests/lib/async/future_value_chain2_test.dart
new file mode 100644
index 0000000..3b31c3d
--- /dev/null
+++ b/tests/lib/async/future_value_chain2_test.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2013, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async';
+
+
+main() {
+ asyncStart();
+ var errorFuture = new Future.error(499);
+ var valueChainFuture = new Future.sync(() => errorFuture);
+ // The errorFuture must not be propagated immediately as we would otherwise
+ // not have time to catch the error.
+ valueChainFuture.catchError((error) {
+ Expect.equals(499, error);
+ asyncEnd();
+ });
+}
diff --git a/tests/lib/async/future_value_chain3_test.dart b/tests/lib/async/future_value_chain3_test.dart
new file mode 100644
index 0000000..d6d9abf
--- /dev/null
+++ b/tests/lib/async/future_value_chain3_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2013, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async';
+
+
+main() {
+ asyncStart();
+ var errorFuture = new Future.error(499);
+ errorFuture.catchError((x) {
+ Expect.equals(499, x);
+ var valueChainFuture = new Future.sync(() => errorFuture);
+ // The errorFuture must not be propagated immediately as we would otherwise
+ // not have time to catch the error.
+ valueChainFuture.catchError((error) {
+ Expect.equals(499, error);
+ asyncEnd();
+ });
+ });
+}
diff --git a/tests/lib/async/future_value_chain4_test.dart b/tests/lib/async/future_value_chain4_test.dart
new file mode 100644
index 0000000..2c7b652
--- /dev/null
+++ b/tests/lib/async/future_value_chain4_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2013, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async';
+
+class MyFuture implements Future {
+ then(valueHandler, {onError}) {
+ runAsync(() { valueHandler(499); });
+ }
+ catchError(_) => null;
+ whenComplete(_) => null;
+}
+
+main() {
+ asyncStart();
+ Completer completer = new Completer();
+ completer.complete(new MyFuture());
+ Expect.isTrue(completer.isCompleted);
+ Expect.throws(() => completer.complete(42));
+ completer.future.then((_) => asyncEnd());
+}
diff --git a/tests/lib/async/future_value_chain_test.dart b/tests/lib/async/future_value_chain_test.dart
new file mode 100644
index 0000000..3cec185
--- /dev/null
+++ b/tests/lib/async/future_value_chain_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2013, 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async';
+
+
+main() {
+ asyncStart();
+ var errorFuture = new Future.error(499);
+ errorFuture.catchError((x) {
+ Expect.equals(499, x);
+ var valueChainFuture = new Future.value(errorFuture);
+ // The errorFuture must not be propagated immediately as we would otherwise
+ // not have time to catch the error.
+ valueChainFuture.catchError((error) {
+ Expect.equals(499, error);
+ asyncEnd();
+ });
+ });
+}
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index aecf6a9..bc28219 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -19,6 +19,7 @@
mirrors/invoke_test: Fail # Issue 11954
mirrors/invoke_named_test: Fail # Issue 10471, 12863
mirrors/invoke_private_test: Fail # Issue 12164
+mirrors/invoke_throws_test: Fail # Issue 11954
mirrors/library_uri_io_test: Skip # Not intended for dart2js as it uses dart:io.
mirrors/method_mirror_name_test: Fail # Issue 6335
mirrors/method_mirror_properties_test: Fail # Issue 11861
@@ -26,6 +27,7 @@
mirrors/method_mirror_source_test : Fail # Issue 6490
mirrors/mirrors_test: Fail # TODO(ahe): I'm working on fixing this.
mirrors/mixin_test/none: Fail # Issue 12464
+mirrors/new_instance_with_type_arguments_test: Fail # Issue 12333
mirrors/null_test : Fail # Issue 12129
mirrors/parameter_test: Fail # Issue 6490
mirrors/parameter_metadata_test: Fail # Issue 10905
@@ -106,6 +108,8 @@
async/deferred/deferred_api_test: Pass, Timeout # http://dartbug.com/12635
convert/streamed_conversion_utf8_decode_test: Pass, Timeout # http://dartbug.com/12768
+[ $compiler == dart2js ]
+typed_data/typed_data_hierarchy_int64_test: Fail # Issue 10275
[ $runtime == opera ]
async/multiple_timer_test: Pass, Fail
diff --git a/tests/lib/mirrors/invoke_throws_test.dart b/tests/lib/mirrors/invoke_throws_test.dart
new file mode 100644
index 0000000..f3198a1
--- /dev/null
+++ b/tests/lib/mirrors/invoke_throws_test.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2013, 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.
+
+library test.invoke_throws_test;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+class MyException {
+}
+
+class Class {
+ Class.noException();
+ Class.generative() { throw new MyException(); }
+ Class.redirecting() : this.generative();
+ factory Class.faktory() { throw new MyException(); }
+ factory Class.redirectingFactory() = Class.faktory;
+
+ get getter { throw new MyException(); }
+ set setter(v) { throw new MyException(); }
+ method() { throw new MyException(); }
+
+ noSuchMethod(invocation) { throw new MyException(); }
+
+ static get staticGetter { throw new MyException(); }
+ static set staticSetter(v) { throw new MyException(); }
+ static staticFunction() { throw new MyException(); }
+}
+
+get libraryGetter { throw new MyException(); }
+set librarySetter(v) { throw new MyException(); }
+libraryFunction() { throw new MyException(); }
+
+main() {
+ InstanceMirror im = reflect(new Class.noException());
+ Expect.throws(() => im.getField(const Symbol('getter')),
+ (e) => e is MyException);
+ Expect.throws(() => im.setField(const Symbol('setter'), ['arg']),
+ (e) => e is MyException);
+ Expect.throws(() => im.invoke(const Symbol('method'), []),
+ (e) => e is MyException);
+ Expect.throws(() => im.invoke(const Symbol('triggerNoSuchMethod'), []),
+ (e) => e is MyException);
+
+ ClassMirror cm = reflectClass(Class);
+ Expect.throws(() => cm.getField(const Symbol('staticGetter')),
+ (e) => e is MyException);
+ Expect.throws(() => cm.setField(const Symbol('staticSetter'), ['arg']),
+ (e) => e is MyException);
+ Expect.throws(() => cm.invoke(const Symbol('staticFunction'), []),
+ (e) => e is MyException);
+ Expect.throws(() => cm.newInstance(const Symbol('generative'), []),
+ (e) => e is MyException);
+ Expect.throws(() => cm.newInstance(const Symbol('redirecting'), []),
+ (e) => e is MyException);
+ Expect.throws(() => cm.newInstance(const Symbol('faktory'), []),
+ (e) => e is MyException);
+ Expect.throws(() => cm.newInstance(const Symbol('redirectingFactory'), []),
+ (e) => e is MyException);
+
+ LibraryMirror lm = reflectClass(Class).owner;
+ Expect.throws(() => lm.getField(const Symbol('libraryGetter')),
+ (e) => e is MyException);
+ Expect.throws(() => lm.setField(const Symbol('librarySetter'), ['arg']),
+ (e) => e is MyException);
+ Expect.throws(() => lm.invoke(const Symbol('libraryFunction'), []),
+ (e) => e is MyException);
+}
diff --git a/tests/lib/mirrors/new_instance_with_type_arguments_test.dart b/tests/lib/mirrors/new_instance_with_type_arguments_test.dart
new file mode 100644
index 0000000..44238ad
--- /dev/null
+++ b/tests/lib/mirrors/new_instance_with_type_arguments_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2013, 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.
+
+library test.new_instance_with_type_arguments_test;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+class A<T> {
+ Type get t => T;
+}
+class B extends A<int> {}
+class C<S> extends A<num> {
+ Type get s => S;
+}
+
+main() {
+ ClassMirror cmA = reflectClass(A);
+ ClassMirror cmB = reflectClass(B);
+ ClassMirror cmC = reflectClass(C);
+
+ var a_int = new A<int>();
+ var a_dynamic = new A();
+ var b = new B();
+ var c_string = new C<String>();
+ var c_dynamic = new C();
+
+ Expect.equals(int, a_int.t);
+ Expect.equals(dynamic, a_dynamic.t);
+ Expect.equals(int, b.t);
+ Expect.equals(num, c_string.t);
+ Expect.equals(num, c_dynamic.t);
+
+ Expect.equals(String, c_string.s);
+ Expect.equals(dynamic, c_dynamic.s);
+
+
+ var reflective_a_int =
+ cmB.superclass.newInstance(const Symbol(''), []).reflectee;
+ var reflective_a_dynamic =
+ cmA.newInstance(const Symbol(''), []).reflectee;
+ var reflective_b =
+ cmB.newInstance(const Symbol(''), []).reflectee;
+ // TODO(rmacnak): Uncomment when reflectType is added to the API.
+ // var reflective_c_string =
+ // reflectType(cmC.runtimeType).newInstance(const Symbol(''), []).reflectee;
+ var reflective_c_dynamic =
+ cmC.newInstance(const Symbol(''), []).reflectee;
+
+ Expect.equals(int, reflective_a_int.t);
+ Expect.equals(dynamic, reflective_a_dynamic.t);
+ Expect.equals(int, reflective_b.t);
+ // Expect.equals(num, c_string.t);
+ Expect.equals(num, reflective_c_dynamic.t);
+
+ // Expect.equals(String, c_string.s);
+ Expect.equals(dynamic, reflective_c_dynamic.s);
+
+ Expect.equals(a_int.runtimeType, reflective_a_int.runtimeType);
+ Expect.equals(a_dynamic.runtimeType, reflective_a_dynamic.runtimeType);
+ Expect.equals(b.runtimeType, reflective_b.runtimeType);
+ // Expect.equals(c_string.runtimeType, reflective_c_string.runtimeType);
+ Expect.equals(c_dynamic.runtimeType, reflective_c_dynamic.runtimeType);
+}
diff --git a/tests/lib/mirrors/parameter_test.dart b/tests/lib/mirrors/parameter_test.dart
index 7c538a7..6a86c53 100644
--- a/tests/lib/mirrors/parameter_test.dart
+++ b/tests/lib/mirrors/parameter_test.dart
@@ -31,6 +31,12 @@
waldo(int z);
}
+class C <S extends int, T> {
+ // TODO(6490): Currently only supported by the VM.
+ foo(int a, S b) => b;
+ bar(S a, T b, num c) {}
+}
+
main() {
ClassMirror cm = reflectClass(B);
Map<Symbol, MethodMirror> constructors = cm.constructors;
@@ -141,4 +147,27 @@
' type = Class(s(int) in s(dart.core), top-level))]',
waldo.parameters);
expect('<null>', waldo.parameters[0].defaultValue);
+
+ cm = reflectClass(C);
+
+ MethodMirror fooInC = cm.members[const Symbol("foo")];
+ expect('Method(s(foo) in s(C))', fooInC);
+ expect('[Parameter(s(a) in s(foo),'
+ ' type = Class(s(int) in s(dart.core), top-level)), '
+ 'Parameter(s(b) in s(foo),'
+ ' type = TypeVariable(s(S) in s(C),'
+ ' upperBound = Class(s(int) in s(dart.core), top-level)))]',
+ fooInC.parameters);
+
+ MethodMirror barInC = cm.members[const Symbol("bar")];
+ expect('Method(s(bar) in s(C))', barInC);
+ expect('[Parameter(s(a) in s(bar),'
+ ' type = TypeVariable(s(S) in s(C),'
+ ' upperBound = Class(s(int) in s(dart.core), top-level))), '
+ 'Parameter(s(b) in s(bar),'
+ ' type = TypeVariable(s(T) in s(C),'
+ ' upperBound = Class(s(Object) in s(dart.core), top-level))), '
+ 'Parameter(s(c) in s(bar),'
+ ' type = Class(s(num) in s(dart.core), top-level))]',
+ barInC.parameters);
}
diff --git a/tests/lib/mirrors/stringify.dart b/tests/lib/mirrors/stringify.dart
index ddb0668..369f2c1 100644
--- a/tests/lib/mirrors/stringify.dart
+++ b/tests/lib/mirrors/stringify.dart
@@ -83,6 +83,13 @@
return 'Parameter($buffer)';
}
+stringifyTypeVariable(TypeVariableMirror typeVariable) {
+ var buffer = new StringBuffer();
+ writeDeclarationOn(typeVariable, buffer);
+ buffer.write(', upperBound = ${stringify(typeVariable.upperBound)}');
+ return 'TypeVariable($buffer)';
+}
+
stringifyType(TypeMirror type) {
var buffer = new StringBuffer();
writeDeclarationOn(type, buffer);
@@ -116,6 +123,7 @@
if (value is String) return value;
if (value is Symbol) return stringifySymbol(value);
if (value is ClassMirror) return stringifyClass(value);
+ if (value is TypeVariableMirror) return stringifyTypeVariable(value);
if (value is TypeMirror) return stringifyType(value);
if (value == null) return '<null>';
throw 'Unexpected value: $value';
diff --git a/tests/lib/typed_data/typed_data_hierarchy_int64_test.dart b/tests/lib/typed_data/typed_data_hierarchy_int64_test.dart
new file mode 100644
index 0000000..3b38bb6
--- /dev/null
+++ b/tests/lib/typed_data/typed_data_hierarchy_int64_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// VMOptions=--optimization-counter-threshold=10
+
+// Library tag to be able to run in html test framework.
+library typed_data_hierarchy_int64_test;
+
+import 'dart:typed_data';
+import 'package:expect/expect.dart';
+
+var inscrutable = null;
+
+void implementsTypedData() {
+ Expect.isTrue(inscrutable(new Int64List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Uint64List(1)) is TypedData);
+}
+
+void implementsList() {
+ Expect.isTrue(inscrutable(new Int64List(1)) is List<int>);
+ Expect.isTrue(inscrutable(new Uint64List(1)) is List<int>);
+}
+
+main() {
+ inscrutable = (x) => x;
+ implementsTypedData();
+ implementsList();
+}
+
diff --git a/tests/lib/typed_data/typed_data_hierarchy_test.dart b/tests/lib/typed_data/typed_data_hierarchy_test.dart
new file mode 100644
index 0000000..eba0e7b
--- /dev/null
+++ b/tests/lib/typed_data/typed_data_hierarchy_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// VMOptions=--optimization-counter-threshold=10
+
+// Library tag to be able to run in html test framework.
+library typed_data_hierarchy_test;
+
+import 'dart:typed_data';
+import 'package:expect/expect.dart';
+
+var inscrutable = null;
+
+void testClampedList() {
+ // Force lookup of Uint8List first.
+ Expect.isTrue(inscrutable(new Uint8List(1)) is Uint8List);
+
+ Expect.isFalse(new Uint8ClampedList(1) is Uint8List,
+ 'Uint8ClampedList should not be a subtype of Uint8List '
+ 'in optimizable test');
+ Expect.isFalse(inscrutable(new Uint8ClampedList(1)) is Uint8List,
+ 'Uint8ClampedList should not be a subtype of Uint8List in dynamic test');
+}
+
+void implementsTypedData() {
+ Expect.isTrue(inscrutable(new ByteData(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Float32List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Float32x4List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Float64List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Int8List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Int16List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Int32List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Uint8List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Uint8ClampedList(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Uint16List(1)) is TypedData);
+ Expect.isTrue(inscrutable(new Uint32List(1)) is TypedData);
+}
+
+
+void implementsList() {
+ Expect.isTrue(inscrutable(new Float32List(1)) is List<double>);
+ Expect.isTrue(inscrutable(new Float32x4List(1)) is List<Float32x4>);
+ Expect.isTrue(inscrutable(new Float64List(1)) is List<double>);
+ Expect.isTrue(inscrutable(new Int8List(1)) is List<int>);
+ Expect.isTrue(inscrutable(new Int16List(1)) is List<int>);
+ Expect.isTrue(inscrutable(new Int32List(1)) is List<int>);
+ Expect.isTrue(inscrutable(new Uint8List(1)) is List<int>);
+ Expect.isTrue(inscrutable(new Uint8ClampedList(1)) is List<int>);
+ Expect.isTrue(inscrutable(new Uint16List(1)) is List<int>);
+ Expect.isTrue(inscrutable(new Uint32List(1)) is List<int>);
+}
+
+main() {
+ inscrutable = (x) => x;
+
+ // Note: this test must come first to control order of lookup on Uint8List and
+ // Uint8ClampedList.
+ testClampedList();
+
+ implementsTypedData();
+ implementsList();
+}
diff --git a/tests/standalone/io/directory_chdir_test.dart b/tests/standalone/io/directory_chdir_test.dart
index e1c9a1d..682698f 100644
--- a/tests/standalone/io/directory_chdir_test.dart
+++ b/tests/standalone/io/directory_chdir_test.dart
@@ -26,8 +26,15 @@
Directory.current = "..";
Expect.isTrue(new File("111").existsSync());
Expect.isTrue(new File("222/333").existsSync());
+ // Deleting the current working directory causes an error.
+ // On Windows, the deletion fails, and on non-Windows, the getter fails.
+ Expect.throws(() {
+ temp.deleteSync(recursive: true);
+ Directory.current;
+ }, (e) => e is DirectoryException);
Directory.current = initialCurrent;
- temp.deleteSync(recursive: true);
+ Directory.current;
+ if (temp.existsSync()) temp.deleteSync(recursive: true);
asyncEnd();
});
}
diff --git a/tests/standalone/io/file_absolute_path_test.dart b/tests/standalone/io/file_absolute_path_test.dart
new file mode 100644
index 0000000..e2785bf
--- /dev/null
+++ b/tests/standalone/io/file_absolute_path_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2013, 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.
+//
+// Dart test program for testing FileSystemEntity.absolute
+
+import "package:expect/expect.dart";
+import 'dart:io';
+
+main() {
+ if (Platform.isWindows) {
+ testWindows();
+ try {
+ Directory.current = 'C:\\';
+ } catch (e) {
+ return;
+ }
+ testWindows();
+ } else {
+ testPosix();
+ Directory.current = '.';
+ testPosix();
+ Directory.current = '/';
+ testPosix();
+
+ }
+}
+
+testWindows() {
+ String current = Directory.current.path;
+ for (String relative in ['abd', '..', '.', 'efg/hij', 'abc/']) {
+ if (current.endsWith('\\')) {
+ Expect.equals(new File(relative).absolute.path, '$current$relative');
+ } else {
+ Expect.equals(new File(relative).absolute.path, '$current\\$relative');
+ }
+ Expect.isTrue(new File(relative).absolute.isAbsolute);
+ }
+ for (String absolute in ['c:/abd', 'D:\\rf', '\\\\a_share\\folder',
+ '\\\\?\\c:\\prefixed\path\\']) {
+ Expect.isTrue(new File(absolute).absolute.path == absolute);
+ Expect.isTrue(new File(absolute).absolute.isAbsolute);
+ }
+}
+
+testPosix() {
+ String current = Directory.current.path;
+ print(Directory.current.path);
+ for (String relative in ['abd', '..', '.', 'efg/hij', 'abc/']) {
+ if (current.endsWith('/')) {
+ Expect.equals(new File(relative).absolute.path, '$current$relative');
+ } else {
+ Expect.equals(new File(relative).absolute.path, '$current/$relative');
+ }
+ Expect.isTrue(new File(relative).absolute.isAbsolute);
+ Expect.equals(new Directory(relative).absolute.path,
+ new Link(relative).absolute.path);
+ Expect.isTrue(new File(relative).absolute is File);
+ Expect.isTrue(new Directory(relative).absolute is Directory);
+ Expect.isTrue(new Link(relative).absolute is Link);
+ }
+ for (String absolute in ['/abd', '/', '/./..\\', '/efg/hij', '/abc/']) {
+ Expect.equals(new File(absolute).absolute.path, absolute);
+ Expect.isTrue(new File(absolute).absolute.isAbsolute);
+ }
+}
diff --git a/tests/standalone/io/file_read_encoded_test.dart b/tests/standalone/io/file_read_encoded_test.dart
new file mode 100644
index 0000000..23715c0
--- /dev/null
+++ b/tests/standalone/io/file_read_encoded_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2012, 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:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+import 'dart:io';
+
+void testReadAsString() {
+ var tmp = new Directory('').createTempSync();
+
+ var file = new File('${tmp.path}/file');
+ file.createSync();
+
+ file.writeAsBytesSync([0xb0]);
+
+ Expect.throws(file.readAsStringSync, (e) => e is FileException);
+
+ asyncStart();
+ file.readAsString().then((_) {
+ Expect.fail("expected exception");
+ }).catchError((e) {
+ tmp.deleteSync(recursive: true);
+ asyncEnd();
+ }, test: (e) => e is FileException);
+}
+
+void testReadAsLines() {
+ var tmp = new Directory('').createTempSync();
+
+ var file = new File('${tmp.path}/file');
+ file.createSync();
+
+ file.writeAsBytesSync([0xb0]);
+
+ Expect.throws(file.readAsLinesSync, (e) => e is FileException);
+
+ asyncStart();
+ file.readAsLines().then((_) {
+ Expect.fail("expected exception");
+ }).catchError((e) {
+ tmp.deleteSync(recursive: true);
+ asyncEnd();
+ }, test: (e) => e is FileException);
+}
+
+void main() {
+ testReadAsString();
+ testReadAsLines();
+}
diff --git a/tests/standalone/io/file_test.dart b/tests/standalone/io/file_test.dart
index 2123b89..14e4780 100644
--- a/tests/standalone/io/file_test.dart
+++ b/tests/standalone/io/file_test.dart
@@ -1015,7 +1015,7 @@
Expect.listEquals(expected, text.codeUnits);
// First character is not ASCII. The default ASCII decoder will throw.
Expect.throws(() => new File(name).readAsStringSync(encoding: ASCII),
- (e) => e is FormatException);
+ (e) => e is FileException);
// We can use an ASCII decoder that inserts the replacement character.
var lenientAscii = const AsciiCodec(allowInvalid: true);
text = new File(name).readAsStringSync(encoding: lenientAscii);
diff --git a/tests/standalone/io/process_run_test.dart b/tests/standalone/io/process_run_test.dart
new file mode 100644
index 0000000..2e07d0d
--- /dev/null
+++ b/tests/standalone/io/process_run_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2013, 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:expect/expect.dart";
+
+import "process_test_util.dart";
+
+
+void testProcessRunBinaryOutput() {
+ var result = Process.runSync(getProcessTestFileName(),
+ const ["0", "0", "0", "0"],
+ stdoutEncoding: null);
+ Expect.isTrue(result.stdout is List<int>);
+ Expect.isTrue(result.stderr is String);
+
+ result = Process.runSync(getProcessTestFileName(),
+ const ["0", "0", "0", "0"],
+ stderrEncoding: null);
+ Expect.isTrue(result.stdout is String);
+ Expect.isTrue(result.stderr is List<int>);
+
+ result = Process.runSync(getProcessTestFileName(),
+ const ["0", "0", "0", "0"],
+ stdoutEncoding: null,
+ stderrEncoding: null);
+ Expect.isTrue(result.stdout is List<int>);
+ Expect.isTrue(result.stderr is List<int>);
+}
+
+
+void main() {
+ testProcessRunBinaryOutput();
+}
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 77e8f0e..b8028ab 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -15,6 +15,9 @@
package/invalid_uri_test: Fail, OK # Fails intentionally
+[ $runtime == vm && $system == windows ]
+io/file_system_watcher_test: Pass, Timeout # Issue 13228
+
[ $runtime == vm ]
package/package_isolate_test: Fail # http://dartbug.com/7520.
@@ -51,6 +54,9 @@
[ $runtime == vm && $system == windows ]
io/http_server_early_client_close_test: Crash, Pass # Issue 12982
+[ $compiler == none && $runtime == vm && $system == windows && $mode == debug ]
+io/secure_socket_bad_data_test: Crash, Pass # co19-roll r576: Please triage this failure
+
[ $compiler == none && $runtime == drt ]
typed_data_isolate_test: Skip # This test uses dart:io
io/*: Skip # Don't run tests using dart:io in the browser
diff --git a/tests/standalone/typed_data_test.dart b/tests/standalone/typed_data_test.dart
index 3ea844b..47a1787 100644
--- a/tests/standalone/typed_data_test.dart
+++ b/tests/standalone/typed_data_test.dart
@@ -32,7 +32,7 @@
typed_data = new Uint8ClampedList(0);
Expect.isTrue(typed_data is Uint8ClampedList);
- Expect.isTrue(typed_data is Uint8List);
+ Expect.isFalse(typed_data is Uint8List);
Expect.equals(0, typed_data.length);
Expect.equals(0, typed_data.lengthInBytes);
diff --git a/tests/utils/dummy_compiler_test.dart b/tests/utils/dummy_compiler_test.dart
index be63346..4c0a4aa9 100644
--- a/tests/utils/dummy_compiler_test.dart
+++ b/tests/utils/dummy_compiler_test.dart
@@ -67,6 +67,7 @@
class JSString implements JSIndexable {
var split;
var concat;
+ operator+(other) {}
var toString;
}
class JSFunction {}
@@ -115,5 +116,5 @@
}
}, onError: (e) {
throw 'Compilation failed';
- }).whenComplete(() => asyncEnd());
+ }).then(asyncSuccess);
}
diff --git a/tests/utils/recursive_import_test.dart b/tests/utils/recursive_import_test.dart
index 0bb8769..9d288aa 100644
--- a/tests/utils/recursive_import_test.dart
+++ b/tests/utils/recursive_import_test.dart
@@ -54,6 +54,7 @@
split(x) => null;
concat(x) => null;
toString() => null;
+ operator+(other) => null;
}
class JSNull {
}
@@ -127,5 +128,5 @@
Expect.equals(1, errorCount);
}, onError: (e) {
throw 'Compilation failed';
- }).whenComplete(() => asyncEnd());
+ }).then(asyncSuccess);
}
diff --git a/tests/utils/utils.status b/tests/utils/utils.status
index b1c695f..34e6ff0 100644
--- a/tests/utils/utils.status
+++ b/tests/utils/utils.status
@@ -19,9 +19,3 @@
[ $system == macos || $system == windows ]
*_layout_test: Skip
-
-[ $compiler == dartanalyzer ]
-dart2js_test: Fail # Issue 13012
-
-[ $compiler == dart2analyzer ]
-dart2js_test: Fail # Issue 13012
diff --git a/tools/VERSION b/tools/VERSION
index c1c78df..d4dbee5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
MAJOR 0
MINOR 7
-BUILD 2
-PATCH 1
+BUILD 3
+PATCH 0
diff --git a/tools/bots/compiler.py b/tools/bots/compiler.py
index 2b11178..f4264ab 100644
--- a/tools/bots/compiler.py
+++ b/tools/bots/compiler.py
@@ -118,8 +118,8 @@
def UseBrowserController(runtime, system):
supported_platforms = {
'linux': ['ff', 'chromeOnAndroid', 'chrome'],
- 'mac': ['safari'],
- 'windows': ['ie9', 'ie10']
+ 'mac': ['safari', 'chrome'],
+ 'windows': ['ie9', 'ie10', 'ff', 'chrome']
}
# Platforms that we run on the fyi waterfall only.
fyi_supported_platforms = {
@@ -183,6 +183,7 @@
cmd.extend(targets)
print 'Running: %s' % (' '.join(map(lambda arg: '"%s"' % arg, cmd)))
+ sys.stdout.flush()
bot.RunProcess(cmd)
diff --git a/tools/coverage.dart b/tools/coverage.dart
index 34018c5..15333a6 100644
--- a/tools/coverage.dart
+++ b/tools/coverage.dart
@@ -354,7 +354,7 @@
void getLibraries() {
queuedCommands.add(new GetLibrariesCmd(isolateId));
}
-
+
void enableDebugging(libraryId, enable) {
queuedCommands.add(new SetLibraryPropertiesCmd(isolateId, libraryId, enable));
}
@@ -441,7 +441,7 @@
if (buffer == null || buffer.length == 0) {
buffer = s;
} else {
- buffer = buffer.concat(s);
+ buffer = buffer + s;
}
}
@@ -471,7 +471,7 @@
}
if (i >= buffer.length) {
return false;
- } else {
+ } else {
return char != "{";
}
}
diff --git a/tools/dom/docs/docs.json b/tools/dom/docs/docs.json
index ba4c2b3..3b71d3c 100644
--- a/tools/dom/docs/docs.json
+++ b/tools/dom/docs/docs.json
@@ -188,10 +188,11 @@
"HTMLDivElement": {
"comment": [
"/**",
- " * Represents an HTML <div> element.",
+ " * A generic container for content on an HTML page;",
+ " * corresponds to the <div> tag.",
" *",
- " * The [DivElement] is a generic container for content and does not have any",
- " * special significance. It is functionally similar to [SpanElement].",
+ " * The [DivElement] is a generic container and does not have any semantic",
+ " * significance. It is functionally similar to [SpanElement].",
" *",
" * The [DivElement] is a block-level element, as opposed to [SpanElement],",
" * which is an inline-level element.",
@@ -479,6 +480,16 @@
}
}
},
+ "dart.dom.indexed_db": {
+ "IDBDatabase": {
+ "comment": [
+ "/**",
+ " * An indexed database object for storing client-side data",
+ " * in web apps.",
+ " */"
+ ]
+ }
+ },
"dart.dom.web_audio": {
"ScriptProcessorNode": {
"members": {
diff --git a/tools/dom/scripts/dartdomgenerator.py b/tools/dom/scripts/dartdomgenerator.py
index 2ba67aa..787af48 100755
--- a/tools/dom/scripts/dartdomgenerator.py
+++ b/tools/dom/scripts/dartdomgenerator.py
@@ -138,6 +138,36 @@
cpp_library_emitter.EmitResolver(
template_loader.Load('cpp_resolver.template'), dartium_output_dir)
+ path = os.path.join(cpp_output_dir, 'DartWebkitClassIds.h')
+ e = emitters.FileEmitter(path)
+ e.Emit("""
+// WARNING: Do not edit - generated code.
+// See dart/tools/dom/scripts/dartdomgenerator.py
+
+#ifndef DartWebkitClassIds_h
+#define DartWebkitClassIds_h
+
+namespace WebCore {
+
+enum {
+ _HistoryCrossFrameClassId = 0,
+ _LocationCrossFrameClassId,
+ _DOMWindowCrossFrameClassId,
+ _NPObjectClassId,
+ // New types that are not auto-generated should be added here.
+""")
+ for interface in webkit_database.GetInterfaces():
+ interface_name = interface.id
+ e.Emit(' %sClassId,\n' % interface_name)
+ e.Emit("""
+ NumWebkitClassIds
+};
+
+} // namespace WebCore
+
+#endif // DartWebkitClassIds_h
+""")
+
_logger.info('Flush...')
emitters.Flush()
diff --git a/tools/dom/scripts/htmldartgenerator.py b/tools/dom/scripts/htmldartgenerator.py
index acfccc1..7b7ca8e 100644
--- a/tools/dom/scripts/htmldartgenerator.py
+++ b/tools/dom/scripts/htmldartgenerator.py
@@ -166,8 +166,11 @@
full_operation_str = self._GetStringRepresentation(interface, operation)
if (full_operation_str in renamed_overloads and
renamed_overloads[full_operation_str] not in already_renamed):
- operation.ext_attrs['DartName'] = renamed_overloads[
- full_operation_str]
+ dart_name = renamed_overloads[full_operation_str]
+ if not dart_name:
+ continue
+
+ operation.ext_attrs['DartName'] = dart_name
potential_added_operations.add(operation.id)
self._EnsureNoMultipleTypeSignatures(interface, operation,
operations_by_name)
@@ -635,6 +638,20 @@
has_length = False
has_length_setter = False
+ def _HasExplicitIndexedGetter(self):
+ return any(op.id == 'getItem' for op in self._interface.operations)
+
+ def _HasCustomIndexedGetter(self):
+ return 'CustomIndexedGetter' in self._interface.ext_attrs
+
+ def _HasNativeIndexedGetter(self):
+ return not (_HasCustomIndexedGetter(self) or _HasExplicitIndexedGetter(self))
+
+ if _HasExplicitIndexedGetter(self):
+ getter_name = 'getItem'
+ else:
+ getter_name = '_nativeIndexedGetter'
+
for attr in self._interface.attributes:
if attr.id == 'length':
has_length = True
@@ -648,8 +665,9 @@
{
'DEFINE_LENGTH_AS_NUM_ITEMS': not has_length and has_num_items,
'DEFINE_LENGTH_SETTER': not has_length_setter,
+ 'USE_NATIVE_INDEXED_GETTER': _HasNativeIndexedGetter(self) or _HasExplicitIndexedGetter(self),
})
- self._members_emitter.Emit(template, E=element_name)
+ self._members_emitter.Emit(template, E=element_name, GETTER=getter_name)
def SecureOutputType(self, type_name, is_dart_type=False,
can_narrow_type=False):
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index 4a21a49..7242512 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -157,8 +157,6 @@
# $dom in installments instead of all at once, but the intent is to move all of
# these either into private_html_members or remove them from this list entirely.
dom_private_html_members = monitored.Set('htmlrenamer.private_html_members', [
- 'Document.createElement',
- 'Document.createElementNS',
'EventTarget.addEventListener',
'EventTarget.removeEventListener',
])
@@ -273,6 +271,7 @@
'MutationEvent.initMutationEvent',
'MutationObserver.observe',
'Node.attributes',
+ 'Node.baseURI',
'Node.localName',
'Node.namespaceURI',
'Node.removeChild',
@@ -367,6 +366,8 @@
'DOMString repetitionType)': 'createPatternFromImage',
'DataTransferItemList.add(File file)': 'addFile',
'DataTransferItemList.add(DOMString data, DOMString type)': 'addData',
+ 'Document.createElement(DOMString tagName)': None,
+ 'Document.createElementNS(DOMString namespaceURI, DOMString qualifiedName)': None,
'FormData.append(DOMString name, Blob value, DOMString filename)':
'appendBlob',
'IDBDatabase.transaction(DOMStringList storeNames, DOMString mode)':
@@ -706,7 +707,6 @@
'Node.get:DOCUMENT_POSITION_FOLLOWING',
'Node.get:DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC',
'Node.get:DOCUMENT_POSITION_PRECEDING',
- 'Node.get:baseURI',
'Node.get:prefix',
'Node.hasAttributes',
'Node.isDefaultNamespace',
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index fbe61c34..40c75d0 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -295,11 +295,11 @@
_factory_ctr_strings = {
'html': {
'provider_name': 'document',
- 'constructor_name': '$dom_createElement'
+ 'constructor_name': 'createElement'
},
'indexed_db': {
'provider_name': 'document',
- 'constructor_name': '$dom_createElement'
+ 'constructor_name': 'createElement'
},
'svg': {
'provider_name': '_SvgElementFactoryProvider',
@@ -307,19 +307,19 @@
},
'typed_data': {
'provider_name': 'document',
- 'constructor_name': '$dom_createElement'
+ 'constructor_name': 'createElement'
},
'web_audio': {
'provider_name': 'document',
- 'constructor_name': '$dom_createElement'
+ 'constructor_name': 'createElement'
},
'web_gl': {
'provider_name': 'document',
- 'constructor_name': '$dom_createElement'
+ 'constructor_name': 'createElement'
},
'web_sql': {
'provider_name': 'document',
- 'constructor_name': '$dom_createElement'
+ 'constructor_name': 'createElement'
},
}
diff --git a/tools/dom/scripts/templateloader.py b/tools/dom/scripts/templateloader.py
index 647362b..88ef894 100644
--- a/tools/dom/scripts/templateloader.py
+++ b/tools/dom/scripts/templateloader.py
@@ -82,7 +82,7 @@
variable = words[1]
if variable in conditions:
condition_stack.append((active, seen_else))
- active = conditions[variable]
+ active = active and conditions[variable]
seen_else = False
else:
error(lineno, "Unknown $if variable '%s'" % variable)
@@ -93,7 +93,8 @@
if seen_else:
raise error(lineno, 'Double $else')
seen_else = True
- active = not active
+ (parentactive, _) = condition_stack[len(condition_stack) - 1]
+ active = not active and parentactive
elif directive == '$endif':
if not condition_stack:
diff --git a/tools/dom/src/dart2js_CustomElementSupport.dart b/tools/dom/src/dart2js_CustomElementSupport.dart
index 95c64b6..3ad3381 100644
--- a/tools/dom/src/dart2js_CustomElementSupport.dart
+++ b/tools/dom/src/dart2js_CustomElementSupport.dart
@@ -18,7 +18,32 @@
convertDartClosureToJS(_callCreated, 1));
}
-void _registerCustomElement(context, document, String tag, Type type) {
+const _typeNameToTag = const {
+ 'HTMLAnchorElement': 'a',
+ 'HTMLAudioElement': 'audio',
+ 'HTMLButtonElement': 'button',
+ 'HTMLCanvasElement': 'canvas',
+ 'HTMLDivElement': 'div',
+ 'HTMLImageElement': 'img',
+ 'HTMLInputElement': 'input',
+ 'HTMLLIElement': 'li',
+ 'HTMLLabelElement': 'label',
+ 'HTMLMenuElement': 'menu',
+ 'HTMLMeterElement': 'meter',
+ 'HTMLOListElement': 'ol',
+ 'HTMLOptionElement': 'option',
+ 'HTMLOutputElement': 'output',
+ 'HTMLParagraphElement': 'p',
+ 'HTMLPreElement': 'pre',
+ 'HTMLProgressElement': 'progress',
+ 'HTMLSelectElement': 'select',
+ 'HTMLSpanElement': 'span',
+ 'HTMLUListElement': 'ul',
+ 'HTMLVideoElement': 'video',
+};
+
+void _registerCustomElement(context, document, String tag, Type type,
+ String extendsTagName) {
// Function follows the same pattern as the following JavaScript code for
// registering a custom element.
//
@@ -38,6 +63,10 @@
throw new ArgumentError(type);
}
+ // Workaround for 13190- use an article element to ensure that HTMLElement's
+ // interceptor is resolved correctly.
+ getNativeInterceptor(new Element.tag('article'));
+
String baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
if (baseClassName == null) {
throw new ArgumentError(type);
@@ -60,6 +89,15 @@
setNativeSubclassDispatchRecord(proto, interceptor);
- JS('void', '#.register(#, #)',
- document, tag, JS('', '{prototype: #}', proto));
+ var options = JS('=Object', '{prototype: #}', proto);
+
+ if (baseClassName != 'HTMLElement') {
+ if (extendsTagName != null) {
+ JS('=Object', '#.extends = #', options, extendsTagName);
+ } else if (_typeNameToTag.containsKey(baseClassName)) {
+ JS('=Object', '#.extends = #', options, _typeNameToTag[baseClassName]);
+ }
+ }
+
+ JS('void', '#.register(#, #)', document, tag, options);
}
diff --git a/tools/dom/src/native_DOMImplementation.dart b/tools/dom/src/native_DOMImplementation.dart
index 80676b0..2a063c0 100644
--- a/tools/dom/src/native_DOMImplementation.dart
+++ b/tools/dom/src/native_DOMImplementation.dart
@@ -4,6 +4,31 @@
part of html;
+class _ConsoleVariables {
+ Map<String, Object> _data = new Map<String, Object>();
+
+ /**
+ * Forward member accesses to the backing JavaScript object.
+ */
+ noSuchMethod(Invocation invocation) {
+ String member = MirrorSystem.getName(invocation.memberName);
+ if (invocation.isGetter) {
+ return _data[member];
+ } else if (invocation.isSetter) {
+ _data[member] = invocation.positionalArguments[0];
+ } else {
+ return Function.apply(_data[member], invocation.positionalArguments, invocation.namedArguments);
+ }
+ }
+
+ void clear() => _data.clear();
+
+ /**
+ * List all variables currently defined.
+ */
+ List variables() => _data.keys.toList(growable: false);
+}
+
class _Utils {
static double dateTimeToDouble(DateTime dateTime) =>
dateTime.millisecondsSinceEpoch.toDouble();
@@ -102,6 +127,41 @@
return map;
}
+ static _ConsoleVariables _consoleTempVariables = new _ConsoleVariables();
+ /**
+ * Takes an [expression] and a list of [local] variable and returns an
+ * expression for a closure with a body matching the original expression
+ * where locals are passed in as arguments. Returns a list containing the
+ * String expression for the closure and the list of arguments that should
+ * be passed to it.
+ *
+ * For example:
+ * <code>wrapExpressionAsClosure("foo + bar", ["bar", 40, "foo", 2])</code>
+ * will return:
+ * <code>["(final $var, final bar, final foo) => foo + bar", [40, 2]]</code>
+ */
+ static List wrapExpressionAsClosure(String expression, List locals) {
+ var args = {};
+ var sb = new StringBuffer("(");
+ addArg(arg, value) {
+ arg = stripMemberName(arg);
+ if (args.containsKey(arg)) return;
+ if (args.isNotEmpty) {
+ sb.write(", ");
+ }
+ sb.write("final $arg");
+ args[arg] = value;
+ }
+
+ addArg("\$var", _consoleTempVariables);
+
+ for (int i = 0; i < locals.length; i+= 2) {
+ addArg(locals[i], locals[i+1]);
+ }
+ sb..write(')=>\n$expression');
+ return [sb.toString(), args.values.toList(growable: false)];
+ }
+
/**
* Convenience helper to get the keys of a [Map] as a [List].
*/
diff --git a/tools/dom/src/shared_SVGFactoryProviders.dart b/tools/dom/src/shared_SVGFactoryProviders.dart
index 6f4b9a7..eca6a9c 100644
--- a/tools/dom/src/shared_SVGFactoryProviders.dart
+++ b/tools/dom/src/shared_SVGFactoryProviders.dart
@@ -7,7 +7,7 @@
class _SvgElementFactoryProvider {
static SvgElement createSvgElement_tag(String tag) {
final Element temp =
- document.$dom_createElementNS("http://www.w3.org/2000/svg", tag);
+ document.createElementNS("http://www.w3.org/2000/svg", tag);
return temp;
}
}
diff --git a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
index 67490a8..eba39e5 100644
--- a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
@@ -20,7 +20,7 @@
*
* * If you've never written a web app before, try our
* tutorials—[A Game of Darts](http://dartlang.org/docs/tutorials).
- *
+ *
* * To see some web-based Dart apps in action and to play with the code,
* download
* [Dart Editor](http://www.dartlang.org/#get-started)
@@ -51,7 +51,8 @@
JSName, Null, Returns,
findDispatchTagForInterceptorClass, setNativeSubclassDispatchRecord;
import 'dart:_interceptors' show
- Interceptor, JSExtendableArray, findInterceptorConstructorForType;
+ Interceptor, JSExtendableArray, findInterceptorConstructorForType,
+ getNativeInterceptor;
import 'dart:_isolate_helper' show IsolateNatives;
import 'dart:_foreign_helper' show JS;
diff --git a/tools/dom/templates/html/dartium/cpp_header.template b/tools/dom/templates/html/dartium/cpp_header.template
index 00b95c2..5a6a8de 100644
--- a/tools/dom/templates/html/dartium/cpp_header.template
+++ b/tools/dom/templates/html/dartium/cpp_header.template
@@ -16,6 +16,7 @@
struct Dart$INTERFACE {
static const char* const dartImplementationClassName;
static const char* const dartImplementationLibraryName;
+ static const int dartClassId;
typedef $WEBCORE_CLASS_NAME NativeType;
static const bool isNode = $IS_NODE;
static const bool isActive = $IS_ACTIVE;
diff --git a/tools/dom/templates/html/dartium/cpp_implementation.template b/tools/dom/templates/html/dartium/cpp_implementation.template
index 3ca2ad6..4e3d741 100644
--- a/tools/dom/templates/html/dartium/cpp_implementation.template
+++ b/tools/dom/templates/html/dartium/cpp_implementation.template
@@ -5,6 +5,7 @@
// WARNING: Do not edit - generated code.
#include "config.h"
+#include "DartWebkitClassIds.h"
#include "Dart$(INTERFACE).h"
$INCLUDES
@@ -28,5 +29,5 @@
const char* const Dart$(INTERFACE)::dartImplementationClassName = "$DART_IMPLEMENTATION_CLASS";
const char* const Dart$(INTERFACE)::dartImplementationLibraryName = "$DART_IMPLEMENTATION_LIBRARY";
-
+const int Dart$(INTERFACE)::dartClassId = $(INTERFACE)ClassId;
}
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 1a556b2..6cf45d4 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -354,8 +354,8 @@
*
* * [isTagSupported]
*/
- factory $CLASSNAME.tag(String tag) =>
- _$(CLASSNAME)FactoryProvider.createElement_tag(tag);
+ factory $CLASSNAME.tag(String tag, [String typeExtention]) =>
+ _$(CLASSNAME)FactoryProvider.createElement_tag(tag, typeExtention);
/// Creates a new `<a>` element.
///
@@ -672,7 +672,7 @@
* The tag should be a valid HTML tag name.
*/
static bool isTagSupported(String tag) {
- var e = _ElementFactoryProvider.createElement_tag(tag);
+ var e = _ElementFactoryProvider.createElement_tag(tag, null);
return e is Element && !(e is UnknownElement);
}
@@ -944,7 +944,7 @@
@SupportedBrowser(SupportedBrowser.CHROME, '25')
@Experimental()
ShadowRoot get shadowRoot =>
- JS('ShadowRoot', '#.shadowRoot || #.webkitShadowRoot', this, this);
+ JS('ShadowRoot|Null', '#.shadowRoot || #.webkitShadowRoot', this, this);
$endif
@@ -1236,12 +1236,17 @@
if (_parseDocument == null) {
_parseDocument = document.implementation.createHtmlDocument('');
_parseRange = _parseDocument.createRange();
+
+ // Workaround for Chrome bug 229142- URIs are not resolved in new doc.
+ var base = _parseDocument.createElement('base');
+ base.href = document._baseUri;
+ _parseDocument.head.append(base);
}
var contextElement;
if (this is BodyElement) {
contextElement = _parseDocument.body;
} else {
- contextElement = _parseDocument.$dom_createElement(tagName);
+ contextElement = _parseDocument.createElement(tagName);
_parseDocument.body.append(contextElement);
}
var fragment;
@@ -1322,12 +1327,22 @@
$if DART2JS
// Optimization to improve performance until the dart2js compiler inlines this
// method.
- static dynamic createElement_tag(String tag) =>
- // Firefox may return a JS function for some types (Embed, Object).
- JS('Element|=Object', 'document.createElement(#)', tag);
+ static dynamic createElement_tag(String tag, String typeExtension) {
+ // Firefox may return a JS function for some types (Embed, Object).
+ if (typeExtension != null) {
+ return JS('Element|=Object', 'document.createElement(#, #)',
+ tag, typeExtension);
+ }
+ // Should be able to eliminate this and just call the two-arg version above
+ // with null typeExtension, but Chrome treats the tag as case-sensitive if
+ // typeExtension is null.
+ // https://code.google.com/p/chromium/issues/detail?id=282467
+ return JS('Element|=Object', 'document.createElement(#)', tag);
+ }
+
$else
- static Element createElement_tag(String tag) =>
- document.$dom_createElement(tag);
+ static Element createElement_tag(String tag, String typeExtension) =>
+ document.createElement(tag, typeExtension);
$endif
}
diff --git a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
index e4aaa29..a29e0c1 100644
--- a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
+++ b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
@@ -174,13 +174,81 @@
$endif
@Experimental
+ /**
+ * Register a custom subclass of Element to be instantiatable by the DOM.
+ *
+ * This is necessary to allow the construction of any custom elements.
+ *
+ * The class being registered must either subclass HtmlElement or SvgElement.
+ * If they subclass these directly then they can be used as:
+ *
+ * class FooElement extends HtmlElement{
+ * void created() {
+ * print('FooElement created!');
+ * }
+ * }
+ *
+ * main() {
+ * document.register('x-foo', FooElement);
+ * var myFoo = new Element.tag('x-foo');
+ * // prints 'FooElement created!' to the console.
+ * }
+ *
+ * The custom element can also be instantiated via HTML using the syntax
+ * `<x-foo></x-foo>`
+ *
+ * Other elements can be subclassed as well:
+ *
+ * class BarElement extends InputElement{
+ * void created() {
+ * print('BarElement created!');
+ * }
+ * }
+ *
+ * main() {
+ * document.register('x-bar', BarElement);
+ * var myBar = new Element.tag('input', 'x-bar');
+ * // prints 'BarElement created!' to the console.
+ * }
+ *
+ * This custom element can also be instantiated via HTML using the syntax
+ * `<input is="x-bar"></input>`
+ *
+ * The [nativeTagName] parameter is needed by platforms without native support
+ * when subclassing a native type other than:
+ *
+ * * HtmlElement
+ * * SvgElement
+ * * AnchorElement
+ * * AudioElement
+ * * ButtonElement
+ * * CanvasElement
+ * * DivElement
+ * * ImageElement
+ * * InputElement
+ * * LIElement
+ * * LabelElement
+ * * MenuElement
+ * * MeterElement
+ * * OListElement
+ * * OptionElement
+ * * OutputElement
+ * * ParagraphElement
+ * * PreElement
+ * * ProgressElement
+ * * SelectElement
+ * * SpanElement
+ * * UListElement
+ * * VideoElement
+ */
$if DART2JS
- void register(String tag, Type customElementClass) {
- _registerCustomElement(JS('', 'window'), this, tag, customElementClass);
+ void register(String tag, Type customElementClass, {String nativeTagName}) {
+ _registerCustomElement(JS('', 'window'), this, tag, customElementClass,
+ nativeTagName);
}
$else
- void register(String tag, Type custom) {
- _Utils.register(tag, custom);
+ void register(String tag, Type customElementClass, {String nativeTagName}) {
+ _Utils.register(tag, customElementClass);
}
$endif
diff --git a/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate b/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate
index 2aa82b0..fe9ae78 100644
--- a/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate
+++ b/tools/dom/templates/html/impl/impl_HTMLInputElement.darttemplate
@@ -29,7 +29,7 @@
$NATIVESPEC {
factory InputElement({String type}) {
- var e = document.$dom_createElement("input");
+ var e = document.createElement("input");
if (type != null) {
try {
// IE throws an exception for unknown types.
diff --git a/tools/dom/templates/html/impl/impl_HTMLTemplateElement.darttemplate b/tools/dom/templates/html/impl/impl_HTMLTemplateElement.darttemplate
index f54aef7..f1b7bdd 100644
--- a/tools/dom/templates/html/impl/impl_HTMLTemplateElement.darttemplate
+++ b/tools/dom/templates/html/impl/impl_HTMLTemplateElement.darttemplate
@@ -201,7 +201,7 @@
// + <td>Bar</td>
//
static Element _extractTemplateFromAttributeTemplate(Element el) {
- var template = el.document.$dom_createElement('template');
+ var template = el.document.createElement('template');
el.parentNode.insertBefore(template, el);
for (var name in el.attributes.keys.toList()) {
diff --git a/tools/dom/templates/html/impl/impl_SVGElement.darttemplate b/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
index 576854b..c9c70a7 100644
--- a/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
+++ b/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
@@ -34,7 +34,7 @@
static final _START_TAG_REGEXP = new RegExp('<(\\w+)');
factory $CLASSNAME.tag(String tag) =>
- document.$dom_createElementNS("http://www.w3.org/2000/svg", tag);
+ document.createElementNS("http://www.w3.org/2000/svg", tag);
factory $CLASSNAME.svg(String svg,
{NodeValidator validator, NodeTreeSanitizer treeSanitizer}) {
diff --git a/tools/dom/templates/immutable_list_mixin.darttemplate b/tools/dom/templates/immutable_list_mixin.darttemplate
index fc48b49..78a846f 100644
--- a/tools/dom/templates/immutable_list_mixin.darttemplate
+++ b/tools/dom/templates/immutable_list_mixin.darttemplate
@@ -17,8 +17,12 @@
$if DART2JS
return JS('$E', '#[0]', this);
$else
+$if USE_NATIVE_INDEXED_GETTER
+ return $GETTER(0);
+$else
return this[0];
$endif
+$endif
}
throw new StateError("No elements");
}
@@ -29,8 +33,12 @@
$if DART2JS
return JS('$E', '#[#]', this, len - 1);
$else
+$if USE_NATIVE_INDEXED_GETTER
+ return $GETTER(len - 1);
+$else
return this[len - 1];
$endif
+$endif
}
throw new StateError("No elements");
}
@@ -41,8 +49,12 @@
$if DART2JS
return JS('$E', '#[0]', this);
$else
+$if USE_NATIVE_INDEXED_GETTER
+ return $GETTER(0);
+$else
return this[0];
$endif
+$endif
}
if (len == 0) throw new StateError("No elements");
throw new StateError("More than one element");
diff --git a/tools/task_kill.py b/tools/task_kill.py
index 56b0de9..ec6b574 100755
--- a/tools/task_kill.py
+++ b/tools/task_kill.py
@@ -166,8 +166,7 @@
status += KillDart();
if (options.kill_browsers):
status += KillBrowsers()
- # Investigating hanging firefox, see issue 13121
- return 0
+ return status
if __name__ == '__main__':
sys.exit(Main())
diff --git a/tools/testing/dart/browser_controller.dart b/tools/testing/dart/browser_controller.dart
index c6e07e7..f370863 100644
--- a/tools/testing/dart/browser_controller.dart
+++ b/tools/testing/dart/browser_controller.dart
@@ -147,10 +147,9 @@
if (_cleanup != null) {
_cleanup();
}
- doneCompleter.complete(true);
}).catchError((error) {
_logEvent("Error closing browsers: $error");
- });
+ }).whenComplete(() => doneCompleter.complete(true));
});
return true;
}).catchError((error) {
@@ -317,34 +316,69 @@
class Chrome extends Browser {
- /**
- * The binary used to run chrome - changing this can be nececcary for
- * testing or using non standard chrome installation.
- */
- static const String binary = "google-chrome";
+ static String _binary = _getBinary();
+
+ String _version = "Version not found yet";
+
+ // This is extracted to a function since we may need to support several
+ // locations.
+ static String _getWindowsBinary() {
+ return "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
+ }
+
+ static String _getBinary() {
+ if (Platform.isWindows) return _getWindowsBinary();
+ if (Platform.isMacOS) {
+ return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
+ }
+ if (Platform.isLinux) return 'google-chrome';
+ }
+
+ Future<bool> _getVersion() {
+ if (Platform.isWindows) {
+ // The version flag does not work on windows.
+ // See issue:
+ // https://code.google.com/p/chromium/issues/detail?id=158372
+ // The registry hack does not seem to work.
+ _version = "Can't get version on windows";
+ // We still validate that the binary exists so that we can give good
+ // feedback.
+ return new File(_binary).exists().then((exists) {
+ if (!exists) {
+ _logEvent("Chrome binary not available.");
+ _logEvent("Make sure $_binary is a valid program for running chrome");
+ }
+ return exists;
+ });
+ }
+ return Process.run(_binary, ["--version"]).then((var versionResult) {
+ if (versionResult.exitCode != 0) {
+ _logEvent("Failed to chrome get version");
+ _logEvent("Make sure $_binary is a valid program for running chrome");
+ return false;
+ }
+ _version = versionResult.stdout;
+ return true;
+ });
+ }
+
Future<bool> start(String url) {
_logEvent("Starting chrome browser on: $url");
// Get the version and log that.
- return Process.run(binary, ["--version"]).then((var versionResult) {
- if (versionResult.exitCode != 0) {
- _logEvent("Failed to chrome get version");
- _logEvent("Make sure $binary is a valid program for running chrome");
- return new Future.value(false);
- }
- version = versionResult.stdout;
- _logEvent("Got version: $version");
+ return _getVersion().then((success) {
+ if (!success) return false;
+ _logEvent("Got version: $_version");
return new Directory('').createTemp().then((userDir) {
_cleanup = () { userDir.deleteSync(recursive: true); };
var args = ["--user-data-dir=${userDir.path}", url,
"--disable-extensions", "--disable-popup-blocking",
"--bwsi", "--no-first-run"];
- return startBrowser(binary, args);
-
+ return startBrowser(_binary, args);
});
}).catchError((e) {
- _logEvent("Running $binary --version failed with $e");
+ _logEvent("Running $_binary --version failed with $e");
return false;
});
}
@@ -455,12 +489,6 @@
}
class Firefox extends Browser {
- /**
- * The binary used to run firefox - changing this can be nececcary for
- * testing or using non standard firefox installation.
- */
- static const String binary = "firefox";
-
static const String enablePopUp =
'user_pref("dom.disable_open_during_load", false);';
static const String disableDefaultCheck =
@@ -468,6 +496,8 @@
static const String disableScriptTimeLimit =
'user_pref("dom.max_script_run_time", 0);';
+ static string _binary = _getBinary();
+
Future _createPreferenceFile(var path) {
var file = new File("${path.toString()}/user.js");
var randomFile = file.openSync(mode: FileMode.WRITE);
@@ -477,11 +507,21 @@
randomFile.close();
}
+ // This is extracted to a function since we may need to support several
+ // locations.
+ static String _getWindowsBinary() {
+ return "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe";
+ }
+
+ static String _getBinary() {
+ if (Platform.isWindows) return _getWindowsBinary();
+ if (Platform.isLinux) return 'firefox';
+ }
Future<bool> start(String url) {
_logEvent("Starting firefox browser on: $url");
// Get the version and log that.
- return Process.run(binary, ["--version"]).then((var versionResult) {
+ return Process.run(_binary, ["--version"]).then((var versionResult) {
if (versionResult.exitCode != 0) {
_logEvent("Failed to firefox get version");
_logEvent("Make sure $binary is a valid program for running firefox");
@@ -495,7 +535,7 @@
_cleanup = () { userDir.deleteSync(recursive: true); };
var args = ["-profile", "${userDir.path}",
"-no-remote", "-new-instance", url];
- return startBrowser(binary, args);
+ return startBrowser(_binary, args);
});
}).catchError((e) {