Version 0.2.9.0
svn merge -r 15867:16053 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@16054 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/.gitignore b/.gitignore
index 1a58486..41e6819 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@
/ipch
/out
/xcodebuild
+/.flaky.log
/*.Makefile
/*.opensdf
/*.pyc
diff --git a/compiler/java/com/google/dart/compiler/parser/DartParser.java b/compiler/java/com/google/dart/compiler/parser/DartParser.java
index f9f7700..129eb6a 100644
--- a/compiler/java/com/google/dart/compiler/parser/DartParser.java
+++ b/compiler/java/com/google/dart/compiler/parser/DartParser.java
@@ -2564,6 +2564,12 @@
reportErrorAtPosition(prevPositionStart, prevPositionEnd,
ParserErrorCode.SUPER_IS_NOT_VALID_AS_A_BOOLEAN_OPERAND);
}
+ if (token == Token.EQ_STRICT) {
+ reportError(tokenOffset, ParserErrorCode.DEPRECATED_STRICT_EQ);
+ }
+ if (token == Token.NE_STRICT) {
+ reportError(tokenOffset, ParserErrorCode.DEPRECATED_STRICT_NE);
+ }
DartExpression right;
if (token == Token.IS) {
beginTypeExpression();
@@ -3564,6 +3570,11 @@
rollback();
return null;
}
+ if (function != null && function.getReturnTypeNode() != null) {
+ reportError(function.getReturnTypeNode(), ParserErrorCode.DEPRECATED_FUNCTION_LITERAL);
+ } else if (namePtr[0] != null) {
+ reportError(namePtr[0], ParserErrorCode.DEPRECATED_FUNCTION_LITERAL);
+ }
return done(new DartFunctionExpression(namePtr[0], doneWithoutConsuming(function), false));
}
diff --git a/compiler/java/com/google/dart/compiler/parser/ParserErrorCode.java b/compiler/java/com/google/dart/compiler/parser/ParserErrorCode.java
index 48f05f5..c035768 100644
--- a/compiler/java/com/google/dart/compiler/parser/ParserErrorCode.java
+++ b/compiler/java/com/google/dart/compiler/parser/ParserErrorCode.java
@@ -34,12 +34,15 @@
DEPRECATED_CATCH("This style of catch clause has been deprecated. Please use the 'on' <type> " +
"'catch' '(' <identifier> (',' <identifier>)? ')' form."),
DEPRECATED_ABSTRACT_METHOD(ErrorSeverity.WARNING, "Modifier 'abstract' is deprecated for methods without body. Remove it."),
+ DEPRECATED_FUNCTION_LITERAL("Deprecated function literal syntax, remove return type and name"),
DEPRECATED_GETTER("The presence of parentheses after the name of the getter "
+ "has been deprecated and will soon be disallowed. Please remove the parentheses."),
DEPRECATED_INTERFACE("Deprecated declaration of the 'interface', use abstract 'class' instead"),
DEPRECATED_USE_OF_FACTORY_KEYWORD("Deprecated use of the 'factory' keyword: use 'default' instead"),
DEPRECATED_RAW_STRING("The use of '@' to prefix a raw string has been deprecated; use 'r' instead"),
DEPRECATED_RESOURCE_DIRECTIVE("The #resource directive has been deprecated and will soon be disallowed"),
+ DEPRECATED_STRICT_EQ("Deprecated use of '===', use 'identical()' or '==' instead."),
+ DEPRECATED_STRICT_NE("Deprecated use of '!==', use '!identical()' or '!=' instead."),
DEPRECATED_LIBRARY_DIRECTIVE("The '#library(url)' directive has been deprecated, use 'library name' instead"),
DEPRECATED_IMPORT_DIRECTIVE("The '#import(url)' directive has been deprecated, use 'import url' instead"),
DEPRECATED_SOURCE_DIRECTIVE("The '#source(url)' directive has been deprecated, use 'part url' instead"),
diff --git a/compiler/java/com/google/dart/compiler/resolver/Resolver.java b/compiler/java/com/google/dart/compiler/resolver/Resolver.java
index 588a000..89ed7f1 100644
--- a/compiler/java/com/google/dart/compiler/resolver/Resolver.java
+++ b/compiler/java/com/google/dart/compiler/resolver/Resolver.java
@@ -19,7 +19,6 @@
import com.google.dart.compiler.ast.DartBlock;
import com.google.dart.compiler.ast.DartBooleanLiteral;
import com.google.dart.compiler.ast.DartBreakStatement;
-import com.google.dart.compiler.ast.DartCase;
import com.google.dart.compiler.ast.DartCatchBlock;
import com.google.dart.compiler.ast.DartClass;
import com.google.dart.compiler.ast.DartComment;
@@ -768,7 +767,7 @@
break;
}
} else {
- targetConstructor = ((ClassElement) element).lookupConstructor(element.getName());
+ targetConstructor = ((ClassElement) element).lookupConstructor("");
}
Elements.setRedirectingFactoryConstructor(((ConstructorElement) member),
targetConstructor);
@@ -1132,21 +1131,6 @@
return null;
}
- @Override
- public Element visitCase(DartCase node) {
- super.visitCase(node);
- List<DartStatement> statements = node.getStatements();
- // the last statement should be: break, continue, return, throw
- if (!statements.isEmpty()) {
- DartStatement lastStatement = statements.get(statements.size() - 1);
- if (!isValidLastSwitchCaseStatement(lastStatement)) {
- onError(lastStatement, ResolverErrorCode.SWITCH_CASE_FALL_THROUGH);
- }
- }
- // done
- return null;
- }
-
private boolean isValidLastSwitchCaseStatement(DartStatement statement) {
if (statement instanceof DartExprStmt) {
DartExprStmt exprStmt = (DartExprStmt) statement;
@@ -1160,9 +1144,23 @@
@Override
public Element visitSwitchMember(DartSwitchMember x) {
- getContext().pushScope("<switch member>");
- x.visitChildren(this);
- getContext().popScope();
+ // visit children
+ {
+ getContext().pushScope("<switch member>");
+ x.visitChildren(this);
+ getContext().popScope();
+ }
+ // check fall-through
+ {
+ List<DartStatement> statements = x.getStatements();
+ // the last statement should be: break, continue, return, throw
+ if (!statements.isEmpty()) {
+ DartStatement lastStatement = statements.get(statements.size() - 1);
+ if (!isValidLastSwitchCaseStatement(lastStatement)) {
+ onError(lastStatement, ResolverErrorCode.SWITCH_CASE_FALL_THROUGH);
+ }
+ }
+ }
return null;
}
diff --git a/compiler/java/com/google/dart/compiler/resolver/ResolverErrorCode.java b/compiler/java/com/google/dart/compiler/resolver/ResolverErrorCode.java
index ebe4554..c2cefc9 100644
--- a/compiler/java/com/google/dart/compiler/resolver/ResolverErrorCode.java
+++ b/compiler/java/com/google/dart/compiler/resolver/ResolverErrorCode.java
@@ -24,7 +24,7 @@
CANNOT_ASSIGN_TO_METHOD(ErrorSeverity.WARNING, "cannot assign value to method '%s'."),
CANNOT_BE_RESOLVED("cannot resolve %s"),
// TODO(zundel): error message needs JUnit test - how to test #imports in junit?
- CANNOT_BE_RESOLVED_LIBRARY("cannot resolve %s in library %s"),
+ CANNOT_BE_RESOLVED_LIBRARY(ErrorSeverity.WARNING, "cannot resolve %s in library %s"),
CANNOT_CALL_FUNCTION_TYPE_ALIAS("Function type aliases cannot be called"),
// TODO(zundel): error message needs JUnit test - how to test #imports in junit?
CANNOT_CALL_LIBRARY_PREFIX("Library prefixes cannot be called"),
diff --git a/compiler/java/com/google/dart/compiler/resolver/TypeErrorCode.java b/compiler/java/com/google/dart/compiler/resolver/TypeErrorCode.java
index 36c182d..0f360ac 100644
--- a/compiler/java/com/google/dart/compiler/resolver/TypeErrorCode.java
+++ b/compiler/java/com/google/dart/compiler/resolver/TypeErrorCode.java
@@ -62,6 +62,8 @@
OPERATOR_WRONG_OPERAND_TYPE("operand of \"%s\" must be assignable to \"%s\", found \"%s\""),
OVERRIDING_STATIC_MEMBER("overriding static member \"%s\" of \"%s\""),
PLUS_CANNOT_BE_USED_FOR_STRING_CONCAT("'%s' cannot be used for string concatentation, use string interpolation or a StringBuffer instead"),
+ REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_SUBTYPE(
+ "Target type of redirecting factory constructor '%s' is not subtype of '%s'"),
SETTER_RETURN_TYPE("Specified return type of setter '%s' is non-void"),
SETTER_TYPE_MUST_BE_ASSIGNABLE("Setter type '%s' must be assignable to getter type '%s'"),
STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE(
diff --git a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
index e1790bf..1f2e102 100644
--- a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
+++ b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java
@@ -2096,7 +2096,7 @@
DartTypeNode returnTypeNode = node.getFunction().getReturnTypeNode();
if (modifiers.isFactory()
&& ElementKind.of(methodElement).equals(ElementKind.CONSTRUCTOR)) {
- analyzeFactory(node.getName(), (ConstructorElement) methodElement);
+ analyzeFactory(node, node.getName(), (ConstructorElement) methodElement);
} else if (modifiers.isSetter()) {
if (returnTypeNode != null && returnTypeNode.getType() != voidType) {
typeError(returnTypeNode, TypeErrorCode.SETTER_RETURN_TYPE, methodElement.getName());
@@ -2187,7 +2187,8 @@
return superTypes;
}
- private void analyzeFactory(DartExpression name, final ConstructorElement methodElement) {
+ private void analyzeFactory(DartMethodDefinition node, DartExpression name,
+ final ConstructorElement methodElement) {
ASTVisitor<Void> visitor = new ASTVisitor<Void>() {
@Override
public Void visitParameterizedTypeNode(DartParameterizedTypeNode node) {
@@ -2207,6 +2208,25 @@
}
};
name.accept(visitor);
+ // redirecting factory constructor
+ if (methodElement instanceof ConstructorElement) {
+ ConstructorElement constructorElement = (ConstructorElement) methodElement;
+ ConstructorElement targetElement = constructorElement.getRedirectingFactoryConstructor();
+ if (targetElement != null) {
+ ClassElement targetEnclosingClass = (ClassElement) targetElement.getEnclosingElement();
+ Type sourceMethodType = methodElement.getType();
+ Type targetMethodType = targetElement.getType();
+ InterfaceType targetClassType = (InterfaceType) node.getRedirectedTypeName().getType();
+ targetMethodType = targetMethodType.subst(targetClassType.getArguments(),
+ targetEnclosingClass.getTypeParameters());
+ if (!types.isSubtype(targetMethodType, sourceMethodType)) {
+ DartNode errorNode = node.getRedirectedConstructorName() != null
+ ? node.getRedirectedConstructorName() : node.getRedirectedTypeName();
+ typeError(errorNode, TypeErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_SUBTYPE,
+ targetMethodType, sourceMethodType);
+ }
+ }
+ }
}
@Override
diff --git a/compiler/java/com/google/dart/compiler/util/DartSourceString.java b/compiler/java/com/google/dart/compiler/util/DartSourceString.java
index 9bc3c0b..407d18b 100644
--- a/compiler/java/com/google/dart/compiler/util/DartSourceString.java
+++ b/compiler/java/com/google/dart/compiler/util/DartSourceString.java
@@ -79,7 +79,7 @@
@Override
public URI getUri() {
try {
- return new URI(null, null, getName(), null).normalize();
+ return new URI(null, null, getName(), null, null).normalize();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
diff --git a/compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart b/compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart
index 9535c88..7faaeac 100644
--- a/compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart
+++ b/compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart
@@ -77,20 +77,12 @@
id(x) { return x; }
int id(x) { return x; }
Box<int, double> id(x) { return x; }
- var f = hest() { };
- var f = int horse() { };
- var f = Box<int> llama() { };
- var f = Box<int, double> llama() { };
+ var f = () { };
assert(x == 12);
- id(void foo(x) {});
- id(foo(x) {});
- id(Box<int> foo(x) {});
- id(Box<int, double> foo(x) {});
- id(Box<int, double> foo);
+ id((x) {});
a < b;
int x = a < b;
id(() {});
- id(void _() {});
}
}
diff --git a/compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java b/compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java
index 65a57e1..e1c94ca 100644
--- a/compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java
+++ b/compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java
@@ -25,6 +25,24 @@
parseExpectErrors("get foo() {}", errEx(ParserErrorCode.DEPRECATED_GETTER, 1, 5, 3));
}
+ public void test_deprecatedStrictEQ() {
+ parseExpectErrors(makeCode(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "main() {",
+ " 1 === 2;",
+ "}",
+ ""), errEx(ParserErrorCode.DEPRECATED_STRICT_EQ, 3, 5, 3));
+ }
+
+ public void test_deprecatedStrictNE() {
+ parseExpectErrors(makeCode(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "main() {",
+ " 1 !== 2;",
+ "}",
+ ""), errEx(ParserErrorCode.DEPRECATED_STRICT_NE, 3, 5, 3));
+ }
+
public void test_deprecatedAbstract() {
parseExpectWarnings(makeCode(
"// filler filler filler filler filler filler filler filler filler filler",
@@ -41,6 +59,19 @@
errEx(ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF, 1, 30, 7));
}
+ public void test_deprecatedFunctionLiteral() {
+ parseExpectWarnings(
+ makeCode(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "main() {",
+ " var x = f() => 42;",
+ " var y = int g() => 87;",
+ "}",
+ ""),
+ errEx(ParserErrorCode.DEPRECATED_FUNCTION_LITERAL, 3, 11, 1),
+ errEx(ParserErrorCode.DEPRECATED_FUNCTION_LITERAL, 4, 11, 3));
+ }
+
public void testFieldInitializerInRedirectionConstructor2() {
parseExpectErrors(
"class A { A(x) { } A.foo() : y = 5, this(5); var y; }",
@@ -515,28 +546,6 @@
errEx(ParserErrorCode.NO_SPACE_AFTER_PLUS, 7, 9, 1));
}
- /**
- * Separate test for invocation of function literal which has both return type and name.
- */
- public void test_invokeFunctionLiteral_returnType_name() {
- DartParserRunner parserRunner =
- parseExpectErrors(Joiner.on("\n").join(
- "// filler filler filler filler filler filler filler filler filler filler",
- "topLevelFunctionWithVeryLongNameToForceLineWrapping() {",
- " int f(p){}(0);", // invocation of function literal in statement, has type and name
- "}",
- ""));
- assertEquals(
- makeCode(
- "// unit " + getName(),
- "",
- "topLevelFunctionWithVeryLongNameToForceLineWrapping() {",
- " int f(p) {",
- " }(0);",
- "}"),
- parserRunner.getDartUnit().toSource());
- }
-
public void test_formalParameters_const() throws Exception {
parseExpectErrors(
Joiner.on("\n").join(
@@ -558,7 +567,6 @@
" int f1(p){}", // declaration of function as statement, has type
" var res = (p){}(1);", // invocation of function literal in assignment
" (p){}(2);", // invocation of function literal in statement, no name
- " f2(p){}(3);", // invocation of function literal in statement, has name
" f3(p) => 4;", // function with => arrow ends with ';'
" (5);", // this is separate statement, not invocation of previous function
" join(promises, (p) => 6);", // function with => arrow as argument
@@ -578,8 +586,6 @@
" }(1);",
" (p) {",
" }(2);",
- " f2(p) {",
- " }(3);",
" f3(p) {",
" return 4;",
" };",
diff --git a/compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java b/compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java
index 2e908c1..5f90a79 100644
--- a/compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java
+++ b/compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java
@@ -1010,12 +1010,12 @@
" }",
" }",
" L: switch(1) {",
- " case 1: var result = f() { continue L; };", // bad
+ " case 1: var result = () { continue L; };", // bad
" }",
" }",
"}"),
ParserErrorCode.CONTINUE_IN_CASE_MUST_HAVE_LABEL, 4, 23,
- ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, 12, 42);
+ ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, 12, 41);
}
public void testRedundantAbruptlyTermainatedCaseStatement() throws Exception {
diff --git a/compiler/javatests/com/google/dart/compiler/resolver/CompileTimeConstantTest.java b/compiler/javatests/com/google/dart/compiler/resolver/CompileTimeConstantTest.java
index 5c270db..a86b588d 100644
--- a/compiler/javatests/com/google/dart/compiler/resolver/CompileTimeConstantTest.java
+++ b/compiler/javatests/com/google/dart/compiler/resolver/CompileTimeConstantTest.java
@@ -236,12 +236,6 @@
errEx(ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION, 3, 26,2),
errEx(ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION, 4, 19, 12));
}
-
- public void test_expressionsWithNull() {
- resolveAndTestCtConst(Joiner.on("\n").join(
- "class Object {}",
- "var b = null === '';"));
- }
public void test_parameterDefaultValue_inLocalFunction() {
resolveAndTestCtConstExpectErrors(
@@ -541,8 +535,6 @@
" static const BOP7 = false || BOOL_LIT;",
" static const BOP8 = STRING_LIT == 'World!';",
" static const BOP9 = 'Hello' != STRING_LIT;",
- " static const BOP10 = INT_LIT === INT_LIT_REF;",
- " static const BOP11 = BOOL_LIT !== true;",
"}"));
}
@@ -625,32 +617,29 @@
}
public void testConstantBinaryExpression9() {
- resolveAndTestCtConst(Joiner.on("\n").join(
- "class Object {}",
- "class bool {}",
- "class int {}",
- "class double {}",
- "class num {}",
- "class A {",
- " static Object foo() { return true; }",
- "}",
- "class B {",
- " const B();",
- " static const OBJECT_LIT = const B();",
- " static const INT_LIT = 1;",
- " static const STRING_LIT = 'true';",
- " static const BOP1 = STRING_LIT && true;",
- " static const BOP2 = false || STRING_LIT;",
- " static const BOP3 = 59 == OBJECT_LIT;",
- " static const BOP4 = OBJECT_LIT != 59;",
- " static const BOP5 = INT_LIT === OBJECT_LIT;",
- " static const BOP6 = OBJECT_LIT !== true;",
- "}"),
+ resolveAndTestCtConst(
+ Joiner.on("\n").join(
+ "class Object {}",
+ "class bool {}",
+ "class int {}",
+ "class double {}",
+ "class num {}",
+ "class A {",
+ " static Object foo() { return true; }",
+ "}",
+ "class B {",
+ " const B();",
+ " static const OBJECT_LIT = const B();",
+ " static const INT_LIT = 1;",
+ " static const STRING_LIT = 'true';",
+ " static const BOP1 = STRING_LIT && true;",
+ " static const BOP2 = false || STRING_LIT;",
+ " static const BOP3 = 59 == OBJECT_LIT;",
+ " static const BOP4 = OBJECT_LIT != 59;",
+ "}"),
ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_BOOLEAN,
ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_BOOLEAN,
ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL,
- ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL,
- ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL,
ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL);
}
diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java
index dc76e65..bcad2fa 100644
--- a/compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java
+++ b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java
@@ -1162,8 +1162,8 @@
public void testUndercoreInNamedParameterFunctionDefinition() {
resolveAndTest(Joiner.on("\n").join(
"class Object {}",
- "var f = func({_foo}) {};"),
- errEx(ResolverErrorCode.NAMED_PARAMETERS_CANNOT_START_WITH_UNDER, 2, 15, 4));
+ "func({_foo}) {}"),
+ errEx(ResolverErrorCode.NAMED_PARAMETERS_CANNOT_START_WITH_UNDER, 2, 7, 4));
}
public void testUndercoreInNamedParameterFunctionAlias() {
diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerCompilerTest.java b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerCompilerTest.java
index 910e9d1..9f8cd3c 100644
--- a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerCompilerTest.java
+++ b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerCompilerTest.java
@@ -470,6 +470,8 @@
" throw new Exception();",
" case 4:",
" bar();",
+ " default:",
+ " bar();",
" }",
" }",
"}",
@@ -477,7 +479,8 @@
"");
assertErrors(
libraryResult.getErrors(),
- errEx(ResolverErrorCode.SWITCH_CASE_FALL_THROUGH, 14, 9, 6));
+ errEx(ResolverErrorCode.SWITCH_CASE_FALL_THROUGH, 14, 9, 6),
+ errEx(ResolverErrorCode.SWITCH_CASE_FALL_THROUGH, 16, 9, 6));
}
/**
@@ -1146,14 +1149,19 @@
" assert('message');", // not 'bool'
" assert('null');", // not 'bool'
" assert(0);", // not 'bool'
- " assert(f() {});", // OK, dynamic
- " assert(bool f() {});", // OK, '() -> bool'
- " assert(Object f() {});", // OK, 'Object' compatible with 'bool'
- " assert(String f() {});", // not '() -> bool', return type
- " assert(bool f(x) {});", // not '() -> bool', parameter
+ " assert(f1);", // OK, dynamic
+ " assert(f2);", // OK, '() -> bool'
+ " assert(f3);", // OK, 'Object' compatible with 'bool'
+ " assert(f4);", // not '() -> bool', return type
+ " assert(f5);", // not '() -> bool', parameter
" assert(true, false);", // not single argument
" assert;", // incomplete
"}",
+ "f1() {}",
+ "bool f2() {}",
+ "Object f3() {}",
+ "String f4() {}",
+ "bool f5(x) {}",
"");
assertErrors(
libraryResult.getErrors(),
@@ -1162,8 +1170,8 @@
errEx(TypeErrorCode.ASSERT_BOOL, 5, 10, 9),
errEx(TypeErrorCode.ASSERT_BOOL, 6, 10, 6),
errEx(TypeErrorCode.ASSERT_BOOL, 7, 10, 1),
- errEx(TypeErrorCode.ASSERT_BOOL, 11, 10, 13),
- errEx(TypeErrorCode.ASSERT_BOOL, 12, 10, 12));
+ errEx(TypeErrorCode.ASSERT_BOOL, 11, 10, 2),
+ errEx(TypeErrorCode.ASSERT_BOOL, 12, 10, 2));
}
/**
@@ -3358,6 +3366,7 @@
" switch (true) {",
" default:",
" int v = foo;",
+ " break;",
" }",
"}",
"");
@@ -3885,42 +3894,43 @@
public void test_assignMethod() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"// filler filler filler filler filler filler filler filler filler filler",
- "class C {" +
+ "class C {",
" method() { }",
"}",
"main () {",
- " new C().method = _() {};",
- "}");
- assertErrors(
- libraryResult.getErrors(),
- errEx(TypeErrorCode.CANNOT_ASSIGN_TO, 5, 3, 14));
+ " new C().method = f;",
+ "}",
+ "f() {}",
+ "");
+ assertErrors(libraryResult.getErrors(), errEx(TypeErrorCode.CANNOT_ASSIGN_TO, 6, 3, 14));
}
public void test_assignSetter() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"// filler filler filler filler filler filler filler filler filler filler",
- "class C {" +
+ "class C {",
" set method(arg) { }",
"}",
"main () {",
- " new C().method = _() {};",
- "}");
- assertErrors(
- libraryResult.getErrors());
+ " new C().method = f;",
+ "}",
+ "f() {}",
+ "");
+ assertErrors(libraryResult.getErrors());
}
public void test_assignGetter() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"// filler filler filler filler filler filler filler filler filler filler",
- "class C {" +
+ "class C {",
" get method { }",
"}",
"main () {",
- " new C().method = _() {};",
- "}");
- assertErrors(
- libraryResult.getErrors(),
- errEx(TypeErrorCode.FIELD_HAS_NO_SETTER, 5, 11, 6));
+ " new C().method = f;",
+ "}",
+ "f() {}",
+ "");
+ assertErrors(libraryResult.getErrors(), errEx(TypeErrorCode.FIELD_HAS_NO_SETTER, 6, 11, 6));
}
/**
@@ -4590,7 +4600,7 @@
assertNotNull(access.getElement());
}
}
-
+
/**
* <p>
* http://code.google.com/p/dart/issues/detail?id=4315
@@ -4620,6 +4630,22 @@
}
/**
+ * Test that we already support cascaded calls in initializers. No implementation was changed.
+ * <p>
+ * http://code.google.com/p/dart/issues/detail?id=6955
+ */
+ public void test_cascade_inInitializer() throws Exception {
+ AnalyzeLibraryResult libraryResult = analyzeLibrary(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "class A {",
+ " var f;",
+ " A() : f = ''..length;",
+ "}",
+ "");
+ assertErrors(libraryResult.getErrors());
+ }
+
+ /**
* Source is invalid, but should not cause {@link NullPointerException}.
* <p>
* http://code.google.com/p/dart/issues/detail?id=4354
@@ -5041,7 +5067,7 @@
errEx(ResolverErrorCode.REDIRECTION_CONSTRUCTOR_CYCLE, 6, 11, 7),
errEx(ResolverErrorCode.REDIRECTION_CONSTRUCTOR_CYCLE, 9, 11, 7));
}
-
+
public void test_redirectingFactoryConstructor_notConst_fromConst() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"// filler filler filler filler filler filler filler filler filler filler",
@@ -5058,6 +5084,78 @@
errEx(ResolverErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_CONST, 7, 29, 5));
}
+ public void test_redirectingFactoryConstructor_shouldBeSubType() throws Exception {
+ AnalyzeLibraryResult libraryResult = analyzeLibrary(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "class A {",
+ " A() {}",
+ " A.named(p0) {}",
+ "}",
+ "",
+ "class B {",
+ " factory B.foo(p0) = A;",
+ " factory B.bar() = A.named;",
+ "}",
+ "");
+ assertErrors(
+ libraryResult.getErrors(),
+ errEx(TypeErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_SUBTYPE, 8, 23, 1),
+ errEx(TypeErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_SUBTYPE, 9, 23, 5));
+ }
+
+ public void test_redirectingFactoryConstructor_shouldBeSubType2() throws Exception {
+ AnalyzeLibraryResult libraryResult = analyzeLibrary(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "class A<T> {",
+ " A.named(T t) {}",
+ "}",
+ "",
+ "class B<T> {",
+ " factory B.foo(T t) = A<T>.named;",
+ "}",
+ "class B2<T, V> {",
+ " factory B2.foo(V v) = A<V>.named;",
+ "}",
+ "");
+ assertErrors(libraryResult.getErrors());
+ }
+
+ public void test_redirectingFactoryConstructor_shouldBeSubType3() throws Exception {
+ AnalyzeLibraryResult libraryResult = analyzeLibrary(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "class A<T> {",
+ " A(A<T> p) {}",
+ "}",
+ "",
+ "class B<T> {",
+ " factory B.foo(A<T> p) = A;",
+ "}",
+ "");
+ assertErrors(libraryResult.getErrors());
+ }
+
+ public void test_redirectingFactoryConstructor_notSubType_typeParameterBounds() throws Exception {
+ AnalyzeLibraryResult libraryResult = analyzeLibrary(
+ "// filler filler filler filler filler filler filler filler filler filler",
+ "class A1<T extends String> {",
+ " A1() {}",
+ "}",
+ "class A2<T> {",
+ " A2() {}",
+ "}",
+ "",
+ "class B {",
+ " factory B.name1() = A1<String>;",
+ " factory B.name2() = A1<int>;",
+ " factory B.name3() = A2<String>;",
+ " factory B.name4() = A2<int>;",
+ "}",
+ "");
+ assertErrors(
+ libraryResult.getErrors(),
+ errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 11, 26, 3));
+ }
+
/**
* <p>
* http://code.google.com/p/dart/issues/detail?id=4778
@@ -5831,11 +5929,11 @@
"// filler filler filler filler filler filler filler filler filler filler",
"class A {",
" var x;",
- " B() : x = (foo() { }) {}",
+ " B() : x = 0 {}",
"}",
"");
assertErrors(result.getErrors(),
- errEx(ResolverErrorCode.INITIALIZER_ONLY_IN_GENERATIVE_CONSTRUCTOR, 4, 9, 15));
+ errEx(ResolverErrorCode.INITIALIZER_ONLY_IN_GENERATIVE_CONSTRUCTOR, 4, 9, 5));
}
public void test_variableReferencesItselfInInitializer() throws Exception {
diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java
index 5889eb0..8f67edc 100644
--- a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java
+++ b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java
@@ -123,9 +123,7 @@
}
EnumSet<Token> equalityOperators = EnumSet.of(Token.EQ,
- Token.NE,
- Token.EQ_STRICT,
- Token.NE_STRICT);
+ Token.NE);
for (Token op : equalityOperators) {
String expression;
expression = String.format("untyped %s untyped", op.getSyntax());
@@ -420,16 +418,9 @@
}
public void testFunctionObjectLiterals() {
- analyze("{ bool b = foo() {}(); }");
- analyze("{ int i = foo() {}(); }");
- analyze("{ bool b = bool foo() { return null; }(); }");
- analyze("{ int i = int foo() { return null; }(); }");
- analyzeFail("{ int i = bool foo() { return null; }(); }",
- TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
- analyze("{ int i = Object _(Object x) { return x; }('fisk'); }");
- analyzeFail("{ int i = String _(Object x) { return x; }(1); }",
- TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
- analyze("Function f = foo() {};");
+ analyze("{ bool b = () {}(); }");
+ analyze("{ int i = () {}(); }");
+ analyze("Function f = () {};");
}
public void testFunctionTypeAlias() {
@@ -475,8 +466,8 @@
analyzeIn(foo, "voidFunction = stringFunction", 0);
analyzeIn(foo, "stringFunction = intToStringFunction", 1);
- analyzeIn(foo, "stringFunction = String foo() { return ''; }", 0);
- analyzeIn(foo, "intToStringFunction = String foo() { return ''; }", 1);
+ analyzeIn(foo, "stringFunction = () { return ''; }", 0);
+ analyzeIn(foo, "intToStringFunction = () { return ''; }", 1);
}
public void testFunctionTypes() {
@@ -748,7 +739,7 @@
public void testNamedFunctionTypeAlias() {
loadFile("named_function_type_alias.dart");
- analyze("VoidFunction f = foo() {};");
+ analyze("VoidFunction f = () {};");
}
public void testOddStuff() {
@@ -978,13 +969,13 @@
analyzeIn(cls, "tField = ''", 1);
analyzeIn(cls, "tField = true", 1);
- analyzeIn(cls, "foo() { A a = null; T t = a; }()", 0);
- analyzeIn(cls, "foo() { B b = null; T t = b; }()", 0);
- analyzeIn(cls, "foo() { T t = null; A a = t; }()", 0);
- analyzeIn(cls, "foo() { T t = null; B b = t; }()", 0);
- analyzeIn(cls, "foo() { T t = 1; }()", 1);
- analyzeIn(cls, "foo() { T t = ''; }()", 1);
- analyzeIn(cls, "foo() { T t = true; }()", 1);
+ analyzeIn(cls, "() { A a = null; T t = a; }()", 0);
+ analyzeIn(cls, "() { B b = null; T t = b; }()", 0);
+ analyzeIn(cls, "() { T t = null; A a = t; }()", 0);
+ analyzeIn(cls, "() { T t = null; B b = t; }()", 0);
+ analyzeIn(cls, "() { T t = 1; }()", 1);
+ analyzeIn(cls, "() { T t = ''; }()", 1);
+ analyzeIn(cls, "() { T t = true; }()", 1);
}
public void testUnaryOperators() {
@@ -1085,11 +1076,11 @@
checkAssignIn(element, "int", "intField('x')", 1);
checkAssignIn(element, "String", "intField(2.2)", 1);
- analyzeIn(element, "f(x) { x(); }", 0);
- analyzeIn(element, "f(int x) { x(); }", 1);
- analyzeIn(element, "f(int x()) { int i = x(); }", 0);
- analyzeIn(element, "f(int x(String s)) { int i = x(1); }", 1);
- analyzeIn(element, "f(int x(String s)) { int i = x(''); }", 0);
+ analyzeIn(element, "(x) { x(); }", 0);
+ analyzeIn(element, "(int x) { x(); }", 1);
+ analyzeIn(element, "(int x()) { int i = x(); }", 0);
+ analyzeIn(element, "(int x(String s)) { int i = x(1); }", 1);
+ analyzeIn(element, "(int x(String s)) { int i = x(''); }", 0);
}
public void testUnqualifiedGeneric() {
diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTestCase.java b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTestCase.java
index e859745..ab903c7 100644
--- a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTestCase.java
+++ b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTestCase.java
@@ -211,7 +211,7 @@
}
private String assign(String type, String expression) {
- return String.format("void foo() { %s x = %s; }", type, expression);
+ return String.format("() { %s x = %s; }", type, expression);
}
protected Type checkAssignIn(ClassElement element, String type, String expression, int errorCount) {
diff --git a/pkg/http/lib/src/base_request.dart b/pkg/http/lib/src/base_request.dart
index 4a8b5ce..eb36703 100644
--- a/pkg/http/lib/src/base_request.dart
+++ b/pkg/http/lib/src/base_request.dart
@@ -125,4 +125,6 @@
if (!finalized) return;
throw new StateError("Can't modify a finalized Request.");
}
+
+ String toString() => "$method $url";
}
diff --git a/pkg/http/test/request_test.dart b/pkg/http/test/request_test.dart
index e26ee75..1b3d4ca 100644
--- a/pkg/http/test/request_test.dart
+++ b/pkg/http/test/request_test.dart
@@ -387,5 +387,12 @@
expect(request.finalize, throwsStateError);
});
});
+
+ group('#toString()', () {
+ test('includes the method and URL', () {
+ var request = new http.Request('POST', dummyUrl);
+ expect(request.toString(), 'POST $dummyUrl');
+ });
+ });
}
diff --git a/pkg/http/test/utils.dart b/pkg/http/test/utils.dart
index 9e0d010..3548df0 100644
--- a/pkg/http/test/utils.dart
+++ b/pkg/http/test/utils.dart
@@ -29,7 +29,7 @@
(request, response) {
response.statusCode = 400;
response.contentLength = 0;
- closeResponse(request, response);
+ response.outputStream.close();
});
_server.addRequestHandler((request) => request.path == '/loop',
@@ -39,7 +39,7 @@
response.headers.set('location',
serverUrl.resolve('/loop?${n + 1}').toString());
response.contentLength = 0;
- closeResponse(request, response);
+ response.outputStream.close();
});
_server.addRequestHandler((request) => request.path == '/redirect',
@@ -47,7 +47,7 @@
response.statusCode = 302;
response.headers.set('location', serverUrl.resolve('/').toString());
response.contentLength = 0;
- closeResponse(request, response);
+ response.outputStream.close();
});
_server.defaultRequestHandler = (request, response) {
@@ -104,15 +104,6 @@
_server = null;
}
-/// Closes [response] while ignoring the body of [request].
-///
-/// Due to issue 6984, it's necessary to drain the request body before closing
-/// the response.
-void closeResponse(HttpRequest request, HttpResponse response) {
- request.inputStream.onData = request.inputStream.read;
- request.inputStream.onClosed = response.outputStream.close;
-}
-
/// A matcher that matches JSON that parses to a value that matches the inner
/// matcher.
Matcher parse(matcher) => new _Parse(matcher);
diff --git a/pkg/intl/test/date_time_format_file_test_stub.dart b/pkg/intl/test/date_time_format_file_test_stub.dart
index 18b596c..6306bfb 100644
--- a/pkg/intl/test/date_time_format_file_test_stub.dart
+++ b/pkg/intl/test/date_time_format_file_test_stub.dart
@@ -6,7 +6,6 @@
* Tests date formatting and parsing using locale data read from the
* local file system.
*/
-
library date_time_format_file_test;
import '../lib/intl.dart';
@@ -25,11 +24,13 @@
}
void runEverything(Function getSubset) {
- // Initialize all locales and wait for them to finish before running tests.
- var futures = DateFormat.allLocalesWithSymbols().map(
+ // Initialize all locales sequentially before running tests. Be sure not
+ // to do it in parallel or we can run into ulimit problems on fast machines.
+ var futureList = Futures.forEach(DateFormat.allLocalesWithSymbols(),
(locale) => initializeDateFormatting(locale, dataDirectory));
+
test('Run all date formatting tests nested test', () {
- Futures.wait(futures).then(
+ futureList.then(
expectAsync1((results) => runDateTests(getSubset())));
});
}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index ca3ea25..a7383e2 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -34,9 +34,6 @@
intl/test/find_default_locale_browser_test: Skip
intl/test/date_time_format_http_request_test: Skip
-[ $runtime == vm && $system == windows ]
-http/test/http_test: Pass, Fail # http://dartbug.com/6967
-
# Skip http request tests on Dartium while resolving an odd
# error there that causes the tests to timeout.
[ $runtime == dartium || $runtime == drt ]
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc
index ca6c601..4d8c9fa 100644
--- a/runtime/bin/directory_win.cc
+++ b/runtime/bin/directory_win.cc
@@ -9,50 +9,28 @@
#include "bin/log.h"
-static int SetOsErrorMessage(char* os_error_message,
- int os_error_message_len) {
- int error_code = GetLastError();
- DWORD message_size =
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error_code,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- os_error_message,
- os_error_message_len,
- NULL);
- if (message_size == 0) {
- if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
- Log::PrintErr("FormatMessage failed %d\n", GetLastError());
- }
- snprintf(os_error_message, os_error_message_len, "OS Error %d", error_code);
- }
- os_error_message[os_error_message_len - 1] = '\0';
- return error_code;
-}
-
-
// Forward declaration.
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(const wchar_t* dir_name,
bool recursive,
DirectoryListing* listing);
-static bool DeleteRecursively(const char* dir_name);
+static bool DeleteRecursively(const wchar_t* dir_name);
-static bool HandleDir(char* dir_name,
- char* path,
+static bool HandleDir(wchar_t* dir_name,
+ wchar_t* path,
int path_length,
bool recursive,
DirectoryListing* listing) {
- if (strcmp(dir_name, ".") != 0 &&
- strcmp(dir_name, "..") != 0) {
- size_t written = snprintf(path + path_length,
- MAX_PATH - path_length,
- "%s",
- dir_name);
- if (written != strlen(dir_name)) {
+ if (wcscmp(dir_name, L".") != 0 &&
+ wcscmp(dir_name, L"..") != 0) {
+ size_t written = _snwprintf(path + path_length,
+ MAX_PATH - path_length,
+ L"%s",
+ dir_name);
+ if (written != wcslen(dir_name)) {
return false;
}
- char* utf8_path = StringUtils::SystemStringToUtf8(path);
+ char* utf8_path = StringUtils::WideToUtf8(path);
bool ok = listing->HandleDirectory(utf8_path);
free(utf8_path);
if (!ok) return ok;
@@ -64,26 +42,26 @@
}
-static bool HandleFile(char* file_name,
- char* path,
+static bool HandleFile(wchar_t* file_name,
+ wchar_t* path,
int path_length,
DirectoryListing* listing) {
- size_t written = snprintf(path + path_length,
- MAX_PATH - path_length,
- "%s",
- file_name);
- if (written != strlen(file_name)) {
+ size_t written = _snwprintf(path + path_length,
+ MAX_PATH - path_length,
+ L"%s",
+ file_name);
+ if (written != wcslen(file_name)) {
return false;
};
- char* utf8_path = StringUtils::SystemStringToUtf8(path);
+ char* utf8_path = StringUtils::WideToUtf8(path);
bool ok = listing->HandleFile(utf8_path);
free(utf8_path);
return ok;
}
-static bool HandleEntry(LPWIN32_FIND_DATA find_file_data,
- char* path,
+static bool HandleEntry(LPWIN32_FIND_DATAW find_file_data,
+ wchar_t* path,
int path_length,
bool recursive,
DirectoryListing* listing) {
@@ -102,24 +80,24 @@
// ComputeFullSearchPath must be called with a path array of size at
// least MAX_PATH.
-static bool ComputeFullSearchPath(const char* dir_name,
- char* path,
+static bool ComputeFullSearchPath(const wchar_t* dir_name,
+ wchar_t* path,
int* path_length) {
// GetFullPathName only works in a multi-threaded environment if
// SetCurrentDirectory is not used. We currently have no plan for
// exposing SetCurrentDirectory.
- size_t written = GetFullPathName(dir_name, MAX_PATH, path, NULL);
+ size_t written = GetFullPathNameW(dir_name, MAX_PATH, path, NULL);
// GetFullPathName only accepts input strings of size less than
// MAX_PATH and returns 0 to indicate failure for paths longer than
// that. Therefore the path buffer is always big enough.
- if (written == 0) {
+ if (written == 0 || written > MAX_PATH) {
return false;
}
*path_length = written;
- written = snprintf(path + *path_length,
- MAX_PATH - *path_length,
- "%s",
- "\\*");
+ written = _snwprintf(path + *path_length,
+ MAX_PATH - *path_length,
+ L"%s",
+ L"\\*");
if (written != 2) {
return false;
}
@@ -128,14 +106,14 @@
}
static void PostError(DirectoryListing* listing,
- const char* dir_name) {
- const char* utf8_path = StringUtils::SystemStringToUtf8(dir_name);
+ const wchar_t* dir_name) {
+ const char* utf8_path = StringUtils::WideToUtf8(dir_name);
listing->HandleError(utf8_path);
free(const_cast<char*>(utf8_path));
}
-static bool ListRecursively(const char* dir_name,
+static bool ListRecursively(const wchar_t* dir_name,
bool recursive,
DirectoryListing* listing) {
// Compute full path for the directory currently being listed. The
@@ -143,7 +121,7 @@
// recursive traversal. path_length does not always equal
// strlen(path) but indicates the current prefix of path that is the
// path of the current directory in the traversal.
- char* path = static_cast<char*>(malloc(MAX_PATH));
+ wchar_t* path = static_cast<wchar_t*>(malloc(MAX_PATH * sizeof(wchar_t)));
int path_length = 0;
bool valid = ComputeFullSearchPath(dir_name, path, &path_length);
if (!valid) {
@@ -152,8 +130,8 @@
return false;
}
- WIN32_FIND_DATA find_file_data;
- HANDLE find_handle = FindFirstFile(path, &find_file_data);
+ WIN32_FIND_DATAW find_file_data;
+ HANDLE find_handle = FindFirstFileW(path, &find_file_data);
// Adjust the path by removing the '*' used for the search.
path_length -= 1;
@@ -171,7 +149,7 @@
recursive,
listing);
- while ((FindNextFile(find_handle, &find_file_data) != 0)) {
+ while ((FindNextFileW(find_handle, &find_file_data) != 0)) {
success = HandleEntry(&find_file_data,
path,
path_length,
@@ -194,18 +172,18 @@
}
-static bool DeleteFile(char* file_name,
- char* path,
+static bool DeleteFile(wchar_t* file_name,
+ wchar_t* path,
int path_length) {
- size_t written = snprintf(path + path_length,
- MAX_PATH - path_length,
- "%s",
- file_name);
- if (written != strlen(file_name)) {
+ size_t written = _snwprintf(path + path_length,
+ MAX_PATH - path_length,
+ L"%s",
+ file_name);
+ if (written != wcslen(file_name)) {
return false;
}
- if (DeleteFile(path) != 0) {
+ if (DeleteFileW(path) != 0) {
return true;
}
@@ -213,7 +191,7 @@
// again. This mirrors Linux/Mac where a directory containing read-only files
// can still be recursively deleted.
if (GetLastError() == ERROR_ACCESS_DENIED) {
- DWORD attributes = GetFileAttributes(path);
+ DWORD attributes = GetFileAttributesW(path);
if (attributes == INVALID_FILE_ATTRIBUTES) {
return false;
}
@@ -221,11 +199,11 @@
if ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) {
attributes &= ~FILE_ATTRIBUTE_READONLY;
- if (SetFileAttributes(path, attributes) == 0) {
+ if (SetFileAttributesW(path, attributes) == 0) {
return false;
}
- return DeleteFile(path) != 0;
+ return DeleteFileW(path) != 0;
}
}
@@ -233,16 +211,16 @@
}
-static bool DeleteDir(char* dir_name,
- char* path,
+static bool DeleteDir(wchar_t* dir_name,
+ wchar_t* path,
int path_length) {
- if (strcmp(dir_name, ".") != 0 &&
- strcmp(dir_name, "..") != 0) {
- size_t written = snprintf(path + path_length,
- MAX_PATH - path_length,
- "%s",
- dir_name);
- if (written != strlen(dir_name)) {
+ if (wcscmp(dir_name, L".") != 0 &&
+ wcscmp(dir_name, L"..") != 0) {
+ size_t written = _snwprintf(path + path_length,
+ MAX_PATH - path_length,
+ L"%s",
+ dir_name);
+ if (written != wcslen(dir_name)) {
return false;
}
return DeleteRecursively(path);
@@ -251,8 +229,8 @@
}
-static bool DeleteEntry(LPWIN32_FIND_DATA find_file_data,
- char* path,
+static bool DeleteEntry(LPWIN32_FIND_DATAW find_file_data,
+ wchar_t* path,
int path_length) {
DWORD attributes = find_file_data->dwFileAttributes;
@@ -264,14 +242,14 @@
}
-static bool DeleteRecursively(const char* dir_name) {
+static bool DeleteRecursively(const wchar_t* dir_name) {
// If the directory is a junction, it's pointing to some other place in the
// filesystem that we do not want to recurse into.
- DWORD attributes = GetFileAttributes(dir_name);
+ DWORD attributes = GetFileAttributesW(dir_name);
if ((attributes != INVALID_FILE_ATTRIBUTES) &&
(attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
// Just delete the junction itself.
- return RemoveDirectory(dir_name) != 0;
+ return RemoveDirectoryW(dir_name) != 0;
}
// Compute full path for the directory currently being deleted. The
@@ -279,7 +257,7 @@
// recursive traversal. path_length does not always equal
// strlen(path) but indicates the current prefix of path that is the
// path of the current directory in the traversal.
- char* path = static_cast<char*>(malloc(MAX_PATH));
+ wchar_t* path = static_cast<wchar_t*>(malloc(MAX_PATH * sizeof(wchar_t)));
int path_length = 0;
bool valid = ComputeFullSearchPath(dir_name, path, &path_length);
if (!valid) {
@@ -287,8 +265,8 @@
return false;
}
- WIN32_FIND_DATA find_file_data;
- HANDLE find_handle = FindFirstFile(path, &find_file_data);
+ WIN32_FIND_DATAW find_file_data;
+ HANDLE find_handle = FindFirstFileW(path, &find_file_data);
// Adjust the path by removing the '*' used for the search.
path_length -= 1;
@@ -301,7 +279,7 @@
bool success = DeleteEntry(&find_file_data, path, path_length);
- while ((FindNextFile(find_handle, &find_file_data) != 0) && success) {
+ while ((FindNextFileW(find_handle, &find_file_data) != 0) && success) {
success = success && DeleteEntry(&find_file_data, path, path_length);
}
@@ -309,7 +287,7 @@
if ((GetLastError() != ERROR_NO_MORE_FILES) ||
(FindClose(find_handle) == 0) ||
- (RemoveDirectory(dir_name) == 0)) {
+ (RemoveDirectoryW(dir_name) == 0)) {
return false;
}
@@ -320,15 +298,15 @@
bool Directory::List(const char* dir_name,
bool recursive,
DirectoryListing* listing) {
- const char* system_name = StringUtils::Utf8ToSystemString(dir_name);
+ const wchar_t* system_name = StringUtils::Utf8ToWide(dir_name);
bool completed = ListRecursively(system_name, recursive, listing);
- free(const_cast<char*>(system_name));
+ free(const_cast<wchar_t*>(system_name));
return completed;
}
-static Directory::ExistsResult ExistsHelper(const char* dir_name) {
- DWORD attributes = GetFileAttributes(dir_name);
+static Directory::ExistsResult ExistsHelper(const wchar_t* dir_name) {
+ DWORD attributes = GetFileAttributesW(dir_name);
if (attributes == INVALID_FILE_ATTRIBUTES) {
DWORD last_error = GetLastError();
if (last_error == ERROR_FILE_NOT_FOUND ||
@@ -347,33 +325,33 @@
Directory::ExistsResult Directory::Exists(const char* dir_name) {
- const char* system_name = StringUtils::Utf8ToSystemString(dir_name);
+ const wchar_t* system_name = StringUtils::Utf8ToWide(dir_name);
Directory::ExistsResult result = ExistsHelper(system_name);
- free(const_cast<char*>(system_name));
+ free(const_cast<wchar_t*>(system_name));
return result;
}
char* Directory::Current() {
- int length = GetCurrentDirectory(0, NULL);
- char* current = reinterpret_cast<char*>(malloc(length + 1));
- GetCurrentDirectory(length + 1, current);
- char* result = StringUtils::SystemStringToUtf8(current);
- free(current);
+ int length = GetCurrentDirectoryW(0, NULL);
+ wchar_t* current = new wchar_t[length + 1];
+ GetCurrentDirectoryW(length + 1, current);
+ char* result = StringUtils::WideToUtf8(current);
+ delete[] current;
return result;
}
bool Directory::Create(const char* dir_name) {
- const char* system_name = StringUtils::Utf8ToSystemString(dir_name);
+ const wchar_t* system_name = StringUtils::Utf8ToWide(dir_name);
// If the directory already exists and is a directory do not
// attempt to create it again and treat it as a success.
if (ExistsHelper(system_name) == EXISTS) {
- free(const_cast<char*>(system_name));
+ free(const_cast<wchar_t*>(system_name));
return true;
}
- int create_status = CreateDirectory(system_name, NULL);
- free(const_cast<char*>(system_name));
+ int create_status = CreateDirectoryW(system_name, NULL);
+ free(const_cast<wchar_t*>(system_name));
return (create_status != 0);
}
@@ -383,30 +361,29 @@
// dir_template. Creates this directory, with a default security
// descriptor inherited from its parent directory.
// The return value must be freed by the caller.
- char* path = static_cast<char*>(malloc(MAX_PATH));
+ wchar_t* path = static_cast<wchar_t*>(malloc(MAX_PATH * sizeof(wchar_t)));
int path_length;
if (0 == strncmp(const_template, "", 1)) {
- path_length = GetTempPath(MAX_PATH, path);
+ path_length = GetTempPathW(MAX_PATH, path);
if (path_length == 0) {
free(path);
return NULL;
}
} else {
- const char* system_template =
- StringUtils::Utf8ToSystemString(const_template);
- snprintf(path, MAX_PATH, "%s", system_template);
- free(const_cast<char*>(system_template));
- path_length = strlen(path);
+ const wchar_t* system_template = StringUtils::Utf8ToWide(const_template);
+ _snwprintf(path, MAX_PATH, L"%s", system_template);
+ free(const_cast<wchar_t*>(system_template));
+ path_length = wcslen(path);
}
// Length of tempdir-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx is 44.
if (path_length > MAX_PATH - 44) {
free(path);
return NULL;
}
- if ((path)[path_length - 1] == '\\') {
+ if ((path)[path_length - 1] == L'\\') {
// No base name for the directory - use "tempdir".
- snprintf(path + path_length, MAX_PATH - path_length, "tempdir");
- path_length = strlen(path);
+ _snwprintf(path + path_length, MAX_PATH - path_length, L"tempdir");
+ path_length = wcslen(path);
}
UUID uuid;
@@ -415,19 +392,20 @@
free(path);
return NULL;
}
- RPC_CSTR uuid_string;
- status = UuidToString(&uuid, &uuid_string);
+ RPC_WSTR uuid_string;
+ status = UuidToStringW(&uuid, &uuid_string);
if (status != RPC_S_OK) {
free(path);
return NULL;
}
- snprintf(path + path_length, MAX_PATH - path_length, "-%s", uuid_string);
- if (!CreateDirectory(path, NULL)) {
+ _snwprintf(path + path_length, MAX_PATH - path_length, L"-%s", uuid_string);
+ RpcStringFreeW(&uuid_string);
+ if (!CreateDirectoryW(path, NULL)) {
free(path);
return NULL;
}
- char* result = StringUtils::SystemStringToUtf8(path);
+ char* result = StringUtils::WideToUtf8(path);
free(path);
return result;
}
@@ -435,22 +413,20 @@
bool Directory::Delete(const char* dir_name, bool recursive) {
bool result = false;
- const char* system_dir_name =
- StringUtils::Utf8ToSystemString(dir_name);
+ const wchar_t* system_dir_name = StringUtils::Utf8ToWide(dir_name);
if (!recursive) {
- result = (RemoveDirectory(system_dir_name) != 0);
+ result = (RemoveDirectoryW(system_dir_name) != 0);
} else {
result = DeleteRecursively(system_dir_name);
}
- free(const_cast<char*>(system_dir_name));
+ free(const_cast<wchar_t*>(system_dir_name));
return result;
}
bool Directory::Rename(const char* path, const char* new_path) {
- const char* system_path = StringUtils::Utf8ToSystemString(path);
- const char* system_new_path =
- StringUtils::Utf8ToSystemString(new_path);
+ const wchar_t* system_path = StringUtils::Utf8ToWide(path);
+ const wchar_t* system_new_path = StringUtils::Utf8ToWide(new_path);
ExistsResult exists = ExistsHelper(system_path);
if (exists != EXISTS) return false;
ExistsResult new_exists = ExistsHelper(system_new_path);
@@ -463,8 +439,8 @@
}
DWORD flags = MOVEFILE_WRITE_THROUGH;
int move_status =
- MoveFileEx(system_path, system_new_path, flags);
- free(const_cast<char*>(system_path));
- free(const_cast<char*>(system_new_path));
+ MoveFileExW(system_path, system_new_path, flags);
+ free(const_cast<wchar_t*>(system_path));
+ free(const_cast<wchar_t*>(system_new_path));
return (move_status != 0);
}
diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc
index a9cef38..9f6bae8c 100644
--- a/runtime/bin/file_win.cc
+++ b/runtime/bin/file_win.cc
@@ -105,9 +105,9 @@
if ((mode & kTruncate) != 0) {
flags = flags | O_TRUNC;
}
- const char* system_name = StringUtils::Utf8ToSystemString(name);
- int fd = open(system_name, flags, 0666);
- free(const_cast<char*>(system_name));
+ const wchar_t* system_name = StringUtils::Utf8ToWide(name);
+ int fd = _wopen(system_name, flags, 0666);
+ free(const_cast<wchar_t*>(system_name));
if (fd < 0) {
return NULL;
}
@@ -128,10 +128,10 @@
bool File::Exists(const char* name) {
- struct stat st;
- const char* system_name = StringUtils::Utf8ToSystemString(name);
- bool stat_status = stat(system_name, &st);
- free(const_cast<char*>(system_name));
+ struct _stat st;
+ const wchar_t* system_name = StringUtils::Utf8ToWide(name);
+ bool stat_status = _wstat(system_name, &st);
+ free(const_cast<wchar_t*>(system_name));
if (stat_status == 0) {
return ((st.st_mode & S_IFMT) == S_IFREG);
} else {
@@ -141,9 +141,9 @@
bool File::Create(const char* name) {
- const char* system_name = StringUtils::Utf8ToSystemString(name);
- int fd = open(system_name, O_RDONLY | O_CREAT, 0666);
- free(const_cast<char*>(system_name));
+ const wchar_t* system_name = StringUtils::Utf8ToWide(name);
+ int fd = _wopen(system_name, O_RDONLY | O_CREAT, 0666);
+ free(const_cast<wchar_t*>(system_name));
if (fd < 0) {
return false;
}
@@ -152,9 +152,9 @@
bool File::Delete(const char* name) {
- const char* system_name = StringUtils::Utf8ToSystemString(name);
- int status = remove(system_name);
- free(const_cast<char*>(system_name));
+ const wchar_t* system_name = StringUtils::Utf8ToWide(name);
+ int status = _wremove(system_name);
+ free(const_cast<wchar_t*>(system_name));
if (status == -1) {
return false;
}
@@ -163,10 +163,10 @@
off_t File::LengthFromName(const char* name) {
- struct stat st;
- const char* system_name = StringUtils::Utf8ToSystemString(name);
- int stat_status = stat(system_name, &st);
- free(const_cast<char*>(system_name));
+ struct _stat st;
+ const wchar_t* system_name = StringUtils::Utf8ToWide(name);
+ int stat_status = _wstat(system_name, &st);
+ free(const_cast<wchar_t*>(system_name));
if (stat_status == 0) {
return st.st_size;
}
@@ -175,10 +175,10 @@
time_t File::LastModified(const char* name) {
- struct stat st;
- const char* system_name = StringUtils::Utf8ToSystemString(name);
- int stat_status = stat(system_name, &st);
- free(const_cast<char*>(system_name));
+ struct _stat st;
+ const wchar_t* system_name = StringUtils::Utf8ToWide(name);
+ int stat_status = _wstat(system_name, &st);
+ free(const_cast<wchar_t*>(system_name));
if (stat_status == 0) {
return st.st_mtime;
}
@@ -196,29 +196,30 @@
char* File::GetCanonicalPath(const char* pathname) {
- struct stat st;
- const char* system_name = StringUtils::Utf8ToSystemString(pathname);
- int stat_status = stat(system_name, &st);
+ struct _stat st;
+ const wchar_t* system_name = StringUtils::Utf8ToWide(pathname);
+ int stat_status = _wstat(system_name, &st);
if (stat_status != 0) {
SetLastError(ERROR_FILE_NOT_FOUND);
- free(const_cast<char*>(system_name));
+ free(const_cast<wchar_t*>(system_name));
return NULL;
}
- int required_size = GetFullPathName(system_name, 0, NULL, NULL);
- char* path = static_cast<char*>(malloc(required_size));
- int written = GetFullPathName(system_name, required_size, path, NULL);
- free(const_cast<char*>(system_name));
+ int required_size = GetFullPathNameW(system_name, 0, NULL, NULL);
+ wchar_t* path =
+ static_cast<wchar_t*>(malloc(required_size * sizeof(wchar_t)));
+ int written = GetFullPathNameW(system_name, required_size, path, NULL);
+ free(const_cast<wchar_t*>(system_name));
ASSERT(written == (required_size - 1));
- char* result = StringUtils::SystemStringToUtf8(path);
+ char* result = StringUtils::WideToUtf8(path);
free(path);
return result;
}
char* File::GetContainingDirectory(char* pathname) {
- struct stat st;
- char* system_name = StringUtils::Utf8ToSystemString(pathname);
- int stat_status = stat(system_name, &st);
+ struct _stat st;
+ wchar_t* system_name = StringUtils::Utf8ToWide(pathname);
+ int stat_status = _wstat(system_name, &st);
if (stat_status == 0) {
if ((st.st_mode & S_IFMT) != S_IFREG) {
SetLastError(ERROR_FILE_NOT_FOUND);
@@ -230,29 +231,32 @@
free(system_name);
return NULL;
}
- int required_size = GetFullPathName(system_name, 0, NULL, NULL);
- char* path = static_cast<char*>(malloc(required_size));
- char* file_part = NULL;
+ int required_size = GetFullPathNameW(system_name, 0, NULL, NULL);
+ wchar_t* path =
+ static_cast<wchar_t*>(malloc(required_size * sizeof(wchar_t)));
+ wchar_t* file_part = NULL;
int written =
- GetFullPathName(system_name, required_size, path, &file_part);
+ GetFullPathNameW(system_name, required_size, path, &file_part);
free(system_name);
ASSERT(written == (required_size - 1));
ASSERT(file_part != NULL);
ASSERT(file_part > path);
- ASSERT(file_part[-1] == '\\');
+ ASSERT(file_part[-1] == L'\\');
file_part[-1] = '\0';
- char* result = StringUtils::SystemStringToUtf8(path);
+ char* result = StringUtils::WideToUtf8(path);
free(path);
return result;
}
const char* File::PathSeparator() {
+ // This is already UTF-8 encoded.
return "\\";
}
const char* File::StringEscapedPathSeparator() {
+ // This is already UTF-8 encoded.
return "\\\\";
}
diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc
index 9c8ebb3..750fde4 100644
--- a/runtime/bin/io_natives.cc
+++ b/runtime/bin/io_natives.cc
@@ -40,10 +40,11 @@
V(Socket_GetError, 1) \
V(Socket_GetStdioHandle, 2) \
V(Socket_NewServicePort, 0) \
- V(SecureSocket_Connect, 5) \
+ V(SecureSocket_Connect, 8) \
V(SecureSocket_Destroy, 1) \
V(SecureSocket_Handshake, 1) \
V(SecureSocket_Init, 1) \
+ V(SecureSocket_PeerCertificate, 1) \
V(SecureSocket_ProcessBuffer, 2) \
V(SecureSocket_RegisterBadCertificateCallback, 2) \
V(SecureSocket_RegisterHandshakeCompleteCallback, 2) \
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 8700364..63d00dc 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -257,7 +257,7 @@
bool result = false;
for (int i = 0; i < argc; i++) {
char* arg = argv[i];
- argv[i] = StringUtils::SystemStringToUtf8(arg);
+ argv[i] = StringUtils::ConsoleStringToUtf8(arg);
if (i == 0) {
result = argv[i] != arg;
} else {
diff --git a/runtime/bin/platform.h b/runtime/bin/platform.h
index 38cafbe..9ee2172 100644
--- a/runtime/bin/platform.h
+++ b/runtime/bin/platform.h
@@ -20,10 +20,6 @@
// deallocated by the caller.
static const char* OperatingSystem();
- // Returns a string representation of an error code. The returned
- // string must be deallocated by the caller.
- static char* StrError(int error_code);
-
// Extracts the local hostname.
static bool LocalHostname(char* buffer, intptr_t buffer_length);
diff --git a/runtime/bin/platform_android.cc b/runtime/bin/platform_android.cc
index 206eca3..a05de34 100644
--- a/runtime/bin/platform_android.cc
+++ b/runtime/bin/platform_android.cc
@@ -65,12 +65,3 @@
void Platform::FreeEnvironment(char** env, intptr_t count) {
delete[] env;
}
-
-
-char* Platform::StrError(int error_code) {
- static const int kBufferSize = 1024;
- char* error = static_cast<char*>(malloc(kBufferSize));
- error[0] = '\0';
- strerror_r(error_code, error, kBufferSize);
- return error;
-}
diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc
index 2d77576..da91bc5 100644
--- a/runtime/bin/platform_linux.cc
+++ b/runtime/bin/platform_linux.cc
@@ -65,16 +65,3 @@
void Platform::FreeEnvironment(char** env, intptr_t count) {
delete[] env;
}
-
-
-char* Platform::StrError(int error_code) {
- static const int kBufferSize = 1024;
- char* error = static_cast<char*>(malloc(kBufferSize));
- error[0] = '\0';
- char* error_str = strerror_r(error_code, error, kBufferSize);
- if (error_str != error) {
- size_t written = snprintf(error, kBufferSize, "%s", error_str);
- ASSERT(written == strlen(error_str));
- }
- return error;
-}
diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc
index 822399d..87e8c6f 100644
--- a/runtime/bin/platform_macos.cc
+++ b/runtime/bin/platform_macos.cc
@@ -70,12 +70,3 @@
void Platform::FreeEnvironment(char** env, intptr_t count) {
delete[] env;
}
-
-
-char* Platform::StrError(int error_code) {
- static const int kBufferSize = 1024;
- char* error = static_cast<char*>(malloc(kBufferSize));
- error[0] = '\0';
- strerror_r(error_code, error, kBufferSize);
- return error;
-}
diff --git a/runtime/bin/platform_win.cc b/runtime/bin/platform_win.cc
index 750caa6..090fcf2 100644
--- a/runtime/bin/platform_win.cc
+++ b/runtime/bin/platform_win.cc
@@ -36,56 +36,27 @@
char** Platform::Environment(intptr_t* count) {
- char* strings = GetEnvironmentStrings();
+ wchar_t* strings = GetEnvironmentStringsW();
if (strings == NULL) return NULL;
- char* tmp = strings;
+ wchar_t* tmp = strings;
intptr_t i = 0;
while (*tmp != '\0') {
i++;
- tmp += (strlen(tmp) + 1);
+ tmp += (wcslen(tmp) + 1);
}
*count = i;
char** result = new char*[i];
tmp = strings;
for (intptr_t current = 0; current < i; current++) {
- result[current] = StringUtils::SystemStringToUtf8(tmp);
- tmp += (strlen(tmp) + 1);
+ result[current] = StringUtils::WideToUtf8(tmp);
+ tmp += (wcslen(tmp) + 1);
}
- FreeEnvironmentStrings(strings);
+ FreeEnvironmentStringsW(strings);
return result;
}
+
void Platform::FreeEnvironment(char** env, int count) {
for (int i = 0; i < count; i++) free(env[i]);
delete[] env;
}
-
-
-char* Platform::StrError(int error_code) {
- static const int kBufferSize = 1024;
- char* error = static_cast<char*>(malloc(kBufferSize));
- DWORD message_size =
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error_code,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- error,
- kBufferSize,
- NULL);
- if (message_size == 0) {
- if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
- Log::PrintErr("FormatMessage failed %d\n", GetLastError());
- }
- snprintf(error, kBufferSize, "OS Error %d", error_code);
- }
- // Strip out \r\n at the end of the generated message and ensure
- // null termination.
- if (message_size > 2 &&
- error[message_size - 2] == '\r' &&
- error[message_size - 1] == '\n') {
- error[message_size - 2] = '\0';
- } else {
- error[kBufferSize - 1] = '\0';
- }
- return error;
-}
diff --git a/runtime/bin/process.cc b/runtime/bin/process.cc
index e513939..740e4a4 100644
--- a/runtime/bin/process.cc
+++ b/runtime/bin/process.cc
@@ -122,8 +122,7 @@
Dart_Handle err_handle = Dart_GetNativeArgument(args, 7);
Dart_Handle exit_handle = Dart_GetNativeArgument(args, 8);
intptr_t pid = -1;
- static const int kMaxChildOsErrorMessageLength = 256;
- char os_error_message[kMaxChildOsErrorMessageLength];
+ char* os_error_message = NULL;
int error_code = Process::Start(path,
string_args,
@@ -136,7 +135,7 @@
&err,
&pid,
&exit_event,
- os_error_message, kMaxChildOsErrorMessageLength);
+ &os_error_message);
if (error_code == 0) {
Socket::SetSocketIdNativeField(in_handle, in);
Socket::SetSocketIdNativeField(out_handle, out);
@@ -151,6 +150,7 @@
}
delete[] string_args;
delete[] string_environment;
+ free(os_error_message);
Dart_SetReturnValue(args, Dart_NewBoolean(error_code == 0));
Dart_ExitScope();
}
@@ -194,7 +194,7 @@
Dart_PropagateError(result);
}
char* str =
- StringUtils::SystemStringToUtf8(reinterpret_cast<char*>(buffer));
+ StringUtils::ConsoleStringToUtf8(reinterpret_cast<char*>(buffer));
Dart_SetReturnValue(args, DartUtils::NewString(str));
if (str != reinterpret_cast<char*>(buffer)) free(str);
Dart_ExitScope();
@@ -205,7 +205,7 @@
Dart_EnterScope();
Dart_Handle str = Dart_GetNativeArgument(args, 0);
const char* utf8 = DartUtils::GetStringValue(str);
- const char* system_string = StringUtils::Utf8ToSystemString(utf8);
+ const char* system_string = StringUtils::Utf8ToConsoleString(utf8);
int external_length = strlen(system_string);
uint8_t* buffer = NULL;
Dart_Handle external_array = IOBuffer::Allocate(external_length, &buffer);
diff --git a/runtime/bin/process.h b/runtime/bin/process.h
index f3ef5d8..0d3f704 100644
--- a/runtime/bin/process.h
+++ b/runtime/bin/process.h
@@ -24,8 +24,7 @@
intptr_t* err,
intptr_t* id,
intptr_t* exit_handler,
- char* os_error_message,
- int os_error_message_len);
+ char** os_error_message);
// Kill a process with a given pid.
static bool Kill(intptr_t id, int signal);
diff --git a/runtime/bin/process_android.cc b/runtime/bin/process_android.cc
index 3d593e2..d18c92b 100644
--- a/runtime/bin/process_android.cc
+++ b/runtime/bin/process_android.cc
@@ -258,16 +258,8 @@
dart::Monitor ExitCodeHandler::thread_terminate_monitor_;
-static char* SafeStrNCpy(char* dest, const char* src, size_t n) {
- strncpy(dest, src, n);
- dest[n - 1] = '\0';
- return dest;
-}
-
-
-static void SetChildOsErrorMessage(char* os_error_message,
- int os_error_message_len) {
- SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len);
+static void SetChildOsErrorMessage(char** os_error_message) {
+ *os_error_message = strdup(strerror(errno));
}
@@ -315,8 +307,7 @@
intptr_t* err,
intptr_t* id,
intptr_t* exit_event,
- char* os_error_message,
- int os_error_message_len) {
+ char** os_error_message) {
pid_t pid;
int read_in[2]; // Pipe for stdout to child process.
int read_err[2]; // Pipe for stderr to child process.
@@ -326,49 +317,49 @@
bool initialized = ExitCodeHandler::EnsureInitialized();
if (!initialized) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
Log::PrintErr("Error initializing exit code handler: %s\n",
- os_error_message);
+ *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(read_in));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ SetChildOsErrorMessage(os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(read_err));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(write_out));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(exec_control));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
TEMP_FAILURE_RETRY(close(write_out[0]));
TEMP_FAILURE_RETRY(close(write_out[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -378,7 +369,7 @@
F_SETFD,
TEMP_FAILURE_RETRY(fcntl(exec_control[1], F_GETFD)) | FD_CLOEXEC));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
@@ -387,7 +378,7 @@
TEMP_FAILURE_RETRY(close(write_out[1]));
TEMP_FAILURE_RETRY(close(exec_control[0]));
TEMP_FAILURE_RETRY(close(exec_control[1]));
- Log::PrintErr("fcntl failed: %s\n", os_error_message);
+ Log::PrintErr("fcntl failed: %s\n", *os_error_message);
return errno;
}
@@ -416,7 +407,7 @@
}
pid = TEMP_FAILURE_RETRY(fork());
if (pid < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
delete[] program_arguments;
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
@@ -481,14 +472,14 @@
int event_fds[2];
result = TEMP_FAILURE_RETRY(pipe(event_fds));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
TEMP_FAILURE_RETRY(close(write_out[0]));
TEMP_FAILURE_RETRY(close(write_out[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -514,10 +505,13 @@
FDUtils::ReadFromBlocking(
exec_control[0], &child_errno, sizeof(child_errno));
if (bytes_read == sizeof(child_errno)) {
- bytes_read = FDUtils::ReadFromBlocking(exec_control[0],
- os_error_message,
- os_error_message_len);
- os_error_message[os_error_message_len - 1] = '\0';
+ static const int kMaxMessageSize = 256;
+ char* message = static_cast<char*>(malloc(kMaxMessageSize));
+ bytes_read = FDUtils::ReadFromBlocking(exec_control[0],
+ message,
+ kMaxMessageSize);
+ message[kMaxMessageSize - 1] = '\0';
+ *os_error_message = message;
}
TEMP_FAILURE_RETRY(close(exec_control[0]));
diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc
index bd4ed87..01b17a9 100644
--- a/runtime/bin/process_linux.cc
+++ b/runtime/bin/process_linux.cc
@@ -259,16 +259,8 @@
dart::Monitor ExitCodeHandler::thread_terminate_monitor_;
-static char* SafeStrNCpy(char* dest, const char* src, size_t n) {
- strncpy(dest, src, n);
- dest[n - 1] = '\0';
- return dest;
-}
-
-
-static void SetChildOsErrorMessage(char* os_error_message,
- int os_error_message_len) {
- SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len);
+static void SetChildOsErrorMessage(char** os_error_message) {
+ *os_error_message = strdup(strerror(errno));
}
@@ -316,8 +308,7 @@
intptr_t* err,
intptr_t* id,
intptr_t* exit_event,
- char* os_error_message,
- int os_error_message_len) {
+ char** os_error_message) {
pid_t pid;
int read_in[2]; // Pipe for stdout to child process.
int read_err[2]; // Pipe for stderr to child process.
@@ -327,50 +318,50 @@
bool initialized = ExitCodeHandler::EnsureInitialized();
if (!initialized) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
Log::PrintErr(
"Error initializing exit code handler: %s\n",
- os_error_message);
+ *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(read_in));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ SetChildOsErrorMessage(os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(read_err));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(write_out));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(exec_control));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
TEMP_FAILURE_RETRY(close(write_out[0]));
TEMP_FAILURE_RETRY(close(write_out[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -380,7 +371,7 @@
F_SETFD,
TEMP_FAILURE_RETRY(fcntl(exec_control[1], F_GETFD)) | FD_CLOEXEC));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
@@ -389,7 +380,7 @@
TEMP_FAILURE_RETRY(close(write_out[1]));
TEMP_FAILURE_RETRY(close(exec_control[0]));
TEMP_FAILURE_RETRY(close(exec_control[1]));
- Log::PrintErr("fcntl failed: %s\n", os_error_message);
+ Log::PrintErr("fcntl failed: %s\n", *os_error_message);
return errno;
}
@@ -418,7 +409,7 @@
}
pid = TEMP_FAILURE_RETRY(fork());
if (pid < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
delete[] program_arguments;
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
@@ -481,14 +472,14 @@
int event_fds[2];
result = TEMP_FAILURE_RETRY(pipe(event_fds));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
TEMP_FAILURE_RETRY(close(write_out[0]));
TEMP_FAILURE_RETRY(close(write_out[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -514,10 +505,13 @@
FDUtils::ReadFromBlocking(
exec_control[0], &child_errno, sizeof(child_errno));
if (bytes_read == sizeof(child_errno)) {
- bytes_read = FDUtils::ReadFromBlocking(exec_control[0],
- os_error_message,
- os_error_message_len);
- os_error_message[os_error_message_len - 1] = '\0';
+ static const int kMaxMessageSize = 256;
+ char* message = static_cast<char*>(malloc(kMaxMessageSize));
+ bytes_read = FDUtils::ReadFromBlocking(exec_control[0],
+ message,
+ kMaxMessageSize);
+ message[kMaxMessageSize - 1] = '\0';
+ *os_error_message = message;
}
TEMP_FAILURE_RETRY(close(exec_control[0]));
diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc
index aa15940..6256b31 100644
--- a/runtime/bin/process_macos.cc
+++ b/runtime/bin/process_macos.cc
@@ -258,16 +258,8 @@
dart::Monitor ExitCodeHandler::thread_terminate_monitor_;
-static char* SafeStrNCpy(char* dest, const char* src, size_t n) {
- strncpy(dest, src, n);
- dest[n - 1] = '\0';
- return dest;
-}
-
-
-static void SetChildOsErrorMessage(char* os_error_message,
- int os_error_message_len) {
- SafeStrNCpy(os_error_message, strerror(errno), os_error_message_len);
+static void SetChildOsErrorMessage(char** os_error_message) {
+ *os_error_message = strdup(strerror(errno));
}
@@ -315,8 +307,7 @@
intptr_t* err,
intptr_t* id,
intptr_t* exit_event,
- char* os_error_message,
- int os_error_message_len) {
+ char** os_error_message) {
pid_t pid;
int read_in[2]; // Pipe for stdout to child process.
int read_err[2]; // Pipe for stderr to child process.
@@ -326,49 +317,49 @@
bool initialized = ExitCodeHandler::EnsureInitialized();
if (!initialized) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
Log::PrintErr("Error initializing exit code handler: %s\n",
- os_error_message);
+ *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(read_in));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ SetChildOsErrorMessage(os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(read_err));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(write_out));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
result = TEMP_FAILURE_RETRY(pipe(exec_control));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
TEMP_FAILURE_RETRY(close(write_out[0]));
TEMP_FAILURE_RETRY(close(write_out[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -378,7 +369,7 @@
F_SETFD,
TEMP_FAILURE_RETRY(fcntl(exec_control[1], F_GETFD)) | FD_CLOEXEC));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
@@ -387,7 +378,7 @@
TEMP_FAILURE_RETRY(close(write_out[1]));
TEMP_FAILURE_RETRY(close(exec_control[0]));
TEMP_FAILURE_RETRY(close(exec_control[1]));
- Log::PrintErr("fcntl failed: %s\n", os_error_message);
+ Log::PrintErr("fcntl failed: %s\n", *os_error_message);
return errno;
}
@@ -416,7 +407,7 @@
}
pid = TEMP_FAILURE_RETRY(fork());
if (pid < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
delete[] program_arguments;
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
@@ -479,14 +470,14 @@
int event_fds[2];
result = TEMP_FAILURE_RETRY(pipe(event_fds));
if (result < 0) {
- SetChildOsErrorMessage(os_error_message, os_error_message_len);
+ SetChildOsErrorMessage(os_error_message);
TEMP_FAILURE_RETRY(close(read_in[0]));
TEMP_FAILURE_RETRY(close(read_in[1]));
TEMP_FAILURE_RETRY(close(read_err[0]));
TEMP_FAILURE_RETRY(close(read_err[1]));
TEMP_FAILURE_RETRY(close(write_out[0]));
TEMP_FAILURE_RETRY(close(write_out[1]));
- Log::PrintErr("Error pipe creation failed: %s\n", os_error_message);
+ Log::PrintErr("Error pipe creation failed: %s\n", *os_error_message);
return errno;
}
@@ -512,10 +503,13 @@
FDUtils::ReadFromBlocking(
exec_control[0], &child_errno, sizeof(child_errno));
if (bytes_read == sizeof(child_errno)) {
- bytes_read = FDUtils::ReadFromBlocking(exec_control[0],
- os_error_message,
- os_error_message_len);
- os_error_message[os_error_message_len - 1] = '\0';
+ static const int kMaxMessageSize = 256;
+ char* message = static_cast<char*>(malloc(kMaxMessageSize));
+ bytes_read = FDUtils::ReadFromBlocking(exec_control[0],
+ message,
+ kMaxMessageSize);
+ message[kMaxMessageSize - 1] = '\0';
+ *os_error_message = message;
}
TEMP_FAILURE_RETRY(close(exec_control[0]));
diff --git a/runtime/bin/process_win.cc b/runtime/bin/process_win.cc
index 0a9eb52..624b1f2 100644
--- a/runtime/bin/process_win.cc
+++ b/runtime/bin/process_win.cc
@@ -294,24 +294,27 @@
CloseProcessPipe(handles4);
}
-static int SetOsErrorMessage(char* os_error_message,
- int os_error_message_len) {
+
+static int SetOsErrorMessage(char** os_error_message) {
int error_code = GetLastError();
+ static const int kMaxMessageLength = 256;
+ wchar_t message[kMaxMessageLength];
DWORD message_size =
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error_code,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- os_error_message,
- os_error_message_len,
- NULL);
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ message,
+ kMaxMessageLength,
+ NULL);
if (message_size == 0) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
Log::PrintErr("FormatMessage failed %d\n", GetLastError());
}
- snprintf(os_error_message, os_error_message_len, "OS Error %d", error_code);
+ _snwprintf(message, kMaxMessageLength, L"OS Error %d", error_code);
}
- os_error_message[os_error_message_len - 1] = '\0';
+ message[kMaxMessageLength - 1] = '\0';
+ *os_error_message = StringUtils::WideToUtf8(message);
return error_code;
}
@@ -327,8 +330,7 @@
intptr_t* err,
intptr_t* id,
intptr_t* exit_handler,
- char* os_error_message,
- int os_error_message_len) {
+ char** os_error_message) {
HANDLE stdin_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
HANDLE stdout_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
HANDLE stderr_handles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
@@ -339,15 +341,15 @@
UUID uuid;
RPC_STATUS status = UuidCreateSequential(&uuid);
if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
+ SetOsErrorMessage(os_error_message);
Log::PrintErr("UuidCreateSequential failed %d\n", status);
- SetOsErrorMessage(os_error_message, os_error_message_len);
return status;
}
RPC_CSTR uuid_string;
status = UuidToString(&uuid, &uuid_string);
if (status != RPC_S_OK) {
+ SetOsErrorMessage(os_error_message);
Log::PrintErr("UuidToString failed %d\n", status);
- SetOsErrorMessage(os_error_message, os_error_message_len);
return status;
}
for (int i = 0; i < 4; i++) {
@@ -358,52 +360,95 @@
}
status = RpcStringFree(&uuid_string);
if (status != RPC_S_OK) {
+ SetOsErrorMessage(os_error_message);
Log::PrintErr("RpcStringFree failed %d\n", status);
- SetOsErrorMessage(os_error_message, os_error_message_len);
return status;
}
if (!CreateProcessPipe(stdin_handles, pipe_names[0], kInheritRead)) {
- int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
+ int error_code = SetOsErrorMessage(os_error_message);
CloseProcessPipes(
stdin_handles, stdout_handles, stderr_handles, exit_handles);
return error_code;
}
if (!CreateProcessPipe(stdout_handles, pipe_names[1], kInheritWrite)) {
- int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
+ int error_code = SetOsErrorMessage(os_error_message);
CloseProcessPipes(
stdin_handles, stdout_handles, stderr_handles, exit_handles);
return error_code;
}
if (!CreateProcessPipe(stderr_handles, pipe_names[2], kInheritWrite)) {
- int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
+ int error_code = SetOsErrorMessage(os_error_message);
CloseProcessPipes(
stdin_handles, stdout_handles, stderr_handles, exit_handles);
return error_code;
}
if (!CreateProcessPipe(exit_handles, pipe_names[3], kInheritNone)) {
- int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
+ int error_code = SetOsErrorMessage(os_error_message);
CloseProcessPipes(
stdin_handles, stdout_handles, stderr_handles, exit_handles);
return error_code;
}
// Setup info structures.
- STARTUPINFO startup_info;
+ STARTUPINFOEX startup_info;
ZeroMemory(&startup_info, sizeof(startup_info));
- startup_info.cb = sizeof(startup_info);
- startup_info.hStdInput = stdin_handles[kReadHandle];
- startup_info.hStdOutput = stdout_handles[kWriteHandle];
- startup_info.hStdError = stderr_handles[kWriteHandle];
- startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.StartupInfo.cb = sizeof(startup_info);
+ startup_info.StartupInfo.hStdInput = stdin_handles[kReadHandle];
+ startup_info.StartupInfo.hStdOutput = stdout_handles[kWriteHandle];
+ startup_info.StartupInfo.hStdError = stderr_handles[kWriteHandle];
+ startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+
+ // Setup the handles to inherit. We only want to inherit the three handles
+ // for stdin, stdout and stderr.
+ SIZE_T size = 0;
+ // The call to determine the size of an attribute list always fails with
+ // ERROR_INSUFFICIENT_BUFFER and that error should be ignored.
+ if (!InitializeProcThreadAttributeList(NULL, 1, 0, &size) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ int error_code = SetOsErrorMessage(os_error_message);
+ CloseProcessPipes(
+ stdin_handles, stdout_handles, stderr_handles, exit_handles);
+ return error_code;
+ }
+ LPPROC_THREAD_ATTRIBUTE_LIST attribute_list =
+ reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(size));
+ ZeroMemory(attribute_list, size);
+ if (!InitializeProcThreadAttributeList(attribute_list, 1, 0, &size)) {
+ int error_code = SetOsErrorMessage(os_error_message);
+ CloseProcessPipes(
+ stdin_handles, stdout_handles, stderr_handles, exit_handles);
+ free(attribute_list);
+ return error_code;
+ }
+ static const int kNumInheritedHandles = 3;
+ HANDLE inherited_handles[kNumInheritedHandles] =
+ { stdin_handles[kReadHandle],
+ stdout_handles[kWriteHandle],
+ stderr_handles[kWriteHandle] };
+ if (!UpdateProcThreadAttribute(attribute_list,
+ 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ inherited_handles,
+ kNumInheritedHandles * sizeof(HANDLE),
+ NULL,
+ NULL)) {
+ DeleteProcThreadAttributeList(attribute_list);
+ int error_code = SetOsErrorMessage(os_error_message);
+ CloseProcessPipes(
+ stdin_handles, stdout_handles, stderr_handles, exit_handles);
+ free(attribute_list);
+ return error_code;
+ }
+ startup_info.lpAttributeList = attribute_list;
PROCESS_INFORMATION process_info;
ZeroMemory(&process_info, sizeof(process_info));
// Transform input strings to system format.
- path = StringUtils::Utf8ToSystemString(path);
+ path = StringUtils::Utf8ToConsoleString(path);
for (int i = 0; i < arguments_length; i++) {
- arguments[i] = StringUtils::Utf8ToSystemString(arguments[i]);
+ arguments[i] = StringUtils::Utf8ToConsoleString(arguments[i]);
}
// Compute command-line length.
@@ -415,11 +460,13 @@
command_line_length += arguments_length + 1;
static const int kMaxCommandLineLength = 32768;
if (command_line_length > kMaxCommandLineLength) {
- int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
+ int error_code = SetOsErrorMessage(os_error_message);
CloseProcessPipes(
stdin_handles, stdout_handles, stderr_handles, exit_handles);
free(const_cast<char*>(path));
for (int i = 0; i < arguments_length; i++) free(arguments[i]);
+ DeleteProcThreadAttributeList(attribute_list);
+ free(attribute_list);
return error_code;
}
@@ -445,7 +492,7 @@
if (environment != NULL) {
// Convert environment strings to system strings.
for (intptr_t i = 0; i < environment_length; i++) {
- environment[i] = StringUtils::Utf8ToSystemString(environment[i]);
+ environment[i] = StringUtils::Utf8ToConsoleString(environment[i]);
}
// An environment block is a sequence of zero-terminated strings
@@ -473,7 +520,7 @@
}
if (working_directory != NULL) {
- working_directory = StringUtils::Utf8ToSystemString(working_directory);
+ working_directory = StringUtils::Utf8ToConsoleString(working_directory);
}
// Create process.
@@ -482,10 +529,10 @@
NULL, // ProcessAttributes
NULL, // ThreadAttributes
TRUE, // InheritHandles
- 0, // CreationFlags
+ EXTENDED_STARTUPINFO_PRESENT,
environment_block,
working_directory,
- &startup_info,
+ reinterpret_cast<STARTUPINFO*>(&startup_info),
&process_info);
// Deallocate command-line and environment block strings.
@@ -495,8 +542,11 @@
free(const_cast<char*>(working_directory));
}
+ DeleteProcThreadAttributeList(attribute_list);
+ free(attribute_list);
+
if (result == 0) {
- int error_code = SetOsErrorMessage(os_error_message, os_error_message_len);
+ int error_code = SetOsErrorMessage(os_error_message);
CloseProcessPipes(
stdin_handles, stdout_handles, stderr_handles, exit_handles);
return error_code;
diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc
index 1e28e52..c378683 100644
--- a/runtime/bin/secure_socket.cc
+++ b/runtime/bin/secure_socket.cc
@@ -73,43 +73,40 @@
Dart_EnterScope();
Dart_Handle host_name_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
Dart_Handle port_object = ThrowIfError(Dart_GetNativeArgument(args, 2));
- Dart_Handle is_server_object = ThrowIfError(Dart_GetNativeArgument(args, 3));
+ bool is_server = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3));
Dart_Handle certificate_name_object =
ThrowIfError(Dart_GetNativeArgument(args, 4));
+ bool request_client_certificate =
+ DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 5));
+ bool require_client_certificate =
+ DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 6));
+ bool send_client_certificate =
+ DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 7));
const char* host_name = NULL;
// TODO(whesse): Is truncating a Dart string containing \0 what we want?
ThrowIfError(Dart_StringToCString(host_name_object, &host_name));
int64_t port;
- if (!DartUtils::GetInt64Value(port_object, &port) ||
- port < 0 || port > 65535) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "Illegal port parameter in _SSLFilter.connect"));
+ if (!DartUtils::GetInt64Value(port_object, &port)) {
+ FATAL("The range of port_object was checked in Dart - it cannot fail here");
}
- if (!Dart_IsBoolean(is_server_object)) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "Illegal is_server parameter in _SSLFilter.connect"));
- }
- bool is_server = DartUtils::GetBooleanValue(is_server_object);
-
const char* certificate_name = NULL;
- // If this is a server connection, get the certificate to connect with.
- // TODO(whesse): Use this parameter for a client certificate as well.
- if (is_server) {
- if (!Dart_IsString(certificate_name_object)) {
- Dart_ThrowException(DartUtils::NewDartArgumentError(
- "Non-String certificate parameter in _SSLFilter.connect"));
- }
+ if (Dart_IsString(certificate_name_object)) {
ThrowIfError(Dart_StringToCString(certificate_name_object,
&certificate_name));
}
+ // If this is a server connection, it must have a certificate to connect with.
+ ASSERT(!is_server || certificate_name != NULL);
GetFilter(args)->Connect(host_name,
- static_cast<int>(port),
- is_server,
- certificate_name);
+ static_cast<int>(port),
+ is_server,
+ certificate_name,
+ request_client_certificate,
+ require_client_certificate,
+ send_client_certificate);
Dart_ExitScope();
}
@@ -221,17 +218,30 @@
}
-static bool CallBadCertificateCallback(Dart_Handle callback,
- const char* subject_name,
- const char* issuer_name,
- int64_t start_validity,
- int64_t end_validity) {
- if (callback == NULL || Dart_IsNull(callback)) return false;
+void FUNCTION_NAME(SecureSocket_PeerCertificate)
+ (Dart_NativeArguments args) {
Dart_EnterScope();
- Dart_Handle subject_name_object = DartUtils::NewString(subject_name);
- Dart_Handle issuer_name_object = DartUtils::NewString(issuer_name);
- Dart_Handle start_validity_int = Dart_NewInteger(start_validity);
- Dart_Handle end_validity_int = Dart_NewInteger(end_validity);
+ Dart_SetReturnValue(args, GetFilter(args)->PeerCertificate());
+ Dart_ExitScope();
+}
+
+
+static Dart_Handle X509FromCertificate(CERTCertificate* certificate) {
+ PRTime start_validity;
+ PRTime end_validity;
+ SECStatus status =
+ CERT_GetCertTimes(certificate, &start_validity, &end_validity);
+ if (status != SECSuccess) {
+ ThrowPRException("Cannot get validity times from certificate");
+ }
+ int64_t start_epoch_ms = start_validity / PR_USEC_PER_MSEC;
+ int64_t end_epoch_ms = end_validity / PR_USEC_PER_MSEC;
+ Dart_Handle subject_name_object =
+ DartUtils::NewString(certificate->subjectName);
+ Dart_Handle issuer_name_object =
+ DartUtils::NewString(certificate->issuerName);
+ Dart_Handle start_epoch_ms_int = Dart_NewInteger(start_epoch_ms);
+ Dart_Handle end_epoch_ms_int = Dart_NewInteger(end_epoch_ms);
Dart_Handle date_class =
DartUtils::GetDartClass(DartUtils::kCoreLibURL, "Date");
@@ -239,9 +249,9 @@
DartUtils::NewString("fromMillisecondsSinceEpoch");
Dart_Handle start_validity_date =
- Dart_New(date_class, from_milliseconds, 1, &start_validity_int);
+ Dart_New(date_class, from_milliseconds, 1, &start_epoch_ms_int);
Dart_Handle end_validity_date =
- Dart_New(date_class, from_milliseconds, 1, &end_validity_int);
+ Dart_New(date_class, from_milliseconds, 1, &end_epoch_ms_int);
Dart_Handle x509_class =
DartUtils::GetDartClass(DartUtils::kIOLibURL, "X509Certificate");
@@ -249,13 +259,7 @@
issuer_name_object,
start_validity_date,
end_validity_date };
- Dart_Handle certificate = Dart_New(x509_class, Dart_Null(), 4, arguments);
-
- Dart_Handle result =
- ThrowIfError(Dart_InvokeClosure(callback, 1, &certificate));
- bool c_result = Dart_IsBoolean(result) && DartUtils::GetBooleanValue(result);
- Dart_ExitScope();
- return c_result;
+ return Dart_New(x509_class, Dart_Null(), 4, arguments);
}
@@ -341,21 +345,21 @@
SECMOD_DB,
init_flags);
if (status != SECSuccess) {
- ThrowPRException("Unsuccessful NSS_Init call.");
+ ThrowPRException("Failed NSS_Init call.");
}
status = NSS_SetDomesticPolicy();
if (status != SECSuccess) {
- ThrowPRException("Unsuccessful NSS_SetDomesticPolicy call.");
+ ThrowPRException("Failed NSS_SetDomesticPolicy call.");
}
// Enable TLS, as well as SSL3 and SSL2.
status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE);
if (status != SECSuccess) {
- ThrowPRException("Unsuccessful SSL_OptionSetDefault enable TLS call.");
+ ThrowPRException("Failed SSL_OptionSetDefault enable TLS call.");
}
status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL);
if (status != SECSuccess) {
- ThrowPRException("Unsuccessful SSL_ConfigServerSessionIDCache call.");
+ ThrowPRException("Failed SSL_ConfigServerSessionIDCache call.");
}
} else {
@@ -373,36 +377,36 @@
SECStatus BadCertificateCallback(void* filter, PRFileDesc* fd) {
- return static_cast<SSLFilter*>(filter)->HandleBadCertificate(fd);
+ SSLFilter* ssl_filter = static_cast<SSLFilter*>(filter);
+ Dart_Handle callback = ssl_filter->bad_certificate_callback();
+ if (callback == NULL || Dart_IsNull(callback)) return SECFailure;
+
+ Dart_EnterScope();
+ Dart_Handle x509_object = ssl_filter->PeerCertificate();
+ Dart_Handle result =
+ ThrowIfError(Dart_InvokeClosure(callback, 1, &x509_object));
+ bool c_result = Dart_IsBoolean(result) && DartUtils::GetBooleanValue(result);
+ Dart_ExitScope();
+ return c_result ? SECSuccess : SECFailure;
}
-SECStatus SSLFilter::HandleBadCertificate(PRFileDesc* fd) {
- ASSERT(fd == filter_);
- CERTCertificate* certificate = SSL_PeerCertificate(fd);
- PRTime start_validity;
- PRTime end_validity;
- SECStatus status =
- CERT_GetCertTimes(certificate, &start_validity, &end_validity);
- if (status != SECSuccess) {
- ThrowPRException("Cannot get validity times from certificate");
- }
- int64_t start_epoch_ms = start_validity / PR_USEC_PER_MSEC;
- int64_t end_epoch_ms = end_validity / PR_USEC_PER_MSEC;
- bool accept = CallBadCertificateCallback(bad_certificate_callback_,
- certificate->subjectName,
- certificate->issuerName,
- start_epoch_ms,
- end_epoch_ms);
+Dart_Handle SSLFilter::PeerCertificate() {
+ CERTCertificate* certificate = SSL_PeerCertificate(filter_);
+ if (certificate == NULL) return Dart_Null();
+ Dart_Handle x509_object = X509FromCertificate(certificate);
CERT_DestroyCertificate(certificate);
- return accept ? SECSuccess : SECFailure;
+ return x509_object;
}
void SSLFilter::Connect(const char* host_name,
int port,
bool is_server,
- const char* certificate_name) {
+ const char* certificate_name,
+ bool request_client_certificate,
+ bool require_client_certificate,
+ bool send_client_certificate) {
is_server_ = is_server;
if (in_handshake_) {
ThrowException("Connect called while already in handshake state.");
@@ -410,7 +414,7 @@
filter_ = SSL_ImportFD(NULL, filter_);
if (filter_ == NULL) {
- ThrowPRException("Unsuccessful SSL_ImportFD call");
+ ThrowPRException("Failed SSL_ImportFD call");
}
SECStatus status;
@@ -420,6 +424,8 @@
if (certificate_database == NULL) {
ThrowPRException("Certificate database cannot be loaded");
}
+ // TODO(whesse): Switch to a function that looks up certs by nickname,
+ // so that server and client uses of certificateName agree.
CERTCertificate* certificate = CERT_FindCertByNameString(
certificate_database,
const_cast<char*>(certificate_name));
@@ -434,7 +440,7 @@
if (PR_GetError() == -8177) {
ThrowPRException("Certificate database password incorrect");
} else {
- ThrowPRException("Unsuccessful PK11_FindKeyByAnyCert call."
+ ThrowPRException("Failed PK11_FindKeyByAnyCert call."
" Cannot find private key for certificate");
}
}
@@ -444,11 +450,41 @@
CERT_DestroyCertificate(certificate);
SECKEY_DestroyPrivateKey(key);
if (status != SECSuccess) {
- ThrowPRException("Unsuccessful SSL_ConfigSecureServer call");
+ ThrowPRException("Failed SSL_ConfigSecureServer call");
+ }
+
+ if (request_client_certificate) {
+ status = SSL_OptionSet(filter_, SSL_REQUEST_CERTIFICATE, PR_TRUE);
+ if (status != SECSuccess) {
+ ThrowPRException("Failed SSL_OptionSet(REQUEST_CERTIFICATE) call");
+ }
+ PRBool require_cert = require_client_certificate ? PR_TRUE : PR_FALSE;
+ status = SSL_OptionSet(filter_, SSL_REQUIRE_CERTIFICATE, require_cert);
+ if (status != SECSuccess) {
+ ThrowPRException("Failed SSL_OptionSet(REQUIRE_CERTIFICATE) call");
+ }
}
} else { // Client.
if (SSL_SetURL(filter_, host_name) == -1) {
- ThrowPRException("Unsuccessful SetURL call");
+ ThrowPRException("Failed SetURL call");
+ }
+
+ // This disables the SSL session cache for client connections.
+ // This resolves issue 7208, but degrades performance.
+ // TODO(7230): Reenable session cache, without breaking client connections.
+ status = SSL_OptionSet(filter_, SSL_NO_CACHE, PR_TRUE);
+ if (status != SECSuccess) {
+ ThrowPRException("Failed SSL_OptionSet(NO_CACHE) call");
+ }
+
+ if (send_client_certificate) {
+ status = SSL_GetClientAuthDataHook(
+ filter_,
+ NSS_GetClientAuthData,
+ static_cast<void*>(const_cast<char*>(certificate_name)));
+ if (status != SECSuccess) {
+ ThrowPRException("Failed SSL_GetClientAuthDataHook call");
+ }
}
}
@@ -460,7 +496,7 @@
PRBool as_server = is_server ? PR_TRUE : PR_FALSE;
status = SSL_ResetHandshake(filter_, as_server);
if (status != SECSuccess) {
- ThrowPRException("Unsuccessful SSL_ResetHandshake call");
+ ThrowPRException("Failed SSL_ResetHandshake call");
}
// SetPeerAddress
@@ -470,12 +506,12 @@
PRStatus rv = PR_GetHostByName(host_name, host_entry_buffer,
PR_NETDB_BUF_SIZE, &host_entry);
if (rv != PR_SUCCESS) {
- ThrowPRException("Unsuccessful PR_GetHostByName call");
+ ThrowPRException("Failed PR_GetHostByName call");
}
int index = PR_EnumerateHostEnt(0, &host_entry, port, &host_address);
if (index == -1 || index == 0) {
- ThrowPRException("Unsuccessful PR_EnumerateHostEnt call");
+ ThrowPRException("Failed PR_EnumerateHostEnt call");
}
memio_SetPeerName(filter_, &host_address);
}
diff --git a/runtime/bin/secure_socket.h b/runtime/bin/secure_socket.h
index 520693a..f20ca7d 100644
--- a/runtime/bin/secure_socket.h
+++ b/runtime/bin/secure_socket.h
@@ -74,18 +74,20 @@
void Connect(const char* host,
int port,
bool is_server,
- const char* certificate_name);
+ const char* certificate_name,
+ bool request_client_certificate,
+ bool require_client_certificate,
+ bool send_client_certificate);
void Destroy();
void Handshake();
void RegisterHandshakeCompleteCallback(Dart_Handle handshake_complete);
void RegisterBadCertificateCallback(Dart_Handle callback);
+ Dart_Handle bad_certificate_callback() { return bad_certificate_callback_; }
static void InitializeLibrary(const char* certificate_database,
const char* password,
bool use_builtin_root_certificates);
-
intptr_t ProcessBuffer(int bufferIndex);
-
- SECStatus HandleBadCertificate(PRFileDesc* fd);
+ Dart_Handle PeerCertificate();
private:
static const int kMemioBufferSize = 20 * KB;
diff --git a/runtime/bin/secure_socket_patch.dart b/runtime/bin/secure_socket_patch.dart
index c511465..9e79c86 100644
--- a/runtime/bin/secure_socket_patch.dart
+++ b/runtime/bin/secure_socket_patch.dart
@@ -37,7 +37,10 @@
void connect(String hostName,
int port,
bool is_server,
- String certificate_name) native "SecureSocket_Connect";
+ String certificateName,
+ bool requestClientCertificate,
+ bool requireClientCertificate,
+ bool sendClientCertificate) native "SecureSocket_Connect";
void destroy() {
buffers = null;
@@ -50,6 +53,8 @@
void init() native "SecureSocket_Init";
+ X509Certificate get peerCertificate native "SecureSocket_PeerCertificate";
+
int processBuffer(int bufferIndex) native "SecureSocket_ProcessBuffer";
void registerBadCertificateCallback(Function callback)
diff --git a/runtime/bin/stdio_patch.dart b/runtime/bin/stdio_patch.dart
index bcdc671..5dd5d87 100644
--- a/runtime/bin/stdio_patch.dart
+++ b/runtime/bin/stdio_patch.dart
@@ -3,8 +3,39 @@
// BSD-style license that can be found in the LICENSE file.
patch class _StdIOUtils {
- /* patch */ static _getStdioHandle(Socket socket, int num)
- native "Socket_GetStdioHandle";
- /* patch */ static _getStdioHandleType(int num)
- native "File_GetStdioHandleType";
+ static InputStream _getStdioInputStream() {
+ switch (_getStdioHandleType(0)) {
+ case _STDIO_HANDLE_TYPE_TERMINAL:
+ case _STDIO_HANDLE_TYPE_PIPE:
+ case _STDIO_HANDLE_TYPE_SOCKET:
+ Socket s = new _Socket._internalReadOnly();
+ _getStdioHandle(s, 0);
+ s._closed = false;
+ return s.inputStream;
+ case _STDIO_HANDLE_TYPE_FILE:
+ return new _FileInputStream.fromStdio(0);
+ default:
+ throw new FileIOException("Unsupported stdin type");
+ }
+ }
+
+ static OutputStream _getStdioOutputStream(int fd) {
+ assert(fd == 1 || fd == 2);
+ switch (_getStdioHandleType(fd)) {
+ case _STDIO_HANDLE_TYPE_TERMINAL:
+ case _STDIO_HANDLE_TYPE_PIPE:
+ case _STDIO_HANDLE_TYPE_SOCKET:
+ Socket s = new _Socket._internalWriteOnly();
+ _getStdioHandle(s, fd);
+ s._closed = false;
+ return s.outputStream;
+ case _STDIO_HANDLE_TYPE_FILE:
+ return new _FileOutputStream.fromStdio(fd);
+ default:
+ throw new FileIOException("Unsupported stdin type");
+ }
+ }
}
+
+_getStdioHandle(Socket socket, int num) native "Socket_GetStdioHandle";
+_getStdioHandleType(int num) native "File_GetStdioHandleType";
\ No newline at end of file
diff --git a/runtime/bin/utils.h b/runtime/bin/utils.h
index 70545ab..e655f6c 100644
--- a/runtime/bin/utils.h
+++ b/runtime/bin/utils.h
@@ -59,10 +59,14 @@
// conversions are only needed on Windows. If the methods returns a
// pointer that is different from the input pointer the returned
// pointer is allocated with malloc and should be freed using free.
- static const char* SystemStringToUtf8(const char* str);
- static char* SystemStringToUtf8(char* str);
- static const char* Utf8ToSystemString(const char* utf8);
- static char* Utf8ToSystemString(char* utf8);
+ static const char* ConsoleStringToUtf8(const char* str);
+ static char* ConsoleStringToUtf8(char* str);
+ static const char* Utf8ToConsoleString(const char* utf8);
+ static char* Utf8ToConsoleString(char* utf8);
+ static char* WideToUtf8(wchar_t* wide);
+ static const char* WideToUtf8(const wchar_t* wide);
+ static wchar_t* Utf8ToWide(char* utf8);
+ static const wchar_t* Utf8ToWide(const char* utf8);
};
#endif // BIN_UTILS_H_
diff --git a/runtime/bin/utils_android.cc b/runtime/bin/utils_android.cc
index ab48f55..5661f74 100644
--- a/runtime/bin/utils_android.cc
+++ b/runtime/bin/utils_android.cc
@@ -27,18 +27,38 @@
}
}
-const char* StringUtils::SystemStringToUtf8(const char* str) {
+const char* StringUtils::ConsoleStringToUtf8(const char* str) {
return str;
}
-const char* StringUtils::Utf8ToSystemString(const char* utf8) {
+const char* StringUtils::Utf8ToConsoleString(const char* utf8) {
return utf8;
}
-char* StringUtils::SystemStringToUtf8(char* str) {
+char* StringUtils::ConsoleStringToUtf8(char* str) {
return str;
}
-char* StringUtils::Utf8ToSystemString(char* utf8) {
+char* StringUtils::Utf8ToConsoleString(char* utf8) {
return utf8;
}
+
+wchar_t* StringUtils::Utf8ToWide(char* utf8) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+const wchar_t* StringUtils::Utf8ToWide(const char* utf8) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+char* StringUtils::WideToUtf8(wchar_t* str) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+const char* StringUtils::WideToUtf8(const wchar_t* str) {
+ UNIMPLEMENTED();
+ return NULL;
+}
diff --git a/runtime/bin/utils_linux.cc b/runtime/bin/utils_linux.cc
index ab48f55..5661f74 100644
--- a/runtime/bin/utils_linux.cc
+++ b/runtime/bin/utils_linux.cc
@@ -27,18 +27,38 @@
}
}
-const char* StringUtils::SystemStringToUtf8(const char* str) {
+const char* StringUtils::ConsoleStringToUtf8(const char* str) {
return str;
}
-const char* StringUtils::Utf8ToSystemString(const char* utf8) {
+const char* StringUtils::Utf8ToConsoleString(const char* utf8) {
return utf8;
}
-char* StringUtils::SystemStringToUtf8(char* str) {
+char* StringUtils::ConsoleStringToUtf8(char* str) {
return str;
}
-char* StringUtils::Utf8ToSystemString(char* utf8) {
+char* StringUtils::Utf8ToConsoleString(char* utf8) {
return utf8;
}
+
+wchar_t* StringUtils::Utf8ToWide(char* utf8) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+const wchar_t* StringUtils::Utf8ToWide(const char* utf8) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+char* StringUtils::WideToUtf8(wchar_t* str) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+const char* StringUtils::WideToUtf8(const wchar_t* str) {
+ UNIMPLEMENTED();
+ return NULL;
+}
diff --git a/runtime/bin/utils_macos.cc b/runtime/bin/utils_macos.cc
index ab48f55..5661f74 100644
--- a/runtime/bin/utils_macos.cc
+++ b/runtime/bin/utils_macos.cc
@@ -27,18 +27,38 @@
}
}
-const char* StringUtils::SystemStringToUtf8(const char* str) {
+const char* StringUtils::ConsoleStringToUtf8(const char* str) {
return str;
}
-const char* StringUtils::Utf8ToSystemString(const char* utf8) {
+const char* StringUtils::Utf8ToConsoleString(const char* utf8) {
return utf8;
}
-char* StringUtils::SystemStringToUtf8(char* str) {
+char* StringUtils::ConsoleStringToUtf8(char* str) {
return str;
}
-char* StringUtils::Utf8ToSystemString(char* utf8) {
+char* StringUtils::Utf8ToConsoleString(char* utf8) {
return utf8;
}
+
+wchar_t* StringUtils::Utf8ToWide(char* utf8) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+const wchar_t* StringUtils::Utf8ToWide(const char* utf8) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+char* StringUtils::WideToUtf8(wchar_t* str) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+const char* StringUtils::WideToUtf8(const wchar_t* str) {
+ UNIMPLEMENTED();
+ return NULL;
+}
diff --git a/runtime/bin/utils_win.cc b/runtime/bin/utils_win.cc
index f9603f8..cf8f93a 100644
--- a/runtime/bin/utils_win.cc
+++ b/runtime/bin/utils_win.cc
@@ -8,21 +8,21 @@
#include "bin/log.h"
static void FormatMessageIntoBuffer(DWORD code,
- char* buffer,
+ wchar_t* buffer,
int buffer_length) {
DWORD message_size =
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- code,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- buffer,
- buffer_length,
- NULL);
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ buffer,
+ buffer_length,
+ NULL);
if (message_size == 0) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
Log::PrintErr("FormatMessage failed %d\n", GetLastError());
}
- snprintf(buffer, buffer_length, "OS Error %d", code);
+ _snwprintf(buffer, buffer_length, L"OS Error %d", code);
}
buffer[buffer_length - 1] = '\0';
}
@@ -32,9 +32,11 @@
set_code(GetLastError());
static const int kMaxMessageLength = 256;
- char message[kMaxMessageLength];
+ wchar_t message[kMaxMessageLength];
FormatMessageIntoBuffer(code_, message, kMaxMessageLength);
- SetMessage(message);
+ char* utf8 = StringUtils::WideToUtf8(message);
+ SetMessage(utf8);
+ free(utf8);
}
void OSError::SetCodeAndMessage(SubSystem sub_system, int code) {
@@ -42,41 +44,63 @@
set_code(code);
static const int kMaxMessageLength = 256;
- char message[kMaxMessageLength];
+ wchar_t message[kMaxMessageLength];
FormatMessageIntoBuffer(code_, message, kMaxMessageLength);
- SetMessage(message);
+ char* utf8 = StringUtils::WideToUtf8(message);
+ SetMessage(utf8);
+ free(utf8);
}
-char* StringUtils::SystemStringToUtf8(char* str) {
+char* StringUtils::ConsoleStringToUtf8(char* str) {
int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
wchar_t* unicode = new wchar_t[len+1];
MultiByteToWideChar(CP_ACP, 0, str, -1, unicode, len);
unicode[len] = '\0';
- len = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
- char* utf8 = reinterpret_cast<char*>(malloc(len+1));
- WideCharToMultiByte(CP_UTF8, 0, unicode, -1, utf8, len, NULL, NULL);
- utf8[len] = '\0';
+ char* utf8 = StringUtils::WideToUtf8(unicode);
delete[] unicode;
return utf8;
}
-char* StringUtils::Utf8ToSystemString(char* utf8) {
- int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
- wchar_t* unicode = new wchar_t[len+1];
- MultiByteToWideChar(CP_UTF8, 0, utf8, -1, unicode, len);
- unicode[len] = '\0';
- len = WideCharToMultiByte(CP_ACP, 0, unicode, -1, NULL, 0, NULL, NULL);
- char* ansi = reinterpret_cast<char*>(malloc(len+1));
+char* StringUtils::Utf8ToConsoleString(char* utf8) {
+ wchar_t* unicode = Utf8ToWide(utf8);
+ int len = WideCharToMultiByte(CP_ACP, 0, unicode, -1, NULL, 0, NULL, NULL);
+ char* ansi = reinterpret_cast<char*>(malloc(len + 1));
WideCharToMultiByte(CP_ACP, 0, unicode, -1, ansi, len, NULL, NULL);
ansi[len] = '\0';
- delete[] unicode;
+ free(unicode);
return ansi;
}
-const char* StringUtils::Utf8ToSystemString(const char* utf8) {
- return const_cast<const char*>(Utf8ToSystemString(const_cast<char*>(utf8)));
+char* StringUtils::WideToUtf8(wchar_t* wide) {
+ int len = WideCharToMultiByte(CP_UTF8, 0, wide, -1, NULL, 0, NULL, NULL);
+ char* utf8 = reinterpret_cast<char*>(malloc(len + 1));
+ WideCharToMultiByte(CP_UTF8, 0, wide, -1, utf8, len, NULL, NULL);
+ utf8[len] = '\0';
+ return utf8;
}
-const char* StringUtils::SystemStringToUtf8(const char* str) {
- return const_cast<const char*>(Utf8ToSystemString(const_cast<char*>(str)));
+
+wchar_t* StringUtils::Utf8ToWide(char* utf8) {
+ int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
+ wchar_t* unicode =
+ reinterpret_cast<wchar_t*>(malloc((len + 1) * sizeof(wchar_t)));
+ MultiByteToWideChar(CP_UTF8, 0, utf8, -1, unicode, len);
+ unicode[len] = '\0';
+ return unicode;
+}
+
+const char* StringUtils::Utf8ToConsoleString(const char* utf8) {
+ return const_cast<const char*>(Utf8ToConsoleString(const_cast<char*>(utf8)));
+}
+
+const char* StringUtils::ConsoleStringToUtf8(const char* str) {
+ return const_cast<const char*>(ConsoleStringToUtf8(const_cast<char*>(str)));
+}
+
+const char* StringUtils::WideToUtf8(const wchar_t* wide) {
+ return const_cast<const char*>(WideToUtf8(const_cast<wchar_t*>(wide)));
+}
+
+const wchar_t* StringUtils::Utf8ToWide(const char* utf8) {
+ return const_cast<const wchar_t*>(Utf8ToWide(const_cast<char*>(utf8)));
}
diff --git a/runtime/lib/array.cc b/runtime/lib/array.cc
index ac4dc52..8b273a4 100644
--- a/runtime/lib/array.cc
+++ b/runtime/lib/array.cc
@@ -17,7 +17,7 @@
AbstractTypeArguments::CheckedHandle(arguments->NativeArgAt(0));
ASSERT(type_arguments.IsNull() ||
(type_arguments.IsInstantiated() && (type_arguments.Length() == 1)));
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1));
intptr_t len = length.Value();
if (len < 0 || len > Array::kMaxElements) {
const String& error = String::Handle(String::NewFormatted(
@@ -35,7 +35,7 @@
DEFINE_NATIVE_ENTRY(ObjectArray_getIndexed, 2) {
const Array& array = Array::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
if ((index.Value() < 0) || (index.Value() >= array.Length())) {
GrowableArray<const Object*> arguments;
arguments.Add(&index);
@@ -47,7 +47,7 @@
DEFINE_NATIVE_ENTRY(ObjectArray_setIndexed, 3) {
const Array& array = Array::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
const Instance& value = Instance::CheckedHandle(arguments->NativeArgAt(2));
if ((index.Value() < 0) || (index.Value() >= array.Length())) {
GrowableArray<const Object*> arguments;
@@ -68,10 +68,10 @@
// ObjectArray src, int srcStart, int dstStart, int count.
DEFINE_NATIVE_ENTRY(ObjectArray_copyFromObjectArray, 5) {
const Array& dest = Array::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Array, source, arguments->NativeArgAt(1));
- GET_NATIVE_ARGUMENT(Smi, src_start, arguments->NativeArgAt(2));
- GET_NATIVE_ARGUMENT(Smi, dst_start, arguments->NativeArgAt(3));
- GET_NATIVE_ARGUMENT(Smi, count, arguments->NativeArgAt(4));
+ GET_NON_NULL_NATIVE_ARGUMENT(Array, source, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, src_start, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, dst_start, arguments->NativeArgAt(3));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, count, arguments->NativeArgAt(4));
intptr_t icount = count.Value();
if (icount < 0) {
GrowableArray<const Object*> args;
diff --git a/runtime/lib/bool_patch.dart b/runtime/lib/bool_patch.dart
new file mode 100644
index 0000000..fd84119
--- /dev/null
+++ b/runtime/lib/bool_patch.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.
+
+// Dart core library.
+
+patch class bool {
+
+ /* patch */ int get hashCode {
+ return this ? 1231 : 1237;
+ }
+
+ /* patch */ bool operator ==(other) => identical(this, other);
+}
diff --git a/runtime/lib/byte_array.cc b/runtime/lib/byte_array.cc
index 11daaf1..0d98f98 100644
--- a/runtime/lib/byte_array.cc
+++ b/runtime/lib/byte_array.cc
@@ -43,111 +43,112 @@
}
-#define GETTER_ARGUMENTS(ArrayT, ValueT) \
- GET_NATIVE_ARGUMENT(ArrayT, array, arguments->NativeArgAt(0)); \
- GET_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
+#define GETTER_ARGUMENTS(ArrayT, ValueT) \
+ GET_NON_NULL_NATIVE_ARGUMENT(ArrayT, array, arguments->NativeArgAt(0)); \
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
-#define SETTER_ARGUMENTS(ArrayT, ObjectT, ValueT) \
- GET_NATIVE_ARGUMENT(ArrayT, array, arguments->NativeArgAt(0)); \
- GET_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1)); \
- GET_NATIVE_ARGUMENT(ObjectT, value_object, arguments->NativeArgAt(2));
+#define SETTER_ARGUMENTS(ArrayT, ObjectT, ValueT) \
+ GET_NON_NULL_NATIVE_ARGUMENT(ArrayT, array, arguments->NativeArgAt(0)); \
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1)); \
+ GET_NON_NULL_NATIVE_ARGUMENT( \
+ ObjectT, value_object, arguments->NativeArgAt(2));
-#define GETTER(ArrayT, ObjectT, ValueT) \
- GETTER_ARGUMENTS(ArrayT, ValueT); \
- RangeCheck(array, index.Value() * sizeof(ValueT), sizeof(ValueT)); \
- ValueT result = array.At(index.Value()); \
+#define GETTER(ArrayT, ObjectT, ValueT) \
+ GETTER_ARGUMENTS(ArrayT, ValueT); \
+ RangeCheck(array, index.Value() * sizeof(ValueT), sizeof(ValueT)); \
+ ValueT result = array.At(index.Value()); \
return ObjectT::New(result);
-#define SETTER(ArrayT, ObjectT, Getter, ValueT) \
- SETTER_ARGUMENTS(ArrayT, ObjectT, ValueT); \
- RangeCheck(array, index.Value() * sizeof(ValueT), sizeof(ValueT)); \
- ValueT value = value_object.Getter(); \
- array.SetAt(index.Value(), value); \
+#define SETTER(ArrayT, ObjectT, Getter, ValueT) \
+ SETTER_ARGUMENTS(ArrayT, ObjectT, ValueT); \
+ RangeCheck(array, index.Value() * sizeof(ValueT), sizeof(ValueT)); \
+ ValueT value = value_object.Getter(); \
+ array.SetAt(index.Value(), value); \
return Object::null();
-#define UNALIGNED_GETTER(ArrayT, ObjectT, ValueT) \
- GETTER_ARGUMENTS(ArrayT, ValueT); \
- RangeCheck(array, index.Value(), sizeof(ValueT)); \
- ValueT result; \
- ByteArray::Copy(&result, array, index.Value(), sizeof(ValueT)); \
+#define UNALIGNED_GETTER(ArrayT, ObjectT, ValueT) \
+ GETTER_ARGUMENTS(ArrayT, ValueT); \
+ RangeCheck(array, index.Value(), sizeof(ValueT)); \
+ ValueT result; \
+ ByteArray::Copy(&result, array, index.Value(), sizeof(ValueT)); \
return ObjectT::New(result);
-#define UNALIGNED_SETTER(ArrayT, ObjectT, Getter, ValueT) \
- SETTER_ARGUMENTS(ArrayT, ObjectT, ValueT); \
- RangeCheck(array, index.Value(), sizeof(ValueT)); \
- ValueT src = value_object.Getter(); \
- ByteArray::Copy(array, index.Value(), &src, sizeof(ValueT)); \
+#define UNALIGNED_SETTER(ArrayT, ObjectT, Getter, ValueT) \
+ SETTER_ARGUMENTS(ArrayT, ObjectT, ValueT); \
+ RangeCheck(array, index.Value(), sizeof(ValueT)); \
+ ValueT src = value_object.Getter(); \
+ ByteArray::Copy(array, index.Value(), &src, sizeof(ValueT)); \
return Integer::New(index.Value() + sizeof(ValueT));
-#define UINT64_TO_INTEGER(value, integer) \
- if (value > static_cast<uint64_t>(Mint::kMaxValue)) { \
- result = BigintOperations::NewFromUint64(value); \
- } else if (value > static_cast<uint64_t>(Smi::kMaxValue)) { \
- result = Mint::New(value); \
- } else { \
- result = Smi::New(value); \
+#define UINT64_TO_INTEGER(value, integer) \
+ if (value > static_cast<uint64_t>(Mint::kMaxValue)) { \
+ result = BigintOperations::NewFromUint64(value); \
+ } else if (value > static_cast<uint64_t>(Smi::kMaxValue)) { \
+ result = Mint::New(value); \
+ } else { \
+ result = Smi::New(value); \
}
-#define GETTER_UINT64(ArrayT) \
- GETTER_ARGUMENTS(ArrayT, uint64_t); \
- intptr_t size = sizeof(uint64_t); \
- RangeCheck(array, index.Value() * size, size); \
- uint64_t value = array.At(index.Value()); \
- Integer& result = Integer::Handle(); \
- UINT64_TO_INTEGER(value, result); \
+#define GETTER_UINT64(ArrayT) \
+ GETTER_ARGUMENTS(ArrayT, uint64_t); \
+ intptr_t size = sizeof(uint64_t); \
+ RangeCheck(array, index.Value() * size, size); \
+ uint64_t value = array.At(index.Value()); \
+ Integer& result = Integer::Handle(); \
+ UINT64_TO_INTEGER(value, result); \
return result.raw();
-#define UNALIGNED_GETTER_UINT64(ArrayT) \
- GETTER_ARGUMENTS(ArrayT, uint64_t); \
- RangeCheck(array, index.Value(), sizeof(uint64_t)); \
- uint64_t value; \
- ByteArray::Copy(&value, array, index.Value(), sizeof(uint64_t)); \
- Integer& result = Integer::Handle(); \
- UINT64_TO_INTEGER(value, result); \
+#define UNALIGNED_GETTER_UINT64(ArrayT) \
+ GETTER_ARGUMENTS(ArrayT, uint64_t); \
+ RangeCheck(array, index.Value(), sizeof(uint64_t)); \
+ uint64_t value; \
+ ByteArray::Copy(&value, array, index.Value(), sizeof(uint64_t)); \
+ Integer& result = Integer::Handle(); \
+ UINT64_TO_INTEGER(value, result); \
return result.raw();
-#define INTEGER_TO_UINT64(integer, uint64) \
- if (integer.IsBigint()) { \
- Bigint& bigint = Bigint::Handle(); \
- bigint ^= integer.raw(); \
- ASSERT(BigintOperations::FitsIntoUint64(bigint)); \
- value = BigintOperations::AbsToUint64(bigint); \
- } else { \
- ASSERT(integer.IsMint() || integer.IsSmi()); \
- value = integer.AsInt64Value(); \
- } \
+#define INTEGER_TO_UINT64(integer, uint64) \
+ if (integer.IsBigint()) { \
+ Bigint& bigint = Bigint::Handle(); \
+ bigint ^= integer.raw(); \
+ ASSERT(BigintOperations::FitsIntoUint64(bigint)); \
+ value = BigintOperations::AbsToUint64(bigint); \
+ } else { \
+ ASSERT(integer.IsMint() || integer.IsSmi()); \
+ value = integer.AsInt64Value(); \
+ } \
-#define SETTER_UINT64(ArrayT) \
- SETTER_ARGUMENTS(ArrayT, Integer, uint64_t); \
- intptr_t size = sizeof(uint64_t); \
- RangeCheck(array, index.Value() * size, size); \
- uint64_t value; \
- INTEGER_TO_UINT64(value_object, value); \
- array.SetAt(index.Value(), value); \
+#define SETTER_UINT64(ArrayT) \
+ SETTER_ARGUMENTS(ArrayT, Integer, uint64_t); \
+ intptr_t size = sizeof(uint64_t); \
+ RangeCheck(array, index.Value() * size, size); \
+ uint64_t value; \
+ INTEGER_TO_UINT64(value_object, value); \
+ array.SetAt(index.Value(), value); \
return Object::null();
-#define UNALIGNED_SETTER_UINT64(ArrayT) \
- SETTER_ARGUMENTS(ArrayT, Integer, uint64_t); \
- RangeCheck(array, index.Value(), sizeof(uint64_t)); \
- uint64_t value; \
- INTEGER_TO_UINT64(value_object, value); \
- ByteArray::Copy(array, index.Value(), &value, sizeof(uint64_t)); \
+#define UNALIGNED_SETTER_UINT64(ArrayT) \
+ SETTER_ARGUMENTS(ArrayT, Integer, uint64_t); \
+ RangeCheck(array, index.Value(), sizeof(uint64_t)); \
+ uint64_t value; \
+ INTEGER_TO_UINT64(value_object, value); \
+ ByteArray::Copy(array, index.Value(), &value, sizeof(uint64_t)); \
return Integer::New(index.Value() + sizeof(uint64_t));
DEFINE_NATIVE_ENTRY(ByteArray_getLength, 1) {
- GET_NATIVE_ARGUMENT(ByteArray, array, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(ByteArray, array, arguments->NativeArgAt(0));
return Smi::New(array.Length());
}
@@ -254,10 +255,10 @@
DEFINE_NATIVE_ENTRY(ByteArray_setRange, 5) {
ByteArray& dst = ByteArray::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, dst_start, arguments->NativeArgAt(1));
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(2));
- GET_NATIVE_ARGUMENT(ByteArray, src, arguments->NativeArgAt(3));
- GET_NATIVE_ARGUMENT(Smi, src_start, arguments->NativeArgAt(4));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, dst_start, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(ByteArray, src, arguments->NativeArgAt(3));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, src_start, arguments->NativeArgAt(4));
intptr_t length_value = length.Value();
intptr_t src_start_value = src_start.Value();
intptr_t dst_start_value = dst_start.Value();
@@ -278,7 +279,7 @@
// Int8Array
DEFINE_NATIVE_ENTRY(Int8Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int8Array::kMaxElements);
return Int8Array::New(len);
@@ -286,7 +287,7 @@
DEFINE_NATIVE_ENTRY(Int8Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int8Array::kMaxElements);
int8_t* bytes = OS::AllocateAlignedArray<int8_t>(
@@ -312,7 +313,7 @@
// Uint8Array
DEFINE_NATIVE_ENTRY(Uint8Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint8Array::kMaxElements);
return Uint8Array::New(len);
@@ -320,7 +321,7 @@
DEFINE_NATIVE_ENTRY(Uint8Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint8Array::kMaxElements);
uint8_t* bytes = OS::AllocateAlignedArray<uint8_t>(
@@ -346,7 +347,7 @@
// Uint8ClampedArray
DEFINE_NATIVE_ENTRY(Uint8ClampedArray_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint8ClampedArray::kMaxElements);
return Uint8ClampedArray::New(len);
@@ -354,7 +355,7 @@
DEFINE_NATIVE_ENTRY(Uint8ClampedArray_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint8ClampedArray::kMaxElements);
uint8_t* bytes = OS::AllocateAlignedArray<uint8_t>(
@@ -380,7 +381,7 @@
// Int16Array
DEFINE_NATIVE_ENTRY(Int16Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int16Array::kMaxElements);
return Int16Array::New(len);
@@ -388,7 +389,7 @@
DEFINE_NATIVE_ENTRY(Int16Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int16Array::kMaxElements);
int16_t* bytes = OS::AllocateAlignedArray<int16_t>(
@@ -414,7 +415,7 @@
// Uint16Array
DEFINE_NATIVE_ENTRY(Uint16Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint16Array::kMaxElements);
return Uint16Array::New(len);
@@ -422,7 +423,7 @@
DEFINE_NATIVE_ENTRY(Uint16Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint16Array::kMaxElements);
uint16_t* bytes = OS::AllocateAlignedArray<uint16_t>(
@@ -448,7 +449,7 @@
// Int32Array
DEFINE_NATIVE_ENTRY(Int32Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int32Array::kMaxElements);
return Int32Array::New(len);
@@ -456,7 +457,7 @@
DEFINE_NATIVE_ENTRY(Int32Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int32Array::kMaxElements);
int32_t* bytes = OS::AllocateAlignedArray<int32_t>(
@@ -482,7 +483,7 @@
// Uint32Array
DEFINE_NATIVE_ENTRY(Uint32Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint32Array::kMaxElements);
return Uint32Array::New(len);
@@ -490,7 +491,7 @@
DEFINE_NATIVE_ENTRY(Uint32Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint32Array::kMaxElements);
uint32_t* bytes = OS::AllocateAlignedArray<uint32_t>(
@@ -516,7 +517,7 @@
// Int64Array
DEFINE_NATIVE_ENTRY(Int64Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int64Array::kMaxElements);
return Int64Array::New(len);
@@ -524,7 +525,7 @@
DEFINE_NATIVE_ENTRY(Int64Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Int64Array::kMaxElements);
int64_t* bytes = OS::AllocateAlignedArray<int64_t>(
@@ -550,7 +551,7 @@
// Uint64Array
DEFINE_NATIVE_ENTRY(Uint64Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint64Array::kMaxElements);
return Uint64Array::New(len);
@@ -558,7 +559,7 @@
DEFINE_NATIVE_ENTRY(Uint64Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Uint64Array::kMaxElements);
uint64_t* bytes = OS::AllocateAlignedArray<uint64_t>(
@@ -584,7 +585,7 @@
// Float32Array
DEFINE_NATIVE_ENTRY(Float32Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Float32Array::kMaxElements);
return Float32Array::New(len);
@@ -592,7 +593,7 @@
DEFINE_NATIVE_ENTRY(Float32Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Float32Array::kMaxElements);
float* bytes = OS::AllocateAlignedArray<float>(
@@ -618,7 +619,7 @@
// Float64Array
DEFINE_NATIVE_ENTRY(Float64Array_new, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Float64Array::kMaxElements);
return Float64Array::New(len);
@@ -626,7 +627,7 @@
DEFINE_NATIVE_ENTRY(Float64Array_newTransferable, 1) {
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(0));
intptr_t len = length.Value();
LengthCheck(len, Float64Array::kMaxElements);
double* bytes = OS::AllocateAlignedArray<double>(
diff --git a/runtime/lib/byte_array.dart b/runtime/lib/byte_array.dart
index 96715db..61be89d 100644
--- a/runtime/lib/byte_array.dart
+++ b/runtime/lib/byte_array.dart
@@ -59,7 +59,8 @@
return new _Int16Array.transferable(length);
}
- /* patch */ factory Int16List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Int16List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Int16ArrayView(array, start, length);
}
}
@@ -74,7 +75,8 @@
return new _Uint16Array.transferable(length);
}
- /* patch */ factory Uint16List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Uint16List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Uint16ArrayView(array, start, length);
}
}
@@ -89,7 +91,8 @@
return new _Int32Array.transferable(length);
}
- /* patch */ factory Int32List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Int32List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Int32ArrayView(array, start, length);
}
}
@@ -104,7 +107,8 @@
return new _Uint32Array.transferable(length);
}
- /* patch */ factory Uint32List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Uint32List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Uint32ArrayView(array, start, length);
}
}
@@ -119,7 +123,8 @@
return new _Int64Array.transferable(length);
}
- /* patch */ factory Int64List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Int64List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Int64ArrayView(array, start, length);
}
}
@@ -134,7 +139,8 @@
return new _Uint64Array.transferable(length);
}
- /* patch */ factory Uint64List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Uint64List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Uint64ArrayView(array, start, length);
}
}
@@ -149,7 +155,8 @@
return new _Float32Array.transferable(length);
}
- /* patch */ factory Float32List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Float32List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Float32ArrayView(array, start, length);
}
}
@@ -164,7 +171,8 @@
return new _Float64Array.transferable(length);
}
- /* patch */ factory Float64List.view(ByteArray array, [int start = 0, int length]) {
+ /* patch */ factory Float64List.view(ByteArray array,
+ [int start = 0, int length]) {
return new _Float64ArrayView(array, start, length);
}
}
diff --git a/runtime/lib/date.cc b/runtime/lib/date.cc
index 7bd1a72..ad66673 100644
--- a/runtime/lib/date.cc
+++ b/runtime/lib/date.cc
@@ -16,7 +16,8 @@
static int32_t kMaxAllowedSeconds = 2100000000;
DEFINE_NATIVE_ENTRY(DateNatives_timeZoneName, 1) {
- GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ Integer, dart_seconds, arguments->NativeArgAt(0));
int64_t seconds = dart_seconds.AsInt64Value();
if (seconds < 0 || seconds > kMaxAllowedSeconds) {
GrowableArray<const Object*> args;
@@ -29,7 +30,8 @@
DEFINE_NATIVE_ENTRY(DateNatives_timeZoneOffsetInSeconds, 1) {
- GET_NATIVE_ARGUMENT(Integer, dart_seconds, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ Integer, dart_seconds, arguments->NativeArgAt(0));
int64_t seconds = dart_seconds.AsInt64Value();
if (seconds < 0 || seconds > kMaxAllowedSeconds) {
GrowableArray<const Object*> args;
diff --git a/runtime/lib/double.cc b/runtime/lib/double.cc
index abadf12..350fbde 100644
--- a/runtime/lib/double.cc
+++ b/runtime/lib/double.cc
@@ -30,7 +30,7 @@
DEFINE_NATIVE_ENTRY(Double_add, 2) {
double left = Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
double right = right_object.value();
if (FLAG_trace_intrinsified_natives) {
OS::Print("Double_add %f + %f\n", left, right);
@@ -41,7 +41,7 @@
DEFINE_NATIVE_ENTRY(Double_sub, 2) {
double left = Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
double right = right_object.value();
if (FLAG_trace_intrinsified_natives) {
OS::Print("Double_sub %f - %f\n", left, right);
@@ -52,7 +52,7 @@
DEFINE_NATIVE_ENTRY(Double_mul, 2) {
double left = Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
double right = right_object.value();
if (FLAG_trace_intrinsified_natives) {
OS::Print("Double_mul %f * %f\n", left, right);
@@ -63,7 +63,7 @@
DEFINE_NATIVE_ENTRY(Double_div, 2) {
double left = Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
double right = right_object.value();
if (FLAG_trace_intrinsified_natives) {
OS::Print("Double_div %f / %f\n", left, right);
@@ -74,7 +74,7 @@
DEFINE_NATIVE_ENTRY(Double_trunc_div, 2) {
double left = Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
double right = right_object.value();
if (FLAG_trace_intrinsified_natives) {
OS::Print("Double_trunc_div %f ~/ %f\n", left, right);
@@ -85,7 +85,7 @@
DEFINE_NATIVE_ENTRY(Double_modulo, 2) {
double left = Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
double right = right_object.value();
double remainder = fmod_ieee(left, right);
@@ -105,7 +105,7 @@
DEFINE_NATIVE_ENTRY(Double_remainder, 2) {
double left = Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
double right = right_object.value();
return Double::New(fmod_ieee(left, right));
}
@@ -113,7 +113,7 @@
DEFINE_NATIVE_ENTRY(Double_greaterThan, 2) {
const Double& left = Double::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Double, right, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right, arguments->NativeArgAt(1));
bool result = right.IsNull() ? false : (left.value() > right.value());
if (FLAG_trace_intrinsified_natives) {
OS::Print("Double_greaterThan %s > %s\n",
@@ -125,14 +125,14 @@
DEFINE_NATIVE_ENTRY(Double_greaterThanFromInteger, 2) {
const Double& right = Double::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
return Bool::Get(left.AsDoubleValue() > right.value());
}
DEFINE_NATIVE_ENTRY(Double_equal, 2) {
const Double& left = Double::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Double, right, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, right, arguments->NativeArgAt(1));
bool result = right.IsNull() ? false : (left.value() == right.value());
if (FLAG_trace_intrinsified_natives) {
OS::Print("Double_equal %s == %s\n",
@@ -144,7 +144,7 @@
DEFINE_NATIVE_ENTRY(Double_equalToInteger, 2) {
const Double& left = Double::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, right, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, right, arguments->NativeArgAt(1));
return Bool::Get(left.value() == right.AsDoubleValue());
}
@@ -174,7 +174,8 @@
DEFINE_NATIVE_ENTRY(Double_pow, 2) {
const double operand =
Double::CheckedHandle(arguments->NativeArgAt(0)).value();
- GET_NATIVE_ARGUMENT(Double, exponent_object, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ Double, exponent_object, arguments->NativeArgAt(1));
const double exponent = exponent_object.value();
return Double::New(pow(operand, exponent));
}
@@ -205,7 +206,7 @@
DEFINE_NATIVE_ENTRY(Double_parse, 1) {
- GET_NATIVE_ARGUMENT(String, value, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, value, arguments->NativeArgAt(0));
const String& dummy_key = String::Handle(Symbols::Empty());
Scanner scanner(value, dummy_key);
const Scanner::GrowableTokenStream& tokens = scanner.GetStream();
@@ -265,7 +266,7 @@
static const double kUpperBoundary = 1e21;
const Double& arg = Double::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, fraction_digits, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, fraction_digits, arguments->NativeArgAt(1));
double d = arg.value();
intptr_t fraction_digits_value = fraction_digits.Value();
if (0 <= fraction_digits_value && fraction_digits_value <= 20
@@ -283,7 +284,7 @@
DEFINE_NATIVE_ENTRY(Double_toStringAsExponential, 2) {
const Double& arg = Double::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, fraction_digits, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, fraction_digits, arguments->NativeArgAt(1));
double d = arg.value();
intptr_t fraction_digits_value = fraction_digits.Value();
if (-1 <= fraction_digits_value && fraction_digits_value <= 20) {
@@ -301,7 +302,7 @@
DEFINE_NATIVE_ENTRY(Double_toStringAsPrecision, 2) {
const Double& arg = Double::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, precision, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, precision, arguments->NativeArgAt(1));
double d = arg.value();
intptr_t precision_value = precision.Value();
if (1 <= precision_value && precision_value <= 21) {
diff --git a/runtime/lib/error.cc b/runtime/lib/error.cc
index 247b555..ea39065 100644
--- a/runtime/lib/error.cc
+++ b/runtime/lib/error.cc
@@ -82,7 +82,7 @@
// Arg0: index of the case clause token into which we fall through.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(FallThroughError_throwNew, 1) {
- GET_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
intptr_t fallthrough_pos = smi_pos.Value();
// Allocate a new instance of type FallThroughError.
@@ -114,8 +114,8 @@
// Arg1: class name of the abstract class that cannot be instantiated.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(AbstractClassInstantiationError_throwNew, 2) {
- GET_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(String, class_name, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, class_name, arguments->NativeArgAt(1));
intptr_t error_pos = smi_pos.Value();
// Allocate a new instance of type AbstractClassInstantiationError.
@@ -141,13 +141,19 @@
}
+// TODO(regis): This helper is used to compile a throw when a call cannot be
+// resolved at compile time. The thrown instance of NoSuchMethodError is of a
+// different type than a NoSuchMethodError thrown at runtime. This should be
+// merged.
+//
// Allocate and throw NoSuchMethodError.
// Arg0: index of the call that was not resolved at compile time.
// Arg1: name of the method that was not resolved at compile time.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(NoSuchMethodError_throwNew, 2) {
- GET_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(String, function_name, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ String, function_name, arguments->NativeArgAt(1));
intptr_t call_pos = smi_pos.Value();
// Allocate a new instance of type NoSuchMethodError.
const Instance& error = Instance::Handle(
diff --git a/runtime/lib/error.dart b/runtime/lib/error.dart
index b3ce3b7..7aa960f 100644
--- a/runtime/lib/error.dart
+++ b/runtime/lib/error.dart
@@ -91,7 +91,8 @@
}
-// TODO(regis): This class will change once mirrors are available.
+// TODO(regis): This class should be removed and the corresponding class in the
+// core lib should be used.
class NoSuchMethodErrorImplementation implements NoSuchMethodError {
factory NoSuchMethodErrorImplementation._uninstantiable() {
throw new UnsupportedError(
diff --git a/runtime/lib/growable_array.cc b/runtime/lib/growable_array.cc
index 6168d75..ac679af 100644
--- a/runtime/lib/growable_array.cc
+++ b/runtime/lib/growable_array.cc
@@ -17,7 +17,7 @@
AbstractTypeArguments::CheckedHandle(arguments->NativeArgAt(0));
ASSERT(type_arguments.IsNull() ||
(type_arguments.IsInstantiated() && (type_arguments.Length() == 1)));
- GET_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
if ((data.Length() <= 0)) {
const Integer& index = Integer::Handle(Integer::New(data.Length()));
GrowableArray<const Object*> args;
@@ -34,7 +34,7 @@
DEFINE_NATIVE_ENTRY(GrowableObjectArray_getIndexed, 2) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
if ((index.Value() < 0) || (index.Value() >= array.Length())) {
GrowableArray<const Object*> args;
args.Add(&index);
@@ -48,13 +48,13 @@
DEFINE_NATIVE_ENTRY(GrowableObjectArray_setIndexed, 3) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
if ((index.Value() < 0) || (index.Value() >= array.Length())) {
GrowableArray<const Object*> args;
args.Add(&index);
Exceptions::ThrowByType(Exceptions::kRange, args);
}
- GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(2));
array.SetAt(index.Value(), value);
return Object::null();
}
@@ -77,7 +77,7 @@
DEFINE_NATIVE_ENTRY(GrowableObjectArray_setLength, 2) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1));
if ((length.Value() < 0) || (length.Value() > array.Capacity())) {
GrowableArray<const Object*> args;
args.Add(&length);
@@ -91,7 +91,7 @@
DEFINE_NATIVE_ENTRY(GrowableObjectArray_setData, 2) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
ASSERT(data.Length() > 0);
array.SetData(data);
return Object::null();
diff --git a/runtime/lib/identical_patch.dart b/runtime/lib/identical_patch.dart
new file mode 100644
index 0000000..218b3c8
--- /dev/null
+++ b/runtime/lib/identical_patch.dart
@@ -0,0 +1,7 @@
+// 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.
+
+patch bool identical(Object a, Object b) {
+ throw new Error('Should not reach the body of identical');
+}
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index f81e078..0b10b38a 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -38,7 +38,7 @@
DEFINE_NATIVE_ENTRY(Integer_bitAndFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
@@ -53,7 +53,7 @@
DEFINE_NATIVE_ENTRY(Integer_bitOrFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
@@ -68,7 +68,7 @@
DEFINE_NATIVE_ENTRY(Integer_bitXorFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
@@ -83,7 +83,7 @@
DEFINE_NATIVE_ENTRY(Integer_addFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
if (FLAG_trace_intrinsified_natives) {
@@ -98,7 +98,7 @@
DEFINE_NATIVE_ENTRY(Integer_subFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
if (FLAG_trace_intrinsified_natives) {
@@ -113,7 +113,7 @@
DEFINE_NATIVE_ENTRY(Integer_mulFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
if (FLAG_trace_intrinsified_natives) {
@@ -128,7 +128,7 @@
DEFINE_NATIVE_ENTRY(Integer_truncDivFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(left_int));
ASSERT(!right_int.IsZero());
@@ -140,7 +140,7 @@
DEFINE_NATIVE_ENTRY(Integer_moduloFromInteger, 2) {
const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right_int));
ASSERT(CheckInteger(right_int));
if (FLAG_trace_intrinsified_natives) {
@@ -159,7 +159,7 @@
DEFINE_NATIVE_ENTRY(Integer_greaterThanFromInteger, 2) {
const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1));
ASSERT(CheckInteger(right));
ASSERT(CheckInteger(left));
if (FLAG_trace_intrinsified_natives) {
@@ -172,7 +172,7 @@
DEFINE_NATIVE_ENTRY(Integer_equalToInteger, 2) {
const Integer& left = Integer::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, right, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, right, arguments->NativeArgAt(1));
ASSERT(CheckInteger(left));
ASSERT(CheckInteger(right));
if (FLAG_trace_intrinsified_natives) {
@@ -184,7 +184,7 @@
DEFINE_NATIVE_ENTRY(Integer_parse, 1) {
- GET_NATIVE_ARGUMENT(String, value, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, value, arguments->NativeArgAt(0));
const String& dummy_key = String::Handle(Symbols::Empty());
Scanner scanner(value, dummy_key);
const Scanner::GrowableTokenStream& tokens = scanner.GetStream();
@@ -257,7 +257,7 @@
DEFINE_NATIVE_ENTRY(Smi_shrFromInt, 2) {
const Smi& amount = Smi::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1));
ASSERT(CheckInteger(amount));
ASSERT(CheckInteger(value));
const Integer& result = Integer::Handle(
@@ -269,7 +269,7 @@
DEFINE_NATIVE_ENTRY(Smi_shlFromInt, 2) {
const Smi& amount = Smi::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1));
ASSERT(CheckInteger(amount));
ASSERT(CheckInteger(value));
if (FLAG_trace_intrinsified_natives) {
diff --git a/runtime/lib/invocation_mirror_patch.dart b/runtime/lib/invocation_mirror_patch.dart
index 4fb83f9..e81fe50 100644
--- a/runtime/lib/invocation_mirror_patch.dart
+++ b/runtime/lib/invocation_mirror_patch.dart
@@ -7,16 +7,51 @@
static final int GETTER = 1;
static final int SETTER = 2;
+ // TODO(regis): Compute lazily the value of these fields, and save the
+ // arguments passed into _allocateInvocationMirror.
+
final String memberName;
final List positionalArguments;
- final Map<String,dynamic> namedArguments = null;
+ final Map<String, dynamic> namedArguments;
final int _type;
- _InvocationMirror(this.memberName, this._type, this.positionalArguments);
+ _InvocationMirror(this.memberName,
+ this._type,
+ this.positionalArguments,
+ this.namedArguments);
- static _allocateInvocationMirror(name, arguments) {
- return new _InvocationMirror(name, METHOD, arguments);
+ static _allocateInvocationMirror(String name,
+ List argumentsDescriptor,
+ List arguments) {
+ var memberName;
+ var type;
+ if (name.startsWith("get:")) {
+ type = GETTER;
+ memberName = name.substring(4);
+ } else if (name.startsWith("set:")) {
+ type = SETTER;
+ memberName = name.substring(4).concat("=");
+ } else {
+ type = METHOD;
+ memberName = name;
+ }
+ // Exclude receiver.
+ int numArguments = argumentsDescriptor[0] - 1;
+ int numPositionalArguments = argumentsDescriptor[1] - 1;
+ int numNamedArguments = numArguments - numPositionalArguments;
+ List positionalArguments = arguments.getRange(1, numPositionalArguments);
+ Map<String, dynamic> namedArguments;
+ if (numNamedArguments > 0) {
+ namedArguments = new Map<String, dynamic>();
+ for (int i = 0; i < numNamedArguments; i++) {
+ String arg_name = argumentsDescriptor[2 + 2*i];
+ var arg_value = arguments[argumentsDescriptor[3 + 2*i]];
+ namedArguments[arg_name] = arg_value;
+ }
+ }
+ return new _InvocationMirror(memberName, type,
+ positionalArguments, namedArguments);
}
bool get isMethod => _type == METHOD;
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 3585ba9..abb15ba 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -125,17 +125,17 @@
DEFINE_NATIVE_ENTRY(ReceivePortImpl_closeInternal, 1) {
- GET_NATIVE_ARGUMENT(Smi, id, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, id, arguments->NativeArgAt(0));
PortMap::ClosePort(id.Value());
return Object::null();
}
DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 3) {
- GET_NATIVE_ARGUMENT(Smi, send_id, arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, reply_id, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, send_id, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, reply_id, arguments->NativeArgAt(1));
// TODO(iposva): Allow for arbitrary messages to be sent.
- GET_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(2));
uint8_t* data = NULL;
MessageWriter writer(&data, &allocator);
@@ -398,7 +398,7 @@
DEFINE_NATIVE_ENTRY(isolate_spawnFunction, 1) {
- GET_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(0));
bool throw_exception = false;
Function& func = Function::Handle();
if (closure.IsClosure()) {
@@ -427,7 +427,7 @@
DEFINE_NATIVE_ENTRY(isolate_spawnUri, 1) {
- GET_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(0));
// Canonicalize the uri with respect to the current isolate.
char* error = NULL;
diff --git a/runtime/lib/isolate_patch.dart b/runtime/lib/isolate_patch.dart
index 451b219..ad3864f 100644
--- a/runtime/lib/isolate_patch.dart
+++ b/runtime/lib/isolate_patch.dart
@@ -127,3 +127,37 @@
patch spawnUri(String uri) native "isolate_spawnUri";
+patch class Timer {
+ /* patch */ factory Timer(int milliseconds, void callback(Timer timer)) {
+ if (_TimerFactory._factory == null) {
+ throw new UnsupportedError("Timer interface not supported.");
+ }
+ return _TimerFactory._factory(milliseconds, callback, false);
+ }
+
+ /**
+ * Creates a new repeating timer. The [callback] is invoked every
+ * [milliseconds] millisecond until cancelled.
+ */
+ /* patch */ factory Timer.repeating(int milliseconds,
+ void callback(Timer timer)) {
+ if (_TimerFactory._factory == null) {
+ throw new UnsupportedError("Timer interface not supported.");
+ }
+ return _TimerFactory._factory(milliseconds, callback, true);
+ }
+}
+
+typedef Timer _TimerFactoryClosure(int milliseconds,
+ void callback(Timer timer),
+ bool repeating);
+
+class _TimerFactory {
+ static _TimerFactoryClosure _factory;
+}
+
+// TODO(ahe): Warning: this is NOT called by Dartium. Instead, it sets
+// [_TimerFactory._factory] directly.
+void _setTimerFactoryClosure(_TimerFactoryClosure closure) {
+ _TimerFactory._factory = closure;
+}
diff --git a/runtime/lib/lib_sources.gypi b/runtime/lib/lib_sources.gypi
index 28c9afd..3de2cde 100644
--- a/runtime/lib/lib_sources.gypi
+++ b/runtime/lib/lib_sources.gypi
@@ -6,6 +6,7 @@
{
'sources': [
+ 'bool_patch.dart',
'date.cc',
'date_patch.dart',
'array.cc',
@@ -22,6 +23,7 @@
'function_patch.dart',
'growable_array.cc',
'growable_array.dart',
+ 'identical_patch.dart',
'immutable_map.dart',
'integers.cc',
'integers.dart',
diff --git a/runtime/lib/math.cc b/runtime/lib/math.cc
index 5a434c4..1116dd36 100644
--- a/runtime/lib/math.cc
+++ b/runtime/lib/math.cc
@@ -16,53 +16,53 @@
namespace dart {
DEFINE_NATIVE_ENTRY(Math_sqrt, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(sqrt(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_sin, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(sin(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_cos, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(cos(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_tan, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(tan(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_asin, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(asin(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_acos, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(acos(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_atan, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(atan(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_atan2, 2) {
- GET_NATIVE_ARGUMENT(Double, operand1, arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Double, operand2, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand1, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand2, arguments->NativeArgAt(1));
return Double::New(atan2_ieee(operand1.value(), operand2.value()));
}
DEFINE_NATIVE_ENTRY(Math_exp, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(exp(operand.value()));
}
DEFINE_NATIVE_ENTRY(Math_log, 1) {
- GET_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Double, operand, arguments->NativeArgAt(0));
return Double::New(log(operand.value()));
}
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index afd1bc5..fce7051 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -20,7 +20,7 @@
DEFINE_NATIVE_ENTRY(Mirrors_isLocalPort, 1) {
- GET_NATIVE_ARGUMENT(Instance, port, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, port, arguments->NativeArgAt(0));
// Get the port id from the SendPort instance.
const Object& id_obj = Object::Handle(DartLibraryCalls::PortGetId(port));
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index 6f1fef5..3b06b58 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -17,36 +17,39 @@
}
-DEFINE_NATIVE_ENTRY(Object_noSuchMethod, 3) {
- const Instance& instance =
- Instance::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(String, function_name, arguments->NativeArgAt(1));
- GET_NATIVE_ARGUMENT(Array, func_args, arguments->NativeArgAt(2));
- const Object& null_object = Object::Handle(Object::null());
- GrowableArray<const Object*> dart_arguments(4);
+DEFINE_NATIVE_ENTRY(Object_noSuchMethod, 5) {
+ const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Bool, is_method, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, member_name, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, func_args, arguments->NativeArgAt(3));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ Instance, func_named_args, arguments->NativeArgAt(4));
+ GrowableArray<const Object*> dart_arguments(5);
dart_arguments.Add(&instance);
- dart_arguments.Add(&function_name);
+ dart_arguments.Add(&member_name);
dart_arguments.Add(&func_args);
- dart_arguments.Add(&null_object);
+ dart_arguments.Add(&func_named_args);
- // Report if a function with same name (but different arguments) has been
- // found.
- Class& instance_class = Class::Handle(instance.clazz());
- Function& function =
- Function::Handle(instance_class.LookupDynamicFunction(function_name));
- while (function.IsNull()) {
- instance_class = instance_class.SuperClass();
- if (instance_class.IsNull()) break;
- function = instance_class.LookupDynamicFunction(function_name);
- }
- if (!function.IsNull()) {
- const int total_num_parameters = function.NumParameters();
- const Array& array = Array::Handle(Array::New(total_num_parameters - 1));
- // Skip receiver.
- for (int i = 1; i < total_num_parameters; i++) {
- array.SetAt(i - 1, String::Handle(function.ParameterNameAt(i)));
+ if (is_method.value()) {
+ // Report if a function with same name (but different arguments) has been
+ // found.
+ Class& instance_class = Class::Handle(instance.clazz());
+ Function& function =
+ Function::Handle(instance_class.LookupDynamicFunction(member_name));
+ while (function.IsNull()) {
+ instance_class = instance_class.SuperClass();
+ if (instance_class.IsNull()) break;
+ function = instance_class.LookupDynamicFunction(member_name);
}
- dart_arguments.Add(&array);
+ if (!function.IsNull()) {
+ const int total_num_parameters = function.NumParameters();
+ const Array& array = Array::Handle(Array::New(total_num_parameters - 1));
+ // Skip receiver.
+ for (int i = 1; i < total_num_parameters; i++) {
+ array.SetAt(i - 1, String::Handle(function.ParameterNameAt(i)));
+ }
+ dart_arguments.Add(&array);
+ }
}
Exceptions::ThrowByType(Exceptions::kNoSuchMethod, dart_arguments);
return Object::null();
diff --git a/runtime/lib/object_patch.dart b/runtime/lib/object_patch.dart
index 401d94f..d29a4ae 100644
--- a/runtime/lib/object_patch.dart
+++ b/runtime/lib/object_patch.dart
@@ -23,13 +23,17 @@
// A statically dispatched version of Object.toString.
static String _toString(obj) native "Object_toString";
- dynamic _noSuchMethod(String functionName, List args)
+ _noSuchMethod(bool isMethod,
+ String memberName,
+ List arguments,
+ Map<String, dynamic> namedArguments)
native "Object_noSuchMethod";
- /* patch */ dynamic noSuchMethod(InvocationMirror invocation) {
- var methodName = invocation.memberName;
- var args = invocation.positionalArguments;
- return _noSuchMethod(methodName, args);
+ /* patch */ noSuchMethod(InvocationMirror invocation) {
+ return _noSuchMethod(invocation.isMethod,
+ invocation.memberName,
+ invocation.positionalArguments,
+ invocation.namedArguments);
}
/* patch */ Type get runtimeType native "Object_runtimeType";
diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc
index 969d5ff..f55767c 100644
--- a/runtime/lib/regexp.cc
+++ b/runtime/lib/regexp.cc
@@ -15,9 +15,11 @@
DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_factory, 4) {
ASSERT(AbstractTypeArguments::CheckedHandle(
arguments->NativeArgAt(0)).IsNull());
- GET_NATIVE_ARGUMENT(String, pattern, arguments->NativeArgAt(1));
- GET_NATIVE_ARGUMENT(Instance, handle_multi_line, arguments->NativeArgAt(2));
- GET_NATIVE_ARGUMENT(Instance, handle_ignore_case, arguments->NativeArgAt(3));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, pattern, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ Instance, handle_multi_line, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ Instance, handle_ignore_case, arguments->NativeArgAt(3));
bool ignore_case = handle_ignore_case.raw() == Bool::True();
bool multi_line = handle_multi_line.raw() == Bool::True();
return Jscre::Compile(pattern, multi_line, ignore_case);
@@ -65,8 +67,8 @@
DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_ExecuteMatch, 3) {
const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->NativeArgAt(0));
ASSERT(!regexp.IsNull());
- GET_NATIVE_ARGUMENT(String, str, arguments->NativeArgAt(1));
- GET_NATIVE_ARGUMENT(Smi, start_index, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, str, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_index, arguments->NativeArgAt(2));
return Jscre::Execute(regexp, str, start_index.Value());
}
diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc
index 8f7b028..528a179 100644
--- a/runtime/lib/string.cc
+++ b/runtime/lib/string.cc
@@ -13,7 +13,7 @@
namespace dart {
DEFINE_NATIVE_ENTRY(StringBase_createFromCodePoints, 1) {
- GET_NATIVE_ARGUMENT(Array, a, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Array, a, arguments->NativeArgAt(0));
// TODO(srdjan): Check that parameterized type is an int.
Zone* zone = isolate->current_zone();
intptr_t array_len = a.Length();
@@ -53,8 +53,8 @@
DEFINE_NATIVE_ENTRY(StringBase_substringUnchecked, 3) {
const String& receiver = String::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1));
- GET_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2));
intptr_t start = start_obj.Value();
intptr_t end = end_obj.Value();
@@ -65,8 +65,8 @@
DEFINE_NATIVE_ENTRY(OneByteString_substringUnchecked, 3) {
const String& receiver = String::CheckedHandle(arguments->NativeArgAt(0));
ASSERT(receiver.IsOneByteString());
- GET_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1));
- GET_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_obj, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, end_obj, arguments->NativeArgAt(2));
const intptr_t start = start_obj.Value();
const intptr_t end = end_obj.Value();
@@ -79,7 +79,7 @@
const String& receiver = String::CheckedHandle(isolate,
arguments->NativeArgAt(0));
ASSERT(receiver.IsOneByteString());
- GET_NATIVE_ARGUMENT(Smi, smi_split_code, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_split_code, arguments->NativeArgAt(1));
const intptr_t len = receiver.Length();
const intptr_t split_code = smi_split_code.Value();
const GrowableObjectArray& result = GrowableObjectArray::Handle(
@@ -94,7 +94,7 @@
start,
(i - start),
Heap::kNew);
- result.Add(isolate, str);
+ result.Add(str);
start = i + 1;
}
}
@@ -102,7 +102,7 @@
start,
(i - start),
Heap::kNew);
- result.Add(isolate, str);
+ result.Add(str);
return result.raw();
}
@@ -145,7 +145,7 @@
DEFINE_NATIVE_ENTRY(String_charAt, 2) {
const String& receiver = String::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
uint32_t value = StringValueAt(receiver, index);
ASSERT(value <= 0x10FFFF);
return Symbols::FromCharCode(value);
@@ -153,7 +153,7 @@
DEFINE_NATIVE_ENTRY(String_charCodeAt, 2) {
const String& receiver = String::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
int32_t value = StringValueAt(receiver, index);
ASSERT(value >= 0);
@@ -164,7 +164,7 @@
DEFINE_NATIVE_ENTRY(String_concat, 2) {
const String& receiver = String::CheckedHandle(arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(String, b, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, b, arguments->NativeArgAt(1));
return String::Concat(receiver, b);
}
@@ -184,7 +184,7 @@
DEFINE_NATIVE_ENTRY(Strings_concatAll, 1) {
- GET_NATIVE_ARGUMENT(Array, strings, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Array, strings, arguments->NativeArgAt(0));
ASSERT(!strings.IsNull());
// Check that the array contains strings.
Instance& elem = Instance::Handle();
diff --git a/runtime/lib/weak_property.cc b/runtime/lib/weak_property.cc
index 657b42c..80da580 100644
--- a/runtime/lib/weak_property.cc
+++ b/runtime/lib/weak_property.cc
@@ -11,8 +11,8 @@
namespace dart {
DEFINE_NATIVE_ENTRY(WeakProperty_new, 2) {
- GET_NATIVE_ARGUMENT(Instance, key, arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, key, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(1));
const WeakProperty& weak_property = WeakProperty::Handle(WeakProperty::New());
weak_property.set_key(key);
weak_property.set_value(value);
@@ -21,20 +21,23 @@
DEFINE_NATIVE_ENTRY(WeakProperty_getKey, 1) {
- GET_NATIVE_ARGUMENT(WeakProperty, weak_property, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ WeakProperty, weak_property, arguments->NativeArgAt(0));
return weak_property.key();
}
DEFINE_NATIVE_ENTRY(WeakProperty_getValue, 1) {
- GET_NATIVE_ARGUMENT(WeakProperty, weak_property, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ WeakProperty, weak_property, arguments->NativeArgAt(0));
return weak_property.value();
}
DEFINE_NATIVE_ENTRY(WeakProperty_setValue, 2) {
- GET_NATIVE_ARGUMENT(WeakProperty, weak_property, arguments->NativeArgAt(0));
- GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(
+ WeakProperty, weak_property, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(1));
weak_property.set_value(value);
return Object::null();
}
diff --git a/runtime/vm/assembler.cc b/runtime/vm/assembler.cc
index 32b4812..261fdaf 100644
--- a/runtime/vm/assembler.cc
+++ b/runtime/vm/assembler.cc
@@ -151,7 +151,7 @@
void AssemblerBuffer::EmitObject(const Object& object) {
// Since we are going to store the handle as part of the fixup information
// the handle needs to be a zone handle.
- ASSERT(object.IsZoneHandle());
+ ASSERT(object.IsNotTemporaryScopedHandle());
ASSERT(object.IsOld());
EmitFixup(new PatchCodeWithHandle(pointer_offsets_, object));
cursor_ += kWordSize; // Reserve space for pointer.
diff --git a/runtime/vm/assembler_ia32.cc b/runtime/vm/assembler_ia32.cc
index 72f84ed..017c98f 100644
--- a/runtime/vm/assembler_ia32.cc
+++ b/runtime/vm/assembler_ia32.cc
@@ -1554,7 +1554,7 @@
if (object.IsSmi() || object.IsNull()) {
movl(dst, Immediate(reinterpret_cast<int32_t>(object.raw())));
} else {
- ASSERT(object.IsZoneHandle());
+ ASSERT(object.IsNotTemporaryScopedHandle());
ASSERT(object.IsOld());
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xB8 + dst);
@@ -1567,7 +1567,7 @@
if (object.IsSmi() || object.IsNull()) {
pushl(Immediate(reinterpret_cast<int32_t>(object.raw())));
} else {
- ASSERT(object.IsZoneHandle());
+ ASSERT(object.IsNotTemporaryScopedHandle());
ASSERT(object.IsOld());
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x68);
@@ -1580,7 +1580,7 @@
if (object.IsSmi() || object.IsNull()) {
cmpl(reg, Immediate(reinterpret_cast<int32_t>(object.raw())));
} else {
- ASSERT(object.IsZoneHandle());
+ ASSERT(object.IsNotTemporaryScopedHandle());
ASSERT(object.IsOld());
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
if (reg == EAX) {
diff --git a/runtime/vm/assembler_x64.cc b/runtime/vm/assembler_x64.cc
index 04feed8..075a73c 100644
--- a/runtime/vm/assembler_x64.cc
+++ b/runtime/vm/assembler_x64.cc
@@ -1652,7 +1652,7 @@
if (object.IsSmi() || object.IsNull()) {
movq(dst, Immediate(reinterpret_cast<int64_t>(object.raw())));
} else {
- ASSERT(object.IsZoneHandle());
+ ASSERT(object.IsNotTemporaryScopedHandle());
ASSERT(object.IsOld());
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitRegisterREX(dst, REX_W);
@@ -1666,7 +1666,7 @@
if (object.IsSmi() || object.IsNull()) {
movq(dst, Immediate(reinterpret_cast<int64_t>(object.raw())));
} else {
- ASSERT(object.IsZoneHandle());
+ ASSERT(object.IsNotTemporaryScopedHandle());
ASSERT(object.IsOld());
LoadObject(TMP, object);
movq(dst, TMP);
diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h
index f3267ab..1be3a90 100644
--- a/runtime/vm/ast.h
+++ b/runtime/vm/ast.h
@@ -303,7 +303,7 @@
public:
LiteralNode(intptr_t token_pos, const Instance& literal)
: AstNode(token_pos), literal_(literal) {
- ASSERT(literal_.IsZoneHandle());
+ ASSERT(literal_.IsNotTemporaryScopedHandle());
ASSERT(literal_.IsSmi() || literal_.IsOld());
#if defined(DEBUG)
if (literal_.IsString()) {
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index 24c8884..a7bd996 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -14,7 +14,7 @@
// List of bootstrap native entry points used in the core dart library.
#define BOOTSTRAP_NATIVE_LIST(V) \
V(Object_toString, 1) \
- V(Object_noSuchMethod, 3) \
+ V(Object_noSuchMethod, 5) \
V(Object_runtimeType, 1) \
V(AbstractType_toString, 1) \
V(Integer_bitAndFromInteger, 2) \
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index c7e2ad1..69f033e 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -436,9 +436,8 @@
}
const Class& target_class = Class::Handle(type.type_class());
String& target_class_name = String::Handle(target_class.Name());
- const String& period = String::Handle(Symbols::Dot());
String& target_name = String::Handle(
- String::Concat(target_class_name, period));
+ String::Concat(target_class_name, Symbols::DotHandle()));
const String& identifier = String::Handle(factory.RedirectionIdentifier());
if (!identifier.IsNull()) {
target_name = String::Concat(target_name, identifier);
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index 6b84f7b..78f0625 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -1000,6 +1000,64 @@
}
+// Handle a miss of a megamorphic cache.
+// Arg0: Receiver.
+// Arg1: ICData object.
+// Arg2: Arguments descriptor array.
+
+// Returns: target instructions to call or null if the
+// InstanceFunctionLookup stub should be used (e.g., to invoke no such
+// method and implicit closures)..
+DEFINE_RUNTIME_ENTRY(MegamorphicCacheMissHandler, 3) {
+ ASSERT(arguments.ArgCount() ==
+ kMegamorphicCacheMissHandlerRuntimeEntry.argument_count());
+ const Instance& receiver = Instance::CheckedHandle(arguments.ArgAt(0));
+ const ICData& ic_data = ICData::CheckedHandle(arguments.ArgAt(1));
+ const Array& descriptor = Array::CheckedHandle(arguments.ArgAt(2));
+ const String& name = String::Handle(ic_data.target_name());
+ const MegamorphicCache& cache = MegamorphicCache::Handle(
+ isolate->megamorphic_cache_table()->Lookup(name, descriptor));
+ Class& cls = Class::Handle(receiver.clazz());
+ // For lookups treat null as an instance of class Object.
+ if (cls.IsNullClass()) {
+ cls = isolate->object_store()->object_class();
+ }
+ ASSERT(!cls.IsNull());
+ if (FLAG_trace_ic || FLAG_trace_ic_miss_in_optimized) {
+ OS::Print("Megamorphic IC miss, class=%s, function=%s\n",
+ cls.ToCString(), name.ToCString());
+ }
+
+ intptr_t arg_count =
+ Smi::Cast(Object::Handle(descriptor.At(0))).Value();
+ intptr_t named_arg_count =
+ arg_count - Smi::Cast(Object::Handle(descriptor.At(1))).Value();
+ const Function& target = Function::Handle(
+ Resolver::ResolveDynamicForReceiverClass(cls,
+ name,
+ arg_count,
+ named_arg_count));
+
+ Instructions& instructions = Instructions::Handle();
+ if (!target.IsNull()) {
+ if (!target.HasCode()) {
+ const Error& error =
+ Error::Handle(Compiler::CompileFunction(target));
+ if (!error.IsNull()) Exceptions::PropagateError(error);
+ }
+ ASSERT(target.HasCode());
+ instructions = Code::Handle(target.CurrentCode()).instructions();
+ }
+ arguments.SetReturn(instructions);
+ if (instructions.IsNull()) return;
+
+ cache.EnsureCapacity();
+ const Smi& class_id = Smi::Handle(Smi::New(cls.id()));
+ cache.Insert(class_id, target);
+ return;
+}
+
+
// Updates IC data for two arguments. Used by the equality operation when
// the control flow bypasses regular inline cache (null arguments).
// Arg0: Receiver object.
@@ -1126,9 +1184,9 @@
getter_function_name,
kNumArguments,
kNumNamedArguments));
- Code& code = Code::Handle();
+ const Object& null_object = Object::Handle();
if (function.IsNull()) {
- arguments.SetReturn(code);
+ arguments.SetReturn(null_object);
return; // No getter function found so can't be an implicit closure.
}
GrowableArray<const Object*> invoke_arguments(0);
@@ -1141,7 +1199,7 @@
if (result.IsError()) {
if (result.IsUnhandledException()) {
// If the getter throws an exception, treat as no such method.
- arguments.SetReturn(code);
+ arguments.SetReturn(null_object);
return;
} else {
Exceptions::PropagateError(Error::Cast(result));
@@ -1168,7 +1226,6 @@
// TODO(regis): Resolve and invoke "call" method, if existing.
- const Object& null_object = Object::Handle();
dart_arguments.Add(&result);
dart_arguments.Add(&function_name);
dart_arguments.Add(&function_args);
@@ -1207,7 +1264,7 @@
ASSERT(arguments.ArgCount() ==
kInvokeImplicitClosureFunctionRuntimeEntry.argument_count());
const Instance& closure = Instance::CheckedHandle(arguments.ArgAt(0));
- const Array& arg_descriptor = Array::CheckedHandle(arguments.ArgAt(1));
+ const Array& args_descriptor = Array::CheckedHandle(arguments.ArgAt(1));
const Array& func_arguments = Array::CheckedHandle(arguments.ArgAt(2));
const Function& function = Function::Handle(Closure::function(closure));
ASSERT(!function.IsNull());
@@ -1223,24 +1280,24 @@
const Instructions& instrs = Instructions::Handle(code.instructions());
ASSERT(!instrs.IsNull());
- // Receiver parameter has already been skipped by caller.
// The closure object is passed as implicit first argument to closure
// functions, since it may be needed to throw a NoSuchMethodError, in case
// the wrong number of arguments is passed.
- GrowableArray<const Object*> invoke_arguments(func_arguments.Length() + 1);
+ // Replace the original receiver in the arguments array by the closure.
+ GrowableArray<const Object*> invoke_arguments(func_arguments.Length());
invoke_arguments.Add(&closure);
- for (intptr_t i = 0; i < func_arguments.Length(); i++) {
+ for (intptr_t i = 1; i < func_arguments.Length(); i++) {
const Object& value = Object::Handle(func_arguments.At(i));
invoke_arguments.Add(&value);
}
- // Now Call the invoke stub which will invoke the closure.
+ // Now call the invoke stub which will invoke the closure.
DartEntry::invokestub entrypoint = reinterpret_cast<DartEntry::invokestub>(
StubCode::InvokeDartCodeEntryPoint());
ASSERT(context.isolate() == Isolate::Current());
const Object& result = Object::Handle(
entrypoint(instrs.EntryPoint(),
- arg_descriptor,
+ args_descriptor,
invoke_arguments.data(),
context));
CheckResultError(result);
@@ -1259,12 +1316,9 @@
const Instance& receiver = Instance::CheckedHandle(arguments.ArgAt(0));
const ICData& ic_data = ICData::CheckedHandle(arguments.ArgAt(1));
const String& original_function_name = String::Handle(ic_data.target_name());
- ASSERT(!Array::CheckedHandle(arguments.ArgAt(2)).IsNull());
+ const Array& orig_arguments_desc = Array::CheckedHandle(arguments.ArgAt(2));
const Array& orig_arguments = Array::CheckedHandle(arguments.ArgAt(3));
// Allocate an InvocationMirror object.
- // TODO(regis): Fill in the InvocationMirror object correctly at
- // this point we do not deal with named arguments and treat them
- // all as positional.
const Library& core_lib = Library::Handle(Library::CoreLibrary());
const String& invocation_mirror_name = String::Handle(
Symbols::InvocationMirror());
@@ -1278,8 +1332,9 @@
allocation_function_name,
Resolver::kIsQualified));
ASSERT(!allocation_function.IsNull());
- GrowableArray<const Object*> allocation_arguments(2);
+ GrowableArray<const Object*> allocation_arguments(3);
allocation_arguments.Add(&original_function_name);
+ allocation_arguments.Add(&orig_arguments_desc);
allocation_arguments.Add(&orig_arguments);
const Array& kNoArgumentNames = Array::Handle();
const Object& invocation_mirror = Object::Handle(
@@ -1311,25 +1366,17 @@
// A non-closure object was invoked as a closure, so call the "call" method
// on it.
// Arg0: non-closure object.
-// Arg1: arguments array.
-// TODO(regis): Rename this entry?
-DEFINE_RUNTIME_ENTRY(ReportObjectNotClosure, 2) {
+// Arg1: arguments descriptor.
+// Arg2: arguments array, including non-closure object.
+DEFINE_RUNTIME_ENTRY(InvokeNonClosure, 3) {
ASSERT(arguments.ArgCount() ==
- kReportObjectNotClosureRuntimeEntry.argument_count());
+ kInvokeNonClosureRuntimeEntry.argument_count());
const Instance& instance = Instance::CheckedHandle(arguments.ArgAt(0));
- const Array& function_args = Array::CheckedHandle(arguments.ArgAt(1));
+ const Array& args_descriptor = Array::CheckedHandle(arguments.ArgAt(1));
+ const Array& function_args = Array::CheckedHandle(arguments.ArgAt(2));
+
+ // Resolve and invoke "call" method, if existing.
const String& function_name = String::Handle(Symbols::Call());
- GrowableArray<const Object*> dart_arguments(5);
-
- // TODO(regis): Resolve and invoke "call" method, if existing.
-
- const Object& null_object = Object::Handle();
- dart_arguments.Add(&instance);
- dart_arguments.Add(&function_name);
- dart_arguments.Add(&function_args);
- dart_arguments.Add(&null_object);
-
- // Report if a function "call" with different arguments has been found.
Class& instance_class = Class::Handle(instance.clazz());
Function& function =
Function::Handle(instance_class.LookupDynamicFunction(function_name));
@@ -1339,14 +1386,47 @@
function = instance_class.LookupDynamicFunction(function_name);
}
if (!function.IsNull()) {
- const int total_num_parameters = function.NumParameters();
- const Array& array = Array::Handle(Array::New(total_num_parameters - 1));
- // Skip receiver.
- for (int i = 1; i < total_num_parameters; i++) {
- array.SetAt(i - 1, String::Handle(function.ParameterNameAt(i)));
+ if (!function.HasCode()) {
+ const Error& error = Error::Handle(Compiler::CompileFunction(function));
+ if (!error.IsNull()) {
+ Exceptions::PropagateError(error);
+ }
}
- dart_arguments.Add(&array);
+ const Code& code = Code::Handle(function.CurrentCode());
+ ASSERT(!code.IsNull());
+ const Instructions& instrs = Instructions::Handle(code.instructions());
+ ASSERT(!instrs.IsNull());
+
+ // The non-closure object is passed as implicit first argument (receiver).
+ // It is already included in the arguments array.
+ GrowableArray<const Object*> invoke_arguments(function_args.Length());
+ for (intptr_t i = 0; i < function_args.Length(); i++) {
+ const Object& value = Object::Handle(function_args.At(i));
+ invoke_arguments.Add(&value);
+ }
+
+ // Now call the invoke stub which will invoke the call method.
+ DartEntry::invokestub entrypoint = reinterpret_cast<DartEntry::invokestub>(
+ StubCode::InvokeDartCodeEntryPoint());
+ const Context& context = Context::ZoneHandle(
+ Isolate::Current()->object_store()->empty_context());
+ const Object& result = Object::Handle(
+ entrypoint(instrs.EntryPoint(),
+ args_descriptor,
+ invoke_arguments.data(),
+ context));
+ CheckResultError(result);
+ arguments.SetReturn(result);
+ return;
}
+ const Object& null_object = Object::Handle();
+ GrowableArray<const Object*> dart_arguments(5);
+ dart_arguments.Add(&instance);
+ dart_arguments.Add(&function_name);
+ dart_arguments.Add(&function_args);
+ dart_arguments.Add(&null_object);
+ // If a function "call" with different arguments exists, it will have been
+ // invoked above, so no need to handle this case here.
Exceptions::ThrowByType(Exceptions::kNoSuchMethod, dart_arguments);
UNREACHABLE();
}
diff --git a/runtime/vm/code_generator.h b/runtime/vm/code_generator.h
index de97dc5..87011a9 100644
--- a/runtime/vm/code_generator.h
+++ b/runtime/vm/code_generator.h
@@ -37,10 +37,11 @@
DECLARE_RUNTIME_ENTRY(InstantiateTypeArguments);
DECLARE_RUNTIME_ENTRY(InvokeImplicitClosureFunction);
DECLARE_RUNTIME_ENTRY(InvokeNoSuchMethodFunction);
+DECLARE_RUNTIME_ENTRY(MegamorphicCacheMissHandler);
DECLARE_RUNTIME_ENTRY(OptimizeInvokedFunction);
DECLARE_RUNTIME_ENTRY(TraceICCall);
DECLARE_RUNTIME_ENTRY(PatchStaticCall);
-DECLARE_RUNTIME_ENTRY(ReportObjectNotClosure);
+DECLARE_RUNTIME_ENTRY(InvokeNonClosure);
DECLARE_RUNTIME_ENTRY(ResolveImplicitClosureFunction);
DECLARE_RUNTIME_ENTRY(ResolveImplicitClosureThroughGetter);
DECLARE_RUNTIME_ENTRY(ReThrow);
diff --git a/runtime/vm/code_generator_test.cc b/runtime/vm/code_generator_test.cc
index 90d1e09..4b18bdf 100644
--- a/runtime/vm/code_generator_test.cc
+++ b/runtime/vm/code_generator_test.cc
@@ -396,7 +396,7 @@
function.set_parameter_types(Array::Handle(Array::New(num_params)));
function.set_parameter_names(Array::Handle(Array::New(num_params)));
const Type& param_type = Type::Handle(Type::DynamicType());
- for (int i = 0; i < num_params - 1; i++) {
+ for (int i = 0; i < num_params; i++) {
function.SetParameterTypeAt(i, param_type);
}
const String& native_name =
@@ -465,6 +465,55 @@
Smi::New(0 + 1 + 1 + 2 + 3))
+// Tested Dart code:
+// int sum(a, b, c) native: "TestNonNullSmiSum";
+// The native entry TestNonNullSmiSum implements sum natively.
+CODEGEN_TEST_GENERATE(NativeNonNullSumCodegen, test) {
+ SequenceNode* node_seq = test->node_sequence();
+ const int num_params = 3;
+ LocalScope* local_scope = node_seq->scope();
+ local_scope->AddVariable(NewTestLocalVariable("a"));
+ local_scope->AddVariable(NewTestLocalVariable("b"));
+ local_scope->AddVariable(NewTestLocalVariable("c"));
+ ASSERT(local_scope->num_variables() == num_params);
+ const Function& function = test->function();
+ function.set_is_native(true);
+ function.set_num_fixed_parameters(num_params);
+ ASSERT(!function.HasOptionalParameters());
+ function.set_parameter_types(Array::Handle(Array::New(num_params)));
+ function.set_parameter_names(Array::Handle(Array::New(num_params)));
+ const Type& param_type = Type::Handle(Type::DynamicType());
+ for (int i = 0; i < num_params; i++) {
+ function.SetParameterTypeAt(i, param_type);
+ }
+ const String& native_name =
+ String::ZoneHandle(Symbols::New("TestNonNullSmiSum"));
+ NativeFunction native_function =
+ reinterpret_cast<NativeFunction>(TestNonNullSmiSum);
+ node_seq->Add(new ReturnNode(kPos,
+ new NativeBodyNode(kPos,
+ function,
+ native_name,
+ native_function)));
+}
+
+
+// Tested Dart code, calling function sum declared above:
+// return sum(1, null, 3);
+CODEGEN_TEST2_GENERATE(StaticNonNullSumCallCodegen, function, test) {
+ SequenceNode* node_seq = test->node_sequence();
+ ArgumentListNode* arguments = new ArgumentListNode(kPos);
+ arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1))));
+ arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle()));
+ arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))));
+ node_seq->Add(new ReturnNode(kPos,
+ new StaticCallNode(kPos, function, arguments)));
+}
+CODEGEN_TEST2_RUN(StaticNonNullSumCallCodegen,
+ NativeNonNullSumCodegen,
+ Smi::New(1 + 3))
+
+
// Test allocation of dart objects.
CODEGEN_TEST_GENERATE(AllocateNewObjectCodegen, test) {
const char* kScriptChars =
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index c82be17..6e4fb44 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -550,6 +550,7 @@
RawError* Compiler::CompileAllFunctions(const Class& cls) {
+ Isolate* isolate = Isolate::Current();
Error& error = Error::Handle();
Array& functions = Array::Handle(cls.functions());
Function& func = Function::Handle();
@@ -565,6 +566,8 @@
if (!func.HasCode() &&
!func.is_abstract() &&
!func.IsRedirectingFactory()) {
+ StackZone zone(isolate);
+ HANDLESCOPE(isolate);
error = CompileFunction(func);
if (!error.IsNull()) {
return error.raw();
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index d37c2d7..d6ca37e 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -23,6 +23,8 @@
namespace dart {
+DEFINE_FLAG(bool, heap_profile_initialize, false,
+ "Writes a heap profile on isolate initialization.");
DECLARE_FLAG(bool, print_bootstrap);
DECLARE_FLAG(bool, print_class_table);
DECLARE_FLAG(bool, trace_isolates);
@@ -167,13 +169,19 @@
reader.ReadFullSnapshot();
if (FLAG_trace_isolates) {
isolate->heap()->PrintSizes();
+ isolate->megamorphic_cache_table()->PrintSizes();
}
if (FLAG_print_bootstrap) {
PrintLibrarySources(isolate);
}
}
+ if (FLAG_heap_profile_initialize) {
+ isolate->heap()->ProfileToFile("initialize");
+ }
+
StubCode::Init(isolate);
+ isolate->megamorphic_cache_table()->InitMissHandler();
isolate->heap()->EnableGrowthControl();
isolate->set_init_callback_data(data);
if (FLAG_print_class_table) {
@@ -185,9 +193,6 @@
void Dart::ShutdownIsolate() {
Isolate* isolate = Isolate::Current();
- if (FLAG_trace_isolates) {
- isolate->heap()->PrintSizes();
- }
void* callback_data = isolate->init_callback_data();
isolate->Shutdown();
delete isolate;
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 5e50431..80938c0 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -793,9 +793,9 @@
}
char* chars = NULL;
- intptr_t len = OS::SNPrint(NULL, 0, "%s/%s", script_uri, main) + 1;
+ intptr_t len = OS::SNPrint(NULL, 0, "%s$%s", script_uri, main) + 1;
chars = reinterpret_cast<char*>(malloc(len));
- OS::SNPrint(chars, len, "%s/%s", script_uri, main);
+ OS::SNPrint(chars, len, "%s$%s", script_uri, main);
return chars;
}
@@ -1908,10 +1908,12 @@
const Instance& instance =
Instance::Handle(isolate, GetListInstance(isolate, obj));
if (!instance.IsNull()) {
- String& name = String::Handle(isolate, Symbols::IndexToken());
- const Function& function =
- Function::Handle(isolate,
- Resolver::ResolveDynamic(instance, name, 2, 0));
+ const Function& function = Function::Handle(
+ isolate,
+ Resolver::ResolveDynamic(instance,
+ Symbols::IndexTokenHandle(),
+ 2,
+ 0));
if (!function.IsNull()) {
GrowableArray<const Object*> args(1);
Integer& indexobj = Integer::Handle(isolate);
@@ -1960,10 +1962,12 @@
const Instance& instance =
Instance::Handle(isolate, GetListInstance(isolate, obj));
if (!instance.IsNull()) {
- String& name = String::Handle(isolate, Symbols::AssignIndexToken());
- const Function& function =
- Function::Handle(isolate,
- Resolver::ResolveDynamic(instance, name, 3, 0));
+ const Function& function = Function::Handle(
+ isolate,
+ Resolver::ResolveDynamic(instance,
+ Symbols::AssignIndexTokenHandle(),
+ 3,
+ 0));
if (!function.IsNull()) {
const Integer& index_obj =
Integer::Handle(isolate, Integer::New(index));
@@ -2043,10 +2047,12 @@
const Instance& instance =
Instance::Handle(isolate, GetListInstance(isolate, obj));
if (!instance.IsNull()) {
- String& name = String::Handle(isolate, Symbols::IndexToken());
- const Function& function =
- Function::Handle(isolate,
- Resolver::ResolveDynamic(instance, name, 2, 0));
+ const Function& function = Function::Handle(
+ isolate,
+ Resolver::ResolveDynamic(instance,
+ Symbols::IndexTokenHandle(),
+ 2,
+ 0));
if (!function.IsNull()) {
Object& result = Object::Handle(isolate);
Integer& intobj = Integer::Handle(isolate);
@@ -2132,10 +2138,12 @@
const Instance& instance =
Instance::Handle(isolate, GetListInstance(isolate, obj));
if (!instance.IsNull()) {
- String& name = String::Handle(isolate, Symbols::AssignIndexToken());
- const Function& function =
- Function::Handle(isolate,
- Resolver::ResolveDynamic(instance, name, 3, 0));
+ const Function& function = Function::Handle(
+ isolate,
+ Resolver::ResolveDynamic(instance,
+ Symbols::AssignIndexTokenHandle(),
+ 3,
+ 0));
if (!function.IsNull()) {
Integer& indexobj = Integer::Handle(isolate);
Integer& valueobj = Integer::Handle(isolate);
@@ -2745,8 +2753,7 @@
// Case 4. Lookup the function with a . appended to find the
// unnamed constructor.
if (func.IsNull()) {
- const String& dot = String::Handle(Symbols::Dot());
- tmp_name = String::Concat(func_name, dot);
+ tmp_name = String::Concat(func_name, Symbols::DotHandle());
func = cls.LookupFunction(tmp_name);
}
} else if (obj.IsLibrary()) {
@@ -3313,8 +3320,7 @@
if (name_obj.IsNull()) {
dot_name = Symbols::Dot();
} else if (name_obj.IsString()) {
- const String& dot = String::Handle(isolate, Symbols::Dot());
- dot_name = String::Concat(dot, String::Cast(name_obj));
+ dot_name = String::Concat(Symbols::DotHandle(), String::Cast(name_obj));
} else {
RETURN_TYPE_ERROR(isolate, constructor_name, String);
}
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 78fb68c..493af6c 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -106,7 +106,7 @@
for (int i = 1; i < num_arguments; i++) {
args.Add(arguments[i - 1]);
}
- // Now Call the invoke stub which will invoke the closure.
+ // Now call the invoke stub which will invoke the closure.
invokestub entrypoint = reinterpret_cast<invokestub>(
StubCode::InvokeDartCodeEntryPoint());
ASSERT(context.isolate() == Isolate::Current());
@@ -223,8 +223,8 @@
constructor_arguments.Add(&Smi::Handle(Smi::New(Function::kCtorPhaseAll)));
constructor_arguments.AddArray(arguments);
- const String& period = String::Handle(Symbols::Dot());
- String& constructor_name = String::Handle(String::Concat(class_name, period));
+ String& constructor_name = String::Handle(
+ String::Concat(class_name, Symbols::DotHandle()));
Function& constructor =
Function::Handle(cls.LookupConstructor(constructor_name));
ASSERT(!constructor.IsNull());
@@ -265,7 +265,6 @@
RawObject* DartLibraryCalls::Equals(const Instance& left,
const Instance& right) {
- const String& function_name = String::Handle(Symbols::EqualOperator());
GrowableArray<const Object*> arguments;
arguments.Add(&right);
const int kNumArguments = 2;
@@ -273,7 +272,7 @@
const Array& kNoArgumentNames = Array::Handle();
const Function& function = Function::Handle(
Resolver::ResolveDynamic(left,
- function_name,
+ Symbols::EqualOperatorHandle(),
kNumArguments,
kNumNamedArguments));
ASSERT(!function.IsNull());
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index f56af6a..432a376 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -9,6 +9,7 @@
#include "vm/debugger.h"
#include "vm/flags.h"
#include "vm/object.h"
+#include "vm/object_store.h"
#include "vm/stack_frame.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
@@ -16,7 +17,10 @@
namespace dart {
DEFINE_FLAG(bool, print_stacktrace_at_throw, false,
- "Prints a stack trace everytime a throw occurs.");
+ "Prints a stack trace everytime a throw occurs.");
+DEFINE_FLAG(bool, heap_profile_out_of_memory, false,
+ "Writes a heap profile on unhandled out-of-memory exceptions.");
+DECLARE_FLAG(bool, heap_profile_out_of_memory);
const char* Exceptions::kCastErrorDstName = "type cast";
@@ -190,6 +194,12 @@
exception,
stacktrace);
} else {
+ if (FLAG_heap_profile_out_of_memory) {
+ Isolate* isolate = Isolate::Current();
+ if (exception.raw() == isolate->object_store()->out_of_memory()) {
+ isolate->heap()->ProfileToFile("out-of-memory");
+ }
+ }
// No dart exception handler found in this invocation sequence,
// so we create an unhandled exception object and return to the
// invocation stub so that it returns this unhandled exception
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index f48ac9e..f2aafe0 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -1749,7 +1749,7 @@
const String& function_name = String::Handle(function.name());
const String& expected_function_name = String::Handle(
- String::Concat(expected_class_name, String::Handle(Symbols::Dot())));
+ String::Concat(expected_class_name, Symbols::DotHandle()));
return function_name.Equals(expected_function_name);
}
@@ -2368,11 +2368,9 @@
Function* super_function = NULL;
if (node->IsSuperLoad()) {
// Resolve the load indexed operator in the super class.
- const String& index_operator_name =
- String::ZoneHandle(Symbols::IndexToken());
super_function = &Function::ZoneHandle(
Resolver::ResolveDynamicAnyArgs(node->super_class(),
- index_operator_name));
+ Symbols::IndexTokenHandle()));
if (super_function->IsNull()) {
// Could not resolve super operator. Generate call noSuchMethod() of the
// super class instead.
@@ -2381,7 +2379,7 @@
StaticCallInstr* call =
BuildStaticNoSuchMethodCall(node->super_class(),
node->array(),
- index_operator_name,
+ Symbols::IndexTokenHandle(),
arguments);
ReturnDefinition(call);
return;
@@ -2409,9 +2407,8 @@
} else {
// Generate dynamic call to index operator.
const intptr_t checked_argument_count = 1;
- const String& name = String::ZoneHandle(Symbols::IndexToken());
InstanceCallInstr* load = new InstanceCallInstr(node->token_pos(),
- name,
+ Symbols::IndexTokenHandle(),
Token::kINDEX,
arguments,
Array::ZoneHandle(),
@@ -2427,11 +2424,9 @@
Function* super_function = NULL;
if (node->IsSuperStore()) {
// Resolve the store indexed operator in the super class.
- const String& store_index_op_name =
- String::ZoneHandle(Symbols::AssignIndexToken());
super_function = &Function::ZoneHandle(
Resolver::ResolveDynamicAnyArgs(node->super_class(),
- store_index_op_name));
+ Symbols::AssignIndexTokenHandle()));
if (super_function->IsNull()) {
// Could not resolve super operator. Generate call noSuchMethod() of the
// super class instead.
@@ -2449,7 +2444,7 @@
StaticCallInstr* call =
BuildStaticNoSuchMethodCall(node->super_class(),
node->array(),
- store_index_op_name,
+ Symbols::AssignIndexTokenHandle(),
arguments);
if (result_is_needed) {
Do(call);
@@ -2809,7 +2804,13 @@
ArgumentListNode* arguments = new ArgumentListNode(args_pos);
// The first argument is the original method name.
arguments->Add(new LiteralNode(args_pos, method_name));
- // The second argument is an array containing the original method arguments.
+ // The second argument is the arguments descriptor of the original method.
+ const Array& args_descriptor =
+ Array::ZoneHandle(ArgumentsDescriptor::New(method_arguments->length(),
+ method_arguments->names()));
+ arguments->Add(new LiteralNode(args_pos, args_descriptor));
+ // The third argument is an array containing the original method arguments,
+ // including the receiver.
ArrayNode* args_array =
new ArrayNode(args_pos, Type::ZoneHandle(Type::ArrayType()));
for (intptr_t i = 0; i < method_arguments->length(); i++) {
@@ -2935,9 +2936,6 @@
initial_loop_depth);
graph_entry_ = new GraphEntryInstr(normal_entry);
EffectGraphVisitor for_effect(this, 0, initial_loop_depth);
- if (InInliningContext()) {
- exits_ = new ZoneGrowableArray<ReturnInstr*>();
- }
// TODO(kmillikin): We can eliminate stack checks in some cases (e.g., the
// stack check on entry for leaf routines).
Instruction* check = new CheckStackOverflowInstr(function.token_pos());
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index d259e97..da86242 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -534,24 +534,25 @@
argument_count, deopt_id, token_pos, locs);
return;
}
+
if (is_optimizing()) {
- // Megamorphic call requires one argument ICData.
- ASSERT(ic_data.num_args_tested() == 1);
- label_address = StubCode::MegamorphicCallEntryPoint();
- } else {
- switch (ic_data.num_args_tested()) {
- case 1:
- label_address = StubCode::OneArgCheckInlineCacheEntryPoint();
- break;
- case 2:
- label_address = StubCode::TwoArgsCheckInlineCacheEntryPoint();
- break;
- case 3:
- label_address = StubCode::ThreeArgsCheckInlineCacheEntryPoint();
- break;
- default:
- UNIMPLEMENTED();
- }
+ EmitMegamorphicInstanceCall(ic_data, arguments_descriptor, argument_count,
+ deopt_id, token_pos, locs);
+ return;
+ }
+
+ switch (ic_data.num_args_tested()) {
+ case 1:
+ label_address = StubCode::OneArgCheckInlineCacheEntryPoint();
+ break;
+ case 2:
+ label_address = StubCode::TwoArgsCheckInlineCacheEntryPoint();
+ break;
+ case 3:
+ label_address = StubCode::ThreeArgsCheckInlineCacheEntryPoint();
+ break;
+ default:
+ UNIMPLEMENTED();
}
ExternalLabel target_label("InlineCache", label_address);
EmitInstanceCall(&target_label, ic_data, arguments_descriptor, argument_count,
diff --git a/runtime/vm/flow_graph_compiler_ia32.cc b/runtime/vm/flow_graph_compiler_ia32.cc
index 47f6ed3..9f4b9b3 100644
--- a/runtime/vm/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/flow_graph_compiler_ia32.cc
@@ -1098,6 +1098,73 @@
}
+void FlowGraphCompiler::EmitMegamorphicInstanceCall(
+ const ICData& ic_data,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs) {
+ MegamorphicCacheTable* table = Isolate::Current()->megamorphic_cache_table();
+ const String& name = String::Handle(ic_data.target_name());
+ const MegamorphicCache& cache =
+ MegamorphicCache::ZoneHandle(table->Lookup(name, arguments_descriptor));
+ Label not_smi, load_cache;
+ __ movl(EAX, Address(ESP, (argument_count - 1) * kWordSize));
+ __ testl(EAX, Immediate(kSmiTagMask));
+ __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump);
+ __ movl(EAX, Immediate(Smi::RawValue(kSmiCid)));
+ __ jmp(&load_cache);
+
+ __ Bind(¬_smi);
+ __ LoadClassId(EAX, EAX);
+ __ SmiTag(EAX);
+
+ // EAX: class ID of the receiver (smi).
+ __ Bind(&load_cache);
+ __ LoadObject(EBX, cache);
+ __ movl(EDI, FieldAddress(EBX, MegamorphicCache::buckets_offset()));
+ __ movl(EBX, FieldAddress(EBX, MegamorphicCache::mask_offset()));
+ // EDI: cache buckets array.
+ // EBX: mask.
+ __ movl(ECX, EAX);
+
+ Label loop, update, call_target_function;
+ __ jmp(&loop);
+
+ __ Bind(&update);
+ __ addl(ECX, Immediate(Smi::RawValue(1)));
+ __ Bind(&loop);
+ __ andl(ECX, EBX);
+ const intptr_t base = Array::data_offset();
+ // ECX is smi tagged, but table entries are two words, so TIMES_4.
+ __ movl(EDX, FieldAddress(EDI, ECX, TIMES_4, base));
+
+ ASSERT(kIllegalCid == 0);
+ __ testl(EDX, EDX);
+ __ j(ZERO, &call_target_function, Assembler::kNearJump);
+ __ cmpl(EDX, EAX);
+ __ j(NOT_EQUAL, &update, Assembler::kNearJump);
+
+ __ Bind(&call_target_function);
+ // Call the target found in the cache. For a class id match, this is a
+ // proper target for the given name and arguments descriptor. If the
+ // illegal class id was found, the target is a cache miss handler that can
+ // be invoked as a normal Dart function.
+ __ movl(EAX, FieldAddress(EDI, ECX, TIMES_4, base + kWordSize));
+ __ movl(EAX, FieldAddress(EAX, Function::code_offset()));
+ __ movl(EAX, FieldAddress(EAX, Code::instructions_offset()));
+ __ LoadObject(ECX, ic_data);
+ __ LoadObject(EDX, arguments_descriptor);
+ __ addl(EAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+ __ call(EAX);
+ AddCurrentDescriptor(PcDescriptors::kOther, Isolate::kNoDeoptId, token_pos);
+ RecordSafepoint(locs);
+ AddDeoptIndexAtCall(deopt_id, token_pos);
+ __ Drop(argument_count);
+}
+
+
void FlowGraphCompiler::EmitStaticCall(const Function& function,
const Array& arguments_descriptor,
intptr_t argument_count,
diff --git a/runtime/vm/flow_graph_compiler_ia32.h b/runtime/vm/flow_graph_compiler_ia32.h
index 4af53ad..f29351c 100644
--- a/runtime/vm/flow_graph_compiler_ia32.h
+++ b/runtime/vm/flow_graph_compiler_ia32.h
@@ -141,6 +141,13 @@
intptr_t token_pos,
LocationSummary* locs);
+ void EmitMegamorphicInstanceCall(const ICData& ic_data,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs);
+
void EmitTestAndCall(const ICData& ic_data,
Register class_id_reg,
intptr_t arg_count,
diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
index ac94468..a998490 100644
--- a/runtime/vm/flow_graph_compiler_x64.cc
+++ b/runtime/vm/flow_graph_compiler_x64.cc
@@ -1102,6 +1102,73 @@
}
+void FlowGraphCompiler::EmitMegamorphicInstanceCall(
+ const ICData& ic_data,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs) {
+ MegamorphicCacheTable* table = Isolate::Current()->megamorphic_cache_table();
+ const String& name = String::Handle(ic_data.target_name());
+ const MegamorphicCache& cache =
+ MegamorphicCache::ZoneHandle(table->Lookup(name, arguments_descriptor));
+ Label not_smi, load_cache;
+ __ movq(RAX, Address(RSP, (argument_count - 1) * kWordSize));
+ __ testq(RAX, Immediate(kSmiTagMask));
+ __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump);
+ __ movq(RAX, Immediate(Smi::RawValue(kSmiCid)));
+ __ jmp(&load_cache);
+
+ __ Bind(¬_smi);
+ __ LoadClassId(RAX, RAX);
+ __ SmiTag(RAX);
+
+ // RAX: class ID of the receiver (smi).
+ __ Bind(&load_cache);
+ __ LoadObject(RBX, cache);
+ __ movq(RDI, FieldAddress(RBX, MegamorphicCache::buckets_offset()));
+ __ movq(RBX, FieldAddress(RBX, MegamorphicCache::mask_offset()));
+ // RDI: cache buckets array.
+ // RBX: mask.
+ __ movq(RCX, RAX);
+
+ Label loop, update, call_target_function;
+ __ jmp(&loop);
+
+ __ Bind(&update);
+ __ addq(RCX, Immediate(Smi::RawValue(1)));
+ __ Bind(&loop);
+ __ andq(RCX, RBX);
+ const intptr_t base = Array::data_offset();
+ // RCX is smi tagged, but table entries are two words, so TIMES_8.
+ __ movq(RDX, FieldAddress(RDI, RCX, TIMES_8, base));
+
+ ASSERT(kIllegalCid == 0);
+ __ testq(RDX, RDX);
+ __ j(ZERO, &call_target_function, Assembler::kNearJump);
+ __ cmpq(RDX, RAX);
+ __ j(NOT_EQUAL, &update, Assembler::kNearJump);
+
+ __ Bind(&call_target_function);
+ // Call the target found in the cache. For a class id match, this is a
+ // proper target for the given name and arguments descriptor. If the
+ // illegal class id was found, the target is a cache miss handler that can
+ // be invoked as a normal Dart function.
+ __ 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);
+ __ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+ __ call(RAX);
+ AddCurrentDescriptor(PcDescriptors::kOther, Isolate::kNoDeoptId, token_pos);
+ RecordSafepoint(locs);
+ AddDeoptIndexAtCall(deopt_id, token_pos);
+ __ Drop(argument_count);
+}
+
+
void FlowGraphCompiler::EmitStaticCall(const Function& function,
const Array& arguments_descriptor,
intptr_t argument_count,
diff --git a/runtime/vm/flow_graph_compiler_x64.h b/runtime/vm/flow_graph_compiler_x64.h
index cb6c742..f62868b 100644
--- a/runtime/vm/flow_graph_compiler_x64.h
+++ b/runtime/vm/flow_graph_compiler_x64.h
@@ -141,6 +141,13 @@
intptr_t token_pos,
LocationSummary* locs);
+ void EmitMegamorphicInstanceCall(const ICData& ic_data,
+ const Array& arguments_descriptor,
+ intptr_t argument_count,
+ intptr_t deopt_id,
+ intptr_t token_pos,
+ LocationSummary* locs);
+
void EmitTestAndCall(const ICData& ic_data,
Register class_id_reg,
intptr_t arg_count,
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 36a5d09..facc555 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -1669,6 +1669,13 @@
known_smis_->Add(value_ssa_index);
rollback_checks_.Add(value_ssa_index);
}
+ } else if (instr->IsBranch()) {
+ for (intptr_t i = 0; i < instr->InputCount(); i++) {
+ Value* use = instr->InputAt(i);
+ if (known_smis_->Contains(use->definition()->ssa_temp_index())) {
+ use->set_reaching_cid(kSmiCid);
+ }
+ }
}
}
diff --git a/runtime/vm/handles.h b/runtime/vm/handles.h
index a512fd8..a4fe592 100644
--- a/runtime/vm/handles.h
+++ b/runtime/vm/handles.h
@@ -214,6 +214,7 @@
HandlesBlock* scoped_blocks_; // List of scoped handles.
friend class HandleScope;
+ friend class Symbols;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(Handles);
};
diff --git a/runtime/vm/heap.cc b/runtime/vm/heap.cc
index d2f1615..79c335d 100644
--- a/runtime/vm/heap.cc
+++ b/runtime/vm/heap.cc
@@ -286,6 +286,26 @@
}
+void Heap::ProfileToFile(const char* reason) const {
+ Dart_FileOpenCallback file_open = Isolate::file_open_callback();
+ ASSERT(file_open != NULL);
+ Dart_FileWriteCallback file_write = Isolate::file_write_callback();
+ ASSERT(file_write != NULL);
+ Dart_FileCloseCallback file_close = Isolate::file_close_callback();
+ ASSERT(file_close != NULL);
+ Isolate* isolate = Isolate::Current();
+ const char* format = "%s-%s.hprof";
+ intptr_t len = OS::SNPrint(NULL, 0, format, isolate->name(), reason);
+ char* filename = isolate->current_zone()->Alloc<char>(len + 1);
+ OS::SNPrint(filename, len + 1, format, isolate->name(), reason);
+ void* file = (*file_open)(filename);
+ if (file != NULL) {
+ Profile(file_write, file);
+ (*file_close)(file);
+ }
+}
+
+
const char* Heap::GCReasonToString(GCReason gc_reason) {
switch (gc_reason) {
case kNewSpace:
diff --git a/runtime/vm/heap.h b/runtime/vm/heap.h
index ae47fea..c466642 100644
--- a/runtime/vm/heap.h
+++ b/runtime/vm/heap.h
@@ -158,6 +158,7 @@
// Generates a profile of the current and VM isolate heaps.
void Profile(Dart_FileWriteCallback callback, void* stream) const;
+ void ProfileToFile(const char* reason) const;
static const char* GCReasonToString(GCReason gc_reason);
diff --git a/runtime/vm/heap_profiler.cc b/runtime/vm/heap_profiler.cc
index 48d24c7..86b6dfc 100644
--- a/runtime/vm/heap_profiler.cc
+++ b/runtime/vm/heap_profiler.cc
@@ -400,8 +400,17 @@
record.WritePointer(raw_class);
// stack trace serial number
record.Write32(0);
- ASSERT(raw_class->ptr()->name_ != String::null());
- record.WritePointer(StringId(raw_class->ptr()->name_));
+ // class name string ID
+ if (raw_class->ptr()->name_ != String::null()) {
+ record.WritePointer(StringId(raw_class->ptr()->name_));
+ } else {
+ const char* format = "<an unnamed class with id %d>";
+ intptr_t len = OS::SNPrint(NULL, 0, format, raw_class->ptr()->id_);
+ char* str = new char[len + 1];
+ OS::SNPrint(str, len + 1, format, raw_class->ptr()->id_);
+ record.WritePointer(StringId(str));
+ delete[] str;
+ }
}
@@ -600,8 +609,9 @@
RawField* raw_field =
reinterpret_cast<RawField*>(raw_array->ptr()->data()[i]);
if (!Field::StaticBit::decode(raw_field->ptr()->kind_bits_)) {
- intptr_t offset =
+ intptr_t offset_in_words =
Smi::Value(reinterpret_cast<RawSmi*>(raw_field->ptr()->value_));
+ intptr_t offset = offset_in_words * kWordSize;
RawObject* ptr = *reinterpret_cast<RawObject**>(base + offset);
sub.WritePointer(ObjectId(ptr));
}
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index efdcb30..3fd0394 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -1323,12 +1323,12 @@
RawAbstractType* BinarySmiOpInstr::CompileType() const {
- return (op_kind() == Token::kSHL) ? Type::IntType() : Type::SmiType();
+ return Type::SmiType();
}
intptr_t BinarySmiOpInstr::ResultCid() const {
- return (op_kind() == Token::kSHL) ? kDynamicCid : kSmiCid;
+ return kSmiCid;
}
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 997aac9..ccfa829 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -2058,7 +2058,7 @@
arguments_(arguments),
argument_names_(argument_names),
checked_argument_count_(checked_argument_count) {
- ASSERT(function_name.IsZoneHandle());
+ ASSERT(function_name.IsNotTemporaryScopedHandle());
ASSERT(!arguments->is_empty());
ASSERT(argument_names.IsZoneHandle());
ASSERT(Token::IsBinaryOperator(token_kind) ||
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index 8a1e32f..a7b2ac5 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -353,7 +353,6 @@
deopt_id,
token_pos);
}
- const String& operator_name = String::ZoneHandle(Symbols::EqualOperator());
const int kNumberOfArguments = 2;
const Array& kNoArgumentNames = Array::Handle();
const int kNumArgumentsChecked = 2;
@@ -362,9 +361,9 @@
Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label check_identity;
__ cmpl(Address(ESP, 0 * kWordSize), raw_null);
- __ j(EQUAL, &check_identity, Assembler::kNearJump);
+ __ j(EQUAL, &check_identity);
__ cmpl(Address(ESP, 1 * kWordSize), raw_null);
- __ j(EQUAL, &check_identity, Assembler::kNearJump);
+ __ j(EQUAL, &check_identity);
ICData& equality_ic_data = ICData::ZoneHandle();
if (compiler->is_optimizing() && FLAG_propagate_ic_data) {
@@ -378,7 +377,7 @@
}
} else {
equality_ic_data = ICData::New(compiler->parsed_function().function(),
- operator_name,
+ Symbols::EqualOperatorHandle(),
deopt_id,
kNumArgumentsChecked);
}
@@ -1748,6 +1747,7 @@
: instruction_(instruction) { }
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("CheckStackOverflowSlowPath");
__ Bind(entry_label());
compiler->SaveLiveRegisters(instruction_->locs());
compiler->GenerateCallRuntime(instruction_->token_pos(),
@@ -2084,6 +2084,7 @@
: instruction_(instruction) { }
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("BoxDoubleSlowPath");
__ Bind(entry_label());
const Class& double_class = compiler->double_class();
const Code& stub =
@@ -2560,6 +2561,7 @@
: instruction_(instruction) { }
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("BoxIntegerSlowPath");
__ Bind(entry_label());
const Class& mint_class =
Class::ZoneHandle(Isolate::Current()->object_store()->mint_class());
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 51bcc51..d18c97d 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -354,7 +354,6 @@
deopt_id,
token_pos);
}
- const String& operator_name = String::ZoneHandle(Symbols::EqualOperator());
const int kNumberOfArguments = 2;
const Array& kNoArgumentNames = Array::Handle();
const int kNumArgumentsChecked = 2;
@@ -363,9 +362,9 @@
Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label check_identity;
__ cmpq(Address(RSP, 0 * kWordSize), raw_null);
- __ j(EQUAL, &check_identity, Assembler::kNearJump);
+ __ j(EQUAL, &check_identity);
__ cmpq(Address(RSP, 1 * kWordSize), raw_null);
- __ j(EQUAL, &check_identity, Assembler::kNearJump);
+ __ j(EQUAL, &check_identity);
ICData& equality_ic_data = ICData::ZoneHandle(original_ic_data.raw());
if (compiler->is_optimizing() && FLAG_propagate_ic_data) {
@@ -379,7 +378,7 @@
}
} else {
equality_ic_data = ICData::New(compiler->parsed_function().function(),
- operator_name,
+ Symbols::EqualOperatorHandle(),
deopt_id,
kNumArgumentsChecked);
}
@@ -1607,6 +1606,7 @@
: instruction_(instruction) { }
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("CheckStackOverflowSlowPath");
__ Bind(entry_label());
compiler->SaveLiveRegisters(instruction_->locs());
compiler->GenerateCallRuntime(instruction_->token_pos(),
@@ -1968,6 +1968,7 @@
: instruction_(instruction) { }
virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("BoxDoubleSlowPath");
__ Bind(entry_label());
const Class& double_class = compiler->double_class();
const Code& stub =
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 6ea6661..5ac23bc 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -452,7 +452,9 @@
if (FLAG_trace_isolates) {
StackZone zone(this);
HandleScope handle_scope(this);
- OS::Print("Number of symbols added = %"Pd"\n", Symbols::Size(this));
+ heap()->PrintSizes();
+ megamorphic_cache_table()->PrintSizes();
+ Symbols::DumpStats();
OS::Print("[-] Stopping isolate:\n"
"\tisolate: %s\n", name());
}
@@ -483,6 +485,9 @@
// Visit objects in the class table.
class_table()->VisitObjectPointers(visitor);
+ // Visit objects in the megamorphic cache.
+ megamorphic_cache_table()->VisitObjectPointers(visitor);
+
// Visit objects in per isolate stubs.
StubCode::VisitObjectPointers(visitor);
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 6fb8623..bfbbaa8 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -7,10 +7,11 @@
#include "include/dart_api.h"
#include "platform/assert.h"
-#include "vm/class_table.h"
#include "platform/thread.h"
#include "vm/base_isolate.h"
+#include "vm/class_table.h"
#include "vm/gc_callbacks.h"
+#include "vm/megamorphic_cache_table.h"
#include "vm/store_buffer.h"
#include "vm/timer.h"
@@ -114,6 +115,10 @@
return OFFSET_OF(Isolate, class_table_);
}
+ MegamorphicCacheTable* megamorphic_cache_table() {
+ return &megamorphic_cache_table_;
+ }
+
Dart_MessageNotifyCallback message_notify_callback() const {
return message_notify_callback_;
}
@@ -343,6 +348,7 @@
StoreBufferBlock store_buffer_block_;
StoreBuffer store_buffer_;
ClassTable class_table_;
+ MegamorphicCacheTable megamorphic_cache_table_;
Dart_MessageNotifyCallback message_notify_callback_;
char* name_;
Dart_Port main_port_;
diff --git a/runtime/vm/megamorphic_cache_table.cc b/runtime/vm/megamorphic_cache_table.cc
new file mode 100644
index 0000000..bec06e3
--- /dev/null
+++ b/runtime/vm/megamorphic_cache_table.cc
@@ -0,0 +1,99 @@
+// 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.
+
+#include "vm/megamorphic_cache_table.h"
+
+#include <stdlib.h>
+#include "vm/object.h"
+#include "vm/stub_code.h"
+#include "vm/symbols.h"
+
+namespace dart {
+
+MegamorphicCacheTable::MegamorphicCacheTable()
+ : miss_handler_(NULL),
+ capacity_(0),
+ length_(0),
+ table_(NULL) {
+}
+
+
+MegamorphicCacheTable::~MegamorphicCacheTable() {
+ free(table_);
+}
+
+
+RawMegamorphicCache* MegamorphicCacheTable::Lookup(const String& name,
+ const Array& descriptor) {
+ for (intptr_t i = 0; i < length_; ++i) {
+ if ((table_[i].name == name.raw()) &&
+ (table_[i].descriptor == descriptor.raw())) {
+ return table_[i].cache;
+ }
+ }
+
+ if (length_ == capacity_) {
+ capacity_ += kCapacityIncrement;
+ table_ =
+ reinterpret_cast<Entry*>(realloc(table_, capacity_ * sizeof(*table_)));
+ }
+
+ ASSERT(length_ < capacity_);
+ const MegamorphicCache& cache =
+ MegamorphicCache::Handle(MegamorphicCache::New());
+ Entry entry = { name.raw(), descriptor.raw(), cache.raw() };
+ table_[length_++] = entry;
+ return cache.raw();
+}
+
+
+void MegamorphicCacheTable::InitMissHandler() {
+ // The miss handler for a class ID not found in the table is invoked as a
+ // normal Dart function.
+ const Code& code =
+ Code::Handle(StubCode::Generate("_stub_MegamorphicMiss",
+ StubCode::GenerateMegamorphicMissStub));
+ const String& name = String::Handle(Symbols::New("megamorphic_miss"));
+ const Class& cls =
+ Class::Handle(Type::Handle(Type::Function()).type_class());
+ const Function& function =
+ Function::Handle(Function::New(name,
+ RawFunction::kRegularFunction,
+ false, // Not static.
+ false, // Not const.
+ false, // Not abstract.
+ false, // Not external.
+ cls,
+ 0)); // No token position.
+ function.SetCode(code);
+ miss_handler_ = function.raw();
+}
+
+
+void MegamorphicCacheTable::VisitObjectPointers(ObjectPointerVisitor* v) {
+ ASSERT(v != NULL);
+ v->VisitPointer(reinterpret_cast<RawObject**>(&miss_handler_));
+ for (intptr_t i = 0; i < length_; ++i) {
+ v->VisitPointer(reinterpret_cast<RawObject**>(&table_[i].name));
+ v->VisitPointer(reinterpret_cast<RawObject**>(&table_[i].descriptor));
+ v->VisitPointer(reinterpret_cast<RawObject**>(&table_[i].cache));
+ }
+}
+
+
+void MegamorphicCacheTable::PrintSizes() {
+ StackZone zone(Isolate::Current());
+ intptr_t size = 0;
+ MegamorphicCache& cache = MegamorphicCache::Handle();
+ Array& buckets = Array::Handle();
+ for (intptr_t i = 0; i < length_; ++i) {
+ cache = table_[i].cache;
+ buckets = cache.buckets();
+ size += MegamorphicCache::InstanceSize();
+ size += Array::InstanceSize(buckets.Length());
+ }
+ OS::Print("%"Pd" megamorphic caches using %"Pd"KB.\n", length_, size / 1024);
+}
+
+} // namespace dart
diff --git a/runtime/vm/megamorphic_cache_table.h b/runtime/vm/megamorphic_cache_table.h
new file mode 100644
index 0000000..1cfd8c2
--- /dev/null
+++ b/runtime/vm/megamorphic_cache_table.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef VM_MEGAMORPHIC_CACHE_TABLE_H_
+#define VM_MEGAMORPHIC_CACHE_TABLE_H_
+
+#include "vm/allocation.h"
+
+namespace dart {
+
+class Array;
+class Function;
+class ObjectPointerVisitor;
+class RawArray;
+class RawFunction;
+class RawMegamorphicCache;
+class RawString;
+class String;
+
+class MegamorphicCacheTable {
+ public:
+ MegamorphicCacheTable();
+ ~MegamorphicCacheTable();
+
+ RawFunction* miss_handler() const { return miss_handler_; }
+ void InitMissHandler();
+
+ RawMegamorphicCache* Lookup(const String& name, const Array& descriptor);
+
+ void VisitObjectPointers(ObjectPointerVisitor* visitor);
+
+ void PrintSizes();
+
+ private:
+ struct Entry {
+ RawString* name;
+ RawArray* descriptor;
+ RawMegamorphicCache* cache;
+ };
+
+ static const int kCapacityIncrement = 128;
+
+ RawFunction* miss_handler_;
+ intptr_t capacity_;
+ intptr_t length_;
+ Entry* table_;
+
+ DISALLOW_COPY_AND_ASSIGN(MegamorphicCacheTable);
+};
+
+} // namespace dart
+
+#endif // VM_MEGAMORPHIC_CACHE_TABLE_H_
diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h
index aad7fb9..6a29b50 100644
--- a/runtime/vm/native_entry.h
+++ b/runtime/vm/native_entry.h
@@ -51,9 +51,9 @@
NativeArguments* arguments)
-// Natives should throw an exception if an illegal argument is passed.
+// Natives should throw an exception if an illegal argument or null is passed.
// type name = value.
-#define GET_NATIVE_ARGUMENT(type, name, value) \
+#define GET_NON_NULL_NATIVE_ARGUMENT(type, name, value) \
const Instance& __##name##_instance__ = \
Instance::CheckedHandle(isolate, value); \
if (!__##name##_instance__.Is##type()) { \
@@ -64,6 +64,21 @@
const type& name = type::Cast(__##name##_instance__);
+// Natives should throw an exception if an illegal argument is passed.
+// type name = value.
+#define GET_NATIVE_ARGUMENT(type, name, value) \
+ const Instance& __##name##_instance__ = \
+ Instance::CheckedHandle(isolate, value); \
+ type& name = type::Handle(isolate); \
+ if (!__##name##_instance__.IsNull()) { \
+ if (!__##name##_instance__.Is##type()) { \
+ GrowableArray<const Object*> __args__; \
+ __args__.Add(&__##name##_instance__); \
+ Exceptions::ThrowByType(Exceptions::kArgument, __args__); \
+ } \
+ } \
+ name ^= value;
+
// Helper class for resolving and handling native functions.
class NativeEntry : public AllStatic {
diff --git a/runtime/vm/native_entry_test.cc b/runtime/vm/native_entry_test.cc
index 8c58b66..3ed8980 100644
--- a/runtime/vm/native_entry_test.cc
+++ b/runtime/vm/native_entry_test.cc
@@ -6,6 +6,7 @@
#include "vm/assembler.h"
#include "vm/code_patcher.h"
+#include "vm/dart_api_impl.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/stack_frame.h"
@@ -55,6 +56,38 @@
}
+// Test for accepting null arguments in native function.
+// Arg0-4: 5 smis or null.
+// Result: a smi representing the sum of all non-null arguments.
+void TestNonNullSmiSum(Dart_NativeArguments args) {
+ Dart_EnterScope();
+ Isolate* isolate = Isolate::Current();
+ int64_t result = 0;
+ int arg_count = Dart_GetNativeArgumentCount(args);
+ // Test the lower level macro GET_NATIVE_ARGUMENT.
+ NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
+ for (int i = 0; i < arg_count; i++) {
+ Dart_Handle arg = Dart_GetNativeArgument(args, i);
+ GET_NATIVE_ARGUMENT(Integer, argument, arguments->NativeArgAt(i));
+ EXPECT(argument.IsInteger()); // May be null.
+ EXPECT_EQ(Api::UnwrapHandle(arg), argument.raw()); // May be null.
+ int64_t arg_value = -1;
+ if (argument.IsNull()) {
+ EXPECT_ERROR(Dart_IntegerToInt64(arg, &arg_value),
+ "Dart_IntegerToInt64 expects argument 'integer' "
+ "to be non-null.");
+ } else {
+ EXPECT_VALID(Dart_IntegerToInt64(arg, &arg_value));
+ EXPECT_EQ(arg_value, argument.AsInt64Value());
+ // Ignoring overflow in the addition below.
+ result += arg_value;
+ }
+ }
+ Dart_SetReturnValue(args, Dart_NewInteger(result));
+ Dart_ExitScope();
+}
+
+
// Test code patching.
void TestStaticCallPatching(Dart_NativeArguments args) {
Dart_EnterScope();
diff --git a/runtime/vm/native_entry_test.h b/runtime/vm/native_entry_test.h
index b00e155..0d4923a 100644
--- a/runtime/vm/native_entry_test.h
+++ b/runtime/vm/native_entry_test.h
@@ -11,6 +11,7 @@
void TestSmiSub(Dart_NativeArguments args);
void TestSmiSum(Dart_NativeArguments args);
+void TestNonNullSmiSum(Dart_NativeArguments args);
void TestStaticCallPatching(Dart_NativeArguments args);
} // namespace dart
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 0dd23e8..934490f 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -97,6 +97,8 @@
RawClass* Object::context_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::context_scope_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::icdata_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
+RawClass* Object::megamorphic_cache_class_ =
+ reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::subtypetestcache_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::api_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
@@ -107,6 +109,9 @@
#undef RAW_NULL
+const double MegamorphicCache::kLoadFactor = 0.75;
+
+
// Takes a vm internal name and makes it suitable for external user.
//
// Examples:
@@ -382,6 +387,9 @@
cls = Class::New<ICData>();
icdata_class_ = cls.raw();
+ cls = Class::New<MegamorphicCache>();
+ megamorphic_cache_class_ = cls.raw();
+
cls = Class::New<SubtypeTestCache>();
subtypetestcache_class_ = cls.raw();
@@ -457,6 +465,7 @@
SET_CLASS_NAME(context, Context);
SET_CLASS_NAME(context_scope, ContextScope);
SET_CLASS_NAME(icdata, ICData);
+ SET_CLASS_NAME(megamorphic_cache, MegamorphicCache);
SET_CLASS_NAME(subtypetestcache, SubtypeTestCache);
SET_CLASS_NAME(api_error, ApiError);
SET_CLASS_NAME(language_error, LanguageError);
@@ -1241,6 +1250,13 @@
};
+bool Object::IsNotTemporaryScopedHandle() const {
+ return (IsZoneHandle() ||
+ Symbols::IsPredefinedHandle(reinterpret_cast<uword>(this)));
+}
+
+
+
RawObject* Object::Clone(const Object& src, Heap::Space space) {
const Class& cls = Class::Handle(src.clazz());
intptr_t size = src.raw()->Size();
@@ -3386,10 +3402,9 @@
bool Function::IsInlineable() const {
// '==' call is handled specially.
- const String& equality_name = String::Handle(Symbols::EqualOperator());
return InlinableBit::decode(raw_ptr()->kind_tag_) &&
HasCode() &&
- name() != equality_name.raw();
+ name() != Symbols::EqualOperator();
}
@@ -7777,6 +7792,120 @@
}
+RawArray* MegamorphicCache::buckets() const {
+ return raw_ptr()->buckets_;
+}
+
+
+void MegamorphicCache::set_buckets(const Array& buckets) const {
+ StorePointer(&raw_ptr()->buckets_, buckets.raw());
+}
+
+
+// Class IDs in the table are smi-tagged, so we use a smi-tagged mask
+// and target class ID to avoid untagging (on each iteration of the
+// test loop) in generated code.
+intptr_t MegamorphicCache::mask() const {
+ return Smi::Value(raw_ptr()->mask_);
+}
+
+
+void MegamorphicCache::set_mask(intptr_t mask) const {
+ raw_ptr()->mask_ = Smi::New(mask);
+}
+
+
+intptr_t MegamorphicCache::filled_entry_count() const {
+ return raw_ptr()->filled_entry_count_;
+}
+
+
+void MegamorphicCache::set_filled_entry_count(intptr_t count) const {
+ raw_ptr()->filled_entry_count_ = count;
+}
+
+
+RawMegamorphicCache* MegamorphicCache::New() {
+ MegamorphicCache& result = MegamorphicCache::Handle();
+ { RawObject* raw = Object::Allocate(MegamorphicCache::kClassId,
+ MegamorphicCache::InstanceSize(),
+ Heap::kOld);
+ NoGCScope no_gc;
+ result ^= raw;
+ }
+ const intptr_t capacity = kInitialCapacity;
+ const Array& buckets = Array::Handle(Array::New(kEntryLength * capacity));
+ const Smi& illegal = Smi::Handle(Smi::New(kIllegalCid));
+ const Function& handler = Function::Handle(
+ Isolate::Current()->megamorphic_cache_table()->miss_handler());
+ for (intptr_t i = 0; i < capacity; ++i) {
+ SetEntry(buckets, i, illegal, handler);
+ }
+ result.set_buckets(buckets);
+ result.set_mask(capacity - 1);
+ result.set_filled_entry_count(0);
+ return result.raw();
+}
+
+
+void MegamorphicCache::EnsureCapacity() const {
+ intptr_t old_capacity = mask() + 1;
+ double load_limit = kLoadFactor * static_cast<double>(old_capacity);
+ if (static_cast<double>(filled_entry_count() + 1) > load_limit) {
+ const Array& old_buckets = Array::Handle(buckets());
+ intptr_t new_capacity = old_capacity * 2;
+ const Array& new_buckets =
+ Array::Handle(Array::New(kEntryLength * new_capacity));
+
+ Smi& class_id = Smi::Handle(Smi::New(kIllegalCid));
+ Function& target = Function::Handle(
+ Isolate::Current()->megamorphic_cache_table()->miss_handler());
+ for (intptr_t i = 0; i < new_capacity; ++i) {
+ SetEntry(new_buckets, i, class_id, target);
+ }
+ set_buckets(new_buckets);
+ set_mask(new_capacity - 1);
+ set_filled_entry_count(0);
+
+ // Rehash the valid entries.
+ for (intptr_t i = 0; i < old_capacity; ++i) {
+ class_id ^= GetClassId(old_buckets, i);
+ if (class_id.Value() != kIllegalCid) {
+ target ^= GetTargetFunction(old_buckets, i);
+ Insert(class_id, target);
+ }
+ }
+ }
+}
+
+
+void MegamorphicCache::Insert(const Smi& class_id,
+ const Function& target) const {
+ ASSERT(static_cast<double>(filled_entry_count() + 1) <=
+ (kLoadFactor * static_cast<double>(mask() + 1)));
+ const Array& backing_array = Array::Handle(buckets());
+ intptr_t id_mask = mask();
+ intptr_t index = class_id.Value() & id_mask;
+ Smi& probe = Smi::Handle();
+ intptr_t i = index;
+ do {
+ probe ^= GetClassId(backing_array, i);
+ if (probe.Value() == kIllegalCid) {
+ SetEntry(backing_array, i, class_id, target);
+ set_filled_entry_count(filled_entry_count() + 1);
+ return;
+ }
+ i = (i + 1) & id_mask;
+ } while (i != index);
+ UNREACHABLE();
+}
+
+
+const char* MegamorphicCache::ToCString() const {
+ return "";
+}
+
+
RawSubtypeTestCache* SubtypeTestCache::New() {
ASSERT(Object::subtypetestcache_class() != Class::null());
SubtypeTestCache& result = SubtypeTestCache::Handle();
@@ -11187,33 +11316,25 @@
void GrowableObjectArray::Add(const Object& value, Heap::Space space) const {
- Add(Isolate::Current(), value, space);
-}
-
-
-void GrowableObjectArray::Add(Isolate* isolate,
- const Object& value,
- Heap::Space space) const {
ASSERT(!IsNull());
- Array& contents = Array::Handle(isolate, data());
if (Length() == Capacity()) {
// TODO(Issue 2500): Need a better growth strategy.
intptr_t new_capacity = (Capacity() == 0) ? 4 : Capacity() * 2;
if (new_capacity <= Capacity()) {
// Use the preallocated out of memory exception to avoid calling
// into dart code or allocating any code.
+ Isolate* isolate = Isolate::Current();
const Instance& exception =
Instance::Handle(isolate->object_store()->out_of_memory());
Exceptions::Throw(exception);
UNREACHABLE();
}
Grow(new_capacity, space);
- contents = data();
}
ASSERT(Length() < Capacity());
intptr_t index = Length();
SetLength(index + 1);
- contents.SetAt(index, value);
+ SetAt(index, value);
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 515248a..367f64c 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -210,6 +210,8 @@
return VMHandles::IsZoneHandle(reinterpret_cast<uword>(this));
}
+ bool IsNotTemporaryScopedHandle() const;
+
static RawObject* Clone(const Object& src, Heap::Space space = Heap::kNew);
static Object& Handle(Isolate* isolate, RawObject* raw_ptr) {
@@ -294,6 +296,9 @@
}
static RawClass* unwind_error_class() { return unwind_error_class_; }
static RawClass* icdata_class() { return icdata_class_; }
+ static RawClass* megamorphic_cache_class() {
+ return megamorphic_cache_class_;
+ }
static RawClass* subtypetestcache_class() { return subtypetestcache_class_; }
static RawError* Init(Isolate* isolate);
@@ -428,6 +433,7 @@
static RawClass* context_class_; // Class of the Context vm object.
static RawClass* context_scope_class_; // Class of ContextScope vm object.
static RawClass* icdata_class_; // Class of ICData.
+ static RawClass* megamorphic_cache_class_; // Class of MegamorphiCache.
static RawClass* subtypetestcache_class_; // Class of SubtypeTestCache.
static RawClass* api_error_class_; // Class of ApiError.
static RawClass* language_error_class_; // Class of LanguageError.
@@ -2932,6 +2938,59 @@
};
+class MegamorphicCache : public Object {
+ public:
+ static const int kInitialCapacity = 16;
+ static const double kLoadFactor;
+
+ RawArray* buckets() const;
+ void set_buckets(const Array& buckets) const;
+
+ intptr_t mask() const;
+ void set_mask(intptr_t mask) const;
+
+ intptr_t filled_entry_count() const;
+ void set_filled_entry_count(intptr_t num) const;
+
+ static intptr_t buckets_offset() {
+ return OFFSET_OF(RawMegamorphicCache, buckets_);
+ }
+ static intptr_t mask_offset() {
+ return OFFSET_OF(RawMegamorphicCache, mask_);
+ }
+
+ static RawMegamorphicCache* New();
+
+ void EnsureCapacity() const;
+
+ void Insert(const Smi& class_id, const Function& target) const;
+
+ static intptr_t InstanceSize() {
+ return RoundedAllocationSize(sizeof(RawMegamorphicCache));
+ }
+
+ private:
+ friend class Class;
+
+ enum {
+ kClassIdIndex,
+ kTargetFunctionIndex,
+ kEntryLength,
+ };
+
+ static inline void SetEntry(const Array& array,
+ intptr_t index,
+ const Smi& class_id,
+ const Function& target);
+
+ static inline RawObject* GetClassId(const Array& array, intptr_t index);
+ static inline RawObject* GetTargetFunction(const Array& array,
+ intptr_t index);
+
+ HEAP_OBJECT_IMPLEMENTATION(MegamorphicCache, Object);
+};
+
+
class SubtypeTestCache : public Object {
public:
enum Entries {
@@ -4433,14 +4492,12 @@
void SetAt(intptr_t index, const Object& value) const {
ASSERT(!IsNull());
ASSERT(index < Length());
- const Array& arr = Array::Handle(data());
- arr.SetAt(index, value);
+
+ // TODO(iposva): Add storing NoGCScope.
+ DataStorePointer(ObjectAddr(index), value.raw());
}
void Add(const Object& value, Heap::Space space = Heap::kNew) const;
- void Add(Isolate* isolate,
- const Object& value,
- Heap::Space space = Heap::kNew) const;
void Grow(intptr_t new_capacity, Heap::Space space = Heap::kNew) const;
RawObject* RemoveLast() const;
@@ -4489,6 +4546,22 @@
ASSERT((index >= 0) && (index < Length()));
return &(DataArray()->data()[index]);
}
+ bool DataContains(uword addr) const {
+ intptr_t data_size = data()->Size();
+ uword data_addr = RawObject::ToAddr(data());
+ return (addr >= data_addr) && (addr < (data_addr + data_size));
+ }
+ void DataStorePointer(RawObject** addr, RawObject* value) const {
+ // Ensure that the backing array object contains the addr.
+ ASSERT(DataContains(reinterpret_cast<uword>(addr)));
+ *addr = value;
+ // Filter stores based on source and target.
+ if (!value->IsHeapObject()) return;
+ if (value->IsNewObject() && data()->IsOldObject()) {
+ uword ptr = reinterpret_cast<uword>(addr);
+ Isolate::Current()->store_buffer()->AddPointer(ptr);
+ }
+ }
static const int kDefaultInitialCapacity = 4;
@@ -6068,6 +6141,26 @@
return true;
}
+
+void MegamorphicCache::SetEntry(const Array& array,
+ intptr_t index,
+ const Smi& class_id,
+ const Function& target) {
+ array.SetAt((index * kEntryLength) + kClassIdIndex, class_id);
+ array.SetAt((index * kEntryLength) + kTargetFunctionIndex, target);
+}
+
+
+RawObject* MegamorphicCache::GetClassId(const Array& array, intptr_t index) {
+ return array.At((index * kEntryLength) + kClassIdIndex);
+}
+
+
+RawObject* MegamorphicCache::GetTargetFunction(const Array& array,
+ intptr_t index) {
+ return array.At((index * kEntryLength) + kTargetFunctionIndex);
+}
+
} // namespace dart
#endif // VM_OBJECT_H_
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 8dc3234..397ccc1 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -435,7 +435,7 @@
}
void AddFinalParameter(intptr_t name_pos,
- String* name,
+ const String* name,
const AbstractType* type) {
this->num_fixed_parameters++;
ParamDesc param;
@@ -449,7 +449,7 @@
void AddReceiver(const Type* receiver_type) {
ASSERT(this->parameters->is_empty());
AddFinalParameter(receiver_type->token_pos(),
- &String::ZoneHandle(Symbols::This()),
+ &Symbols::ThisHandle(),
receiver_type);
}
@@ -1338,10 +1338,16 @@
ArgumentListNode* arguments = new ArgumentListNode(args_pos);
// The first argument is the original function name.
arguments->Add(new LiteralNode(args_pos, function_name));
- // The second argument is an array containing the original function arguments.
+ // The second argument is the arguments descriptor of the original function.
+ const Array& args_descriptor =
+ Array::ZoneHandle(ArgumentsDescriptor::New(function_args.length(),
+ function_args.names()));
+ arguments->Add(new LiteralNode(args_pos, args_descriptor));
+ // The third argument is an array containing the original function arguments,
+ // including the receiver.
ArrayNode* args_array = new ArrayNode(
args_pos, Type::ZoneHandle(Type::ArrayType()));
- for (intptr_t i = 1; i < function_args.length(); i++) {
+ for (intptr_t i = 0; i < function_args.length(); i++) {
args_array->AddElement(function_args.NodeAt(i));
}
arguments->Add(args_array);
@@ -1589,8 +1595,7 @@
return;
}
String& ctor_name = String::Handle(super_class.Name());
- String& ctor_suffix = String::Handle(Symbols::Dot());
- ctor_name = String::Concat(ctor_name, ctor_suffix);
+ ctor_name = String::Concat(ctor_name, Symbols::DotHandle());
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
// Implicit 'this' parameter is the first argument.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, receiver);
@@ -1630,13 +1635,12 @@
const Class& super_class = Class::Handle(cls.SuperClass());
ASSERT(!super_class.IsNull());
String& ctor_name = String::Handle(super_class.Name());
- String& ctor_suffix = String::Handle(Symbols::Dot());
+ ctor_name = String::Concat(ctor_name, Symbols::DotHandle());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
- ctor_suffix = String::Concat(
- ctor_suffix, *ExpectIdentifier("constructor name expected"));
+ ctor_name = String::Concat(ctor_name,
+ *ExpectIdentifier("constructor name expected"));
}
- ctor_name = String::Concat(ctor_name, ctor_suffix);
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("parameter list expected");
}
@@ -1849,14 +1853,13 @@
const intptr_t call_pos = TokenPos();
ConsumeToken();
String& ctor_name = String::Handle(cls.Name());
- String& ctor_suffix = String::Handle(Symbols::Dot());
+ ctor_name = String::Concat(ctor_name, Symbols::DotHandle());
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
- ctor_suffix = String::Concat(
- ctor_suffix, *ExpectIdentifier("constructor name expected"));
+ ctor_name = String::Concat(ctor_name,
+ *ExpectIdentifier("constructor name expected"));
}
- ctor_name = String::Concat(ctor_name, ctor_suffix);
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("parameter list expected");
}
@@ -1900,7 +1903,7 @@
const Class& cls = Class::Handle(func.Owner());
LocalVariable* receiver = new LocalVariable(
ctor_pos,
- String::ZoneHandle(Symbols::This()),
+ Symbols::ThisHandle(),
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(receiver);
@@ -2541,8 +2544,7 @@
ConsumeToken(); // Colon.
ExpectToken(Token::kTHIS);
String& redir_name = String::ZoneHandle(
- String::Concat(members->class_name(),
- String::Handle(Symbols::Dot())));
+ String::Concat(members->class_name(), Symbols::DotHandle()));
if (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
redir_name = String::Concat(redir_name,
@@ -2948,14 +2950,13 @@
}
// We must be dealing with a constructor or named constructor.
member.kind = RawFunction::kConstructor;
- String& ctor_suffix = String::ZoneHandle(Symbols::Dot());
+ *member.name = String::Concat(*member.name, Symbols::DotHandle());
if (CurrentToken() == Token::kPERIOD) {
// Named constructor.
ConsumeToken();
member.constructor_name = ExpectIdentifier("identifier expected");
- ctor_suffix = String::Concat(ctor_suffix, *member.constructor_name);
+ *member.name = String::Concat(*member.name, *member.constructor_name);
}
- *member.name = String::Concat(*member.name, ctor_suffix);
// Ensure that names are symbols.
*member.name = Symbols::New(*member.name);
if (member.type == NULL) {
@@ -3188,7 +3189,7 @@
// The implicit constructor is unnamed, has no explicit parameter,
// and contains a supercall in the initializer list.
String& ctor_name = String::ZoneHandle(
- String::Concat(class_desc->class_name(), String::Handle(Symbols::Dot())));
+ String::Concat(class_desc->class_name(), Symbols::DotHandle()));
ctor_name = Symbols::New(ctor_name);
// The token position for the implicit constructor is the 'class'
// keyword of the constructor's class.
@@ -4075,10 +4076,9 @@
ConsumeToken();
String& lib_name = *ExpectIdentifier("library name expected");
if (CurrentToken() == Token::kPERIOD) {
- const String& dot = String::Handle(Symbols::Dot());
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
- lib_name = String::Concat(lib_name, dot);
+ lib_name = String::Concat(lib_name, Symbols::DotHandle());
lib_name = String::Concat(lib_name,
*ExpectIdentifier("malformed library name"));
}
@@ -4538,8 +4538,7 @@
LocalVariable* Parser::LookupReceiver(LocalScope* from_scope, bool test_only) {
ASSERT(!current_function().is_static());
- const String& this_name = String::Handle(Symbols::This());
- return from_scope->LookupVariable(this_name, test_only);
+ return from_scope->LookupVariable(Symbols::ThisHandle(), test_only);
}
@@ -7697,7 +7696,7 @@
if (current_block_ == NULL) {
return false;
}
- if (ident.Equals(String::Handle(Symbols::This()))) {
+ if (ident.Equals(Symbols::ThisHandle())) {
// 'this' is not a formal parameter.
return false;
}
@@ -8876,9 +8875,8 @@
// for class 'A' is labeled 'A.C', and the static function implementing the
// unnamed constructor for class 'A' is labeled 'A.'.
// This convention prevents users from explicitly calling constructors.
- const String& period = String::Handle(Symbols::Dot());
String& constructor_name =
- String::Handle(String::Concat(type_class_name, period));
+ String::Handle(String::Concat(type_class_name, Symbols::DotHandle()));
if (named_constructor != NULL) {
constructor_name = String::Concat(constructor_name, *named_constructor);
}
@@ -9313,8 +9311,7 @@
}
ASSERT(primary != NULL);
} else if (CurrentToken() == Token::kTHIS) {
- const String& this_name = String::Handle(Symbols::This());
- LocalVariable* local = LookupLocalScope(this_name);
+ LocalVariable* local = LookupLocalScope(Symbols::ThisHandle());
if (local == NULL) {
ErrorMsg("receiver 'this' is not in scope");
}
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index bdb23a3..06362bb 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -552,6 +552,14 @@
}
+intptr_t RawMegamorphicCache::VisitMegamorphicCachePointers(
+ RawMegamorphicCache* raw_obj,
+ ObjectPointerVisitor* visitor) {
+ visitor->VisitPointers(raw_obj->from(), raw_obj->to());
+ return MegamorphicCache::InstanceSize();
+}
+
+
intptr_t RawSubtypeTestCache::VisitSubtypeTestCachePointers(
RawSubtypeTestCache* raw_obj, ObjectPointerVisitor* visitor) {
// Make sure that we got here with the tagged pointer as this.
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 5ac48ec..bf5164e 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -40,6 +40,7 @@
V(Context) \
V(ContextScope) \
V(ICData) \
+ V(MegamorphicCache) \
V(SubtypeTestCache) \
V(Error) \
V(ApiError) \
@@ -936,6 +937,22 @@
};
+class RawMegamorphicCache : public RawObject {
+ RAW_HEAP_OBJECT_IMPLEMENTATION(MegamorphicCache);
+
+ RawObject** from() {
+ return reinterpret_cast<RawObject**>(&ptr()->buckets_);
+ }
+ RawArray* buckets_;
+ RawSmi* mask_;
+ RawObject** to() {
+ return reinterpret_cast<RawObject**>(&ptr()->mask_);
+ }
+
+ intptr_t filled_entry_count_;
+};
+
+
class RawSubtypeTestCache : public RawObject {
RAW_HEAP_OBJECT_IMPLEMENTATION(SubtypeTestCache);
RawArray* cache_;
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 0c4c92b..fd891d4 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -1251,6 +1251,22 @@
}
+RawMegamorphicCache* MegamorphicCache::ReadFrom(SnapshotReader* reader,
+ intptr_t object_id,
+ intptr_t tags,
+ Snapshot::Kind kind) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+void RawMegamorphicCache::WriteTo(SnapshotWriter* writer,
+ intptr_t object_id,
+ Snapshot::Kind kind) {
+ UNREACHABLE();
+}
+
+
RawSubtypeTestCache* SubtypeTestCache::ReadFrom(SnapshotReader* reader,
intptr_t object_id,
intptr_t tags,
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index 588587c..ccc6a3b 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -525,9 +525,8 @@
ContextScope::Handle(ContextScope::New(kNumCapturedVars));
// Create a descriptor for 'this' variable.
- const String& name = String::Handle(Symbols::This());
context_scope.SetTokenIndexAt(0, func.token_pos());
- context_scope.SetNameAt(0, name);
+ context_scope.SetNameAt(0, Symbols::ThisHandle());
context_scope.SetIsFinalAt(0, true);
context_scope.SetIsConstAt(0, false);
const AbstractType& type = AbstractType::Handle(func.ParameterTypeAt(0));
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index 48ed808..da0fe25 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -159,6 +159,8 @@
static const intptr_t kNoInstantiator = 0;
private:
+ friend class MegamorphicCacheTable;
+
static const intptr_t kStubCodeSize = 4 * KB;
#define STUB_CODE_GENERATE(name) \
@@ -184,6 +186,7 @@
static RawCode* Generate(const char* name,
void (*GenerateStub)(Assembler* assembler));
+ static void GenerateMegamorphicMissStub(Assembler* assembler);
static void GenerateAllocationStubForClass(Assembler* assembler,
const Class& cls);
static void GenerateAllocationStubForClosure(Assembler* assembler,
diff --git a/runtime/vm/stub_code_ia32.cc b/runtime/vm/stub_code_ia32.cc
index 46a45b5..5b3ae64 100644
--- a/runtime/vm/stub_code_ia32.cc
+++ b/runtime/vm/stub_code_ia32.cc
@@ -348,8 +348,7 @@
__ pushl(EBX); // Closure object.
__ pushl(EDX); // Arguments descriptor.
__ movl(EDI, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
- __ SmiUntag(EDI);
- __ subl(EDI, Immediate(1)); // Arguments array length, minus the receiver.
+ __ SmiUntag(EDI); // Arguments array length, including the original receiver.
PushArgumentsArray(assembler, (kWordSize * 6));
// Stack layout explaining "(kWordSize * 6)" offset.
// TOS + 0: Argument array.
@@ -375,7 +374,7 @@
__ Bind(&function_not_found);
// The target function was not found, so invoke method
- // "void noSuchMethod(function_name, args_array)".
+ // "dynamic noSuchMethod(InvocationMirror invocation)".
// EAX: receiver.
// ECX: ic-data.
// EDX: arguments descriptor array.
@@ -385,8 +384,7 @@
__ pushl(ECX); // IC-data.
__ pushl(EDX); // Arguments descriptor array.
__ movl(EDI, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
- __ SmiUntag(EDI);
- __ subl(EDI, Immediate(1)); // Arguments array length, minus the receiver.
+ __ SmiUntag(EDI); // Arguments array length, including the original receiver.
// See stack layout below explaining "wordSize * 7" offset.
PushArgumentsArray(assembler, (kWordSize * 7));
@@ -533,6 +531,44 @@
}
+void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
+ AssemblerMacros::EnterStubFrame(assembler);
+ // Load the receiver into EAX. The argument count in the arguments
+ // descriptor in EDX is a smi.
+ __ movl(EAX, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
+ // Two words (return addres, saved fp) in the stack above the last argument.
+ __ movl(EAX, Address(ESP, EAX, TIMES_2, 2 * kWordSize));
+ // Preserve IC data and arguments descriptor.
+ __ pushl(ECX);
+ __ pushl(EDX);
+
+ const Immediate raw_null =
+ Immediate(reinterpret_cast<intptr_t>(Instructions::null()));
+ __ pushl(raw_null); // Space for the result of the runtime call.
+ __ pushl(EAX); // Pass receiver.
+ __ pushl(ECX); // Pass IC data.
+ __ pushl(EDX); // Pass rguments descriptor.
+ __ CallRuntime(kMegamorphicCacheMissHandlerRuntimeEntry);
+ // Discard arguments.
+ __ popl(EAX);
+ __ popl(EAX);
+ __ popl(EAX);
+ __ popl(EAX); // Return value from the runtime call (instructions).
+ __ popl(EDX); // Restore arguments descriptor.
+ __ popl(ECX); // Restore IC data.
+ __ LeaveFrame();
+
+ Label lookup;
+ __ cmpl(EAX, raw_null);
+ __ j(EQUAL, &lookup, Assembler::kNearJump);
+ __ addl(EAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+ __ jmp(EAX);
+
+ __ Bind(&lookup);
+ __ jmp(&StubCode::InstanceFunctionLookupLabel());
+}
+
+
// Called for inline allocation of arrays.
// Input parameters:
// EDX : Array length as Smi.
@@ -739,8 +775,10 @@
__ jmp(ECX);
__ Bind(¬_closure);
- // Call runtime to report that a closure call was attempted on a non-closure
- // object, passing the non-closure object and its arguments array.
+ // Call runtime to attempt to resolve and invoke a call method on a
+ // non-closure object, passing the non-closure object and its arguments array,
+ // returning here.
+ // If no call method exists, throw a NoSuchMethodError.
// EDI: non-closure object.
// EDX: arguments descriptor array.
@@ -750,24 +788,33 @@
__ pushl(raw_null); // Setup space on stack for result from error reporting.
__ pushl(EDI); // Non-closure object.
+ __ pushl(EDX); // Arguments descriptor.
// Load num_args.
__ movl(EDI, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
- __ SmiUntag(EDI);
- __ subl(EDI, Immediate(1)); // Arguments array length, minus the closure.
- // See stack layout below explaining "wordSize * 5" offset.
- PushArgumentsArray(assembler, (kWordSize * 5));
+ __ SmiUntag(EDI); // Arguments array length, including the non-closure.
+ // See stack layout below explaining "wordSize * 6" offset.
+ PushArgumentsArray(assembler, (kWordSize * 6));
// Stack:
// TOS + 0: Argument array.
- // TOS + 1: Non-closure object.
- // TOS + 2: Place for result from reporting the error.
- // TOS + 3: PC marker => RawInstruction object.
- // TOS + 4: Saved EBP of previous frame. <== EBP
- // TOS + 5: Dart code return address
- // TOS + 6: Last argument of caller.
+ // TOS + 1: Arguments descriptor array.
+ // TOS + 2: Non-closure object.
+ // TOS + 3: Place for result from the call.
+ // TOS + 4: PC marker => RawInstruction object.
+ // TOS + 5: Saved EBP of previous frame. <== EBP
+ // TOS + 6: Dart code return address
+ // TOS + 7: Last argument of caller.
// ....
- __ CallRuntime(kReportObjectNotClosureRuntimeEntry);
- __ Stop("runtime call throws an exception");
+ __ CallRuntime(kInvokeNonClosureRuntimeEntry);
+ // Remove arguments.
+ __ popl(EAX);
+ __ popl(EAX);
+ __ popl(EAX);
+ __ popl(EAX); // Get result into EAX.
+
+ // Remove the stub frame as we are about to return.
+ __ LeaveFrame();
+ __ ret();
}
@@ -1407,14 +1454,7 @@
// Uses EAX, EBX, EDI as temporary registers.
void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) {
// The target function was not found, so invoke method
- // "void noSuchMethod(function_name, Array arguments)".
- // TODO(regis): For now, we simply pass the actual arguments, both positional
- // and named, as the argument array. This is not correct if out-of-order
- // named arguments were passed.
- // The signature of the "noSuchMethod" method has to change from
- // noSuchMethod(String name, Array arguments) to something like
- // noSuchMethod(InvocationMirror call).
- // Also, the class NoSuchMethodError has to be modified accordingly.
+ // "dynamic noSuchMethod(InvocationMirror invocation)".
const Immediate raw_null =
Immediate(reinterpret_cast<intptr_t>(Object::null()));
__ movl(EDI, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
@@ -1429,7 +1469,7 @@
__ pushl(EAX); // Receiver.
__ pushl(ECX); // IC data array.
__ pushl(EDX); // Arguments descriptor array.
- __ subl(EDI, Immediate(1)); // Arguments array length, minus the receiver.
+ // EDI: Arguments array length, including the receiver.
// See stack layout below explaining "wordSize * 10" offset.
PushArgumentsArray(assembler, (kWordSize * 10));
@@ -2069,13 +2109,12 @@
__ Bind(&update_ic_data);
// ECX: ICData
- const String& equal_name = String::ZoneHandle(Symbols::EqualOperator());
__ movl(EAX, Address(ESP, 1 * kWordSize));
__ movl(EDI, Address(ESP, 2 * kWordSize));
AssemblerMacros::EnterStubFrame(assembler);
__ pushl(EDI); // arg 0
__ pushl(EAX); // arg 1
- __ PushObject(equal_name); // Target's name.
+ __ PushObject(Symbols::EqualOperatorHandle()); // Target's name.
__ pushl(ECX); // ICData
__ CallRuntime(kUpdateICDataTwoArgsRuntimeEntry);
__ Drop(4);
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index a6ca462..24116c8 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -343,8 +343,7 @@
__ pushq(RCX); // Closure object.
__ pushq(R10); // Arguments descriptor.
__ movq(R13, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
- __ SmiUntag(R13);
- __ subq(R13, Immediate(1)); // Arguments array length, minus the receiver.
+ __ SmiUntag(R13); // Arguments array length, including the original receiver.
PushArgumentsArray(assembler, (kWordSize * 6));
// Stack layout explaining "(kWordSize * 6)" offset.
// TOS + 0: Argument array.
@@ -370,7 +369,7 @@
__ Bind(&function_not_found);
// The target function was not found, so invoke method
- // "void noSuchMethod(function_name, args_array)".
+ // "dynamic noSuchMethod(InvocationMirror invocation)".
// RAX: receiver.
// RBX: ic-data.
// R10: arguments descriptor array.
@@ -380,8 +379,7 @@
__ pushq(RBX); // IC-data array.
__ pushq(R10); // Arguments descriptor array.
__ movq(R13, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
- __ SmiUntag(R13);
- __ subq(R13, Immediate(1)); // Arguments array length, minus the receiver.
+ __ SmiUntag(R13); // Arguments array length, including the original receiver.
// See stack layout below explaining "wordSize * 7" offset.
PushArgumentsArray(assembler, (kWordSize * 7));
@@ -529,6 +527,44 @@
}
+void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
+ AssemblerMacros::EnterStubFrame(assembler);
+ // 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 (return addres, saved fp) in the stack above the last argument.
+ __ movq(RAX, Address(RSP, RAX, TIMES_4, 2 * 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.
+ __ pushq(RAX); // Receiver.
+ __ pushq(RBX); // IC data.
+ __ pushq(R10); // Arguments descriptor.
+ __ CallRuntime(kMegamorphicCacheMissHandlerRuntimeEntry);
+ // Discard arguments.
+ __ popq(RAX);
+ __ popq(RAX);
+ __ popq(RAX);
+ __ popq(RAX); // Return value from the runtime call (instructions).
+ __ popq(R10); // Restore arguments descriptor.
+ __ popq(RBX); // Restore IC data.
+ __ LeaveFrame();
+
+ Label lookup;
+ __ cmpq(RAX, raw_null);
+ __ j(EQUAL, &lookup, Assembler::kNearJump);
+ __ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+ __ jmp(RAX);
+
+ __ Bind(&lookup);
+ __ jmp(&StubCode::InstanceFunctionLookupLabel());
+}
+
+
// Called for inline allocation of arrays.
// Input parameters:
// R10 : Array length as Smi.
@@ -623,6 +659,7 @@
// Initialize all array elements to raw_null.
// RAX: new object start as a tagged pointer.
// R12: new object end address.
+ // R10: Array length as Smi.
__ leaq(RBX, FieldAddress(RAX, Array::data_offset()));
// RBX: iterator which initially points to the start of the variable
// data area to be initialized.
@@ -631,6 +668,7 @@
__ Bind(&init_loop);
__ cmpq(RBX, R12);
__ j(ABOVE_EQUAL, &done, Assembler::kNearJump);
+ // TODO(cshapiro): StoreIntoObjectNoBarrier
__ movq(Address(RBX, 0), raw_null);
__ addq(RBX, Immediate(kWordSize));
__ jmp(&init_loop, Assembler::kNearJump);
@@ -645,6 +683,8 @@
// Unable to allocate the array using the fast inline code, just call
// into the runtime.
__ Bind(&slow_case);
+ // Create a stub frame as we are pushing some objects on the stack before
+ // calling into the runtime.
AssemblerMacros::EnterStubFrame(assembler);
__ pushq(raw_null); // Setup space on stack for return value.
__ pushq(R10); // Array length as Smi.
@@ -724,8 +764,10 @@
__ jmp(RBX);
__ Bind(¬_closure);
- // Call runtime to report that a closure call was attempted on a non-closure
- // object, passing the non-closure object and its arguments array.
+ // Call runtime to attempt to resolve and invoke a call method on a
+ // non-closure object, passing the non-closure object and its arguments array,
+ // returning here.
+ // If no call method exists, throw a NoSuchMethodError.
// R13: non-closure object.
// R10: arguments descriptor array.
@@ -733,26 +775,35 @@
// calling into the runtime.
AssemblerMacros::EnterStubFrame(assembler);
- __ pushq(raw_null); // Setup space on stack for result from error reporting.
+ __ pushq(raw_null); // Setup space on stack for result from call.
__ pushq(R13); // Non-closure object.
+ __ pushq(R10); // Arguments descriptor.
// Load num_args.
__ movq(R13, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
- __ SmiUntag(R13);
- __ subq(R13, Immediate(1)); // Arguments array length, minus the closure.
- // See stack layout below explaining "wordSize * 5" offset.
- PushArgumentsArray(assembler, (kWordSize * 5));
+ __ SmiUntag(R13); // Arguments array length, including the non-closure.
+ // See stack layout below explaining "wordSize * 6" offset.
+ PushArgumentsArray(assembler, (kWordSize * 6));
// Stack:
// TOS + 0: Argument array.
- // TOS + 1: Non-closure object.
- // TOS + 2: Place for result from reporting the error.
- // TOS + 3: PC marker => RawInstruction object.
- // TOS + 4: Saved RBP of previous frame. <== RBP
- // TOS + 5: Dart code return address
- // TOS + 6: Last argument of caller.
+ // TOS + 1: Arguments descriptor array.
+ // TOS + 2: Non-closure object.
+ // TOS + 3: Place for result from the call.
+ // TOS + 4: PC marker => RawInstruction object.
+ // TOS + 5: Saved RBP of previous frame. <== RBP
+ // TOS + 6: Dart code return address
+ // TOS + 7: Last argument of caller.
// ....
- __ CallRuntime(kReportObjectNotClosureRuntimeEntry);
- __ Stop("runtime call throws an exception");
+ __ CallRuntime(kInvokeNonClosureRuntimeEntry);
+ // Remove arguments.
+ __ popq(RAX);
+ __ popq(RAX);
+ __ popq(RAX);
+ __ popq(RAX); // Get result into RAX.
+
+ // Remove the stub frame as we are about to return.
+ __ LeaveFrame();
+ __ ret();
}
@@ -1390,14 +1441,7 @@
// R10 : arguments descriptor array.
void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) {
// The target function was not found, so invoke method
- // "void noSuchMethod(function_name, Array arguments)".
- // TODO(regis): For now, we simply pass the actual arguments, both positional
- // and named, as the argument array. This is not correct if out-of-order
- // named arguments were passed.
- // The signature of the "noSuchMethod" method has to change from
- // noSuchMethod(String name, Array arguments) to something like
- // noSuchMethod(InvocationMirror call).
- // Also, the class NoSuchMethodError has to be modified accordingly.
+ // "dynamic noSuchMethod(InvocationMirror invocation)".
const Immediate raw_null =
Immediate(reinterpret_cast<intptr_t>(Object::null()));
__ movq(R13, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
@@ -1411,7 +1455,7 @@
__ pushq(RAX); // Receiver.
__ pushq(RBX); // IC data array.
__ pushq(R10); // Arguments descriptor array.
- __ subq(R13, Immediate(1)); // Arguments array length, minus the receiver.
+ // R13: Arguments array length, including the receiver.
// See stack layout below explaining "wordSize * 10" offset.
PushArgumentsArray(assembler, (kWordSize * 10));
@@ -2031,13 +2075,12 @@
__ Bind(&update_ic_data);
// RCX: ICData
- const String& equal_name = String::ZoneHandle(Symbols::New("=="));
__ movq(RAX, Address(RSP, 1 * kWordSize));
__ movq(R13, Address(RSP, 2 * kWordSize));
AssemblerMacros::EnterStubFrame(assembler);
__ pushq(R13); // arg 0
__ pushq(RAX); // arg 1
- __ PushObject(equal_name); // Target's name.
+ __ PushObject(Symbols::EqualOperatorHandle()); // Target's name.
__ pushq(RBX); // ICData
__ CallRuntime(kUpdateICDataTwoArgsRuntimeEntry);
__ Drop(4);
diff --git a/runtime/vm/symbols.cc b/runtime/vm/symbols.cc
index 125f072..9813d88 100644
--- a/runtime/vm/symbols.cc
+++ b/runtime/vm/symbols.cc
@@ -4,6 +4,8 @@
#include "vm/symbols.h"
+#include "vm/handles.h"
+#include "vm/handles_impl.h"
#include "vm/isolate.h"
#include "vm/object.h"
#include "vm/object_store.h"
@@ -16,6 +18,13 @@
RawString* Symbols::predefined_[Symbols::kMaxId];
+Symbols::ReadOnlyHandles* Symbols::predefined_handles_ = NULL;
+
+#define DEFINE_SYMBOL_HANDLE(symbol) \
+ String* Symbols::symbol##_handle_ = NULL;
+PREDEFINED_SYMBOL_HANDLES_LIST(DEFINE_SYMBOL_HANDLE)
+#undef DEFINE_SYMBOL_HANDLE
+
static const char* names[] = {
NULL,
@@ -25,6 +34,11 @@
#undef DEFINE_SYMBOL_LITERAL
};
+intptr_t Symbols::num_of_grows_;
+intptr_t Symbols::collision_count_[kMaxCollisionBuckets];
+
+DEFINE_FLAG(bool, dump_symbol_stats, false, "Dump symbol table statistics");
+
const char* Symbols::Name(SymbolId symbol) {
ASSERT((symbol > kIllegal) && (symbol < kMaxPredefinedId));
@@ -36,6 +50,13 @@
// Should only be run by the vm isolate.
ASSERT(isolate == Dart::vm_isolate());
+ if (FLAG_dump_symbol_stats) {
+ num_of_grows_ = 0;
+ for (intptr_t i = 0; i < kMaxCollisionBuckets; i++) {
+ collision_count_[i] = 0;
+ }
+ }
+
// Create and setup a symbol table in the vm isolate.
SetupSymbolTable(isolate);
@@ -59,6 +80,14 @@
ASSERT(kMaxPredefinedId + c < kMaxId);
predefined_[kMaxPredefinedId + c] = FromUTF32(&c, 1);
}
+
+ predefined_handles_ = new ReadOnlyHandles();
+#define INITIALIZE_SYMBOL_HANDLE(symbol) \
+ symbol##_handle_ = reinterpret_cast<String*>( \
+ predefined_handles_->AllocateHandle()); \
+ *symbol##_handle_ = symbol();
+PREDEFINED_SYMBOL_HANDLES_LIST(INITIALIZE_SYMBOL_HANDLE)
+#undef INITIALIZE_SYMBOL_HANDLE
}
@@ -248,8 +277,46 @@
}
+bool Symbols::IsPredefinedHandle(uword address) {
+ return predefined_handles_->IsValidHandle(address);
+}
+
+
+void Symbols::DumpStats() {
+ if (FLAG_dump_symbol_stats) {
+ intptr_t table_size = 0;
+ dart::Smi& used = Smi::Handle();
+ Array& symbol_table = Array::Handle(Array::null());
+
+ // First dump VM symbol table stats.
+ symbol_table = Dart::vm_isolate()->object_store()->symbol_table();
+ table_size = symbol_table.Length() - 1;
+ used ^= symbol_table.At(table_size);
+ OS::Print("VM Isolate: Number of symbols : %"Pd"\n", used.Value());
+ OS::Print("VM Isolate: Symbol table capacity : %"Pd"\n", table_size);
+
+ // Now dump regular isolate symbol table stats.
+ symbol_table = Isolate::Current()->object_store()->symbol_table();
+ table_size = symbol_table.Length() - 1;
+ used ^= symbol_table.At(table_size);
+ OS::Print("Isolate: Number of symbols : %"Pd"\n", used.Value());
+ OS::Print("Isolate: Symbol table capacity : %"Pd"\n", table_size);
+
+ // Dump overall collision and growth counts.
+ OS::Print("Number of symbol table grows = %"Pd"\n", num_of_grows_);
+ OS::Print("Collision counts on add and lookup :\n");
+ intptr_t i = 0;
+ for (i = 0; i < (kMaxCollisionBuckets - 1); i++) {
+ OS::Print(" %"Pd" collisions => %"Pd"\n", i, collision_count_[i]);
+ }
+ OS::Print(" > %"Pd" collisions => %"Pd"\n", i, collision_count_[i]);
+ }
+}
+
+
void Symbols::GrowSymbolTable(const Array& symbol_table) {
// TODO(iposva): Avoid exponential growth.
+ num_of_grows_ += 1;
intptr_t table_size = symbol_table.Length() - 1;
intptr_t new_table_size = table_size * 2;
Array& new_symbol_table = Array::Handle(Array::New(new_table_size + 1));
@@ -263,9 +330,17 @@
intptr_t hash = element.Hash();
intptr_t index = hash % new_table_size;
new_element = new_symbol_table.At(index);
+ intptr_t num_collisions = 0;
while (!new_element.IsNull()) {
index = (index + 1) % new_table_size; // Move to next element.
new_element = new_symbol_table.At(index);
+ num_collisions += 1;
+ }
+ if (FLAG_dump_symbol_stats) {
+ if (num_collisions >= kMaxCollisionBuckets) {
+ num_collisions = (kMaxCollisionBuckets - 1);
+ }
+ collision_count_[num_collisions] += 1;
}
new_symbol_table.SetAt(index, element);
}
@@ -305,12 +380,20 @@
// Last element of the array is the number of used elements.
intptr_t table_size = symbol_table.Length() - 1;
intptr_t index = hash % table_size;
+ intptr_t num_collisions = 0;
String& symbol = String::Handle();
symbol ^= symbol_table.At(index);
while (!symbol.IsNull() && !symbol.Equals(characters, len)) {
index = (index + 1) % table_size; // Move to next element.
symbol ^= symbol_table.At(index);
+ num_collisions += 1;
+ }
+ if (FLAG_dump_symbol_stats) {
+ if (num_collisions >= kMaxCollisionBuckets) {
+ num_collisions = (kMaxCollisionBuckets - 1);
+ }
+ collision_count_[num_collisions] += 1;
}
return index; // Index of symbol if found or slot into which to add symbol.
}
@@ -338,12 +421,20 @@
// Last element of the array is the number of used elements.
intptr_t table_size = symbol_table.Length() - 1;
intptr_t index = hash % table_size;
+ intptr_t num_collisions = 0;
String& symbol = String::Handle();
symbol ^= symbol_table.At(index);
while (!symbol.IsNull() && !symbol.Equals(str, begin_index, len)) {
index = (index + 1) % table_size; // Move to next element.
symbol ^= symbol_table.At(index);
+ num_collisions += 1;
+ }
+ if (FLAG_dump_symbol_stats) {
+ if (num_collisions >= kMaxCollisionBuckets) {
+ num_collisions = (kMaxCollisionBuckets - 1);
+ }
+ collision_count_[num_collisions] += 1;
}
return index; // Index of symbol if found or slot into which to add symbol.
}
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 9900389..17d03fe 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -96,6 +96,7 @@
V(Context, "Context") \
V(ContextScope, "ContextScope") \
V(ICData, "ICData") \
+ V(MegamorphicCache, "MegamorphicCache") \
V(SubtypeTestCache, "SubtypeTestCache") \
V(ApiError, "ApiError") \
V(LanguageError, "LanguageError") \
@@ -144,6 +145,13 @@
V(InvocationMirror, "_InvocationMirror") \
V(AllocateInvocationMirror, "_allocateInvocationMirror") \
+#define PREDEFINED_SYMBOL_HANDLES_LIST(V) \
+ V(Dot) \
+ V(EqualOperator) \
+ V(IndexToken) \
+ V(AssignIndexToken) \
+ V(This) \
+
// Contains a list of frequently used strings in a canonicalized form. This
// list is kept in the vm_isolate in order to share the copy across isolates
// without having to maintain copies in each isolate.
@@ -170,6 +178,12 @@
PREDEFINED_SYMBOLS_LIST(DEFINE_SYMBOL_ACCESSOR)
#undef DEFINE_SYMBOL_ACCESSOR
+ // Access methods for symbol handles stored in the vm isolate.
+#define DEFINE_SYMBOL_HANDLE_ACCESSOR(symbol) \
+ static const String& symbol##Handle() { return *symbol##_handle_; }
+PREDEFINED_SYMBOL_HANDLES_LIST(DEFINE_SYMBOL_HANDLE_ACCESSOR)
+#undef DEFINE_SYMBOL_HANDLE_ACCESSOR
+
// Initialize frequently used symbols in the vm isolate.
static void InitOnce(Isolate* isolate);
@@ -210,10 +224,14 @@
return reinterpret_cast<RawString**>(&predefined_);
}
+ static bool IsPredefinedHandle(uword address);
+
+ static void DumpStats();
+
private:
enum {
- kInitialVMIsolateSymtabSize = ((kMaxId + 15) & -16),
- kInitialSymtabSize = 256
+ kInitialVMIsolateSymtabSize = 512,
+ kInitialSymtabSize = 2048
};
// Helper functions to create a symbol given a string or set of characters.
@@ -255,6 +273,37 @@
// List of symbols that are stored in the vm isolate for easy access.
static RawString* predefined_[kMaxId];
+ // Statistics used to measure the efficiency of the symbol table.
+ static const intptr_t kMaxCollisionBuckets = 10;
+ static intptr_t num_of_grows_;
+ static intptr_t collision_count_[kMaxCollisionBuckets];
+
+ // Structure for managing handles allocation for symbols that are
+ // stored in the vm isolate. We don't want these handles to be
+ // destroyed as part of the C++ static destructors and hence this
+ // object is dynamically allocated.
+ class ReadOnlyHandles {
+ public:
+ ReadOnlyHandles() { }
+ uword AllocateHandle() {
+ return handles_.AllocateScopedHandle();
+ }
+ bool IsValidHandle(uword address) {
+ return handles_.IsValidScopedHandle(address);
+ }
+
+ private:
+ VMHandles handles_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadOnlyHandles);
+ };
+ static ReadOnlyHandles* predefined_handles_;
+
+#define DECLARE_SYMBOL_HANDLE(symbol) \
+ static String* symbol##_handle_;
+PREDEFINED_SYMBOL_HANDLES_LIST(DECLARE_SYMBOL_HANDLE)
+#undef DECLARE_SYMBOL_HANDLE
+
friend class String;
friend class SnapshotReader;
friend class SnapshotWriter;
diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi
index 0b18707..8cc57c1 100644
--- a/runtime/vm/vm_sources.gypi
+++ b/runtime/vm/vm_sources.gypi
@@ -197,6 +197,8 @@
'longjump.cc',
'longjump.h',
'longjump_test.cc',
+ 'megamorphic_cache_table.cc',
+ 'megamorphic_cache_table.h',
'memory_region.cc',
'memory_region.h',
'memory_region_test.cc',
diff --git a/sdk/lib/_internal/compiler/implementation/closure.dart b/sdk/lib/_internal/compiler/implementation/closure.dart
index 3d5feef..7f981dc 100644
--- a/sdk/lib/_internal/compiler/implementation/closure.dart
+++ b/sdk/lib/_internal/compiler/implementation/closure.dart
@@ -9,9 +9,15 @@
import "tree/tree.dart";
import "util/util.dart";
+abstract class ClosureNamer {
+ SourceString getClosureVariableName(SourceString name, int id);
+}
+
+
class ClosureTask extends CompilerTask {
Map<Node, ClosureClassMap> closureMappingCache;
- ClosureTask(Compiler compiler)
+ ClosureNamer namer;
+ ClosureTask(Compiler compiler, this.namer)
: closureMappingCache = new Map<Node, ClosureClassMap>(),
super(compiler);
@@ -25,7 +31,7 @@
if (cached != null) return cached;
ClosureTranslator translator =
- new ClosureTranslator(compiler, elements, closureMappingCache);
+ new ClosureTranslator(compiler, elements, closureMappingCache, namer);
// The translator will store the computed closure-mappings inside the
// cache. One for given node and one for each nested closure.
@@ -58,6 +64,10 @@
bool isInstanceMember() => true;
bool isAssignable() => false;
+ // The names of closure variables don't need renaming, since their use is very
+ // simple and they have 1-character names in the minified mode.
+ bool hasFixedBackendName() => true;
+ String fixedBackendName() => name.slowToString();
String toString() => "ClosureFieldElement($name)";
}
@@ -151,9 +161,9 @@
final Map<Element, Element> parametersWithSentinel;
ClosureClassMap(this.closureElement,
- this.closureClassElement,
- this.callElement,
- this.thisElement)
+ this.closureClassElement,
+ this.callElement,
+ this.thisElement)
: this.freeVariableMapping = new Map<Element, Element>(),
this.capturedFieldMapping = new Map<Element, Element>(),
this.capturingScopes = new Map<Node, ClosureScope>(),
@@ -167,6 +177,7 @@
final Compiler compiler;
final TreeElements elements;
int closureFieldCounter = 0;
+ int boxedFieldCounter = 0;
bool inTryStatement = false;
final Map<Node, ClosureClassMap> closureMappingCache;
@@ -190,9 +201,12 @@
// The closureData of the currentFunctionElement.
ClosureClassMap closureData;
+ ClosureNamer namer;
+
bool insideClosure = false;
- ClosureTranslator(this.compiler, this.elements, this.closureMappingCache)
+ ClosureTranslator(this.compiler, this.elements, this.closureMappingCache,
+ this.namer)
: capturedVariableMapping = new Map<Element, Element>(),
closures = <Expression>[],
mutatedVariables = new Set<Element>();
@@ -225,6 +239,7 @@
// The captured variables that need to be stored in a field of the closure
// class.
Set<Element> fieldCaptures = new Set<Element>();
+ Set<Element> boxes = new Set<Element>();
ClosureClassMap data = closureMappingCache[closure];
Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
// We get a copy of the keys and iterate over it, to avoid modifications
@@ -244,26 +259,30 @@
freeVariableMapping[fromElement] = updatedElement;
Element boxElement = updatedElement.enclosingElement;
assert(boxElement.kind == ElementKind.VARIABLE);
- fieldCaptures.add(boxElement);
+ boxes.add(boxElement);
}
});
ClassElement closureElement = data.closureClassElement;
- assert(closureElement != null || fieldCaptures.isEmpty);
- for (Element capturedElement in fieldCaptures) {
- SourceString name;
- if (capturedElement is BoxElement) {
- // The name is already mangled.
- name = capturedElement.name;
- } else {
- int id = closureFieldCounter++;
- name = new SourceString("${capturedElement.name.slowToString()}_$id");
- }
+ assert(closureElement != null ||
+ (fieldCaptures.isEmpty && boxes.isEmpty));
+ void addElement(Element element, SourceString name) {
Element fieldElement = new ClosureFieldElement(name, closureElement);
closureElement.backendMembers =
closureElement.backendMembers.prepend(fieldElement);
- data.capturedFieldMapping[fieldElement] = capturedElement;
- freeVariableMapping[capturedElement] = fieldElement;
+ data.capturedFieldMapping[fieldElement] = element;
+ freeVariableMapping[element] = fieldElement;
}
+ // Add the box elements first so we get the same ordering.
+ for (Element capturedElement in boxes) {
+ addElement(capturedElement, capturedElement.name);
+ }
+ for (Element capturedElement in fieldCaptures) {
+ int id = closureFieldCounter++;
+ SourceString name =
+ namer.getClosureVariableName(capturedElement.name, id);
+ addElement(capturedElement, name);
+ }
+ closureElement.backendMembers = closureElement.backendMembers.reverse();
}
}
@@ -418,17 +437,18 @@
if (box == null) {
// TODO(floitsch): construct better box names.
SourceString boxName =
- new SourceString("box_${closureFieldCounter++}");
+ namer.getClosureVariableName(const SourceString('box'),
+ closureFieldCounter++);
box = new BoxElement(boxName, currentElement);
}
- // TODO(floitsch): construct better boxed names.
String elementName = element.name.slowToString();
- // We are currently using the name in an HForeign which could replace
- // "$X" with something else.
- String escaped = elementName.replaceAll("\$", "_");
SourceString boxedName =
- new SourceString("${escaped}_${closureFieldCounter++}");
+ namer.getClosureVariableName(new SourceString(elementName),
+ boxedFieldCounter++);
Element boxed = new Element(boxedName, ElementKind.FIELD, box);
+ // No need to rename the fields of a box, so we give them a native name
+ // right now.
+ boxed.setFixedBackendName(boxedName.slowToString());
scopeMapping[element] = boxed;
capturedVariableMapping[element] = boxed;
}
@@ -478,7 +498,7 @@
}
/** Returns a non-unique name for the given closure element. */
- String closureName(Element element) {
+ String computeClosureName(Element element) {
Link<String> parts = const Link<String>();
SourceString ownName = element.name;
if (ownName == null || ownName.stringValue == "") {
@@ -505,7 +525,7 @@
}
ClosureClassMap globalizeClosure(FunctionExpression node, Element element) {
- SourceString closureName = new SourceString(closureName(element));
+ SourceString closureName = new SourceString(computeClosureName(element));
ClassElement globalizedElement = new ClosureClassElement(
closureName, compiler, element, element.getCompilationUnit());
FunctionElement callElement =
@@ -513,7 +533,7 @@
element,
globalizedElement);
globalizedElement.backendMembers =
- const Link<Element>().prepend(callElement);
+ globalizedElement.backendMembers.prepend(callElement);
// The nested function's 'this' is the same as the one for the outer
// function. It could be [null] if we are inside a static method.
Element thisElement = closureData.thisElement;
diff --git a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
index 7eb4167..cb1439a 100644
--- a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
+++ b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
@@ -75,11 +75,6 @@
SourceString helperName = const SourceString('createRuntimeType');
createRuntimeTypeFunction = compiler.findHelper(helperName);
registerStaticUse(createRuntimeTypeFunction);
- // TODO(kasperl): It looks a bit fishy that we have to register
- // this in the resolution queue too. Can we do this in the
- // resolver instead? That would be more consistent with how we
- // register all the other resolution work items.
- compiler.enqueuer.resolution.registerStaticUse(createRuntimeTypeFunction);
}
/**
diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart
index 1aa0452..1de80f0 100644
--- a/sdk/lib/_internal/compiler/implementation/compiler.dart
+++ b/sdk/lib/_internal/compiler/implementation/compiler.dart
@@ -128,6 +128,7 @@
Element _currentElement;
LibraryElement coreLibrary;
LibraryElement isolateLibrary;
+ LibraryElement isolateHelperLibrary;
LibraryElement jsHelperLibrary;
LibraryElement interceptorsLibrary;
LibraryElement foreignLibrary;
@@ -151,6 +152,7 @@
Element identicalFunction;
Element functionApplyMethod;
Element invokeOnMethod;
+ Element createInvocationMirrorElement;
Element get currentElement => _currentElement;
withCurrentElement(Element element, f()) {
@@ -162,6 +164,7 @@
if (!hasCrashed) {
SourceSpan span = spanFromSpannable(ex.node);
reportDiagnostic(span, ex.message, api.Diagnostic.ERROR);
+ pleaseReportCrash();
}
hasCrashed = true;
throw;
@@ -204,6 +207,8 @@
static const SourceString CALL_OPERATOR_NAME = const SourceString('call');
static const SourceString NO_SUCH_METHOD = const SourceString('noSuchMethod');
static const int NO_SUCH_METHOD_ARG_COUNT = 1;
+ static const SourceString CREATE_INVOCATION_MIRROR =
+ const SourceString('createInvocationMirror');
static const SourceString INVOKE_ON = const SourceString('invokeOn');
static const SourceString RUNTIME_TYPE = const SourceString('runtimeType');
static const SourceString START_ROOT_ISOLATE =
@@ -241,11 +246,17 @@
progress = new Stopwatch() {
progress.start();
world = new World(this);
- backend = emitJavaScript ?
- new js_backend.JavaScriptBackend(this,
- generateSourceMap,
- disallowUnsafeEval) :
- new dart_backend.DartBackend(this, strips);
+
+ closureMapping.ClosureNamer closureNamer;
+ if (emitJavaScript) {
+ js_backend.JavaScriptBackend jsBackend =
+ new js_backend.JavaScriptBackend(this, generateSourceMap,
+ disallowUnsafeEval);
+ closureNamer = jsBackend.namer;
+ backend = jsBackend;
+ } else {
+ backend = new dart_backend.DartBackend(this, strips);
+ }
// No-op in production mode.
validator = new TreeValidatorTask(this);
@@ -258,7 +269,7 @@
parser = new ParserTask(this),
patchParser = new PatchParserTask(this),
resolver = new ResolverTask(this),
- closureToClassMapper = new closureMapping.ClosureTask(this),
+ closureToClassMapper = new closureMapping.ClosureTask(this, closureNamer),
checker = new TypeCheckerTask(this),
typesTask = new ti.TypesTask(this, enableConcreteTypeInference),
constantHandler = new ConstantHandler(this, backend.constantSystem),
@@ -304,6 +315,10 @@
reportDiagnostic(spanFromElement(element),
MessageKind.COMPILER_CRASHED.error().toString(),
api.Diagnostic.CRASH);
+ pleaseReportCrash();
+ }
+
+ void pleaseReportCrash() {
print(MessageKind.PLEASE_REPORT_THE_CRASH.message([BUILD_ID]));
}
@@ -379,8 +394,8 @@
enqueuer.resolution.registerInvocation(NO_SUCH_METHOD, selector);
enqueuer.codegen.registerInvocation(NO_SUCH_METHOD, selector);
- Element createInvocationMirrorElement =
- findHelper(const SourceString('createInvocationMirror'));
+ createInvocationMirrorElement =
+ findHelper(CREATE_INVOCATION_MIRROR);
enqueuer.resolution.addToWorkList(createInvocationMirrorElement);
enqueuer.codegen.addToWorkList(createInvocationMirrorElement);
}
@@ -388,12 +403,33 @@
void enableIsolateSupport(LibraryElement element) {
// TODO(ahe): Move this method to Enqueuer.
isolateLibrary = element.patch;
- enqueuer.resolution.addToWorkList(isolateLibrary.find(START_ROOT_ISOLATE));
+ isolateHelperLibrary = scanBuiltinLibrary('_isolate_helper');
+ importForeignLibrary(isolateHelperLibrary);
+ importHelperLibrary(isolateHelperLibrary);
+
+ libraryLoader.importLibrary(isolateLibrary, isolateHelperLibrary, null);
enqueuer.resolution.addToWorkList(
- isolateLibrary.find(const SourceString('_currentIsolate')));
+ isolateHelperLibrary.find(START_ROOT_ISOLATE));
enqueuer.resolution.addToWorkList(
- isolateLibrary.find(const SourceString('_callInIsolate')));
- enqueuer.codegen.addToWorkList(isolateLibrary.find(START_ROOT_ISOLATE));
+ isolateHelperLibrary.find(const SourceString('_currentIsolate')));
+ enqueuer.resolution.addToWorkList(
+ isolateHelperLibrary.find(const SourceString('_callInIsolate')));
+ enqueuer.codegen.addToWorkList(
+ isolateHelperLibrary.find(START_ROOT_ISOLATE));
+
+ // The helper library does not use the native language extension,
+ // so we manually set the native classes this library defines.
+ // TODO(ngeoffray): Enable annotations on these classes.
+ ClassElement cls = isolateHelperLibrary.find(const SourceString('_Window'));
+ cls.setNative('"*DOMWindow"');
+
+ cls = isolateHelperLibrary.find(const SourceString('_WorkerStub'));
+ cls.setNative('"*Worker"');
+
+ enqueuer.resolution.nativeEnqueuer.processNativeClassesInLibrary(
+ isolateHelperLibrary);
+ enqueuer.codegen.nativeEnqueuer.processNativeClassesInLibrary(
+ isolateHelperLibrary);
}
bool hasIsolateSupport() => isolateLibrary != null;
@@ -486,7 +522,7 @@
/** Define the JS helper functions in the given library. */
void importForeignLibrary(LibraryElement library) {
- if (jsHelperLibrary != null) {
+ if (foreignLibrary != null) {
libraryLoader.importLibrary(library, foreignLibrary, null);
}
}
@@ -499,7 +535,6 @@
'dart/tests/compiler/dart2js_native');
if (nativeTest
|| libraryName == 'dart:mirrors'
- || libraryName == 'dart:isolate'
|| libraryName == 'dart:math'
|| libraryName == 'dart:html'
|| libraryName == 'dart:html_common'
@@ -765,12 +800,13 @@
reportDiagnostic(span, "$message", kind);
}
- void onDeprecatedFeature(Spannable span, String feature) {
+ /// Returns true if a diagnostic was emitted.
+ bool onDeprecatedFeature(Spannable span, String feature) {
if (currentElement == null)
throw new SpannableAssertionFailure(span, feature);
if (!checkDeprecationInSdk &&
currentElement.getLibrary().isPlatformLibrary) {
- return;
+ return false;
}
var kind = rejectDeprecatedFeatures
? api.Diagnostic.ERROR : api.Diagnostic.WARNING;
@@ -778,6 +814,7 @@
? MessageKind.DEPRECATED_FEATURE_ERROR.error([feature])
: MessageKind.DEPRECATED_FEATURE_WARNING.error([feature]);
reportMessage(spanFromSpannable(span), message, kind);
+ return true;
}
void reportDiagnostic(SourceSpan span, String message, api.Diagnostic kind);
@@ -804,7 +841,7 @@
if (Elements.isErroneousElement(element)) {
element = element.enclosingElement;
}
- if (element.position() == null) {
+ if (element.position() == null && !element.isCompilationUnit()) {
// Sometimes, the backend fakes up elements that have no
// position. So we use the enclosing element instead. It is
// not a good error location, but cancel really is "internal
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
index fb1cf88..4735b7b 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
@@ -252,6 +252,9 @@
fixedMemberNames.add(element.name.slowToString());
});
}
+ // The VM will automatically invoke the call method of objects
+ // that are invoked as functions. Make sure to not rename that.
+ fixedMemberNames.add('call');
// TODO(antonm): TypeError.srcType and TypeError.dstType are defined in
// runtime/lib/error.dart. Overall, all DartVM specific libs should be
// accounted for.
diff --git a/sdk/lib/_internal/compiler/implementation/diagnostic_listener.dart b/sdk/lib/_internal/compiler/implementation/diagnostic_listener.dart
index 19f5501..c20e6cb 100644
--- a/sdk/lib/_internal/compiler/implementation/diagnostic_listener.dart
+++ b/sdk/lib/_internal/compiler/implementation/diagnostic_listener.dart
@@ -20,5 +20,6 @@
void reportMessage(SourceSpan span, Diagnostic message, api.Diagnostic kind);
- void onDeprecatedFeature(Spannable span, String feature);
+ /// Returns true if a diagnostic was emitted.
+ bool onDeprecatedFeature(Spannable span, String feature);
}
diff --git a/sdk/lib/_internal/compiler/implementation/elements/elements.dart b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
index d0caf29..a67fb06 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/elements.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/elements.dart
@@ -58,8 +58,6 @@
static const int TYPE_VARIABLE = 128;
static const int IMPLIES_TYPE = CLASS | ALIAS | TYPE_VARIABLE;
-
- static const int IS_EXTENDABLE = CLASS | ALIAS;
}
class ElementKind {
@@ -191,7 +189,6 @@
bool isAccessor() => isGetter() || isSetter();
bool isLibrary() => identical(kind, ElementKind.LIBRARY);
bool impliesType() => (kind.category & ElementCategory.IMPLIES_TYPE) != 0;
- bool isExtendable() => (kind.category & ElementCategory.IS_EXTENDABLE) != 0;
/** See [ErroneousElement] for documentation. */
bool isErroneous() => false;
@@ -350,11 +347,19 @@
}
}
- String _nativeName = null;
- bool isNative() => _nativeName != null;
- String nativeName() => _nativeName;
- /// Marks this element as a native element.
- void setNative(String name) { _nativeName = name; }
+ String _fixedBackendName = null;
+ bool _isNative = false;
+ bool isNative() => _isNative;
+ bool hasFixedBackendName() => _fixedBackendName != null;
+ String fixedBackendName() => _fixedBackendName;
+ // Marks this element as a native element.
+ void setNative(String name) {
+ _isNative = true;
+ _fixedBackendName = name;
+ }
+ void setFixedBackendName(String name) {
+ _fixedBackendName = name;
+ }
FunctionElement asFunctionElement() => null;
@@ -1756,6 +1761,9 @@
bool isInterface() => false;
bool isNative() => nativeTagInfo != null;
+ void setNative(String name) {
+ nativeTagInfo = new SourceString(name);
+ }
int get hashCode => id;
Scope buildScope() => new ClassScope(enclosingElement.buildScope(), this);
@@ -1857,6 +1865,16 @@
return new SourceString('$r\$$s');
}
+ static SourceString deconstructConstructorName(SourceString name,
+ ClassElement holder) {
+ String r = '${holder.name.slowToString()}\$';
+ String s = name.slowToString();
+ if (s.startsWith(r)) {
+ return new SourceString(s.substring(r.length));
+ }
+ return null;
+ }
+
/**
* Map an operator-name to a valid Dart identifier.
*
diff --git a/sdk/lib/_internal/compiler/implementation/js/nodes.dart b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
index ec1469c..f206c0e 100644
--- a/sdk/lib/_internal/compiler/implementation/js/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
@@ -149,6 +149,8 @@
accept(NodeVisitor visitor);
void visitChildren(NodeVisitor visitor);
+
+ VariableUse asVariableUse() => null;
}
class Program extends Node {
@@ -657,6 +659,8 @@
VariableUse(String name) : super(name);
accept(NodeVisitor visitor) => visitor.visitVariableUse(this);
+
+ VariableUse asVariableUse() => this;
}
class VariableDeclaration extends VariableReference {
diff --git a/sdk/lib/_internal/compiler/implementation/js/printer.dart b/sdk/lib/_internal/compiler/implementation/js/printer.dart
index ce2e056..048dc71 100644
--- a/sdk/lib/_internal/compiler/implementation/js/printer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/printer.dart
@@ -12,7 +12,7 @@
bool inForInit = false;
bool atStatementBegin = false;
final DanglingElseVisitor danglingElseVisitor;
- final Namer namer;
+ final LocalNamer localNamer;
bool pendingSemicolon = false;
bool pendingSpace = false;
static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]');
@@ -22,11 +22,11 @@
this.compiler = compiler,
outBuffer = new leg.CodeBuffer(),
danglingElseVisitor = new DanglingElseVisitor(compiler),
- namer = determineRenamer(compiler.enableMinification,
- allowVariableMinification);
+ localNamer = determineRenamer(compiler.enableMinification,
+ allowVariableMinification);
- static Namer determineRenamer(bool shouldCompressOutput,
- bool allowVariableMinification) {
+ static LocalNamer determineRenamer(bool shouldCompressOutput,
+ bool allowVariableMinification) {
return (shouldCompressOutput && allowVariableMinification)
? new MinifyRenamer() : new IdentityNamer();
}
@@ -391,7 +391,7 @@
visitNestedExpression(name, PRIMARY,
newInForInit: false, newAtStatementBegin: false);
}
- namer.enterScope(vars);
+ localNamer.enterScope(vars);
out("(");
if (fun.params != null) {
visitCommaSeparated(fun.params, PRIMARY,
@@ -399,7 +399,7 @@
}
out(")");
blockBody(fun.body, needsSeparation: false, needsNewline: false);
- namer.leaveScope();
+ localNamer.leaveScope();
}
visitFunctionDeclaration(FunctionDeclaration declaration) {
@@ -639,7 +639,7 @@
}
visitVariableUse(VariableUse ref) {
- out(namer.getName(ref.name));
+ out(localNamer.getName(ref.name));
}
visitThis(This node) {
@@ -647,11 +647,11 @@
}
visitVariableDeclaration(VariableDeclaration decl) {
- out(namer.getName(decl.name));
+ out(localNamer.getName(decl.name));
}
visitParameter(Parameter param) {
- out(namer.getName(param.name));
+ out(localNamer.getName(param.name));
}
bool isDigit(int charCode) {
@@ -944,7 +944,7 @@
}
-abstract class Namer {
+abstract class LocalNamer {
String getName(String oldName);
String declareVariable(String oldName);
String declareParameter(String oldName);
@@ -953,7 +953,7 @@
}
-class IdentityNamer implements Namer {
+class IdentityNamer implements LocalNamer {
String getName(String oldName) => oldName;
String declareVariable(String oldName) => oldName;
String declareParameter(String oldName) => oldName;
@@ -962,7 +962,7 @@
}
-class MinifyRenamer implements Namer {
+class MinifyRenamer implements LocalNamer {
final List<Map<String, String>> maps = [];
final List<int> parameterNumberStack = [];
final List<int> variableNumberStack = [];
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
index a566b0f..902414d 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
@@ -700,10 +700,8 @@
final RuntimeTypeInformation rti;
- JavaScriptBackend(Compiler compiler,
- bool generateSourceMap,
- bool disableEval)
- : namer = new Namer(compiler),
+ JavaScriptBackend(Compiler compiler, bool generateSourceMap, bool disableEval)
+ : namer = determineNamer(compiler),
returnInfo = new Map<Element, ReturnInfo>(),
invalidateAfterCodegen = new List<Element>(),
interceptors = new Interceptors(compiler),
@@ -724,6 +722,12 @@
fieldTypes = new FieldTypesRegistry(this);
}
+ static Namer determineNamer(Compiler compiler) {
+ return compiler.enableMinification ?
+ new MinifyNamer(compiler) :
+ new Namer(compiler);
+ }
+
bool isInterceptorClass(Element element) {
if (element == null) return false;
return interceptedClasses.containsKey(element);
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 c5a85ca..86717ae 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart
@@ -131,11 +131,11 @@
js.Expression emitCanonicalVersion(Constant constant) {
String name = namer.constantName(constant);
if (inIsolateInitializationContext) {
- // $ISOLATE.$ISOLATE_PROPERTIES.$name
+ // $isolateName.$isolatePropertiesName.$name
return new js.PropertyAccess.field(
new js.PropertyAccess.field(
- new js.VariableUse(namer.ISOLATE),
- namer.ISOLATE_PROPERTIES),
+ new js.VariableUse(namer.isolateName),
+ namer.isolatePropertiesName),
name);
} else {
return new js.PropertyAccess.field(
@@ -222,7 +222,7 @@
js.Expression visitList(ListConstant constant) {
return new js.Call(
new js.PropertyAccess.field(
- new js.VariableUse(namer.ISOLATE),
+ new js.VariableUse(namer.isolateName),
'makeConstantList'),
[new js.ArrayInitializer.from(_array(constant.entries))]);
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index 4d25e3a..8b808f0 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -45,6 +45,10 @@
String isolateProperties;
String classesCollector;
+ String get _ => compiler.enableMinification ? "" : " ";
+ String get n => compiler.enableMinification ? "" : "\n";
+ String get N => compiler.enableMinification ? "\n" : ";\n";
+
/**
* A cache of closures that are used to closurize instance methods.
* A closure is dynamically bound to the instance used when
@@ -59,6 +63,7 @@
*/
final Map<int, String> interceptorClosureCache;
Set<ClassElement> checkedClasses;
+ Set<TypedefElement> checkedTypedefs;
final bool generateSourceMap;
@@ -76,8 +81,13 @@
void computeRequiredTypeChecks() {
assert(checkedClasses == null);
checkedClasses = new Set<ClassElement>();
+ checkedTypedefs = new Set<TypedefElement>();
compiler.codegenWorld.isChecks.forEach((DartType t) {
- if (t is InterfaceType) checkedClasses.add(t.element);
+ if (t is InterfaceType) {
+ checkedClasses.add(t.element);
+ } else if (t is TypedefType) {
+ checkedTypedefs.add(t.element);
+ }
});
}
@@ -92,50 +102,72 @@
String get name => 'CodeEmitter';
String get defineClassName
- => '${namer.ISOLATE}.\$defineClass';
+ => '${namer.isolateName}.\$defineClass';
String get currentGenerateAccessorName
=> '${namer.CURRENT_ISOLATE}.\$generateAccessor';
String get generateAccessorHolder
=> '$isolatePropertiesName.\$generateAccessor';
String get finishClassesName
- => '${namer.ISOLATE}.\$finishClasses';
+ => '${namer.isolateName}.\$finishClasses';
String get finishIsolateConstructorName
- => '${namer.ISOLATE}.\$finishIsolateConstructor';
+ => '${namer.isolateName}.\$finishIsolateConstructor';
String get pendingClassesName
- => '${namer.ISOLATE}.\$pendingClasses';
+ => '${namer.isolateName}.\$pendingClasses';
String get isolatePropertiesName
- => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}';
+ => '${namer.isolateName}.${namer.isolatePropertiesName}';
String get supportsProtoName
=> 'supportsProto';
String get lazyInitializerName
- => '${namer.ISOLATE}.\$lazy';
+ => '${namer.isolateName}.\$lazy';
- final String GETTER_SUFFIX = "?";
- final String SETTER_SUFFIX = "!";
- final String GETTER_SETTER_SUFFIX = "=";
+ // Property name suffixes. If the accessors are renaming then the format
+ // is <accessorName>:<fieldName><suffix>. We use the suffix to know whether
+ // to look for the ':' separator in order to avoid doing the indexOf operation
+ // on every single property (they are quite rare). None of these characters
+ // are legal in an identifier and they are related by bit patterns.
+ // setter < 0x3c
+ // both = 0x3d
+ // getter > 0x3e
+ // renaming setter | 0x7c
+ // renaming both } 0x7d
+ // renaming getter ~ 0x7e
+ const SUFFIX_MASK = 0x3f;
+ const FIRST_SUFFIX_CODE = 0x3c;
+ const SETTER_CODE = 0x3c;
+ const GETTER_SETTER_CODE = 0x3d;
+ const GETTER_CODE = 0x3e;
+ const RENAMING_FLAG = 0x40;
+ String needsGetterCode(String variable) => '($variable & 3) > 0';
+ String needsSetterCode(String variable) => '($variable & 2) == 0';
+ String isRenaming(String variable) => '($variable & $RENAMING_FLAG) != 0';
String get generateAccessorFunction {
return """
function generateAccessor(field, prototype) {
var len = field.length;
- var lastChar = field[len - 1];
- var needsGetter = lastChar == '$GETTER_SUFFIX' || lastChar == '$GETTER_SETTER_SUFFIX';
- var needsSetter = lastChar == '$SETTER_SUFFIX' || lastChar == '$GETTER_SETTER_SUFFIX';
- if (needsGetter || needsSetter) field = field.substring(0, len - 1);
- if (needsGetter) {
- var getterString = "return this." + field + ";";
-"""
- /* The supportsProtoCheck below depends on the getter/setter convention.
- When changing here, update the protoCheck too. */
- """
- prototype["get\$" + field] = new Function(getterString);
+ var lastCharCode = field.charCodeAt(len - 1);
+ var needsAccessor = (lastCharCode & $SUFFIX_MASK) >= $FIRST_SUFFIX_CODE;
+ if (needsAccessor) {
+ var needsGetter = ${needsGetterCode('lastCharCode')};
+ var needsSetter = ${needsSetterCode('lastCharCode')};
+ var renaming = ${isRenaming('lastCharCode')};
+ var accessorName = field = field.substring(0, len - 1);
+ if (renaming) {
+ var divider = field.indexOf(":");
+ accessorName = field.substring(0, divider);
+ field = field.substring(divider + 1);
+ }
+ if (needsGetter) {
+ var getterString = "return this." + field + ";";
+ prototype["get\$" + accessorName] = new Function(getterString);
}
if (needsSetter) {
var setterString = "this." + field + " = v;";
- prototype["set\$" + field] = new Function("v", setterString);
+ prototype["set\$" + accessorName] = new Function("v", setterString);
}
- return field;
- }""";
+ }
+ return field;
+}""";
}
String get defineClassFunction {
@@ -193,7 +225,7 @@
var tmp = $defineClassName('c', ['f?'], {}).prototype;
if (tmp.__proto__) {
tmp.__proto__ = {};
- if (typeof tmp.get\$f !== "undefined") $supportsProtoName = true;
+ if (typeof tmp.get\$f !== 'undefined') $supportsProtoName = true;
}
''';
}
@@ -217,10 +249,17 @@
for (var cls in collectedClasses) {
if (hasOwnProperty.call(collectedClasses, cls)) {
var desc = collectedClasses[cls];
-'''/* Get the superclass and the fields in the format Super;field1,field2 from
- the null-string property on the descriptor. */'''
- var s = desc[''].split(';'), supr = s[0];
- var fields = s[1] == '' ? [] : s[1].split(',');
+'''/* The 'fields' are either a constructor function or a string encoding
+ fields, constructor and superclass. Get the superclass and the fields
+ in the format Super;field1,field2 from the null-string property on the
+ descriptor. */'''
+ var fields = desc[''], supr;
+ if (typeof fields == 'string') {
+ var s = fields.split(';'); supr = s[0];
+ fields = s[1] == '' ? [] : s[1].split(',');
+ } else {
+ supr = desc['super'];
+ }
$isolatePropertiesName[cls] = $defineClassName(cls, fields, desc);
if (supr) $pendingClassesName[cls] = supr;
}
@@ -265,7 +304,7 @@
}
String get finishIsolateConstructorFunction {
- String isolate = namer.ISOLATE;
+ String isolate = namer.isolateName;
// We replace the old Isolate function with a new one that initializes
// all its field with the initial (and often final) value of all globals.
// This has two advantages:
@@ -287,10 +326,10 @@
// We also copy over old values like the prototype, and the
// isolateProperties themselves.
return """function(oldIsolate) {
- var isolateProperties = oldIsolate.${namer.ISOLATE_PROPERTIES};
+ var isolateProperties = oldIsolate.${namer.isolatePropertiesName};
var isolatePrototype = oldIsolate.prototype;
var str = "{\\n";
- str += "var properties = $isolate.${namer.ISOLATE_PROPERTIES};\\n";
+ str += "var properties = $isolate.${namer.isolatePropertiesName};\\n";
for (var staticName in isolateProperties) {
if (Object.prototype.hasOwnProperty.call(isolateProperties, staticName)) {
str += "this." + staticName + "= properties." + staticName + ";\\n";
@@ -300,7 +339,7 @@
var newIsolate = new Function(str);
newIsolate.prototype = isolatePrototype;
isolatePrototype.constructor = newIsolate;
- newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties;
+ newIsolate.${namer.isolatePropertiesName} = isolateProperties;
return newIsolate;
}""";
}
@@ -349,30 +388,30 @@
if (needsDefineClass) {
// Declare function called generateAccessor. This is used in
// defineClassFunction (it's a local declaration in init()).
- buffer.add("$generateAccessorFunction;\n");
- buffer.add("$generateAccessorHolder = generateAccessor\n");
- buffer.add("$defineClassName = $defineClassFunction;\n");
+ buffer.add("$generateAccessorFunction$N");
+ buffer.add("$generateAccessorHolder = generateAccessor$N");
+ buffer.add("$defineClassName = $defineClassFunction$N");
buffer.add(protoSupportCheck);
- buffer.add("$pendingClassesName = {};\n");
- buffer.add("$finishClassesName = $finishClassesFunction;\n");
+ buffer.add("$pendingClassesName = {}$N");
+ buffer.add("$finishClassesName = $finishClassesFunction$N");
}
}
void addLazyInitializerFunctionIfNecessary(CodeBuffer buffer) {
if (needsLazyInitializer) {
- buffer.add("$lazyInitializerName = $lazyInitializerFunction;\n");
+ buffer.add("$lazyInitializerName = $lazyInitializerFunction$N");
}
}
void emitFinishIsolateConstructor(CodeBuffer buffer) {
String name = finishIsolateConstructorName;
String value = finishIsolateConstructorFunction;
- buffer.add("$name = $value;\n");
+ buffer.add("$name = $value$N");
}
void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) {
- String isolate = namer.ISOLATE;
- buffer.add("$isolate = $finishIsolateConstructorName($isolate);\n");
+ String isolate = namer.isolateName;
+ buffer.add("$isolate = $finishIsolateConstructorName($isolate)$N");
}
/**
@@ -475,7 +514,7 @@
});
List<js.Statement> body;
- if (member.isNative()) {
+ if (member.hasFixedBackendName()) {
body = nativeEmitter.generateParameterStubStatements(
member, invocationName, parametersBuffer, argumentsBuffer,
indexOfLastOptionalArgumentInParameters);
@@ -527,7 +566,7 @@
// on A and a typed selector on B could yield the same stub.
Set<String> generatedStubNames = new Set<String>();
if (compiler.enabledFunctionApply
- && member.name == Namer.CLOSURE_INVOCATION_NAME) {
+ && member.name == namer.closureInvocationSelectorName) {
// If [Function.apply] is called, we pessimistically compile all
// possible stubs for this closure.
FunctionSignature signature = member.computeSignature(compiler);
@@ -621,8 +660,8 @@
String compiledFieldName(Element member) {
assert(member.isField());
- return member.isNative()
- ? member.name.slowToString()
+ return member.hasFixedBackendName()
+ ? member.fixedBackendName()
: namer.getName(member);
}
@@ -673,7 +712,7 @@
if (emitLeadingComma) buffer.add(',');
emitLeadingComma = true;
buffer.add('\n');
- buffer.add(' $name: ');
+ buffer.add('$_$name:$_');
buffer.add(memberBuffer);
}
@@ -693,11 +732,11 @@
},
includeBackendMembers: true);
- generateIsTestsOn(classElement, (ClassElement other) {
+ generateIsTestsOn(classElement, (Element other) {
String code;
- if (other.isObject(compiler)) return;
+ if (compiler.objectClass == other) return;
if (nativeEmitter.requiresNativeIsCheck(other)) {
- code = 'function() { return true; }';
+ code = 'function()$_{${_}return true;$_}';
} else {
code = 'true';
}
@@ -726,6 +765,7 @@
void visitClassFields(ClassElement classElement,
void addField(Element member,
String name,
+ String accessorName,
bool needsGetter,
bool needsSetter,
bool needsCheckedSetter)) {
@@ -763,8 +803,8 @@
String accessorName = isShadowed
? namer.shadowedFieldName(member)
: namer.getName(member);
- String fieldName = member.isNative()
- ? member.nativeName()
+ String fieldName = member.hasFixedBackendName()
+ ? member.fixedBackendName()
: accessorName;
bool needsCheckedSetter = false;
if (needsSetter && compiler.enableTypeAssertions
@@ -775,6 +815,7 @@
// Getters and setters with suffixes will be generated dynamically.
addField(member,
fieldName,
+ accessorName,
needsGetter,
needsSetter,
needsCheckedSetter);
@@ -791,16 +832,18 @@
includeSuperMembers: isInstantiated && !classElement.isNative());
}
- void generateGetter(Element member, String fieldName, CodeBuffer buffer) {
- String getterName = namer.getterName(member.getLibrary(), member.name);
- buffer.add("$getterName: function() { return this.$fieldName; }");
- }
-
- void generateSetter(Element member, String fieldName, CodeBuffer buffer) {
- String setterName = namer.setterName(member.getLibrary(), member.name);
- buffer.add("$setterName: function(v) { this.$fieldName = v; }");
- }
-
+ void generateGetter(Element member, String fieldName, String accessorName,
+ CodeBuffer buffer) {
+ String getterName = namer.getterNameFromAccessorName(accessorName);
+ buffer.add("$getterName: function() { return this.$fieldName; }");
+ }
+
+ void generateSetter(Element member, String fieldName, String accessorName,
+ CodeBuffer buffer) {
+ String setterName = namer.setterNameFromAccessorName(accessorName);
+ buffer.add("$setterName: function(v) { this.$fieldName = v; }");
+ }
+
bool canGenerateCheckedSetter(Element member) {
DartType type = member.computeType(compiler);
if (type.element.isTypeVariable()
@@ -814,6 +857,7 @@
void generateCheckedSetter(Element member,
String fieldName,
+ String accessorName,
CodeBuffer buffer) {
assert(canGenerateCheckedSetter(member));
DartType type = member.computeType(compiler);
@@ -822,38 +866,46 @@
String helperName = namer.isolateAccess(helperElement);
String additionalArgument = '';
if (helperElement.computeSignature(compiler).parameterCount != 1) {
- additionalArgument = ", '${namer.operatorIs(type.element)}'";
+ additionalArgument = ",$_'${namer.operatorIs(type.element)}'";
}
- String setterName = namer.setterName(member.getLibrary(), member.name);
- buffer.add("$setterName: function(v) { "
- "this.$fieldName = $helperName(v$additionalArgument); }");
+ String setterName = namer.setterNameFromAccessorName(accessorName);
+ buffer.add("$setterName:${_}function(v)$_{$_"
+ "this.$fieldName$_=$_$helperName(v$additionalArgument);}");
}
void emitClassConstructor(ClassElement classElement, CodeBuffer buffer) {
/* Do nothing. */
}
+ void emitSuper(String superName, CodeBuffer buffer) {
+ /* Do nothing. */
+ }
+
void emitClassFields(ClassElement classElement,
CodeBuffer buffer,
bool emitEndingComma,
{ String superClass: "",
- bool isNative: false}) {
+ bool classIsNative: false}) {
bool isFirstField = true;
bool isAnythingOutput = false;
- if (!isNative) {
+ if (!classIsNative) {
buffer.add('"":"$superClass;');
isAnythingOutput = true;
}
visitClassFields(classElement, (Element member,
String name,
+ String accessorName,
bool needsGetter,
bool needsSetter,
bool needsCheckedSetter) {
- if (!getterAndSetterCanBeImplementedByFieldSpec(
- member, name, needsGetter, needsSetter)) {
- return;
- }
- if (!isNative || needsCheckedSetter || needsGetter || needsSetter) {
+ // Ignore needsCheckedSetter - that is handled below.
+ bool needsAccessor = (needsGetter || needsSetter);
+ // We need to output the fields for non-native classes so we can auto-
+ // generate the constructor. For native classes there are no
+ // constructors, so we don't need the fields unless we are generating
+ // accessors at runtime.
+ if (!classIsNative || needsAccessor) {
+ // Emit correct commas.
if (isFirstField) {
isFirstField = false;
if (!isAnythingOutput) {
@@ -863,13 +915,28 @@
} else {
buffer.add(",");
}
- buffer.add('$name');
+ int flag = 0;
+ if (!needsAccessor) {
+ // Emit field for constructor generation.
+ assert(!classIsNative);
+ buffer.add(name);
+ } else {
+ // Emit (possibly renaming) field name so we can add accessors at
+ // runtime.
+ buffer.add(accessorName);
+ if (name != accessorName) {
+ buffer.add(':$name');
+ // Only the native classes can have renaming accessors.
+ assert(classIsNative);
+ flag = RENAMING_FLAG;
+ }
+ }
if (needsGetter && needsSetter) {
- buffer.add(GETTER_SETTER_SUFFIX);
+ buffer.addCharCode(GETTER_SETTER_CODE + flag);
} else if (needsGetter) {
- buffer.add(GETTER_SUFFIX);
+ buffer.addCharCode(GETTER_CODE + flag);
} else if (needsSetter) {
- buffer.add(SETTER_SUFFIX);
+ buffer.addCharCode(SETTER_CODE + flag);
}
}
});
@@ -887,7 +954,7 @@
bool emitLeadingComma) {
emitComma() {
if (emitLeadingComma) {
- buffer.add(",\n ");
+ buffer.add(",\n$_");
} else {
emitLeadingComma = true;
}
@@ -895,48 +962,28 @@
visitClassFields(classElement, (Element member,
String name,
+ String accessorName,
bool needsGetter,
bool needsSetter,
bool needsCheckedSetter) {
- if (name == null) throw 123;
- if (getterAndSetterCanBeImplementedByFieldSpec(
- member, name, needsGetter, needsSetter)) {
- needsGetter = false;
- needsSetter = false;
- }
- if (needsGetter) {
- emitComma();
- generateGetter(member, name, buffer);
- }
- if (needsSetter) {
- emitComma();
- generateSetter(member, name, buffer);
- }
if (needsCheckedSetter) {
assert(!needsSetter);
emitComma();
- generateCheckedSetter(member, name, buffer);
+ generateCheckedSetter(member, name, accessorName, buffer);
+ }
+ if (!getterAndSetterCanBeImplementedByFieldSpec) {
+ if (needsGetter) {
+ emitComma();
+ generateGetter(member, name, accessorName, buffer);
+ }
+ if (needsSetter) {
+ emitComma();
+ generateSetter(member, name, accessorName, buffer);
+ }
}
});
}
- bool getterAndSetterCanBeImplementedByFieldSpec(Element member,
- String name,
- bool needsGetter,
- bool needsSetter) {
- if (needsGetter) {
- if (namer.getterName(member.getLibrary(), member.name) != 'get\$$name') {
- return false;
- }
- }
- if (needsSetter) {
- if (namer.setterName(member.getLibrary(), member.name) != 'set\$$name') {
- return false;
- }
- }
- return true;
- }
-
/**
* Documentation wanted -- johnniwinther
*
@@ -962,18 +1009,21 @@
superName = namer.getName(superclass);
}
- buffer.add('$classesCollector.$className = {');
+ buffer.add('$classesCollector.$className$_=$_{');
emitClassConstructor(classElement, buffer);
+ emitSuper(superName, buffer);
emitClassFields(classElement, buffer, false,
- superClass: superName, isNative: false);
+ superClass: superName, classIsNative: false);
// TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'.
// That does currently not work because the native classes have a different
// syntax.
emitClassGettersSetters(classElement, buffer, true);
emitInstanceMembers(classElement, buffer, true);
- buffer.add('\n};\n\n');
+ buffer.add('$n}$N$n');
}
+ bool get getterAndSetterCanBeImplementedByFieldSpec => true;
+
void emitInterceptorMethods(
void defineInstanceMember(String name, StringBuffer memberBuffer)) {
JavaScriptBackend backend = compiler.backend;
@@ -1002,7 +1052,7 @@
arguments.add(new js.VariableUse(argName));
}
}
- js.Fun function =
+ js.Fun function =
new js.Fun(parameters,
new js.Block(
<js.Statement>[
@@ -1023,16 +1073,30 @@
* super class because they will be inherited at runtime.
*/
void generateIsTestsOn(ClassElement cls,
- void emitIsTest(ClassElement element)) {
+ void emitIsTest(Element element)) {
if (checkedClasses.contains(cls)) {
emitIsTest(cls);
}
+
Set<Element> generated = new Set<Element>();
// A class that defines a [:call:] method implicitly implements
- // [Function].
- if (checkedClasses.contains(compiler.functionClass)
- && cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME) != null) {
- generateInterfacesIsTests(compiler.functionClass, emitIsTest, generated);
+ // [Function] and needs checks for all typedefs that are used in is-checks.
+ if (checkedClasses.contains(compiler.functionClass) ||
+ !checkedTypedefs.isEmpty) {
+ FunctionElement call = cls.lookupLocalMember(Compiler.CALL_OPERATOR_NAME);
+ if (call != null) {
+ generateInterfacesIsTests(compiler.functionClass,
+ emitIsTest,
+ generated);
+ FunctionType callType = call.computeType(compiler);
+ for (TypedefElement typedef in checkedTypedefs) {
+ FunctionType typedefType =
+ typedef.computeType(compiler).unalias(compiler);
+ if (compiler.types.isSubtype(callType, typedefType)) {
+ emitIsTest(typedef);
+ }
+ }
+ }
}
for (DartType interfaceType in cls.interfaces) {
generateInterfacesIsTests(interfaceType.element, emitIsTest, generated);
@@ -1068,14 +1132,59 @@
}
}
+ /**
+ * Return a function that returns true if its argument is a class
+ * that needs to be emitted.
+ */
+ Function computeClassFilter() {
+ Set<ClassElement> unneededClasses = new Set<ClassElement>();
+ // The [Bool] class is not marked as abstract, but has a factory
+ // constructor that always throws. We never need to emit it.
+ unneededClasses.add(compiler.boolClass);
+
+ JavaScriptBackend backend = compiler.backend;
+
+ // Go over specialized interceptors and then constants to know which
+ // interceptors are needed.
+ Set<ClassElement> needed = new Set<ClassElement>();
+ backend.specializedGetInterceptors.forEach(
+ (_, Collection<ClassElement> elements) {
+ needed.addAll(elements);
+ }
+ );
+
+ ConstantHandler handler = compiler.constantHandler;
+ List<Constant> constants = handler.getConstantsForEmission();
+ for (Constant constant in constants) {
+ if (constant is ConstructedConstant) {
+ Element element = constant.computeType(compiler).element;
+ if (backend.isInterceptorClass(element)) {
+ needed.add(element);
+ }
+ }
+ }
+
+ // Add unneeded interceptors to the [unneededClasses] set.
+ for (ClassElement interceptor in backend.interceptedClasses.keys) {
+ if (!needed.contains(interceptor)) {
+ unneededClasses.add(interceptor);
+ }
+ }
+
+ return (ClassElement cls) => !unneededClasses.contains(cls);
+ }
+
void emitClasses(CodeBuffer buffer) {
// Compute the required type checks to know which classes need a
// 'is$' method.
computeRequiredTypeChecks();
+
Set<ClassElement> instantiatedClasses =
- compiler.codegenWorld.instantiatedClasses;
+ compiler.codegenWorld.instantiatedClasses.filter(computeClassFilter());
+
Set<ClassElement> neededClasses =
new Set<ClassElement>.from(instantiatedClasses);
+
for (ClassElement element in instantiatedClasses) {
for (ClassElement superclass = element.superclass;
superclass != null;
@@ -1124,9 +1233,9 @@
void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) {
if (needsDefineClass) {
- buffer.add("$finishClassesName($classesCollector);\n");
+ buffer.add("$finishClassesName($classesCollector)$N");
// Reset the map.
- buffer.add("$classesCollector = {};\n");
+ buffer.add("$classesCollector$_=$_{}$N");
}
}
@@ -1135,9 +1244,9 @@
CodeBuffer functionBuffer,
String functionNamer(Element element)) {
String functionName = functionNamer(element);
- buffer.add('$isolateProperties.$functionName = ');
+ buffer.add('$isolateProperties.$functionName$_=$_');
buffer.add(functionBuffer);
- buffer.add(';\n\n');
+ buffer.add('$N$n');
}
void emitStaticFunctionsWithNamer(CodeBuffer buffer,
Map<Element, CodeBuffer> generatedCode,
@@ -1168,30 +1277,28 @@
// create a fake element with the correct name.
// Note: the callElement will not have any enclosingElement.
FunctionElement callElement =
- new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, element);
+ new ClosureInvocationElement(namer.closureInvocationSelectorName,
+ element);
String staticName = namer.getName(element);
String invocationName = namer.instanceMethodName(callElement);
String fieldAccess = '$isolateProperties.$staticName';
- buffer.add("$fieldAccess.$invocationName = $fieldAccess;\n");
+ buffer.add("$fieldAccess.$invocationName$_=$_$fieldAccess$N");
addParameterStubs(callElement, (String name, CodeBuffer value) {
- buffer.add('$fieldAccess.$name = $value;\n');
+ buffer.add('$fieldAccess.$name$_=$_$value$N');
});
// If a static function is used as a closure we need to add its name
// in case it is used in spawnFunction.
String fieldName = namer.STATIC_CLOSURE_NAME_NAME;
- buffer.add('$fieldAccess.$fieldName = "$staticName";\n');
+ buffer.add('$fieldAccess.$fieldName$_=$_"$staticName"$N');
}
}
void emitBoundClosureClassHeader(String mangledName,
String superName,
- String extraArgument,
+ List<String> fieldNames,
CodeBuffer buffer) {
- extraArgument = extraArgument.isEmpty ? '' : ",$extraArgument";
- buffer.add("""
-$classesCollector.$mangledName = {'':
-\"$superName;self$extraArgument,target\",
-""");
+ buffer.add('$classesCollector.$mangledName$_=$_'
+ '{"":"$superName;${Strings.join(fieldNames,',')}",');
}
/**
@@ -1227,25 +1334,24 @@
int parameterCount = member.parameterCount(compiler);
Map<int, String> cache;
- String extraArg;
- String extraArgWithoutComma;
- bool hasExtraArgument = false;
- // Methods on foreign classes take an extra parameter, which is
- // the actual receiver of the call.
+ String extraArg = null;
+ // Methods on interceptor classes take an extra parameter, which is the
+ // actual receiver of the call.
JavaScriptBackend backend = compiler.backend;
- if (backend.isInterceptorClass(member.getEnclosingClass())) {
- hasExtraArgument = true;
+ bool inInterceptor = backend.isInterceptorClass(member.getEnclosingClass());
+ if (inInterceptor) {
cache = interceptorClosureCache;
- extraArg = 'receiver, ';
- extraArgWithoutComma = 'receiver';
+ extraArg = 'receiver';
} else {
cache = boundClosureCache;
- extraArg = '';
- extraArgWithoutComma = '';
}
+ List<String> fieldNames = compiler.enableMinification
+ ? inInterceptor ? const ['a', 'b', 'c']
+ : const ['a', 'b']
+ : inInterceptor ? const ['self', 'target', 'receiver']
+ : const ['self', 'target'];
- String closureClass =
- hasOptionalParameters ? null : cache[parameterCount];
+ String closureClass = hasOptionalParameters ? null : cache[parameterCount];
if (closureClass == null) {
// Either the class was not cached yet, or there are optional parameters.
// Create a new closure class.
@@ -1259,33 +1365,45 @@
// Define the constructor with a name so that Object.toString can
// find the class name of the closure class.
emitBoundClosureClassHeader(
- mangledName, superName, extraArgWithoutComma, boundClosureBuffer);
+ mangledName, superName, fieldNames, boundClosureBuffer);
// Now add the methods on the closure class. The instance method does not
// have the correct name. Since [addParameterStubs] use the name to create
// its stubs we simply create a fake element with the correct name.
// Note: the callElement will not have any enclosingElement.
FunctionElement callElement =
- new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member);
-
+ new ClosureInvocationElement(namer.closureInvocationSelectorName,
+ member);
+
String invocationName = namer.instanceMethodName(callElement);
- List<String> arguments = new List<String>(parameterCount);
- for (int i = 0; i < parameterCount; i++) {
- arguments[i] = "p$i";
+
+ List<js.Parameter> parameters = <js.Parameter>[];
+ List<js.Expression> arguments = <js.Expression>[];
+ if (inInterceptor) {
+ arguments.add(new js.This().dot(fieldNames[2]));
}
- String joinedArgs = Strings.join(arguments, ", ");
+ for (int i = 0; i < parameterCount; i++) {
+ String name = 'p$i';
+ parameters.add(new js.Parameter(name));
+ arguments.add(new js.VariableUse(name));
+ }
+
+ js.Expression fun =
+ new js.Fun(parameters,
+ new js.Block(
+ <js.Statement>[
+ new js.Return(
+ new js.PropertyAccess(
+ new js.This().dot(fieldNames[0]),
+ new js.This().dot(fieldNames[1]))
+ .callWith(arguments))]));
+
boundClosureBuffer.add(
- "$invocationName: function($joinedArgs) {");
- String callArgs = hasExtraArgument
- ? joinedArgs.isEmpty
- ? 'this.$extraArgWithoutComma'
- : 'this.$extraArg$joinedArgs'
- : joinedArgs;
- boundClosureBuffer.add(" return this.self[this.target]($callArgs);");
- boundClosureBuffer.add(" }");
+ '$_$invocationName:$_${js.prettyPrint(fun,compiler)}');
+
addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) {
- boundClosureBuffer.add(',\n $stubName: $memberValue');
+ boundClosureBuffer.add(',\n$_$stubName:$_$memberValue');
});
- boundClosureBuffer.add("\n};\n");
+ boundClosureBuffer.add("$n}$N");
closureClass = namer.isolateAccess(closureClassElement);
@@ -1298,9 +1416,27 @@
// And finally the getter.
String getterName = namer.getterName(member.getLibrary(), member.name);
String targetName = namer.instanceMethodName(member);
+
+ List<js.Parameter> parameters = <js.Parameter>[];
+ List<js.Expression> arguments = <js.Expression>[];
+ arguments.add(new js.This());
+ arguments.add(new js.LiteralString("'$targetName'"));
+ if (inInterceptor) {
+ parameters.add(new js.Parameter(extraArg));
+ arguments.add(new js.VariableUse(extraArg));
+ }
+
+ js.Expression getterFunction =
+ new js.Fun(parameters,
+ new js.Block(
+ <js.Statement>[
+ new js.Return(
+ new js.New(
+ new js.VariableUse(closureClass),
+ arguments))]));
+
CodeBuffer getterBuffer = new CodeBuffer();
- getterBuffer.add("function($extraArgWithoutComma) "
- "{ return new $closureClass(this, $extraArg'$targetName'); }");
+ getterBuffer.add(js.prettyPrint(getterFunction, compiler));
defineInstanceMember(getterName, getterBuffer);
}
@@ -1330,8 +1466,8 @@
? <js.Expression>[new js.VariableUse(receiverArgumentName)]
: <js.Expression>[]);
} else {
- String fieldName = member.isNative()
- ? member.nativeName()
+ String fieldName = member.hasFixedBackendName()
+ ? member.fixedBackendName()
: namer.instanceFieldName(memberLibrary, member.name);
return new js.VariableUse('this').dot(fieldName);
}
@@ -1342,7 +1478,7 @@
String invocationName =
namer.instanceMethodInvocationName(memberLibrary, member.name,
selector);
- SourceString callName = Namer.CLOSURE_INVOCATION_NAME;
+ SourceString callName = namer.closureInvocationSelectorName;
String closureCallName =
namer.instanceMethodInvocationName(memberLibrary, callName,
selector);
@@ -1388,7 +1524,7 @@
namer.getName(element)),
constantEmitter.referenceInInitializationContext(initialValue));
buffer.add(js.prettyPrint(init, compiler));
- buffer.add(';\n');
+ buffer.add('$N');
});
}
}
@@ -1410,16 +1546,16 @@
// closure that constructs the initial value.
buffer.add("$lazyInitializerName(");
buffer.add(isolateProperties);
- buffer.add(", '");
+ buffer.add(",$_'");
buffer.add(element.name.slowToString());
- buffer.add("', '");
+ buffer.add("',$_'");
buffer.add(namer.getName(element));
- buffer.add("', '");
+ buffer.add("',$_'");
buffer.add(namer.getLazyInitializerName(element));
- buffer.add("', ");
+ buffer.add("',$_");
buffer.add(code);
emitLazyInitializedGetter(element, buffer);
- buffer.add(");\n");
+ buffer.add(")$N");
}
}
}
@@ -1454,12 +1590,12 @@
name),
constantInitializerExpression(constant));
buffer.add(js.prettyPrint(init, compiler));
- buffer.add(';\n');
+ buffer.add('$N');
}
}
void emitMakeConstantList(CodeBuffer buffer) {
- buffer.add(namer.ISOLATE);
+ buffer.add(namer.isolateName);
buffer.add(r'''.makeConstantList = function(list) {
list.immutable$list = true;
list.fixed$length = true;
@@ -1495,6 +1631,11 @@
String noSuchMethodName = namer.publicInstanceMethodNameByArity(
Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT);
+ Element createInvocationMirrorElement =
+ compiler.findHelper(const SourceString("createInvocationMirror"));
+ 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).
Set<String> addedJsNames = new Set<String>();
@@ -1517,43 +1658,46 @@
return result;
}
- CodeBuffer generateMethod(String methodName, Selector selector) {
+ js.Expression generateMethod(String jsName, Selector selector) {
// Values match JSInvocationMirror in js-helper library.
- const int METHOD = 0;
- const int GETTER = 1;
- const int SETTER = 2;
- int type = METHOD;
- if (selector.isGetter()) {
- type = GETTER;
- assert(methodName.startsWith("get:"));
- methodName = methodName.substring(4);
- } else if (selector.isSetter()) {
- type = SETTER;
- assert(methodName.startsWith("set:"));
- methodName = "${methodName.substring(4)}=";
- }
+ int type = selector.invocationMirrorKind;
+ String methodName = selector.invocationMirrorMemberName;
+ List<js.Parameter> parameters = <js.Parameter>[];
CodeBuffer args = new CodeBuffer();
for (int i = 0; i < selector.argumentCount; i++) {
- if (i != 0) args.add(', ');
- args.add('\$$i');
+ parameters.add(new js.Parameter('\$$i'));
}
- CodeBuffer argNames = new CodeBuffer();
- List<SourceString> names = selector.getOrderedNamedArguments();
- for (int i = 0; i < names.length; i++) {
- if (i != 0) argNames.add(', ');
- argNames.add('"');
- argNames.add(names[i].slowToString());
- argNames.add('"');
- }
+
+ List<js.Expression> argNames =
+ selector.getOrderedNamedArguments().map((SourceString name) =>
+ new js.LiteralString('"${name.slowToString()}"'));
+
String internalName = namer.instanceMethodInvocationName(
selector.library, new SourceString(methodName), selector);
- CodeBuffer buffer = new CodeBuffer();
- buffer.add('function($args) {\n');
- buffer.add(' return this.$noSuchMethodName('
- '\$.createInvocationMirror("$methodName", "$internalName",'
- ' $type, [$args], [$argNames]));\n');
- buffer.add(' }');
- return buffer;
+
+ String createInvocationMirror = namer.getName(
+ compiler.createInvocationMirrorElement);
+
+ js.Expression expression =
+ new js.This()
+ .dot(noSuchMethodName)
+ .callWith(
+ <js.Expression>[
+ new js.VariableUse(namer.CURRENT_ISOLATE)
+ .dot(createInvocationMirror)
+ .callWith(
+ <js.Expression>[
+ new js.LiteralString('"$methodName"'),
+ new js.LiteralString('"$internalName"'),
+ new js.LiteralNumber('$type'),
+ new js.ArrayInitializer.from(
+ parameters.map((param) =>
+ new js.VariableUse(param.name))),
+ new js.ArrayInitializer.from(argNames)])]);
+ js.Expression function =
+ new js.Fun(parameters,
+ new js.Block(<js.Statement>[new js.Return(expression)]));
+ return function;
}
void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) {
@@ -1573,13 +1717,17 @@
// Selector.applies() method.
if (element is AbstractFieldElement) {
AbstractFieldElement field = element;
- if (identical(selector.kind, SelectorKind.GETTER)) {
+ if (selector.isGetter()) {
return field.getter != null;
- } else if (identical(selector.kind, SelectorKind.SETTER)) {
+ } else if (selector.isSetter()) {
return field.setter != null;
} else {
return false;
}
+ } else if (element is VariableElement) {
+ if (selector.isSetter() && element.modifiers.isFinalOrConst()) {
+ return false;
+ }
}
return selector.applies(element, compiler);
}
@@ -1639,28 +1787,11 @@
// does not implement bar.
Set<ClassElement> holders = noSuchMethodHoldersFor(receiverType);
if (holders.every(hasMatchingMember)) continue;
-
- String jsName = null;
- String methodName = null;
- String nameString = selector.name.slowToString();
- if (selector.isGetter()) {
- jsName = namer.getterName(selector.library, selector.name);
- methodName = 'get:$nameString';
- } else if (selector.isSetter()) {
- jsName = namer.setterName(selector.library, selector.name);
- methodName = 'set:$nameString';
- } else if (selector.isCall()) {
- jsName = namer.instanceMethodInvocationName(
- selector.library, selector.name, selector);
- methodName = nameString;
- } else {
- // We simply ignore selectors that do not need
- // noSuchMethod handlers.
- continue;
- }
-
+ String jsName = namer.invocationMirrorInternalName(selector);
if (!addedJsNames.contains(jsName)) {
- CodeBuffer jsCode = generateMethod(methodName, selector);
+ js.Expression method = generateMethod(jsName, selector);
+ CodeBuffer jsCode = new CodeBuffer();
+ jsCode.add(js.prettyPrint(method, compiler));
defineInstanceMember(jsName, jsCode);
addedJsNames.add(jsName);
}
@@ -1697,7 +1828,7 @@
function \$static_init(){};
function \$initGlobals(context) {
- context.isolateStatics = new ${namer.ISOLATE}();
+ context.isolateStatics = new ${namer.isolateName}();
}
function \$setGlobals(context) {
$currentIsolate = context.isolateStatics;
@@ -1712,22 +1843,26 @@
if (compiler.isMockCompilation) return;
Element main = compiler.mainApp.find(Compiler.MAIN);
String mainCall = null;
- if (compiler.isolateLibrary != null) {
+ if (compiler.isolateHelperLibrary != null) {
Element isolateMain =
- compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE);
+ compiler.isolateHelperLibrary.find(Compiler.START_ROOT_ISOLATE);
mainCall = buildIsolateSetup(buffer, main, isolateMain);
} else {
mainCall = '${namer.isolateAccess(main)}()';
}
- buffer.add("""
+ if (!compiler.enableMinification) {
+ buffer.add("""
//
// BEGIN invoke [main].
//
-if (typeof document != 'undefined' && document.readyState != 'complete') {
+""");
+ }
+ buffer.add("""
+if (typeof document !== 'undefined' && document.readyState !== 'complete') {
document.addEventListener('readystatechange', function () {
if (document.readyState == 'complete') {
- if (typeof dartMainRunner == 'function') {
+ if (typeof dartMainRunner === 'function') {
dartMainRunner(function() { ${mainCall}; });
} else {
${mainCall};
@@ -1735,17 +1870,21 @@
}
}, false);
} else {
- if (typeof dartMainRunner == 'function') {
+ if (typeof dartMainRunner === 'function') {
dartMainRunner(function() { ${mainCall}; });
} else {
${mainCall};
}
}
+""");
+ if (!compiler.enableMinification) {
+ buffer.add("""
//
// END invoke [main].
//
""");
+ }
}
/**
@@ -1756,22 +1895,24 @@
JavaScriptBackend backend = compiler.backend;
assert(backend.isInterceptorClass(cls));
if (cls == backend.jsBoolClass) {
- buffer.add("if (typeof receiver == 'boolean')");
+ buffer.add("if$_(typeof receiver$_==$_'boolean')");
} else if (cls == backend.jsIntClass) {
- buffer.add("if (typeof receiver == 'number' "
- "&& Math.floor(receiver) == receiver)");
+ buffer.add("if$_(typeof receiver$_==$_'number'$_"
+ "&&${_}Math.floor(receiver)$_==${_}receiver)");
} else if (cls == backend.jsDoubleClass || cls == backend.jsNumberClass) {
- buffer.add("if (typeof receiver == 'number')");
+ buffer.add("if$_(typeof receiver$_==$_'number')");
} else if (cls == backend.jsArrayClass) {
- buffer.add("if (receiver != null && receiver.constructor == Array)");
+ buffer.add("if$_(receiver$_!=${_}null$_&&$_"
+ "receiver.constructor$_==${_}Array)");
} else if (cls == backend.jsStringClass) {
- buffer.add("if (typeof receiver == 'string')");
+ buffer.add("if$_(typeof receiver$_==$_'string')");
} else if (cls == backend.jsNullClass) {
- buffer.add("if (receiver == null)");
+ buffer.add("if$_(receiver$_==${_}null)");
} else if (cls == backend.jsFunctionClass) {
- buffer.add("if (typeof receiver == 'function')");
+ buffer.add("if$_(typeof receiver$_==$_'function')");
}
- buffer.add(' return ${namer.isolateAccess(cls)}.prototype;');
+ buffer.add('${_}return ${namer.isolateAccess(cls)}.prototype');
+ if (!compiler.enableMinification) buffer.add(';');
}
/**
@@ -1784,28 +1925,30 @@
String objectName = namer.isolateAccess(backend.objectInterceptorClass);
backend.specializedGetInterceptors.forEach(
(String key, Collection<ClassElement> classes) {
- buffer.add('$isolateProperties.$key = function(receiver) {');
+ buffer.add('$isolateProperties.$key$_=${_}function(receiver)$_{');
for (ClassElement cls in classes) {
if (compiler.codegenWorld.instantiatedClasses.contains(cls)) {
- buffer.add('\n ');
+ buffer.add('\n$_$_');
emitInterceptorCheck(cls, buffer);
}
}
- buffer.add('\n return $objectName.prototype;\n};\n');
+ buffer.add('\n$_${_}return $objectName.prototype;$n}$N');
});
}
String assembleProgram() {
measure(() {
- mainBuffer.add(HOOKS_API_USAGE);
- mainBuffer.add('function ${namer.ISOLATE}() {}\n');
- mainBuffer.add('init();\n\n');
+ mainBuffer.add(GENERATED_BY);
+ if (!compiler.enableMinification) mainBuffer.add(HOOKS_API_USAGE);
+ mainBuffer.add('function ${namer.isolateName}()$_{}\n');
+ mainBuffer.add('init()$N$n');
// Shorten the code by using "$$" as temporary.
classesCollector = r"$$";
- mainBuffer.add('var $classesCollector = {};\n');
+ mainBuffer.add('var $classesCollector$_=$_{}$N');
// Shorten the code by using [namer.CURRENT_ISOLATE] as temporary.
isolateProperties = namer.CURRENT_ISOLATE;
- mainBuffer.add('var $isolateProperties = $isolatePropertiesName;\n');
+ mainBuffer.add(
+ 'var $isolateProperties$_=$_$isolatePropertiesName$N');
emitClasses(mainBuffer);
mainBuffer.add(boundClosureBuffer);
// Clear the buffer, so that we can reuse it for the native classes.
@@ -1825,7 +1968,7 @@
isolateProperties = isolatePropertiesName;
// The following code should not use the short-hand for the
// initialStatics.
- mainBuffer.add('var ${namer.CURRENT_ISOLATE} = null;\n');
+ mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_=${_}null$N');
mainBuffer.add(boundClosureBuffer);
emitFinishClassesInvocationIfNecessary(mainBuffer);
// After this assignment we will produce invalid JavaScript code if we use
@@ -1833,13 +1976,13 @@
classesCollector = 'classesCollector should not be used from now on';
emitFinishIsolateConstructorInvocation(mainBuffer);
- mainBuffer.add(
- 'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n');
+ mainBuffer.add('var ${namer.CURRENT_ISOLATE}$_='
+ '${_}new ${namer.isolateName}()$N');
nativeEmitter.assembleCode(mainBuffer);
emitMain(mainBuffer);
- mainBuffer.add('function init() {\n');
- mainBuffer.add('$isolateProperties = {};\n');
+ mainBuffer.add('function init()$_{\n');
+ mainBuffer.add('$isolateProperties$_=$_{}$N');
addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer);
addLazyInitializerFunctionIfNecessary(mainBuffer);
emitFinishIsolateConstructor(mainBuffer);
@@ -1868,8 +2011,10 @@
typedef void DefineMemberFunction(String invocationName, CodeBuffer definition);
-const String HOOKS_API_USAGE = """
+const String GENERATED_BY = """
// Generated by dart2js, the Dart to JavaScript compiler.
+""";
+const String HOOKS_API_USAGE = """
// The code supports the following hooks:
// dartPrint(message) - if this function is defined it is called
// instead of the Dart [print] method.
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 0b6c0f5..e0d1fba 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
@@ -39,7 +39,7 @@
// isolateProperties themselves.
return """
function(oldIsolate) {
- var isolateProperties = oldIsolate.${namer.ISOLATE_PROPERTIES};
+ var isolateProperties = oldIsolate.${namer.isolatePropertiesName};
function Isolate() {
for (var staticName in isolateProperties) {
if (Object.prototype.hasOwnProperty.call(isolateProperties, staticName)) {
@@ -54,7 +54,7 @@
}
Isolate.prototype = oldIsolate.prototype;
Isolate.prototype.constructor = Isolate;
- Isolate.${namer.ISOLATE_PROPERTIES} = isolateProperties;
+ Isolate.${namer.isolatePropertiesName} = isolateProperties;
return Isolate;
}""";
}
@@ -71,29 +71,27 @@
buffer.add(', function() { return $isolate.${namer.getName(element)}; }');
}
+ js.Expression buildConstructor(String mangledName, List<String> fieldNames) {
+ return new js.NamedFunction(
+ new js.VariableDeclaration(mangledName),
+ new js.Fun(
+ fieldNames.map((fieldName) => new js.Parameter(fieldName)),
+ new js.Block(
+ fieldNames.map((fieldName) =>
+ new js.ExpressionStatement(
+ new js.Assignment(
+ new js.This().dot(fieldName),
+ new js.VariableUse(fieldName)))))));
+ }
+
void emitBoundClosureClassHeader(String mangledName,
String superName,
- String extraArgument,
+ List<String> fieldNames,
CodeBuffer buffer) {
- if (!extraArgument.isEmpty) {
- buffer.add("""
-$classesCollector.$mangledName = {'': function $mangledName(
- self, $extraArgument, target) {
- this.self = self;
- this.$extraArgument = $extraArgument,
- this.target = target;
- },
- 'super': '$superName',
-""");
- } else {
- buffer.add("""
-$classesCollector.$mangledName = {'': function $mangledName(self, target) {
- this.self = self;
- this.target = target;
- },
- 'super': '$superName',
-""");
- }
+ buffer.add("$classesCollector.$mangledName = {'': ");
+ buffer.add(
+ js.prettyPrint(buildConstructor(mangledName, fieldNames), compiler));
+ buffer.add(",\n 'super': '$superName',\n");
}
void emitClassConstructor(ClassElement classElement, CodeBuffer buffer) {
@@ -108,42 +106,31 @@
List<String> fields = <String>[];
visitClassFields(classElement, (Element member,
String name,
+ String accessorName,
bool needsGetter,
bool needsSetter,
bool needsCheckedSetter) {
fields.add(name);
});
-
- List<String> argumentNames = fields;
- if (fields.length < ($z - $a)) {
- argumentNames = new List<String>(fields.length);
- for (int i = 0; i < fields.length; i++) {
- argumentNames[i] = new String.fromCharCodes([$a + i]);
- }
- }
String constructorName = namer.safeName(classElement.name.slowToString());
- // Generate the constructor.
- buffer.add("'': function $constructorName(");
- buffer.add(Strings.join(argumentNames, ", "));
- buffer.add(") {\n");
- for (int i = 0; i < fields.length; i++) {
- buffer.add(" this.${fields[i]} = ${argumentNames[i]};\n");
+ buffer.add("'': ");
+ buffer.add(
+ js.prettyPrint(buildConstructor(constructorName, fields), compiler));
+ }
+
+ void emitSuper(String superName, CodeBuffer buffer) {
+ if (superName != '') {
+ buffer.add(",\n 'super': '$superName'");
}
- buffer.add(' }');
}
void emitClassFields(ClassElement classElement,
CodeBuffer buffer,
bool emitEndingComma,
{ String superClass: "",
- bool isNative: false}) {
+ bool classIsNative: false}) {
if (emitEndingComma) buffer.add(', ');
}
- bool getterAndSetterCanBeImplementedByFieldSpec(Element member,
- String name,
- bool needsGetter,
- bool needsSetter) {
- return false;
- }
+ bool get getterAndSetterCanBeImplementedByFieldSpec => false;
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart
index 1f2106e..a25b024 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/js_backend.dart
@@ -23,6 +23,7 @@
part 'constant_system_javascript.dart';
part 'emitter.dart';
part 'emitter_no_eval.dart';
+part 'minify_namer.dart';
part 'namer.dart';
part 'native_emitter.dart';
part 'runtime_types.dart';
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart b/sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart
new file mode 100644
index 0000000..5d176c1
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/minify_namer.dart
@@ -0,0 +1,115 @@
+// Copyright (c) 2011, 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 js_backend;
+
+/**
+ * Assigns JavaScript identifiers to Dart variables, class-names and members.
+ */
+class MinifyNamer extends Namer {
+ MinifyNamer(Compiler compiler) : super(compiler);
+
+ String get isolateName => 'I';
+ String get isolatePropertiesName => 'p';
+ bool get shouldMinify => true;
+
+ const ALPHABET_CHARACTERS = 52; // a-zA-Z.
+ const ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9.
+
+ // You can pass an invalid identifier to this and unlike its non-minifying
+ // counterpart it will never return the proposedName as the new fresh name.
+ String getFreshName(String proposedName, Set<String> usedNames) {
+ var freshName = _getUnusedName(proposedName, usedNames);
+ usedNames.add(freshName);
+ return freshName;
+ }
+
+ SourceString getClosureVariableName(SourceString name, int id) {
+ if (id < ALPHABET_CHARACTERS) {
+ return new SourceString(new String.fromCharCodes([_letterNumber(id)]));
+ }
+ return new SourceString("${getMappedInstanceName('closure')}_$id");
+ }
+
+ // This gets a minified name based on a hash of the proposed name. This
+ // is slightly less efficient than just getting the next name in a series,
+ // but it means that small changes in the input program will give smallish
+ // changes in the output, which can be useful for diffing etc.
+ String _getUnusedName(String proposedName, Set<String> usedNames) {
+ // Try single-character names with characters that occur in the
+ // input.
+ for (int i = 0; i < proposedName.length; i++) {
+ String candidate = proposedName[i];
+ int code = candidate.charCodeAt(0);
+ if (code < $A) continue;
+ if (code > $z) continue;
+ if (code > $Z && code < $a) continue;
+ if (!usedNames.contains(candidate)) return candidate;
+ }
+
+ int hash = _calculateHash(proposedName);
+ // Avoid very small hashes that won't try many names.
+ hash = hash < 1000 ? hash * 314159 : hash; // Yes, it's prime.
+
+ // Try other n-character names based on the hash. We try one to three
+ // character identifiers. For each length we try around 10 different names
+ // in a predictable order determined by the proposed name. This is in order
+ // to make the renamer stable: small changes in the input should nornally
+ // result in relatively small changes in the output.
+ for (var n = 1; n <= 3; n++) {
+ int h = hash;
+ while (h > 10) {
+ var codes = <int>[_letterNumber(h)];
+ int h2 = h ~/ ALPHABET_CHARACTERS;
+ for (var i = 1; i < n; i++) {
+ codes.add(_alphaNumericNumber(h2));
+ h2 ~/= ALPHANUMERIC_CHARACTERS;
+ }
+ final candidate = new String.fromCharCodes(codes);
+ if (!usedNames.contains(candidate) && !jsReserved.contains(candidate)) {
+ return candidate;
+ }
+ // Try again with a slightly different hash. After around 10 turns
+ // around this loop h is zero and we try a longer name.
+ h ~/= 7;
+ }
+ }
+
+ // If we can't find a hash based name in the three-letter space, then base
+ // the name on a letter and a counter.
+ var startLetter = new String.fromCharCodes([_letterNumber(hash)]);
+ var i = 0;
+ while (usedNames.contains("$startLetter$i")) {
+ i++;
+ }
+ return "$startLetter$i";
+ }
+
+ int _calculateHash(String name) {
+ int h = 0;
+ for (int i = 0; i < name.length; i++) {
+ h += name.charCodeAt(i);
+ h &= 0xffffffff;
+ h += h << 10;
+ h &= 0xffffffff;
+ h ^= h >> 6;
+ h &= 0xffffffff;
+ }
+ return h;
+ }
+
+ int _letterNumber(int x) {
+ if (x >= ALPHABET_CHARACTERS) x %= ALPHABET_CHARACTERS;
+ if (x < 26) return $a + x;
+ return $A + x - 26;
+ }
+
+ int _alphaNumericNumber(int x) {
+ if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS;
+ if (x < 26) return $a + x;
+ if (x < 52) return $A + x - 26;
+ return $0 + x - 52;
+ }
+
+}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
index c4888b2..ba55f77 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
@@ -7,9 +7,7 @@
/**
* Assigns JavaScript identifiers to Dart variables, class-names and members.
*/
-class Namer {
- final Compiler compiler;
-
+class Namer implements ClosureNamer {
static Set<String> _jsReserved = null;
Set<String> get jsReserved {
if (_jsReserved == null) {
@@ -20,15 +18,22 @@
return _jsReserved;
}
+ final String CURRENT_ISOLATE = r'$';
+
/**
* Map from top-level or static elements to their unique identifiers provided
* by [getName].
*
* Invariant: Keys must be declaration elements.
*/
+ final Compiler compiler;
final Map<Element, String> globals;
- final Map<String, int> usedGlobals;
final Map<String, LibraryElement> shortPrivateNameOwners;
+ final Set<String> usedGlobalNames;
+ final Set<String> usedInstanceNames;
+ final Map<String, String> instanceNameMap;
+ final Map<String, String> globalNameMap;
+ final Map<String, int> popularNameCounters;
/**
* A cache of names used for bailout methods. We make sure two
@@ -45,22 +50,27 @@
Namer(this.compiler)
: globals = new Map<Element, String>(),
- usedGlobals = new Map<String, int>(),
shortPrivateNameOwners = new Map<String, LibraryElement>(),
bailoutNames = new Map<Element, String>(),
usedBailoutInstanceNames = new Set<String>(),
- constantNames = new Map<Constant, String>();
+ usedGlobalNames = new Set<String>(),
+ usedInstanceNames = new Set<String>(),
+ instanceNameMap = new Map<String, String>(),
+ globalNameMap = new Map<String, String>(),
+ constantNames = new Map<Constant, String>(),
+ popularNameCounters = new Map<String, int>();
- final String CURRENT_ISOLATE = r'$';
- final String ISOLATE = 'Isolate';
- final String ISOLATE_PROPERTIES = r"$isolateProperties";
+ String get isolateName => 'Isolate';
+ String get isolatePropertiesName => r'$isolateProperties';
/**
* Some closures must contain their name. The name is stored in
* [STATIC_CLOSURE_NAME_NAME].
*/
- final String STATIC_CLOSURE_NAME_NAME = r'$name';
- static const SourceString CLOSURE_INVOCATION_NAME =
- Compiler.CALL_OPERATOR_NAME;
+ String get STATIC_CLOSURE_NAME_NAME => r'$name';
+ SourceString get closureInvocationSelectorName => Compiler.CALL_OPERATOR_NAME;
+ bool get shouldMinify => false;
+
+ bool isReserved(String name) => name == isolateName;
String constantName(Constant constant) {
// In the current implementation it doesn't make sense to give names to
@@ -69,15 +79,28 @@
assert(!constant.isFunction());
String result = constantNames[constant];
if (result == null) {
- result = getFreshGlobalName("CTC");
+ String longName;
+ if (shouldMinify) {
+ if (constant.isString()) {
+ StringConstant stringConstant = constant;
+ // The minifier always constructs a new name, using the argument as
+ // input to its hashing algorithm. The given name does not need to be
+ // valid.
+ longName = stringConstant.value.slowToString();
+ } else {
+ longName = "C";
+ }
+ } else {
+ longName = "CONSTANT";
+ }
+ result = getFreshName(longName, usedGlobalNames);
constantNames[constant] = result;
}
return result;
}
String closureInvocationName(Selector selector) {
- // TODO(floitsch): mangle, while not conflicting with instance names.
- return instanceMethodInvocationName(null, CLOSURE_INVOCATION_NAME,
+ return instanceMethodInvocationName(null, closureInvocationSelectorName,
selector);
}
@@ -104,25 +127,32 @@
* mangles the [name] so that each library has a unique name.
*/
String privateName(LibraryElement lib, SourceString name) {
+ String result;
if (name.isPrivate()) {
String nameString = name.slowToString();
// The first library asking for a short private name wins.
- LibraryElement owner =
+ LibraryElement owner = shouldMinify ?
+ lib :
shortPrivateNameOwners.putIfAbsent(nameString, () => lib);
// If a private name could clash with a mangled private name we don't
// use the short name. For example a private name "_lib3_foo" would
// clash with "_foo" from "lib3".
- if (identical(owner, lib) && !nameString.startsWith('_$LIBRARY_PREFIX')) {
- return nameString;
+ if (owner == lib &&
+ !nameString.startsWith('_$LIBRARY_PREFIX') &&
+ !shouldMinify) {
+ result = nameString;
+ } else {
+ String libName = getName(lib);
+ // If a library name does not start with the [LIBRARY_PREFIX] then our
+ // assumptions about clashing with mangled private members do not hold.
+ assert(shouldMinify || libName.startsWith(LIBRARY_PREFIX));
+ // TODO(erikcorry): Fix this with other manglings to avoid clashes.
+ result = '_lib$libName\$$nameString';
}
- String libName = getName(lib);
- // If a library name does not start with the [LIBRARY_PREFIX] then our
- // assumptions about clashing with mangled private members do not hold.
- assert(libName.startsWith(LIBRARY_PREFIX));
- return '_$libName$nameString';
} else {
- return name.slowToString();
+ result = name.slowToString();
}
+ return result;
}
String instanceMethodName(FunctionElement element) {
@@ -135,21 +165,27 @@
FunctionSignature signature = element.computeSignature(compiler);
String methodName =
'${privateName(lib, name)}\$${signature.parameterCount}';
- if (!signature.optionalParametersAreNamed) {
- return methodName;
- } else if (!signature.optionalParameters.isEmpty) {
+ if (signature.optionalParametersAreNamed &&
+ !signature.optionalParameters.isEmpty) {
StringBuffer buffer = new StringBuffer();
signature.orderedOptionalParameters.forEach((Element element) {
buffer.add('\$${JsNames.getValid(element.name.slowToString())}');
});
- return '$methodName$buffer';
+ methodName = '$methodName$buffer';
}
+ if (name == closureInvocationSelectorName) return methodName;
+ return getMappedInstanceName(methodName);
}
String publicInstanceMethodNameByArity(SourceString name, int arity) {
name = Elements.operatorNameToIdentifier(name);
assert(!name.isPrivate());
- return '${name.slowToString()}\$$arity';
+ var base = name.slowToString();
+ // We don't mangle the closure invoking function name because it is
+ // generated in by string concatenation applyFunction from js_helper.dart.
+ var proposedName = '$base\$$arity';
+ if (base == closureInvocationSelectorName) return proposedName;
+ return getMappedInstanceName(proposedName);
}
String instanceMethodInvocationName(LibraryElement lib, SourceString name,
@@ -162,59 +198,126 @@
buffer.add(r'$');
argumentName.printOn(buffer);
}
- return '${privateName(lib, name)}\$${selector.argumentCount}$buffer';
+ if (name == closureInvocationSelectorName) {
+ // We don't mangle the closure invoking function name because it is
+ // generated in by string concatenation applyFunction from js_helper.dart.
+ return '$closureInvocationSelectorName\$${selector.argumentCount}$buffer';
+ }
+ return getMappedInstanceName(
+ '${privateName(lib, name)}\$${selector.argumentCount}$buffer');
+ }
+
+ /**
+ * Returns the internal name used for an invocation mirror of this selector.
+ */
+ String invocationMirrorInternalName(Selector selector) {
+ if (selector.isGetter()) {
+ return getterName(selector.library, selector.name);
+ } else if (selector.isSetter()) {
+ return setterName(selector.library, selector.name);
+ } else {
+ return instanceMethodInvocationName(
+ selector.library, selector.name, selector);
+ }
}
String instanceFieldName(LibraryElement libraryElement, SourceString name) {
String proposedName = privateName(libraryElement, name);
- return safeName(proposedName);
+ return getMappedInstanceName(proposedName);
}
+ // Construct a new name for the element based on the library and class it is
+ // in. The name here is not important, we just need to make sure it is
+ // unique. If we are minifying, we actually construct the name from the
+ // minified versions of the class and instance names, but the result is
+ // minified once again, so that is not visible in the end result.
String shadowedFieldName(Element fieldElement) {
+ // Check for following situation: Native field ${fieldElement.name} has
+ // fixed JSName ${fieldElement.nativeName()}, but a subclass shadows this
+ // name. We normally handle that by renaming the superclass field, but we
+ // can't do that because native fields have fixed JSNames. In practice
+ // this can't happen because we can't inherit from native classes.
+ assert (!fieldElement.hasFixedBackendName());
+
ClassElement cls = fieldElement.getEnclosingClass();
LibraryElement libraryElement = fieldElement.getLibrary();
String libName = getName(libraryElement);
String clsName = getName(cls);
String instanceName = instanceFieldName(libraryElement, fieldElement.name);
- return safeName('$libName\$$clsName\$$instanceName');
+ return getMappedInstanceName('$libName\$$clsName\$$instanceName');
}
String setterName(LibraryElement lib, SourceString name) {
// We dynamically create setters from the field-name. The setter name must
// therefore be derived from the instance field-name.
- String fieldName = safeName(privateName(lib, name));
+ String fieldName = getMappedInstanceName(privateName(lib, name));
return 'set\$$fieldName';
}
+ String setterNameFromAccessorName(String name) {
+ // We dynamically create setters from the field-name. The setter name must
+ // therefore be derived from the instance field-name.
+ return 'set\$$name';
+ }
+
String publicGetterName(SourceString name) {
// We dynamically create getters from the field-name. The getter name must
// therefore be derived from the instance field-name.
- String fieldName = safeName(name.slowToString());
+ String fieldName = getMappedInstanceName(name.slowToString());
return 'get\$$fieldName';
}
+ String getterNameFromAccessorName(String name) {
+ // We dynamically create getters from the field-name. The getter name must
+ // therefore be derived from the instance field-name.
+ return 'get\$$name';
+ }
+
String getterName(LibraryElement lib, SourceString name) {
// We dynamically create getters from the field-name. The getter name must
// therefore be derived from the instance field-name.
- String fieldName = safeName(privateName(lib, name));
+ String fieldName = getMappedInstanceName(privateName(lib, name));
return 'get\$$fieldName';
}
- String getFreshGlobalName(String proposedName) {
- String name = proposedName;
- int count = usedGlobals[name];
- if (count != null) {
- // Not the first time we see this name. Append a number to make it unique.
- do {
- name = '$proposedName${count++}';
- } while (usedGlobals[name] != null);
- // Record the count in case we see this name later. We
- // frequently see names multiple times, as all our closures use
- // the same name for their class.
- usedGlobals[proposedName] = count;
+ String getMappedGlobalName(String proposedName) {
+ var newName = globalNameMap[proposedName];
+ if (newName == null) {
+ newName = getFreshName(proposedName, usedGlobalNames);
+ globalNameMap[proposedName] = newName;
}
- usedGlobals[name] = 0;
- return name;
+ return newName;
+ }
+
+ String getMappedInstanceName(String proposedName) {
+ var newName = instanceNameMap[proposedName];
+ if (newName == null) {
+ newName = getFreshName(proposedName, usedInstanceNames);
+ instanceNameMap[proposedName] = newName;
+ }
+ return newName;
+ }
+
+ String getFreshName(String proposedName, Set<String> usedNames) {
+ var candidate;
+ proposedName = safeName(proposedName);
+ if (!usedNames.contains(proposedName)) {
+ candidate = proposedName;
+ } else {
+ var counter = popularNameCounters[proposedName];
+ var i = counter == null ? 0 : counter;
+ while (usedNames.contains("$proposedName$i")) {
+ i++;
+ }
+ popularNameCounters[proposedName] = i + 1;
+ candidate = "$proposedName$i";
+ }
+ usedNames.add(candidate);
+ return candidate;
+ }
+
+ SourceString getClosureVariableName(SourceString name, int id) {
+ return new SourceString("${name.slowToString()}_$id");
}
static const String LIBRARY_PREFIX = "lib";
@@ -247,29 +350,38 @@
} else {
name = element.name.slowToString();
}
- return safeName(name);
+ return name;
}
String getSpecializedName(Element element, Collection<ClassElement> classes) {
+ // This gets the minified name, but it doesn't really make much difference.
+ // The important thing is that it is a unique name.
StringBuffer buffer = new StringBuffer('${getName(element)}\$');
for (ClassElement cls in classes) {
buffer.add(getName(cls));
}
- return safeName(buffer.toString());
+ return getMappedGlobalName(buffer.toString());
}
String getBailoutName(Element element) {
String name = bailoutNames[element];
if (name != null) return name;
bool global = !element.isInstanceMember();
+ // Despite the name of the variable, this gets the minified name when we
+ // 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';
- name = unminifiedName;
- if (!global) {
+ if (global) {
+ name = getMappedGlobalName(unminifiedName);
+ } else {
+ name = unminifiedName;
int i = 0;
while (usedBailoutInstanceNames.contains(name)) {
name = '$unminifiedName${i++}';
}
usedBailoutInstanceNames.add(name);
+ name = getMappedInstanceName(name);
}
bailoutNames[element] = name;
return name;
@@ -307,21 +419,30 @@
String guess = _computeGuess(element);
ElementKind kind = element.kind;
- if (identical(kind, ElementKind.VARIABLE) ||
- identical(kind, ElementKind.PARAMETER)) {
+ if (kind == ElementKind.VARIABLE ||
+ kind == ElementKind.PARAMETER) {
// The name is not guaranteed to be unique.
- return guess;
+ return safeName(guess);
}
- if (identical(kind, ElementKind.GENERATIVE_CONSTRUCTOR) ||
- identical(kind, ElementKind.FUNCTION) ||
- identical(kind, ElementKind.CLASS) ||
- identical(kind, ElementKind.FIELD) ||
- identical(kind, ElementKind.GETTER) ||
- identical(kind, ElementKind.SETTER) ||
- identical(kind, ElementKind.TYPEDEF) ||
- identical(kind, ElementKind.LIBRARY) ||
- identical(kind, ElementKind.MALFORMED_TYPE)) {
- String result = getFreshGlobalName(guess);
+ if (kind == ElementKind.GENERATIVE_CONSTRUCTOR ||
+ kind == ElementKind.FUNCTION ||
+ kind == ElementKind.CLASS ||
+ kind == ElementKind.FIELD ||
+ kind == ElementKind.GETTER ||
+ kind == ElementKind.SETTER ||
+ kind == ElementKind.TYPEDEF ||
+ kind == ElementKind.LIBRARY ||
+ kind == ElementKind.MALFORMED_TYPE) {
+ bool fixedName = false;
+ if (kind == ElementKind.CLASS) {
+ ClassElement classElement = element;
+ fixedName = classElement.isNative();
+ }
+ if (Elements.isInstanceField(element)) {
+ fixedName = element.hasFixedBackendName();
+ }
+ String result =
+ fixedName ? guess : getFreshName(guess, usedGlobalNames);
globals[element] = result;
return result;
}
@@ -331,13 +452,12 @@
}
String getLazyInitializerName(Element element) {
- // TODO(floitsch): mangle while not conflicting with other statics.
assert(Elements.isStaticOrTopLevelField(element));
- return "get\$${getName(element)}";
+ return getMappedGlobalName("get\$${getName(element)}");
}
String isolatePropertiesAccess(Element element) {
- return "$ISOLATE.$ISOLATE_PROPERTIES.${getName(element)}";
+ return "$isolateName.$isolatePropertiesName.${getName(element)}";
}
String isolateAccess(Element element) {
@@ -345,7 +465,8 @@
}
String isolateBailoutAccess(Element element) {
- return '${isolateAccess(element)}\$bailout';
+ String newName = getMappedGlobalName('${getName(element)}\$bailout');
+ return '$CURRENT_ISOLATE.$newName';
}
String isolateLazyInitializerAccess(Element element) {
@@ -353,6 +474,7 @@
}
String operatorIs(Element element) {
+ // TODO(erikcorry): Reduce from is$x to ix when we are minifying.
return 'is\$${getName(element)}';
}
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 6b8771d..1104974 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
@@ -48,6 +48,10 @@
Compiler get compiler => emitter.compiler;
JavaScriptBackend get backend => compiler.backend;
+ String get _ => emitter._;
+ String get n => emitter.n;
+ String get N => emitter.N;
+
String get dynamicName {
Element element = compiler.findHelper(
const SourceString('dynamicFunction'));
@@ -111,12 +115,12 @@
String nativeCode = quotedNative.substring(2, quotedNative.length - 1);
String className = backend.namer.getName(classElement);
nativeBuffer.add(className);
- nativeBuffer.add(' = ');
+ nativeBuffer.add('$_=$_');
nativeBuffer.add(nativeCode);
- nativeBuffer.add(';\n');
+ nativeBuffer.add('$N');
void defineInstanceMember(String name, CodeBuffer value) {
- nativeBuffer.add("$className.$name = $value;\n");
+ nativeBuffer.add("$className.$name$_=$_$value$N");
}
classElement.implementation.forEachMember((_, Element member) {
@@ -160,7 +164,8 @@
CodeBuffer getterSetterBuffer = new CodeBuffer();
CodeBuffer methodBuffer = new CodeBuffer();
- emitter.emitClassFields(classElement, fieldBuffer, false, isNative: true);
+ emitter.emitClassFields(classElement, fieldBuffer, false,
+ classIsNative: true);
emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false);
emitter.emitInstanceMembers(classElement, methodBuffer, false);
@@ -171,7 +176,7 @@
}
String nativeTag = toNativeTag(classElement);
- nativeBuffer.add("$defineNativeClassName('$nativeTag', ");
+ nativeBuffer.add("$defineNativeClassName('$nativeTag',$_");
nativeBuffer.add('{');
bool firstInMap = true;
if (!fieldBuffer.isEmpty) {
@@ -181,14 +186,14 @@
if (!getterSetterBuffer.isEmpty) {
if (!firstInMap) nativeBuffer.add(",");
firstInMap = false;
- nativeBuffer.add("\n ");
+ nativeBuffer.add("\n$_");
nativeBuffer.add(getterSetterBuffer);
}
if (!methodBuffer.isEmpty) {
if (!firstInMap) nativeBuffer.add(",");
nativeBuffer.add(methodBuffer);
}
- nativeBuffer.add('\n});\n\n');
+ nativeBuffer.add('$n})$N$n');
classesWithDynamicDispatch.add(classElement);
}
@@ -266,7 +271,7 @@
} else {
// When calling a JS method, we call it with the native name, and only the
// arguments up until the last one provided.
- target = member.nativeName();
+ target = member.fixedBackendName();
arguments = argumentsBuffer.getRange(
0, indexOfLastOptionalArgumentInParameters + 1);
}
@@ -337,7 +342,9 @@
void emitDynamicDispatchMetadata() {
if (classesWithDynamicDispatch.isEmpty) return;
int length = classesWithDynamicDispatch.length;
- nativeBuffer.add('// $length dynamic classes.\n');
+ if (!compiler.enableMinification) {
+ nativeBuffer.add('// $length dynamic classes.\n');
+ }
// Build a pre-order traversal over all the classes and their subclasses.
Set<ClassElement> seen = new Set<ClassElement>();
@@ -354,10 +361,14 @@
(cls) => !getDirectSubclasses(cls).isEmpty &&
classesWithDynamicDispatch.contains(cls));
- nativeBuffer.add('// ${classes.length} classes\n');
+ if (!compiler.enableMinification) {
+ nativeBuffer.add('// ${classes.length} classes\n');
+ }
Collection<ClassElement> classesThatHaveSubclasses = classes.filter(
(ClassElement t) => !getDirectSubclasses(t).isEmpty);
- nativeBuffer.add('// ${classesThatHaveSubclasses.length} !leaf\n');
+ if (!compiler.enableMinification) {
+ nativeBuffer.add('// ${classesThatHaveSubclasses.length} !leaf\n');
+ }
// Generate code that builds the map from cls tags used in dynamic dispatch
// to the set of cls tags of classes that extend (TODO: or implement) those
@@ -399,13 +410,13 @@
} else {
// [subclass] is one of the preorderDispatchClasses, so CSE this
// reference with the previous reference.
- if (existing is js.VariableUse &&
- varDefns.containsKey(existing.name)) {
+ js.VariableUse use = existing.asVariableUse();
+ if (use != null && varDefns.containsKey(use.name)) {
// We end up here if the subclasses have a DAG structure. We
// don't have DAGs yet, but if the dispatch is used for mixins
// that will be a possibility.
// Re-use the previously created temporary variable.
- expressions.add(new js.VariableUse(existing.name));
+ expressions.add(new js.VariableUse(use.name));
} else {
String varName = 'v${varNames.length}_${tag.name.slowToString()}';
varNames.add(varName);
@@ -475,6 +486,7 @@
[table])));
// (function(){statements})();
+ if (emitter.compiler.enableMinification) nativeBuffer.add(';');
nativeBuffer.add(
js.prettyPrint(
new js.ExpressionStatement(
@@ -514,7 +526,7 @@
if (!requiresNativeIsCheck(element)) continue;
if (element.isObject(compiler)) continue;
String name = backend.namer.operatorIs(element);
- objectProperties[name] = 'function() { return false; }';
+ objectProperties[name] = 'function()$_{${_}return false;$_}';
}
}
@@ -522,7 +534,7 @@
if (nativeClasses.isEmpty) return;
emitDynamicDispatchMetadata();
targetBuffer.add('$defineNativeClassName = '
- '$defineNativeClassFunction;\n\n');
+ '$defineNativeClassFunction$N$n');
// Because of native classes, we have to generate some is checks
// by calling a method, instead of accessing a property. So we
@@ -540,8 +552,8 @@
'function() { return $toStringHelperName(this); }';
// Same as above, but for hashCode.
- String hashCodeName = backend.namer.publicGetterName(
- const SourceString('hashCode'));
+ String hashCodeName =
+ backend.namer.publicGetterName(const SourceString('hashCode'));
objectProperties[hashCodeName] =
'function() { return $hashCodeHelperName(this); }';
@@ -556,6 +568,7 @@
// If we have any properties to add to Object.prototype, we run
// through them and add them using defineProperty.
if (!objectProperties.isEmpty) {
+ if (emitter.compiler.enableMinification) targetBuffer.add(";");
targetBuffer.add("(function(table) {\n"
" for (var key in table) {\n"
" $defPropName(Object.prototype, key, table[key]);\n"
@@ -564,10 +577,10 @@
bool first = true;
objectProperties.forEach((String name, String function) {
if (!first) targetBuffer.add(",\n");
- targetBuffer.add(" $name: $function");
+ targetBuffer.add("$_$name:$_$function");
first = false;
});
- targetBuffer.add("\n});\n\n");
+ targetBuffer.add("\n})$N$n");
}
targetBuffer.add(nativeBuffer);
targetBuffer.add('\n');
diff --git a/sdk/lib/_internal/compiler/implementation/lib/constant_map.dart b/sdk/lib/_internal/compiler/implementation/lib/constant_map.dart
index fcbd58a..53d276e 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/constant_map.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/constant_map.dart
@@ -2,6 +2,8 @@
// 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 _js_helper;
+
// This class has no constructor. This is on purpose since the instantiation
// is shortcut by the compiler.
class ConstantMap<V> implements Map<String, V> {
diff --git a/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart
index 38b90d1..b375a97 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/core_patch.dart
@@ -211,3 +211,8 @@
multiLine: multiLine,
ignoreCase: ignoreCase);
}
+
+// Patch for 'identical' function.
+patch bool identical(Object a, Object b) {
+ throw new Error('Should not reach the body of identical');
+}
diff --git a/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart
index 9b677d6..dd17e1b 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/io_patch.dart
@@ -162,12 +162,14 @@
patch class ServerSocket {
patch factory ServerSocket(String bindAddress, int port, int backlog) {
- return new _ServerSocket(bindAddress, port, backlog);
+ throw new UnsupportedError("ServerSocket constructor");
}
}
patch class Socket {
- patch factory Socket(String host, int port) => new _Socket(host, port);
+ patch factory Socket(String host, int port) {
+ throw new UnsupportedError("Socket constructor");
+ }
}
patch class SecureSocket {
@@ -185,11 +187,11 @@
}
patch class _StdIOUtils {
- patch static _getStdioHandle(Socket socket, int num) {
- throw new UnsupportedError("StdIOUtils._getStdioHandle");
+ patch static InputStream _getStdioInputStream() {
+ throw new UnsupportedError("StdIOUtils._getStdioInputStream");
}
- patch static _getStdioHandleType(int num) {
- throw new UnsupportedError("StdIOUtils._getStdioHandleType");
+ patch static OutputStream _getStdioOutputStream(int fd) {
+ throw new UnsupportedError("StdIOUtils._getStdioOutputStream");
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
new file mode 100644
index 0000000..f2b8daa
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
@@ -0,0 +1,1283 @@
+// 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 _isolate_helper;
+
+import 'dart:isolate';
+import 'dart:uri';
+
+/**
+ * Called by the compiler to support switching
+ * between isolates when we get a callback from the DOM.
+ */
+void _callInIsolate(_IsolateContext isolate, Function function) {
+ isolate.eval(function);
+ _globalState.topEventLoop.run();
+}
+
+/**
+ * Called by the compiler to fetch the current isolate context.
+ */
+_IsolateContext _currentIsolate() => _globalState.currentContext;
+
+/********************************************************
+ Inserted from lib/isolate/dart2js/compiler_hooks.dart
+ ********************************************************/
+
+/**
+ * Wrapper that takes the dart entry point and runs it within an isolate. The
+ * dart2js compiler will inject a call of the form
+ * [: startRootIsolate(main); :] when it determines that this wrapping
+ * is needed. For single-isolate applications (e.g. hello world), this
+ * call is not emitted.
+ */
+void startRootIsolate(entry) {
+ _globalState = new _Manager();
+
+ // Don't start the main loop again, if we are in a worker.
+ if (_globalState.isWorker) return;
+ final rootContext = new _IsolateContext();
+ _globalState.rootContext = rootContext;
+ _fillStatics(rootContext);
+
+ // BUG(5151491): Setting currentContext should not be necessary, but
+ // because closures passed to the DOM as event handlers do not bind their
+ // isolate automatically we try to give them a reasonable context to live in
+ // by having a "default" isolate (the first one created).
+ _globalState.currentContext = rootContext;
+
+ rootContext.eval(entry);
+ _globalState.topEventLoop.run();
+}
+
+/********************************************************
+ Inserted from lib/isolate/dart2js/isolateimpl.dart
+ ********************************************************/
+
+/**
+ * Concepts used here:
+ *
+ * "manager" - A manager contains one or more isolates, schedules their
+ * execution, and performs other plumbing on their behalf. The isolate
+ * present at the creation of the manager is designated as its "root isolate".
+ * A manager may, for example, be implemented on a web Worker.
+ *
+ * [_Manager] - State present within a manager (exactly once, as a global).
+ *
+ * [_ManagerStub] - A handle held within one manager that allows interaction
+ * with another manager. A target manager may be addressed by zero or more
+ * [_ManagerStub]s.
+ *
+ */
+
+/**
+ * A native object that is shared across isolates. This object is visible to all
+ * isolates running under the same manager (either UI or background web worker).
+ *
+ * This is code that is intended to 'escape' the isolate boundaries in order to
+ * implement the semantics of isolates in JavaScript. Without this we would have
+ * been forced to implement more code (including the top-level event loop) in
+ * JavaScript itself.
+ */
+// TODO(eub, sigmund): move the "manager" to be entirely in JS.
+// Running any Dart code outside the context of an isolate gives it
+// the change to break the isolate abstraction.
+_Manager get _globalState => JS("_Manager", r"$globalState");
+set _globalState(_Manager val) {
+ JS("void", r"$globalState = #", val);
+}
+
+void _fillStatics(context) {
+ JS("void", r"$globals = #.isolateStatics", context);
+ JS("void", r"$static_init()");
+}
+
+ReceivePort lazyPort;
+
+/** State associated with the current manager. See [globalState]. */
+// TODO(sigmund): split in multiple classes: global, thread, main-worker states?
+class _Manager {
+
+ /** Next available isolate id within this [_Manager]. */
+ int nextIsolateId = 0;
+
+ /** id assigned to this [_Manager]. */
+ int currentManagerId = 0;
+
+ /**
+ * Next available manager id. Only used by the main manager to assign a unique
+ * id to each manager created by it.
+ */
+ int nextManagerId = 1;
+
+ /** Context for the currently running [Isolate]. */
+ _IsolateContext currentContext = null;
+
+ /** Context for the root [Isolate] that first run in this [_Manager]. */
+ _IsolateContext rootContext = null;
+
+ /** The top-level event loop. */
+ _EventLoop topEventLoop;
+
+ /** Whether this program is running from the command line. */
+ bool fromCommandLine;
+
+ /** Whether this [_Manager] is running as a web worker. */
+ bool isWorker;
+
+ /** Whether we support spawning web workers. */
+ bool supportsWorkers;
+
+ /**
+ * Whether to use web workers when implementing isolates. Set to false for
+ * debugging/testing.
+ */
+ bool get useWorkers => supportsWorkers;
+
+ /**
+ * Whether to use the web-worker JSON-based message serialization protocol. By
+ * default this is only used with web workers. For debugging, you can force
+ * using this protocol by changing this field value to [true].
+ */
+ bool get needSerialization => useWorkers;
+
+ /**
+ * Registry of isolates. Isolates must be registered if, and only if, receive
+ * ports are alive. Normally no open receive-ports means that the isolate is
+ * dead, but DOM callbacks could resurrect it.
+ */
+ Map<int, _IsolateContext> isolates;
+
+ /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */
+ _ManagerStub mainManager;
+
+ /** Registry of active [_ManagerStub]s. Only used in the main [_Manager]. */
+ Map<int, _ManagerStub> managers;
+
+ _Manager() {
+ _nativeDetectEnvironment();
+ topEventLoop = new _EventLoop();
+ isolates = new Map<int, _IsolateContext>();
+ managers = new Map<int, _ManagerStub>();
+ if (isWorker) { // "if we are not the main manager ourself" is the intent.
+ mainManager = new _MainManagerStub();
+ _nativeInitWorkerMessageHandler();
+ }
+ }
+
+ void _nativeDetectEnvironment() {
+ isWorker = JS("bool", r"$isWorker");
+ supportsWorkers = JS("bool", r"$supportsWorkers");
+ fromCommandLine = JS("bool", r"typeof(window) == 'undefined'");
+ }
+
+ void _nativeInitWorkerMessageHandler() {
+ JS("void", r"""
+$globalThis.onmessage = function (e) {
+ IsolateNatives._processWorkerMessage(this.mainManager, e);
+}""");
+ }
+ /*: TODO: check that _processWorkerMessage is not discarded while treeshaking.
+ """ {
+ IsolateNatives._processWorkerMessage(null, null);
+ }
+ */
+
+
+ /** Close the worker running this code if all isolates are done. */
+ void maybeCloseWorker() {
+ if (isolates.isEmpty) {
+ mainManager.postMessage(_serializeMessage({'command': 'close'}));
+ }
+ }
+}
+
+/** Context information tracked for each isolate. */
+class _IsolateContext {
+ /** Current isolate id. */
+ int id;
+
+ /** Registry of receive ports currently active on this isolate. */
+ Map<int, ReceivePort> ports;
+
+ /** Holds isolate globals (statics and top-level properties). */
+ var isolateStatics; // native object containing all globals of an isolate.
+
+ _IsolateContext() {
+ id = _globalState.nextIsolateId++;
+ ports = new Map<int, ReceivePort>();
+ initGlobals();
+ }
+
+ // these are filled lazily the first time the isolate starts running.
+ void initGlobals() { JS("void", r'$initGlobals(#)', this); }
+
+ /**
+ * Run [code] in the context of the isolate represented by [this]. Note this
+ * is called from JavaScript (see $wrap_call in corejs.dart).
+ */
+ dynamic eval(Function code) {
+ var old = _globalState.currentContext;
+ _globalState.currentContext = this;
+ this._setGlobals();
+ var result = null;
+ try {
+ result = code();
+ } finally {
+ _globalState.currentContext = old;
+ if (old != null) old._setGlobals();
+ }
+ return result;
+ }
+
+ void _setGlobals() { JS("void", r'$setGlobals(#)', this); }
+
+ /** Lookup a port registered for this isolate. */
+ ReceivePort lookup(int portId) => ports[portId];
+
+ /** Register a port on this isolate. */
+ void register(int portId, ReceivePort port) {
+ if (ports.containsKey(portId)) {
+ throw new Exception("Registry: ports must be registered only once.");
+ }
+ ports[portId] = port;
+ _globalState.isolates[id] = this; // indicate this isolate is active
+ }
+
+ /** Unregister a port on this isolate. */
+ void unregister(int portId) {
+ ports.remove(portId);
+ if (ports.isEmpty) {
+ _globalState.isolates.remove(id); // indicate this isolate is not active
+ }
+ }
+}
+
+/** Represent the event loop on a javascript thread (DOM or worker). */
+class _EventLoop {
+ Queue<_IsolateEvent> events;
+
+ _EventLoop() : events = new Queue<_IsolateEvent>();
+
+ void enqueue(isolate, fn, msg) {
+ events.addLast(new _IsolateEvent(isolate, fn, msg));
+ }
+
+ _IsolateEvent dequeue() {
+ if (events.isEmpty) return null;
+ return events.removeFirst();
+ }
+
+ /** Process a single event, if any. */
+ bool runIteration() {
+ final event = dequeue();
+ if (event == null) {
+ if (_globalState.isWorker) {
+ _globalState.maybeCloseWorker();
+ } else if (_globalState.rootContext != null &&
+ _globalState.isolates.containsKey(
+ _globalState.rootContext.id) &&
+ _globalState.fromCommandLine &&
+ _globalState.rootContext.ports.isEmpty) {
+ // We want to reach here only on the main [_Manager] and only
+ // on the command-line. In the browser the isolate might
+ // still be alive due to DOM callbacks, but the presumption is
+ // that on the command-line, no future events can be injected
+ // into the event queue once it's empty. Node has setTimeout
+ // so this presumption is incorrect there. We think(?) that
+ // in d8 this assumption is valid.
+ throw new Exception("Program exited with open ReceivePorts.");
+ }
+ return false;
+ }
+ event.process();
+ return true;
+ }
+
+ /**
+ * Runs multiple iterations of the run-loop. If possible, each iteration is
+ * run asynchronously.
+ */
+ void _runHelper() {
+ if (hasWindow()) {
+ // Run each iteration from the browser's top event loop.
+ void next() {
+ if (!runIteration()) return;
+ _window.setTimeout(next, 0);
+ }
+ next();
+ } else {
+ // Run synchronously until no more iterations are available.
+ while (runIteration()) {}
+ }
+ }
+
+ /**
+ * Call [_runHelper] but ensure that worker exceptions are propragated. Note
+ * this is called from JavaScript (see $wrap_call in corejs.dart).
+ */
+ void run() {
+ if (!_globalState.isWorker) {
+ _runHelper();
+ } else {
+ try {
+ _runHelper();
+ } catch (e, trace) {
+ _globalState.mainManager.postMessage(_serializeMessage(
+ {'command': 'error', 'msg': '$e\n$trace' }));
+ }
+ }
+ }
+}
+
+/** An event in the top-level event queue. */
+class _IsolateEvent {
+ _IsolateContext isolate;
+ Function fn;
+ String message;
+
+ _IsolateEvent(this.isolate, this.fn, this.message);
+
+ void process() {
+ isolate.eval(fn);
+ }
+}
+
+/** An interface for a stub used to interact with a manager. */
+abstract class _ManagerStub {
+ get id;
+ void set id(int i);
+ void set onmessage(Function f);
+ void postMessage(msg);
+ void terminate();
+}
+
+/** A stub for interacting with the main manager. */
+class _MainManagerStub implements _ManagerStub {
+ get id => 0;
+ void set id(int i) { throw new UnimplementedError(); }
+ void set onmessage(f) {
+ throw new Exception("onmessage should not be set on MainManagerStub");
+ }
+ void postMessage(msg) { JS("void", r"$globalThis.postMessage(#)", msg); }
+ void terminate() {} // Nothing useful to do here.
+}
+
+/**
+ * A stub for interacting with a manager built on a web worker. This
+ * definition uses a 'hidden' type (* prefix on the native name) to
+ * enforce that the type is defined dynamically only when web workers
+ * are actually available.
+ */
+// @Native("*Worker");
+class _WorkerStub implements _ManagerStub {
+ get id => JS("var", "#.id", this);
+ void set id(i) { JS("void", "#.id = #", this, i); }
+ void set onmessage(f) { JS("void", "#.onmessage = #", this, f); }
+ void postMessage(msg) => JS("void", "#.postMessage(#)", this, msg);
+ // terminate() is implemented by Worker.
+ void terminate();
+}
+
+const String _SPAWNED_SIGNAL = "spawned";
+
+class IsolateNatives {
+
+ /**
+ * The src url for the script tag that loaded this code. Used to create
+ * JavaScript workers.
+ */
+ static String get _thisScript => JS("String", r"$thisScriptUrl");
+
+ /** Starts a new worker with the given URL. */
+ static _WorkerStub _newWorker(url) => JS("_WorkerStub", r"new Worker(#)", url);
+
+ /**
+ * Assume that [e] is a browser message event and extract its message data.
+ * We don't import the dom explicitly so, when workers are disabled, this
+ * library can also run on top of nodejs.
+ */
+ //static _getEventData(e) => JS("Object", "#.data", e);
+ static _getEventData(e) => JS("", "#.data", e);
+
+ /**
+ * Process messages on a worker, either to control the worker instance or to
+ * pass messages along to the isolate running in the worker.
+ */
+ static void _processWorkerMessage(sender, e) {
+ var msg = _deserializeMessage(_getEventData(e));
+ switch (msg['command']) {
+ case 'start':
+ _globalState.currentManagerId = msg['id'];
+ Function entryPoint = _getJSFunctionFromName(msg['functionName']);
+ var replyTo = _deserializeMessage(msg['replyTo']);
+ _globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
+ _startIsolate(entryPoint, replyTo);
+ }, 'worker-start');
+ _globalState.topEventLoop.run();
+ break;
+ case 'spawn-worker':
+ _spawnWorker(msg['functionName'], msg['uri'], msg['replyPort']);
+ break;
+ case 'message':
+ msg['port'].send(msg['msg'], msg['replyTo']);
+ _globalState.topEventLoop.run();
+ break;
+ case 'close':
+ _log("Closing Worker");
+ _globalState.managers.remove(sender.id);
+ sender.terminate();
+ _globalState.topEventLoop.run();
+ break;
+ case 'log':
+ _log(msg['msg']);
+ break;
+ case 'print':
+ if (_globalState.isWorker) {
+ _globalState.mainManager.postMessage(
+ _serializeMessage({'command': 'print', 'msg': msg}));
+ } else {
+ print(msg['msg']);
+ }
+ break;
+ case 'error':
+ throw msg['msg'];
+ }
+ }
+
+ /** Log a message, forwarding to the main [_Manager] if appropriate. */
+ static _log(msg) {
+ if (_globalState.isWorker) {
+ _globalState.mainManager.postMessage(
+ _serializeMessage({'command': 'log', 'msg': msg }));
+ } else {
+ try {
+ _consoleLog(msg);
+ } catch (e, trace) {
+ throw new Exception(trace);
+ }
+ }
+ }
+
+ static void _consoleLog(msg) {
+ JS("void", r"$globalThis.console.log(#)", msg);
+ }
+
+ /**
+ * Extract the constructor of runnable, so it can be allocated in another
+ * isolate.
+ */
+ static dynamic _getJSConstructor(Isolate runnable) {
+ return JS("Object", "#.constructor", runnable);
+ }
+
+ /** Extract the constructor name of a runnable */
+ // TODO(sigmund): find a browser-generic way to support this.
+ // TODO(floitsch): is this function still used? If yes, should we use
+ // Primitives.objectTypeName instead?
+ static dynamic _getJSConstructorName(Isolate runnable) {
+ return JS("Object", "#.constructor.name", runnable);
+ }
+
+ /** Find a constructor given its name. */
+ static dynamic _getJSConstructorFromName(String factoryName) {
+ return JS("Object", r"$globalThis[#]", factoryName);
+ }
+
+ static dynamic _getJSFunctionFromName(String functionName) {
+ return JS("Object", r"$globalThis[#]", functionName);
+ }
+
+ /**
+ * Get a string name for the function, if possible. The result for
+ * anonymous functions is browser-dependent -- it may be "" or "anonymous"
+ * but you should probably not count on this.
+ */
+ static String _getJSFunctionName(Function f) {
+ return JS("Object", r"(#.$name || #)", f, null);
+ }
+
+ /** Create a new JavaScript object instance given its constructor. */
+ static dynamic _allocate(var ctor) {
+ return JS("Object", "new #()", ctor);
+ }
+
+ static SendPort spawnFunction(void topLevelFunction()) {
+ final name = _getJSFunctionName(topLevelFunction);
+ if (name == null) {
+ throw new UnsupportedError(
+ "only top-level functions can be spawned.");
+ }
+ return spawn(name, null, false);
+ }
+
+ // TODO(sigmund): clean up above, after we make the new API the default:
+
+ static SendPort spawn(String functionName, String uri, bool isLight) {
+ Completer<SendPort> completer = new Completer<SendPort>();
+ ReceivePort port = new ReceivePort();
+ port.receive((msg, SendPort replyPort) {
+ port.close();
+ assert(msg == _SPAWNED_SIGNAL);
+ completer.complete(replyPort);
+ });
+
+ SendPort signalReply = port.toSendPort();
+
+ if (_globalState.useWorkers && !isLight) {
+ _startWorker(functionName, uri, signalReply);
+ } else {
+ _startNonWorker(functionName, uri, signalReply);
+ }
+ return new _BufferingSendPort(
+ _globalState.currentContext.id, completer.future);
+ }
+
+ static SendPort _startWorker(
+ String functionName, String uri, SendPort replyPort) {
+ if (_globalState.isWorker) {
+ _globalState.mainManager.postMessage(_serializeMessage({
+ 'command': 'spawn-worker',
+ 'functionName': functionName,
+ 'uri': uri,
+ 'replyPort': replyPort}));
+ } else {
+ _spawnWorker(functionName, uri, replyPort);
+ }
+ }
+
+ static SendPort _startNonWorker(
+ String functionName, String uri, SendPort replyPort) {
+ // TODO(eub): support IE9 using an iframe -- Dart issue 1702.
+ if (uri != null) throw new UnsupportedError(
+ "Currently spawnUri is not supported without web workers.");
+ _globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
+ final func = _getJSFunctionFromName(functionName);
+ _startIsolate(func, replyPort);
+ }, 'nonworker start');
+ }
+
+ static void _startIsolate(Function topLevel, SendPort replyTo) {
+ _fillStatics(_globalState.currentContext);
+ lazyPort = new ReceivePort();
+ replyTo.send(_SPAWNED_SIGNAL, port.toSendPort());
+
+ topLevel();
+ }
+
+ /**
+ * Spawns an isolate in a worker. [factoryName] is the Javascript constructor
+ * name for the isolate entry point class.
+ */
+ static void _spawnWorker(functionName, uri, replyPort) {
+ if (functionName == null) functionName = 'main';
+ if (uri == null) uri = _thisScript;
+ if (!(new Uri.fromString(uri).isAbsolute())) {
+ // The constructor of dom workers requires an absolute URL. If we use a
+ // relative path we will get a DOM exception.
+ String prefix = _thisScript.substring(0, _thisScript.lastIndexOf('/'));
+ uri = "$prefix/$uri";
+ }
+ final worker = _newWorker(uri);
+ worker.onmessage = (e) { _processWorkerMessage(worker, e); };
+ var workerId = _globalState.nextManagerId++;
+ // We also store the id on the worker itself so that we can unregister it.
+ worker.id = workerId;
+ _globalState.managers[workerId] = worker;
+ worker.postMessage(_serializeMessage({
+ 'command': 'start',
+ 'id': workerId,
+ // Note: we serialize replyPort twice because the child worker needs to
+ // first deserialize the worker id, before it can correctly deserialize
+ // the port (port deserialization is sensitive to what is the current
+ // workerId).
+ 'replyTo': _serializeMessage(replyPort),
+ 'functionName': functionName }));
+ }
+}
+
+/********************************************************
+ Inserted from lib/isolate/dart2js/ports.dart
+ ********************************************************/
+
+/** Common functionality to all send ports. */
+class _BaseSendPort implements SendPort {
+ /** Id for the destination isolate. */
+ final int _isolateId;
+
+ const _BaseSendPort(this._isolateId);
+
+ void _checkReplyTo(SendPort replyTo) {
+ if (replyTo != null
+ && replyTo is! _NativeJsSendPort
+ && replyTo is! _WorkerSendPort
+ && replyTo is! _BufferingSendPort) {
+ throw new Exception("SendPort.send: Illegal replyTo port type");
+ }
+ }
+
+ Future call(var message) {
+ final completer = new Completer();
+ final port = new ReceivePortImpl();
+ send(message, port.toSendPort());
+ port.receive((value, ignoreReplyTo) {
+ port.close();
+ if (value is Exception) {
+ completer.completeException(value);
+ } else {
+ completer.complete(value);
+ }
+ });
+ return completer.future;
+ }
+
+ void send(var message, [SendPort replyTo]);
+ bool operator ==(var other);
+ int get hashCode;
+}
+
+/** A send port that delivers messages in-memory via native JavaScript calls. */
+class _NativeJsSendPort extends _BaseSendPort implements SendPort {
+ final ReceivePortImpl _receivePort;
+
+ const _NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId);
+
+ void send(var message, [SendPort replyTo = null]) {
+ _waitForPendingPorts([message, replyTo], () {
+ _checkReplyTo(replyTo);
+ // Check that the isolate still runs and the port is still open
+ final isolate = _globalState.isolates[_isolateId];
+ if (isolate == null) return;
+ if (_receivePort._callback == null) return;
+
+ // We force serialization/deserialization as a simple way to ensure
+ // isolate communication restrictions are respected between isolates that
+ // live in the same worker. [_NativeJsSendPort] delivers both messages
+ // from the same worker and messages from other workers. In particular,
+ // messages sent from a worker via a [_WorkerSendPort] are received at
+ // [_processWorkerMessage] and forwarded to a native port. In such cases,
+ // here we'll see [_globalState.currentContext == null].
+ final shouldSerialize = _globalState.currentContext != null
+ && _globalState.currentContext.id != _isolateId;
+ var msg = message;
+ var reply = replyTo;
+ if (shouldSerialize) {
+ msg = _serializeMessage(msg);
+ reply = _serializeMessage(reply);
+ }
+ _globalState.topEventLoop.enqueue(isolate, () {
+ if (_receivePort._callback != null) {
+ if (shouldSerialize) {
+ msg = _deserializeMessage(msg);
+ reply = _deserializeMessage(reply);
+ }
+ _receivePort._callback(msg, reply);
+ }
+ }, 'receive $message');
+ });
+ }
+
+ bool operator ==(var other) => (other is _NativeJsSendPort) &&
+ (_receivePort == other._receivePort);
+
+ int get hashCode => _receivePort._id;
+}
+
+/** A send port that delivers messages via worker.postMessage. */
+// TODO(eub): abstract this for iframes.
+class _WorkerSendPort extends _BaseSendPort implements SendPort {
+ final int _workerId;
+ final int _receivePortId;
+
+ const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId)
+ : super(isolateId);
+
+ void send(var message, [SendPort replyTo = null]) {
+ _waitForPendingPorts([message, replyTo], () {
+ _checkReplyTo(replyTo);
+ final workerMessage = _serializeMessage({
+ 'command': 'message',
+ 'port': this,
+ 'msg': message,
+ 'replyTo': replyTo});
+
+ if (_globalState.isWorker) {
+ // communication from one worker to another go through the main worker:
+ _globalState.mainManager.postMessage(workerMessage);
+ } else {
+ _globalState.managers[_workerId].postMessage(workerMessage);
+ }
+ });
+ }
+
+ bool operator ==(var other) {
+ return (other is _WorkerSendPort) &&
+ (_workerId == other._workerId) &&
+ (_isolateId == other._isolateId) &&
+ (_receivePortId == other._receivePortId);
+ }
+
+ int get hashCode {
+ // TODO(sigmund): use a standard hash when we get one available in corelib.
+ return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId;
+ }
+}
+
+/** A port that buffers messages until an underlying port gets resolved. */
+class _BufferingSendPort extends _BaseSendPort implements SendPort {
+ /** Internal counter to assign unique ids to each port. */
+ static int _idCount = 0;
+
+ /** For implementing equals and hashcode. */
+ final int _id;
+
+ /** Underlying port, when resolved. */
+ SendPort _port;
+
+ /**
+ * Future of the underlying port, so that we can detect when this port can be
+ * sent on messages.
+ */
+ Future<SendPort> _futurePort;
+
+ /** Pending messages (and reply ports). */
+ List pending;
+
+ _BufferingSendPort(isolateId, this._futurePort)
+ : super(isolateId), _id = _idCount, pending = [] {
+ _idCount++;
+ _futurePort.then((p) {
+ _port = p;
+ for (final item in pending) {
+ p.send(item['message'], item['replyTo']);
+ }
+ pending = null;
+ });
+ }
+
+ _BufferingSendPort.fromPort(isolateId, this._port)
+ : super(isolateId), _id = _idCount {
+ _idCount++;
+ }
+
+ void send(var message, [SendPort replyTo]) {
+ if (_port != null) {
+ _port.send(message, replyTo);
+ } else {
+ pending.add({'message': message, 'replyTo': replyTo});
+ }
+ }
+
+ bool operator ==(var other) =>
+ other is _BufferingSendPort && _id == other._id;
+ int get hashCode => _id;
+}
+
+/** Implementation of a multi-use [ReceivePort] on top of JavaScript. */
+class ReceivePortImpl implements ReceivePort {
+ int _id;
+ Function _callback;
+ static int _nextFreeId = 1;
+
+ ReceivePortImpl()
+ : _id = _nextFreeId++ {
+ _globalState.currentContext.register(_id, this);
+ }
+
+ void receive(void onMessage(var message, SendPort replyTo)) {
+ _callback = onMessage;
+ }
+
+ void close() {
+ _callback = null;
+ _globalState.currentContext.unregister(_id);
+ }
+
+ SendPort toSendPort() {
+ return new _NativeJsSendPort(this, _globalState.currentContext.id);
+ }
+}
+
+/** Wait until all ports in a message are resolved. */
+_waitForPendingPorts(var message, void callback()) {
+ final finder = new _PendingSendPortFinder();
+ finder.traverse(message);
+ Futures.wait(finder.ports).then((_) => callback());
+}
+
+
+/** Visitor that finds all unresolved [SendPort]s in a message. */
+class _PendingSendPortFinder extends _MessageTraverser {
+ List<Future<SendPort>> ports;
+ _PendingSendPortFinder() : super(), ports = [] {
+ _visited = new _JsVisitedMap();
+ }
+
+ visitPrimitive(x) {}
+
+ visitList(List list) {
+ final seen = _visited[list];
+ if (seen != null) return;
+ _visited[list] = true;
+ // TODO(sigmund): replace with the following: (bug #1660)
+ // list.forEach(_dispatch);
+ list.forEach((e) => _dispatch(e));
+ }
+
+ visitMap(Map map) {
+ final seen = _visited[map];
+ if (seen != null) return;
+
+ _visited[map] = true;
+ // TODO(sigmund): replace with the following: (bug #1660)
+ // map.values.forEach(_dispatch);
+ map.values.forEach((e) => _dispatch(e));
+ }
+
+ visitSendPort(SendPort port) {
+ if (port is _BufferingSendPort && port._port == null) {
+ ports.add(port._futurePort);
+ }
+ }
+}
+
+/********************************************************
+ Inserted from lib/isolate/dart2js/messages.dart
+ ********************************************************/
+
+// Defines message visitors, serialization, and deserialization.
+
+/** Serialize [message] (or simulate serialization). */
+_serializeMessage(message) {
+ if (_globalState.needSerialization) {
+ return new _JsSerializer().traverse(message);
+ } else {
+ return new _JsCopier().traverse(message);
+ }
+}
+
+/** Deserialize [message] (or simulate deserialization). */
+_deserializeMessage(message) {
+ if (_globalState.needSerialization) {
+ return new _JsDeserializer().deserialize(message);
+ } else {
+ // Nothing more to do.
+ return message;
+ }
+}
+
+class _JsSerializer extends _Serializer {
+
+ _JsSerializer() : super() { _visited = new _JsVisitedMap(); }
+
+ visitSendPort(SendPort x) {
+ if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
+ if (x is _WorkerSendPort) return visitWorkerSendPort(x);
+ if (x is _BufferingSendPort) return visitBufferingSendPort(x);
+ throw "Illegal underlying port $x";
+ }
+
+ visitNativeJsSendPort(_NativeJsSendPort port) {
+ return ['sendport', _globalState.currentManagerId,
+ port._isolateId, port._receivePort._id];
+ }
+
+ visitWorkerSendPort(_WorkerSendPort port) {
+ return ['sendport', port._workerId, port._isolateId, port._receivePortId];
+ }
+
+ visitBufferingSendPort(_BufferingSendPort port) {
+ if (port._port != null) {
+ return visitSendPort(port._port);
+ } else {
+ // TODO(floitsch): Use real exception (which one?).
+ throw
+ "internal error: must call _waitForPendingPorts to ensure all"
+ " ports are resolved at this point.";
+ }
+ }
+
+}
+
+
+class _JsCopier extends _Copier {
+
+ _JsCopier() : super() { _visited = new _JsVisitedMap(); }
+
+ visitSendPort(SendPort x) {
+ if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
+ if (x is _WorkerSendPort) return visitWorkerSendPort(x);
+ if (x is _BufferingSendPort) return visitBufferingSendPort(x);
+ throw "Illegal underlying port $p";
+ }
+
+ SendPort visitNativeJsSendPort(_NativeJsSendPort port) {
+ return new _NativeJsSendPort(port._receivePort, port._isolateId);
+ }
+
+ SendPort visitWorkerSendPort(_WorkerSendPort port) {
+ return new _WorkerSendPort(
+ port._workerId, port._isolateId, port._receivePortId);
+ }
+
+ SendPort visitBufferingSendPort(_BufferingSendPort port) {
+ if (port._port != null) {
+ return visitSendPort(port._port);
+ } else {
+ // TODO(floitsch): Use real exception (which one?).
+ throw
+ "internal error: must call _waitForPendingPorts to ensure all"
+ " ports are resolved at this point.";
+ }
+ }
+
+}
+
+class _JsDeserializer extends _Deserializer {
+
+ SendPort deserializeSendPort(List x) {
+ int managerId = x[1];
+ int isolateId = x[2];
+ int receivePortId = x[3];
+ // If two isolates are in the same manager, we use NativeJsSendPorts to
+ // deliver messages directly without using postMessage.
+ if (managerId == _globalState.currentManagerId) {
+ var isolate = _globalState.isolates[isolateId];
+ if (isolate == null) return null; // Isolate has been closed.
+ var receivePort = isolate.lookup(receivePortId);
+ return new _NativeJsSendPort(receivePort, isolateId);
+ } else {
+ return new _WorkerSendPort(managerId, isolateId, receivePortId);
+ }
+ }
+
+}
+
+class _JsVisitedMap implements _MessageTraverserVisitedMap {
+ List tagged;
+
+ /** Retrieves any information stored in the native object [object]. */
+ operator[](var object) {
+ return _getAttachedInfo(object);
+ }
+
+ /** Injects some information into the native [object]. */
+ void operator[]=(var object, var info) {
+ tagged.add(object);
+ _setAttachedInfo(object, info);
+ }
+
+ /** Get ready to rumble. */
+ void reset() {
+ assert(tagged == null);
+ tagged = new List();
+ }
+
+ /** Remove all information injected in the native objects. */
+ void cleanup() {
+ for (int i = 0, length = tagged.length; i < length; i++) {
+ _clearAttachedInfo(tagged[i]);
+ }
+ tagged = null;
+ }
+
+ void _clearAttachedInfo(var o) {
+ JS("void", "#['__MessageTraverser__attached_info__'] = #", o, null);
+ }
+
+ void _setAttachedInfo(var o, var info) {
+ JS("void", "#['__MessageTraverser__attached_info__'] = #", o, info);
+ }
+
+ _getAttachedInfo(var o) {
+ return JS("", "#['__MessageTraverser__attached_info__']", o);
+ }
+}
+
+// only visible for testing purposes
+// TODO(sigmund): remove once we can disable privacy for testing (bug #1882)
+class TestingOnly {
+ static copy(x) {
+ return new _JsCopier().traverse(x);
+ }
+
+ // only visible for testing purposes
+ static serialize(x) {
+ _Serializer serializer = new _JsSerializer();
+ _Deserializer deserializer = new _JsDeserializer();
+ return deserializer.deserialize(serializer.traverse(x));
+ }
+}
+
+/********************************************************
+ Inserted from lib/isolate/serialization.dart
+ ********************************************************/
+
+class _MessageTraverserVisitedMap {
+
+ operator[](var object) => null;
+ void operator[]=(var object, var info) { }
+
+ void reset() { }
+ void cleanup() { }
+
+}
+
+/** Abstract visitor for dart objects that can be sent as isolate messages. */
+class _MessageTraverser {
+
+ _MessageTraverserVisitedMap _visited;
+ _MessageTraverser() : _visited = new _MessageTraverserVisitedMap();
+
+ /** Visitor's entry point. */
+ traverse(var x) {
+ if (isPrimitive(x)) return visitPrimitive(x);
+ _visited.reset();
+ var result;
+ try {
+ result = _dispatch(x);
+ } finally {
+ _visited.cleanup();
+ }
+ return result;
+ }
+
+ _dispatch(var x) {
+ if (isPrimitive(x)) return visitPrimitive(x);
+ if (x is List) return visitList(x);
+ if (x is Map) return visitMap(x);
+ if (x is SendPort) return visitSendPort(x);
+ if (x is SendPortSync) return visitSendPortSync(x);
+
+ // Overridable fallback.
+ return visitObject(x);
+ }
+
+ visitPrimitive(x);
+ visitList(List x);
+ visitMap(Map x);
+ visitSendPort(SendPort x);
+ visitSendPortSync(SendPortSync x);
+
+ visitObject(Object x) {
+ // TODO(floitsch): make this a real exception. (which one)?
+ throw "Message serialization: Illegal value $x passed";
+ }
+
+ static bool isPrimitive(x) {
+ return (x == null) || (x is String) || (x is num) || (x is bool);
+ }
+}
+
+
+/** A visitor that recursively copies a message. */
+class _Copier extends _MessageTraverser {
+
+ visitPrimitive(x) => x;
+
+ List visitList(List list) {
+ List copy = _visited[list];
+ if (copy != null) return copy;
+
+ int len = list.length;
+
+ // TODO(floitsch): we loose the generic type of the List.
+ copy = new List(len);
+ _visited[list] = copy;
+ for (int i = 0; i < len; i++) {
+ copy[i] = _dispatch(list[i]);
+ }
+ return copy;
+ }
+
+ Map visitMap(Map map) {
+ Map copy = _visited[map];
+ if (copy != null) return copy;
+
+ // TODO(floitsch): we loose the generic type of the map.
+ copy = new Map();
+ _visited[map] = copy;
+ map.forEach((key, val) {
+ copy[_dispatch(key)] = _dispatch(val);
+ });
+ return copy;
+ }
+
+}
+
+/** Visitor that serializes a message as a JSON array. */
+class _Serializer extends _MessageTraverser {
+ int _nextFreeRefId = 0;
+
+ visitPrimitive(x) => x;
+
+ visitList(List list) {
+ int copyId = _visited[list];
+ if (copyId != null) return ['ref', copyId];
+
+ int id = _nextFreeRefId++;
+ _visited[list] = id;
+ var jsArray = _serializeList(list);
+ // TODO(floitsch): we are losing the generic type.
+ return ['list', id, jsArray];
+ }
+
+ visitMap(Map map) {
+ int copyId = _visited[map];
+ if (copyId != null) return ['ref', copyId];
+
+ int id = _nextFreeRefId++;
+ _visited[map] = id;
+ var keys = _serializeList(map.keys);
+ var values = _serializeList(map.values);
+ // TODO(floitsch): we are losing the generic type.
+ return ['map', id, keys, values];
+ }
+
+ _serializeList(List list) {
+ int len = list.length;
+ var result = new List(len);
+ for (int i = 0; i < len; i++) {
+ result[i] = _dispatch(list[i]);
+ }
+ return result;
+ }
+}
+
+/** Deserializes arrays created with [_Serializer]. */
+class _Deserializer {
+ Map<int, dynamic> _deserialized;
+
+ _Deserializer();
+
+ static bool isPrimitive(x) {
+ return (x == null) || (x is String) || (x is num) || (x is bool);
+ }
+
+ deserialize(x) {
+ if (isPrimitive(x)) return x;
+ // TODO(floitsch): this should be new HashMap<int, var|Dynamic>()
+ _deserialized = new HashMap();
+ return _deserializeHelper(x);
+ }
+
+ _deserializeHelper(x) {
+ if (isPrimitive(x)) return x;
+ assert(x is List);
+ switch (x[0]) {
+ case 'ref': return _deserializeRef(x);
+ case 'list': return _deserializeList(x);
+ case 'map': return _deserializeMap(x);
+ case 'sendport': return deserializeSendPort(x);
+ default: return deserializeObject(x);
+ }
+ }
+
+ _deserializeRef(List x) {
+ int id = x[1];
+ var result = _deserialized[id];
+ assert(result != null);
+ return result;
+ }
+
+ List _deserializeList(List x) {
+ int id = x[1];
+ // We rely on the fact that Dart-lists are directly mapped to Js-arrays.
+ List dartList = x[2];
+ _deserialized[id] = dartList;
+ int len = dartList.length;
+ for (int i = 0; i < len; i++) {
+ dartList[i] = _deserializeHelper(dartList[i]);
+ }
+ return dartList;
+ }
+
+ Map _deserializeMap(List x) {
+ Map result = new Map();
+ int id = x[1];
+ _deserialized[id] = result;
+ List keys = x[2];
+ List values = x[3];
+ int len = keys.length;
+ assert(len == values.length);
+ for (int i = 0; i < len; i++) {
+ var key = _deserializeHelper(keys[i]);
+ var value = _deserializeHelper(values[i]);
+ result[key] = value;
+ }
+ return result;
+ }
+
+ deserializeSendPort(List x);
+
+ deserializeObject(List x) {
+ // TODO(floitsch): Use real exception (which one?).
+ throw "Unexpected serialized object";
+ }
+}
+
+/********************************************************
+ Inserted from lib/isolate/dart2js/timer_provider.dart
+ ********************************************************/
+
+// We don't want to import the DOM library just because of window.setTimeout,
+// so we reconstruct the Window class here. The only conflict that could happen
+// with the other DOMWindow class would be because of subclasses.
+// Currently, none of the two Dart classes have subclasses.
+typedef void _TimeoutHandler();
+
+// @Native("*DOMWindow");
+class _Window {
+ int setTimeout(_TimeoutHandler handler, int timeout) {
+ return JS('int',
+ '#.setTimeout(#, #)',
+ this,
+ convertDartClosureToJS(handler, 0),
+ timeout);
+ }
+
+ int setInterval(_TimeoutHandler handler, int timeout) {
+ return JS('int',
+ '#.setInterval(#, #)',
+ this,
+ convertDartClosureToJS(handler, 0),
+ timeout);
+ }
+
+ void clearTimeout(int handle) {
+ JS('void', '#.clearTimeout(#)', this, handle);
+ }
+
+ void clearInterval(int handle) {
+ JS('void', '#.clearInterval(#)', this, handle);
+ }
+}
+
+_Window get _window =>
+ JS('bool', 'typeof window != "undefined"') ? JS('_Window', 'window') : null;
+
+bool hasWindow() => _window != null;
+
+class TimerImpl implements Timer {
+ final bool _once;
+ int _handle;
+
+ TimerImpl(int milliseconds, void callback(Timer timer))
+ : _once = true {
+ _handle = _window.setTimeout(() => callback(this), milliseconds);
+ }
+
+ TimerImpl.repeating(int milliseconds, void callback(Timer timer))
+ : _once = false {
+ _handle = _window.setInterval(() => callback(this), milliseconds);
+ }
+
+ void cancel() {
+ if (_once) {
+ _window.clearTimeout(_handle);
+ } else {
+ _window.clearInterval(_handle);
+ }
+ }
+}
diff --git a/sdk/lib/_internal/compiler/implementation/lib/isolate_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/isolate_patch.dart
index c6356bf..3f43fa3 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/isolate_patch.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/isolate_patch.dart
@@ -4,1287 +4,45 @@
// Patch file for the dart:isolate library.
-import 'dart:uri';
-
-/**
- * Called by the compiler to support switching
- * between isolates when we get a callback from the DOM.
- */
-void _callInIsolate(_IsolateContext isolate, Function function) {
- isolate.eval(function);
- _globalState.topEventLoop.run();
-}
-
-/**
- * Called by the compiler to fetch the current isolate context.
- */
-_IsolateContext _currentIsolate() => _globalState.currentContext;
-
-/********************************************************
- Inserted from lib/isolate/dart2js/compiler_hooks.dart
- ********************************************************/
-
-/**
- * Wrapper that takes the dart entry point and runs it within an isolate. The
- * dart2js compiler will inject a call of the form
- * [: startRootIsolate(main); :] when it determines that this wrapping
- * is needed. For single-isolate applications (e.g. hello world), this
- * call is not emitted.
- */
-void startRootIsolate(entry) {
- _globalState = new _Manager();
-
- // Don't start the main loop again, if we are in a worker.
- if (_globalState.isWorker) return;
- final rootContext = new _IsolateContext();
- _globalState.rootContext = rootContext;
- _fillStatics(rootContext);
-
- // BUG(5151491): Setting currentContext should not be necessary, but
- // because closures passed to the DOM as event handlers do not bind their
- // isolate automatically we try to give them a reasonable context to live in
- // by having a "default" isolate (the first one created).
- _globalState.currentContext = rootContext;
-
- if (_window != null) {
- rootContext.eval(() => _setTimerFactoryClosure( _timerFactory));
- }
- rootContext.eval(entry);
- _globalState.topEventLoop.run();
-}
-
-/********************************************************
- Inserted from lib/isolate/dart2js/isolateimpl.dart
- ********************************************************/
-
-/**
- * Concepts used here:
- *
- * "manager" - A manager contains one or more isolates, schedules their
- * execution, and performs other plumbing on their behalf. The isolate
- * present at the creation of the manager is designated as its "root isolate".
- * A manager may, for example, be implemented on a web Worker.
- *
- * [_Manager] - State present within a manager (exactly once, as a global).
- *
- * [_ManagerStub] - A handle held within one manager that allows interaction
- * with another manager. A target manager may be addressed by zero or more
- * [_ManagerStub]s.
- *
- */
-
-/**
- * A native object that is shared across isolates. This object is visible to all
- * isolates running under the same manager (either UI or background web worker).
- *
- * This is code that is intended to 'escape' the isolate boundaries in order to
- * implement the semantics of isolates in JavaScript. Without this we would have
- * been forced to implement more code (including the top-level event loop) in
- * JavaScript itself.
- */
-// TODO(eub, sigmund): move the "manager" to be entirely in JS.
-// Running any Dart code outside the context of an isolate gives it
-// the change to break the isolate abstraction.
-_Manager get _globalState => JS("_Manager", r"$globalState");
-set _globalState(_Manager val) {
- JS("void", r"$globalState = #", val);
-}
-
-void _fillStatics(context) {
- JS("void", r"$globals = #.isolateStatics", context);
- JS("void", r"$static_init()");
-}
-
-ReceivePort _lazyPort;
patch ReceivePort get port {
- if (_lazyPort == null) {
- _lazyPort = new ReceivePort();
+ if (lazyPort == null) {
+ lazyPort = new ReceivePort();
}
- return _lazyPort;
+ return lazyPort;
}
patch SendPort spawnFunction(void topLevelFunction()) {
- final name = _IsolateNatives._getJSFunctionName(topLevelFunction);
- if (name == null) {
- throw new UnsupportedError(
- "only top-level functions can be spawned.");
- }
- return _IsolateNatives._spawn(name, null, false);
+ return IsolateNatives.spawnFunction(topLevelFunction);
}
patch SendPort spawnUri(String uri) {
- return _IsolateNatives._spawn(null, uri, false);
+ return IsolateNatives.spawn(null, uri, false);
}
-/** State associated with the current manager. See [globalState]. */
-// TODO(sigmund): split in multiple classes: global, thread, main-worker states?
-class _Manager {
-
- /** Next available isolate id within this [_Manager]. */
- int nextIsolateId = 0;
-
- /** id assigned to this [_Manager]. */
- int currentManagerId = 0;
-
- /**
- * Next available manager id. Only used by the main manager to assign a unique
- * id to each manager created by it.
- */
- int nextManagerId = 1;
-
- /** Context for the currently running [Isolate]. */
- _IsolateContext currentContext = null;
-
- /** Context for the root [Isolate] that first run in this [_Manager]. */
- _IsolateContext rootContext = null;
-
- /** The top-level event loop. */
- _EventLoop topEventLoop;
-
- /** Whether this program is running from the command line. */
- bool fromCommandLine;
-
- /** Whether this [_Manager] is running as a web worker. */
- bool isWorker;
-
- /** Whether we support spawning web workers. */
- bool supportsWorkers;
-
- /**
- * Whether to use web workers when implementing isolates. Set to false for
- * debugging/testing.
- */
- bool get useWorkers => supportsWorkers;
-
- /**
- * Whether to use the web-worker JSON-based message serialization protocol. By
- * default this is only used with web workers. For debugging, you can force
- * using this protocol by changing this field value to [true].
- */
- bool get needSerialization => useWorkers;
-
- /**
- * Registry of isolates. Isolates must be registered if, and only if, receive
- * ports are alive. Normally no open receive-ports means that the isolate is
- * dead, but DOM callbacks could resurrect it.
- */
- Map<int, _IsolateContext> isolates;
-
- /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */
- _ManagerStub mainManager;
-
- /** Registry of active [_ManagerStub]s. Only used in the main [_Manager]. */
- Map<int, _ManagerStub> managers;
-
- _Manager() {
- _nativeDetectEnvironment();
- topEventLoop = new _EventLoop();
- isolates = new Map<int, _IsolateContext>();
- managers = new Map<int, _ManagerStub>();
- if (isWorker) { // "if we are not the main manager ourself" is the intent.
- mainManager = new _MainManagerStub();
- _nativeInitWorkerMessageHandler();
- }
- }
-
- void _nativeDetectEnvironment() {
- isWorker = JS("bool", r"$isWorker");
- supportsWorkers = JS("bool", r"$supportsWorkers");
- fromCommandLine = JS("bool", r"typeof(window) == 'undefined'");
- }
-
- void _nativeInitWorkerMessageHandler() {
- JS("void", r"""
-$globalThis.onmessage = function (e) {
- _IsolateNatives._processWorkerMessage(this.mainManager, e);
-}""");
- }
- /*: TODO: check that _processWorkerMessage is not discarded while treeshaking.
- """ {
- _IsolateNatives._processWorkerMessage(null, null);
- }
- */
-
-
- /** Close the worker running this code if all isolates are done. */
- void maybeCloseWorker() {
- if (isolates.isEmpty) {
- mainManager.postMessage(_serializeMessage({'command': 'close'}));
- }
- }
-}
-
-/** Context information tracked for each isolate. */
-class _IsolateContext {
- /** Current isolate id. */
- int id;
-
- /** Registry of receive ports currently active on this isolate. */
- Map<int, ReceivePort> ports;
-
- /** Holds isolate globals (statics and top-level properties). */
- var isolateStatics; // native object containing all globals of an isolate.
-
- _IsolateContext() {
- id = _globalState.nextIsolateId++;
- ports = new Map<int, ReceivePort>();
- initGlobals();
- }
-
- // these are filled lazily the first time the isolate starts running.
- void initGlobals() { JS("void", r'$initGlobals(#)', this); }
-
- /**
- * Run [code] in the context of the isolate represented by [this]. Note this
- * is called from JavaScript (see $wrap_call in corejs.dart).
- */
- dynamic eval(Function code) {
- var old = _globalState.currentContext;
- _globalState.currentContext = this;
- this._setGlobals();
- var result = null;
- try {
- result = code();
- } finally {
- _globalState.currentContext = old;
- if (old != null) old._setGlobals();
- }
- return result;
- }
-
- void _setGlobals() { JS("void", r'$setGlobals(#)', this); }
-
- /** Lookup a port registered for this isolate. */
- ReceivePort lookup(int portId) => ports[portId];
-
- /** Register a port on this isolate. */
- void register(int portId, ReceivePort port) {
- if (ports.containsKey(portId)) {
- throw new Exception("Registry: ports must be registered only once.");
- }
- ports[portId] = port;
- _globalState.isolates[id] = this; // indicate this isolate is active
- }
-
- /** Unregister a port on this isolate. */
- void unregister(int portId) {
- ports.remove(portId);
- if (ports.isEmpty) {
- _globalState.isolates.remove(id); // indicate this isolate is not active
- }
- }
-}
-
-/** Represent the event loop on a javascript thread (DOM or worker). */
-class _EventLoop {
- Queue<_IsolateEvent> events;
-
- _EventLoop() : events = new Queue<_IsolateEvent>();
-
- void enqueue(isolate, fn, msg) {
- events.addLast(new _IsolateEvent(isolate, fn, msg));
- }
-
- _IsolateEvent dequeue() {
- if (events.isEmpty) return null;
- return events.removeFirst();
- }
-
- /** Process a single event, if any. */
- bool runIteration() {
- final event = dequeue();
- if (event == null) {
- if (_globalState.isWorker) {
- _globalState.maybeCloseWorker();
- } else if (_globalState.rootContext != null &&
- _globalState.isolates.containsKey(
- _globalState.rootContext.id) &&
- _globalState.fromCommandLine &&
- _globalState.rootContext.ports.isEmpty) {
- // We want to reach here only on the main [_Manager] and only
- // on the command-line. In the browser the isolate might
- // still be alive due to DOM callbacks, but the presumption is
- // that on the command-line, no future events can be injected
- // into the event queue once it's empty. Node has setTimeout
- // so this presumption is incorrect there. We think(?) that
- // in d8 this assumption is valid.
- throw new Exception("Program exited with open ReceivePorts.");
- }
- return false;
- }
- event.process();
- return true;
- }
-
- /**
- * Runs multiple iterations of the run-loop. If possible, each iteration is
- * run asynchronously.
- */
- void _runHelper() {
- // [_window] is defined in timer_provider.dart.
- if (_window != null) {
- // Run each iteration from the browser's top event loop.
- void next() {
- if (!runIteration()) return;
- _window.setTimeout(next, 0);
- }
- next();
- } else {
- // Run synchronously until no more iterations are available.
- while (runIteration()) {}
- }
- }
-
- /**
- * Call [_runHelper] but ensure that worker exceptions are propragated. Note
- * this is called from JavaScript (see $wrap_call in corejs.dart).
- */
- void run() {
- if (!_globalState.isWorker) {
- _runHelper();
- } else {
- try {
- _runHelper();
- } catch (e, trace) {
- _globalState.mainManager.postMessage(_serializeMessage(
- {'command': 'error', 'msg': '$e\n$trace' }));
- }
- }
- }
-}
-
-/** An event in the top-level event queue. */
-class _IsolateEvent {
- _IsolateContext isolate;
- Function fn;
- String message;
-
- _IsolateEvent(this.isolate, this.fn, this.message);
-
- void process() {
- isolate.eval(fn);
- }
-}
-
-/** An interface for a stub used to interact with a manager. */
-abstract class _ManagerStub {
- get id;
- void set id(int i);
- void set onmessage(Function f);
- void postMessage(msg);
- void terminate();
-}
-
-/** A stub for interacting with the main manager. */
-class _MainManagerStub implements _ManagerStub {
- get id => 0;
- void set id(int i) { throw new UnimplementedError(); }
- void set onmessage(f) {
- throw new Exception("onmessage should not be set on MainManagerStub");
- }
- void postMessage(msg) { JS("void", r"$globalThis.postMessage(#)", msg); }
- void terminate() {} // Nothing useful to do here.
-}
-
-/**
- * A stub for interacting with a manager built on a web worker. This
- * definition uses a 'hidden' type (* prefix on the native name) to
- * enforce that the type is defined dynamically only when web workers
- * are actually available.
- */
-class _WorkerStub implements _ManagerStub native "*Worker" {
- get id => JS("var", "#.id", this);
- void set id(i) { JS("void", "#.id = #", this, i); }
- void set onmessage(f) { JS("void", "#.onmessage = #", this, f); }
- void postMessage(msg) => JS("void", "#.postMessage(#)", this, msg);
- // terminate() is implemented by Worker.
- void terminate();
-}
-
-const String _SPAWNED_SIGNAL = "spawned";
-
-class _IsolateNatives {
-
- /**
- * The src url for the script tag that loaded this code. Used to create
- * JavaScript workers.
- */
- static String get _thisScript => JS("String", r"$thisScriptUrl");
-
- /** Starts a new worker with the given URL. */
- static _WorkerStub _newWorker(url) => JS("_WorkerStub", r"new Worker(#)", url);
-
- /**
- * Assume that [e] is a browser message event and extract its message data.
- * We don't import the dom explicitly so, when workers are disabled, this
- * library can also run on top of nodejs.
- */
- //static _getEventData(e) => JS("Object", "#.data", e);
- static _getEventData(e) => JS("", "#.data", e);
-
- /**
- * Process messages on a worker, either to control the worker instance or to
- * pass messages along to the isolate running in the worker.
- */
- static void _processWorkerMessage(sender, e) {
- var msg = _deserializeMessage(_getEventData(e));
- switch (msg['command']) {
- case 'start':
- _globalState.currentManagerId = msg['id'];
- Function entryPoint = _getJSFunctionFromName(msg['functionName']);
- var replyTo = _deserializeMessage(msg['replyTo']);
- _globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
- _startIsolate(entryPoint, replyTo);
- }, 'worker-start');
- _globalState.topEventLoop.run();
- break;
- case 'spawn-worker':
- _spawnWorker(msg['functionName'], msg['uri'], msg['replyPort']);
- break;
- case 'message':
- msg['port'].send(msg['msg'], msg['replyTo']);
- _globalState.topEventLoop.run();
- break;
- case 'close':
- _log("Closing Worker");
- _globalState.managers.remove(sender.id);
- sender.terminate();
- _globalState.topEventLoop.run();
- break;
- case 'log':
- _log(msg['msg']);
- break;
- case 'print':
- if (_globalState.isWorker) {
- _globalState.mainManager.postMessage(
- _serializeMessage({'command': 'print', 'msg': msg}));
- } else {
- print(msg['msg']);
- }
- break;
- case 'error':
- throw msg['msg'];
- }
- }
-
- /** Log a message, forwarding to the main [_Manager] if appropriate. */
- static _log(msg) {
- if (_globalState.isWorker) {
- _globalState.mainManager.postMessage(
- _serializeMessage({'command': 'log', 'msg': msg }));
- } else {
- try {
- _consoleLog(msg);
- } catch (e, trace) {
- throw new Exception(trace);
- }
- }
- }
-
- static void _consoleLog(msg) {
- JS("void", r"$globalThis.console.log(#)", msg);
- }
-
- /**
- * Extract the constructor of runnable, so it can be allocated in another
- * isolate.
- */
- static dynamic _getJSConstructor(Isolate runnable) {
- return JS("Object", "#.constructor", runnable);
- }
-
- /** Extract the constructor name of a runnable */
- // TODO(sigmund): find a browser-generic way to support this.
- // TODO(floitsch): is this function still used? If yes, should we use
- // Primitives.objectTypeName instead?
- static dynamic _getJSConstructorName(Isolate runnable) {
- return JS("Object", "#.constructor.name", runnable);
- }
-
- /** Find a constructor given its name. */
- static dynamic _getJSConstructorFromName(String factoryName) {
- return JS("Object", r"$globalThis[#]", factoryName);
- }
-
- static dynamic _getJSFunctionFromName(String functionName) {
- return JS("Object", r"$globalThis[#]", functionName);
- }
-
- /**
- * Get a string name for the function, if possible. The result for
- * anonymous functions is browser-dependent -- it may be "" or "anonymous"
- * but you should probably not count on this.
- */
- static String _getJSFunctionName(Function f) {
- return JS("Object", r"(#.$name || #)", f, null);
- }
-
- /** Create a new JavaScript object instance given its constructor. */
- static dynamic _allocate(var ctor) {
- return JS("Object", "new #()", ctor);
- }
-
- // TODO(sigmund): clean up above, after we make the new API the default:
-
- static _spawn(String functionName, String uri, bool isLight) {
- Completer<SendPort> completer = new Completer<SendPort>();
- ReceivePort port = new ReceivePort();
- port.receive((msg, SendPort replyPort) {
- port.close();
- assert(msg == _SPAWNED_SIGNAL);
- completer.complete(replyPort);
- });
-
- SendPort signalReply = port.toSendPort();
-
- if (_globalState.useWorkers && !isLight) {
- _startWorker(functionName, uri, signalReply);
- } else {
- _startNonWorker(functionName, uri, signalReply);
- }
- return new _BufferingSendPort(
- _globalState.currentContext.id, completer.future);
- }
-
- static SendPort _startWorker(
- String functionName, String uri, SendPort replyPort) {
- if (_globalState.isWorker) {
- _globalState.mainManager.postMessage(_serializeMessage({
- 'command': 'spawn-worker',
- 'functionName': functionName,
- 'uri': uri,
- 'replyPort': replyPort}));
- } else {
- _spawnWorker(functionName, uri, replyPort);
- }
- }
-
- static SendPort _startNonWorker(
- String functionName, String uri, SendPort replyPort) {
- // TODO(eub): support IE9 using an iframe -- Dart issue 1702.
- if (uri != null) throw new UnsupportedError(
- "Currently spawnUri is not supported without web workers.");
- _globalState.topEventLoop.enqueue(new _IsolateContext(), function() {
- final func = _getJSFunctionFromName(functionName);
- _startIsolate(func, replyPort);
- }, 'nonworker start');
- }
-
- static void _startIsolate(Function topLevel, SendPort replyTo) {
- _fillStatics(_globalState.currentContext);
- _lazyPort = new ReceivePort();
- replyTo.send(_SPAWNED_SIGNAL, port.toSendPort());
-
- if (_window != null) {
- _globalState.currentContext.eval(
- () => _setTimerFactoryClosure(_timerFactory));
- }
-
- topLevel();
- }
-
- /**
- * Spawns an isolate in a worker. [factoryName] is the Javascript constructor
- * name for the isolate entry point class.
- */
- static void _spawnWorker(functionName, uri, replyPort) {
- if (functionName == null) functionName = 'main';
- if (uri == null) uri = _thisScript;
- if (!(new Uri.fromString(uri).isAbsolute())) {
- // The constructor of dom workers requires an absolute URL. If we use a
- // relative path we will get a DOM exception.
- String prefix = _thisScript.substring(0, _thisScript.lastIndexOf('/'));
- uri = "$prefix/$uri";
- }
- final worker = _newWorker(uri);
- worker.onmessage = (e) { _processWorkerMessage(worker, e); };
- var workerId = _globalState.nextManagerId++;
- // We also store the id on the worker itself so that we can unregister it.
- worker.id = workerId;
- _globalState.managers[workerId] = worker;
- worker.postMessage(_serializeMessage({
- 'command': 'start',
- 'id': workerId,
- // Note: we serialize replyPort twice because the child worker needs to
- // first deserialize the worker id, before it can correctly deserialize
- // the port (port deserialization is sensitive to what is the current
- // workerId).
- 'replyTo': _serializeMessage(replyPort),
- 'functionName': functionName }));
- }
-}
-
-/********************************************************
- Inserted from lib/isolate/dart2js/ports.dart
- ********************************************************/
-
-/** Common functionality to all send ports. */
-class _BaseSendPort implements SendPort {
- /** Id for the destination isolate. */
- final int _isolateId;
-
- const _BaseSendPort(this._isolateId);
-
- void _checkReplyTo(SendPort replyTo) {
- if (replyTo != null
- && replyTo is! _NativeJsSendPort
- && replyTo is! _WorkerSendPort
- && replyTo is! _BufferingSendPort) {
- throw new Exception("SendPort.send: Illegal replyTo port type");
- }
- }
-
- Future call(var message) {
- final completer = new Completer();
- final port = new _ReceivePortImpl();
- send(message, port.toSendPort());
- port.receive((value, ignoreReplyTo) {
- port.close();
- if (value is Exception) {
- completer.completeException(value);
- } else {
- completer.complete(value);
- }
- });
- return completer.future;
- }
-
- void send(var message, [SendPort replyTo]);
- bool operator ==(var other);
- int get hashCode;
-}
-
-/** A send port that delivers messages in-memory via native JavaScript calls. */
-class _NativeJsSendPort extends _BaseSendPort implements SendPort {
- final _ReceivePortImpl _receivePort;
-
- const _NativeJsSendPort(this._receivePort, int isolateId) : super(isolateId);
-
- void send(var message, [SendPort replyTo = null]) {
- _waitForPendingPorts([message, replyTo], () {
- _checkReplyTo(replyTo);
- // Check that the isolate still runs and the port is still open
- final isolate = _globalState.isolates[_isolateId];
- if (isolate == null) return;
- if (_receivePort._callback == null) return;
-
- // We force serialization/deserialization as a simple way to ensure
- // isolate communication restrictions are respected between isolates that
- // live in the same worker. [_NativeJsSendPort] delivers both messages
- // from the same worker and messages from other workers. In particular,
- // messages sent from a worker via a [_WorkerSendPort] are received at
- // [_processWorkerMessage] and forwarded to a native port. In such cases,
- // here we'll see [_globalState.currentContext == null].
- final shouldSerialize = _globalState.currentContext != null
- && _globalState.currentContext.id != _isolateId;
- var msg = message;
- var reply = replyTo;
- if (shouldSerialize) {
- msg = _serializeMessage(msg);
- reply = _serializeMessage(reply);
- }
- _globalState.topEventLoop.enqueue(isolate, () {
- if (_receivePort._callback != null) {
- if (shouldSerialize) {
- msg = _deserializeMessage(msg);
- reply = _deserializeMessage(reply);
- }
- _receivePort._callback(msg, reply);
- }
- }, 'receive $message');
- });
- }
-
- bool operator ==(var other) => (other is _NativeJsSendPort) &&
- (_receivePort == other._receivePort);
-
- int get hashCode => _receivePort._id;
-}
-
-/** A send port that delivers messages via worker.postMessage. */
-// TODO(eub): abstract this for iframes.
-class _WorkerSendPort extends _BaseSendPort implements SendPort {
- final int _workerId;
- final int _receivePortId;
-
- const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId)
- : super(isolateId);
-
- void send(var message, [SendPort replyTo = null]) {
- _waitForPendingPorts([message, replyTo], () {
- _checkReplyTo(replyTo);
- final workerMessage = _serializeMessage({
- 'command': 'message',
- 'port': this,
- 'msg': message,
- 'replyTo': replyTo});
-
- if (_globalState.isWorker) {
- // communication from one worker to another go through the main worker:
- _globalState.mainManager.postMessage(workerMessage);
- } else {
- _globalState.managers[_workerId].postMessage(workerMessage);
- }
- });
- }
-
- bool operator ==(var other) {
- return (other is _WorkerSendPort) &&
- (_workerId == other._workerId) &&
- (_isolateId == other._isolateId) &&
- (_receivePortId == other._receivePortId);
- }
-
- int get hashCode {
- // TODO(sigmund): use a standard hash when we get one available in corelib.
- return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId;
- }
-}
-
-/** A port that buffers messages until an underlying port gets resolved. */
-class _BufferingSendPort extends _BaseSendPort implements SendPort {
- /** Internal counter to assign unique ids to each port. */
- static int _idCount = 0;
-
- /** For implementing equals and hashcode. */
- final int _id;
-
- /** Underlying port, when resolved. */
- SendPort _port;
-
- /**
- * Future of the underlying port, so that we can detect when this port can be
- * sent on messages.
- */
- Future<SendPort> _futurePort;
-
- /** Pending messages (and reply ports). */
- List pending;
-
- _BufferingSendPort(isolateId, this._futurePort)
- : super(isolateId), _id = _idCount, pending = [] {
- _idCount++;
- _futurePort.then((p) {
- _port = p;
- for (final item in pending) {
- p.send(item['message'], item['replyTo']);
- }
- pending = null;
- });
- }
-
- _BufferingSendPort.fromPort(isolateId, this._port)
- : super(isolateId), _id = _idCount {
- _idCount++;
- }
-
- void send(var message, [SendPort replyTo]) {
- if (_port != null) {
- _port.send(message, replyTo);
- } else {
- pending.add({'message': message, 'replyTo': replyTo});
- }
- }
-
- bool operator ==(var other) =>
- other is _BufferingSendPort && _id == other._id;
- int get hashCode => _id;
-}
/** Default factory for receive ports. */
patch class ReceivePort {
patch factory ReceivePort() {
- return new _ReceivePortImpl();
- }
-
-}
-
-/** Implementation of a multi-use [ReceivePort] on top of JavaScript. */
-class _ReceivePortImpl implements ReceivePort {
- int _id;
- Function _callback;
- static int _nextFreeId = 1;
-
- _ReceivePortImpl()
- : _id = _nextFreeId++ {
- _globalState.currentContext.register(_id, this);
- }
-
- void receive(void onMessage(var message, SendPort replyTo)) {
- _callback = onMessage;
- }
-
- void close() {
- _callback = null;
- _globalState.currentContext.unregister(_id);
- }
-
- SendPort toSendPort() {
- return new _NativeJsSendPort(this, _globalState.currentContext.id);
+ return new ReceivePortImpl();
}
}
-/** Wait until all ports in a message are resolved. */
-_waitForPendingPorts(var message, void callback()) {
- final finder = new _PendingSendPortFinder();
- finder.traverse(message);
- Futures.wait(finder.ports).then((_) => callback());
-}
-
-
-/** Visitor that finds all unresolved [SendPort]s in a message. */
-class _PendingSendPortFinder extends _MessageTraverser {
- List<Future<SendPort>> ports;
- _PendingSendPortFinder() : super(), ports = [] {
- _visited = new _JsVisitedMap();
- }
-
- visitPrimitive(x) {}
-
- visitList(List list) {
- final seen = _visited[list];
- if (seen != null) return;
- _visited[list] = true;
- // TODO(sigmund): replace with the following: (bug #1660)
- // list.forEach(_dispatch);
- list.forEach((e) => _dispatch(e));
- }
-
- visitMap(Map map) {
- final seen = _visited[map];
- if (seen != null) return;
-
- _visited[map] = true;
- // TODO(sigmund): replace with the following: (bug #1660)
- // map.values.forEach(_dispatch);
- map.values.forEach((e) => _dispatch(e));
- }
-
- visitSendPort(SendPort port) {
- if (port is _BufferingSendPort && port._port == null) {
- ports.add(port._futurePort);
+patch class Timer {
+ patch factory Timer(int milliseconds, void callback(Timer timer)) {
+ if (!hasWindow()) {
+ throw new UnsupportedError("Timer interface not supported.");
}
- }
-}
-
-/********************************************************
- Inserted from lib/isolate/dart2js/messages.dart
- ********************************************************/
-
-// Defines message visitors, serialization, and deserialization.
-
-/** Serialize [message] (or simulate serialization). */
-_serializeMessage(message) {
- if (_globalState.needSerialization) {
- return new _JsSerializer().traverse(message);
- } else {
- return new _JsCopier().traverse(message);
- }
-}
-
-/** Deserialize [message] (or simulate deserialization). */
-_deserializeMessage(message) {
- if (_globalState.needSerialization) {
- return new _JsDeserializer().deserialize(message);
- } else {
- // Nothing more to do.
- return message;
- }
-}
-
-class _JsSerializer extends _Serializer {
-
- _JsSerializer() : super() { _visited = new _JsVisitedMap(); }
-
- visitSendPort(SendPort x) {
- if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
- if (x is _WorkerSendPort) return visitWorkerSendPort(x);
- if (x is _BufferingSendPort) return visitBufferingSendPort(x);
- throw "Illegal underlying port $x";
+ return new TimerImpl(milliseconds, callback);
}
- visitNativeJsSendPort(_NativeJsSendPort port) {
- return ['sendport', _globalState.currentManagerId,
- port._isolateId, port._receivePort._id];
- }
-
- visitWorkerSendPort(_WorkerSendPort port) {
- return ['sendport', port._workerId, port._isolateId, port._receivePortId];
- }
-
- visitBufferingSendPort(_BufferingSendPort port) {
- if (port._port != null) {
- return visitSendPort(port._port);
- } else {
- // TODO(floitsch): Use real exception (which one?).
- throw
- "internal error: must call _waitForPendingPorts to ensure all"
- " ports are resolved at this point.";
+ /**
+ * Creates a new repeating timer. The [callback] is invoked every
+ * [milliseconds] millisecond until cancelled.
+ */
+ patch factory Timer.repeating(int milliseconds, void callback(Timer timer)) {
+ if (!hasWindow()) {
+ throw new UnsupportedError("Timer interface not supported.");
}
- }
-
-}
-
-
-class _JsCopier extends _Copier {
-
- _JsCopier() : super() { _visited = new _JsVisitedMap(); }
-
- visitSendPort(SendPort x) {
- if (x is _NativeJsSendPort) return visitNativeJsSendPort(x);
- if (x is _WorkerSendPort) return visitWorkerSendPort(x);
- if (x is _BufferingSendPort) return visitBufferingSendPort(x);
- throw "Illegal underlying port $p";
- }
-
- SendPort visitNativeJsSendPort(_NativeJsSendPort port) {
- return new _NativeJsSendPort(port._receivePort, port._isolateId);
- }
-
- SendPort visitWorkerSendPort(_WorkerSendPort port) {
- return new _WorkerSendPort(
- port._workerId, port._isolateId, port._receivePortId);
- }
-
- SendPort visitBufferingSendPort(_BufferingSendPort port) {
- if (port._port != null) {
- return visitSendPort(port._port);
- } else {
- // TODO(floitsch): Use real exception (which one?).
- throw
- "internal error: must call _waitForPendingPorts to ensure all"
- " ports are resolved at this point.";
- }
- }
-
-}
-
-class _JsDeserializer extends _Deserializer {
-
- SendPort deserializeSendPort(List x) {
- int managerId = x[1];
- int isolateId = x[2];
- int receivePortId = x[3];
- // If two isolates are in the same manager, we use NativeJsSendPorts to
- // deliver messages directly without using postMessage.
- if (managerId == _globalState.currentManagerId) {
- var isolate = _globalState.isolates[isolateId];
- if (isolate == null) return null; // Isolate has been closed.
- var receivePort = isolate.lookup(receivePortId);
- return new _NativeJsSendPort(receivePort, isolateId);
- } else {
- return new _WorkerSendPort(managerId, isolateId, receivePortId);
- }
- }
-
-}
-
-class _JsVisitedMap implements _MessageTraverserVisitedMap {
- List tagged;
-
- /** Retrieves any information stored in the native object [object]. */
- operator[](var object) {
- return _getAttachedInfo(object);
- }
-
- /** Injects some information into the native [object]. */
- void operator[]=(var object, var info) {
- tagged.add(object);
- _setAttachedInfo(object, info);
- }
-
- /** Get ready to rumble. */
- void reset() {
- assert(tagged == null);
- tagged = new List();
- }
-
- /** Remove all information injected in the native objects. */
- void cleanup() {
- for (int i = 0, length = tagged.length; i < length; i++) {
- _clearAttachedInfo(tagged[i]);
- }
- tagged = null;
- }
-
- void _clearAttachedInfo(var o) {
- JS("void", "#['__MessageTraverser__attached_info__'] = #", o, null);
- }
-
- void _setAttachedInfo(var o, var info) {
- JS("void", "#['__MessageTraverser__attached_info__'] = #", o, info);
- }
-
- _getAttachedInfo(var o) {
- return JS("", "#['__MessageTraverser__attached_info__']", o);
+ return new TimerImpl.repeating(milliseconds, callback);
}
}
-
-// only visible for testing purposes
-// TODO(sigmund): remove once we can disable privacy for testing (bug #1882)
-class TestingOnly {
- static copy(x) {
- return new _JsCopier().traverse(x);
- }
-
- // only visible for testing purposes
- static serialize(x) {
- _Serializer serializer = new _JsSerializer();
- _Deserializer deserializer = new _JsDeserializer();
- return deserializer.deserialize(serializer.traverse(x));
- }
-}
-
-/********************************************************
- Inserted from lib/isolate/serialization.dart
- ********************************************************/
-
-class _MessageTraverserVisitedMap {
-
- operator[](var object) => null;
- void operator[]=(var object, var info) { }
-
- void reset() { }
- void cleanup() { }
-
-}
-
-/** Abstract visitor for dart objects that can be sent as isolate messages. */
-class _MessageTraverser {
-
- _MessageTraverserVisitedMap _visited;
- _MessageTraverser() : _visited = new _MessageTraverserVisitedMap();
-
- /** Visitor's entry point. */
- traverse(var x) {
- if (isPrimitive(x)) return visitPrimitive(x);
- _visited.reset();
- var result;
- try {
- result = _dispatch(x);
- } finally {
- _visited.cleanup();
- }
- return result;
- }
-
- _dispatch(var x) {
- if (isPrimitive(x)) return visitPrimitive(x);
- if (x is List) return visitList(x);
- if (x is Map) return visitMap(x);
- if (x is SendPort) return visitSendPort(x);
- if (x is SendPortSync) return visitSendPortSync(x);
-
- // Overridable fallback.
- return visitObject(x);
- }
-
- visitPrimitive(x);
- visitList(List x);
- visitMap(Map x);
- visitSendPort(SendPort x);
- visitSendPortSync(SendPortSync x);
-
- visitObject(Object x) {
- // TODO(floitsch): make this a real exception. (which one)?
- throw "Message serialization: Illegal value $x passed";
- }
-
- static bool isPrimitive(x) {
- return (x == null) || (x is String) || (x is num) || (x is bool);
- }
-}
-
-
-/** A visitor that recursively copies a message. */
-class _Copier extends _MessageTraverser {
-
- visitPrimitive(x) => x;
-
- List visitList(List list) {
- List copy = _visited[list];
- if (copy != null) return copy;
-
- int len = list.length;
-
- // TODO(floitsch): we loose the generic type of the List.
- copy = new List(len);
- _visited[list] = copy;
- for (int i = 0; i < len; i++) {
- copy[i] = _dispatch(list[i]);
- }
- return copy;
- }
-
- Map visitMap(Map map) {
- Map copy = _visited[map];
- if (copy != null) return copy;
-
- // TODO(floitsch): we loose the generic type of the map.
- copy = new Map();
- _visited[map] = copy;
- map.forEach((key, val) {
- copy[_dispatch(key)] = _dispatch(val);
- });
- return copy;
- }
-
-}
-
-/** Visitor that serializes a message as a JSON array. */
-class _Serializer extends _MessageTraverser {
- int _nextFreeRefId = 0;
-
- visitPrimitive(x) => x;
-
- visitList(List list) {
- int copyId = _visited[list];
- if (copyId != null) return ['ref', copyId];
-
- int id = _nextFreeRefId++;
- _visited[list] = id;
- var jsArray = _serializeList(list);
- // TODO(floitsch): we are losing the generic type.
- return ['list', id, jsArray];
- }
-
- visitMap(Map map) {
- int copyId = _visited[map];
- if (copyId != null) return ['ref', copyId];
-
- int id = _nextFreeRefId++;
- _visited[map] = id;
- var keys = _serializeList(map.keys);
- var values = _serializeList(map.values);
- // TODO(floitsch): we are losing the generic type.
- return ['map', id, keys, values];
- }
-
- _serializeList(List list) {
- int len = list.length;
- var result = new List(len);
- for (int i = 0; i < len; i++) {
- result[i] = _dispatch(list[i]);
- }
- return result;
- }
-}
-
-/** Deserializes arrays created with [_Serializer]. */
-class _Deserializer {
- Map<int, dynamic> _deserialized;
-
- _Deserializer();
-
- static bool isPrimitive(x) {
- return (x == null) || (x is String) || (x is num) || (x is bool);
- }
-
- deserialize(x) {
- if (isPrimitive(x)) return x;
- // TODO(floitsch): this should be new HashMap<int, var|Dynamic>()
- _deserialized = new HashMap();
- return _deserializeHelper(x);
- }
-
- _deserializeHelper(x) {
- if (isPrimitive(x)) return x;
- assert(x is List);
- switch (x[0]) {
- case 'ref': return _deserializeRef(x);
- case 'list': return _deserializeList(x);
- case 'map': return _deserializeMap(x);
- case 'sendport': return deserializeSendPort(x);
- default: return deserializeObject(x);
- }
- }
-
- _deserializeRef(List x) {
- int id = x[1];
- var result = _deserialized[id];
- assert(result != null);
- return result;
- }
-
- List _deserializeList(List x) {
- int id = x[1];
- // We rely on the fact that Dart-lists are directly mapped to Js-arrays.
- List dartList = x[2];
- _deserialized[id] = dartList;
- int len = dartList.length;
- for (int i = 0; i < len; i++) {
- dartList[i] = _deserializeHelper(dartList[i]);
- }
- return dartList;
- }
-
- Map _deserializeMap(List x) {
- Map result = new Map();
- int id = x[1];
- _deserialized[id] = result;
- List keys = x[2];
- List values = x[3];
- int len = keys.length;
- assert(len == values.length);
- for (int i = 0; i < len; i++) {
- var key = _deserializeHelper(keys[i]);
- var value = _deserializeHelper(values[i]);
- result[key] = value;
- }
- return result;
- }
-
- deserializeSendPort(List x);
-
- deserializeObject(List x) {
- // TODO(floitsch): Use real exception (which one?).
- throw "Unexpected serialized object";
- }
-}
-
-/********************************************************
- Inserted from lib/isolate/dart2js/timer_provider.dart
- ********************************************************/
-
-// We don't want to import the DOM library just because of window.setTimeout,
-// so we reconstruct the Window class here. The only conflict that could happen
-// with the other DOMWindow class would be because of subclasses.
-// Currently, none of the two Dart classes have subclasses.
-typedef void _TimeoutHandler();
-
-class _Window native "@*DOMWindow" {
- int setTimeout(_TimeoutHandler handler, int timeout) native;
- int setInterval(_TimeoutHandler handler, int timeout) native;
- void clearTimeout(int handle) native;
- void clearInterval(int handle) native;
-}
-
-_Window get _window =>
- JS('bool', 'typeof window != "undefined"') ? JS('_Window', 'window') : null;
-
-class _Timer implements Timer {
- final bool _once;
- int _handle;
-
- _Timer(int milliSeconds, void callback(Timer timer))
- : _once = true {
- _handle = _window.setTimeout(() => callback(this), milliSeconds);
- }
-
- _Timer.repeating(int milliSeconds, void callback(Timer timer))
- : _once = false {
- _handle = _window.setInterval(() => callback(this), milliSeconds);
- }
-
- void cancel() {
- if (_once) {
- _window.clearTimeout(_handle);
- } else {
- _window.clearInterval(_handle);
- }
- }
-}
-
-Timer _timerFactory(int millis, void callback(Timer timer), bool repeating) =>
- repeating ? new _Timer.repeating(millis, callback)
- : new _Timer(millis, callback);
diff --git a/sdk/lib/_internal/compiler/implementation/lib/mirror_opt_in_message.dart b/sdk/lib/_internal/compiler/implementation/lib/mirror_opt_in_message.dart
index df196a2..b9743de 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/mirror_opt_in_message.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/mirror_opt_in_message.dart
@@ -2,6 +2,8 @@
// 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 _js_helper;
+
// Yeah, seriously: mirrors in dart2js are experimental...
const String MIRROR_OPT_IN_MESSAGE = """
diff --git a/sdk/lib/_internal/compiler/implementation/lib/native_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/native_helper.dart
index 46d81be..f6f88c7 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/native_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/native_helper.dart
@@ -2,24 +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.
+part of _js_helper;
+
String typeNameInChrome(obj) {
String name = JS('String', "#.constructor.name", obj);
- if (name == 'Window') return 'DOMWindow';
- if (name == 'CanvasPixelArray') return 'Uint8ClampedArray';
- if (name == 'WebKitMutationObserver') return 'MutationObserver';
- if (name == 'AudioChannelMerger') return 'ChannelMergerNode';
- if (name == 'AudioChannelSplitter') return 'ChannelSplitterNode';
- if (name == 'AudioGainNode') return 'GainNode';
- if (name == 'AudioPannerNode') return 'PannerNode';
- if (name == 'JavaScriptAudioNode') return 'ScriptProcessorNode';
- if (name == 'Oscillator') return 'OscillatorNode';
- if (name == 'RealtimeAnalyserNode') return 'AnalyserNode';
- return name;
+ return typeNameInWebKitCommon(name);
}
String typeNameInSafari(obj) {
String name = JS('String', '#', constructorNameFallback(obj));
// Safari is very similar to Chrome.
+ return typeNameInWebKitCommon(name);
+}
+
+String typeNameInWebKitCommon(tag) {
+ String name = JS('String', '#', tag);
if (name == 'Window') return 'DOMWindow';
if (name == 'CanvasPixelArray') return 'Uint8ClampedArray';
if (name == 'WebKitMutationObserver') return 'MutationObserver';
@@ -100,6 +97,23 @@
return JS('String', '#.substring(8, # - 1)', string, string.length);
}
+/**
+ * If a lookup on an object [object] that has [tag] fails, this function is
+ * called to provide an alternate tag. This allows us to fail gracefully if we
+ * can make a good guess, for example, when browsers add novel kinds of
+ * HTMLElement that we have never heard of.
+ */
+String alternateTag(object, String tag) {
+ // Does it smell like some kind of HTML element?
+ if (JS('bool', r'!!/^HTML[A-Z].*Element$/.test(#)', tag)) {
+ // Check that it is not a simple JavaScript object.
+ String string = JS('String', 'Object.prototype.toString.call(#)', object);
+ if (string == '[object Object]') return null;
+ return 'HTMLElement';
+ }
+ return null;
+}
+
// TODO(ngeoffray): stop using this method once our optimizers can
// change str1.contains(str2) into str1.indexOf(str2) != -1.
bool contains(String userAgent, String name) {
@@ -122,10 +136,18 @@
return JS('var', '#[#]', object, property);
}
+bool callHasOwnProperty(var function, var object, String property) {
+ return JS('bool', '#.call(#, #)', function, object, property);
+}
+
void propertySet(var object, String property, var value) {
JS('var', '#[#] = #', object, property, value);
}
+getPropertyFromPrototype(var object, String name) {
+ return JS('var', 'Object.getPrototypeOf(#)[#]', object, name);
+}
+
newJsObject() {
return JS('var', '{}');
}
@@ -183,8 +205,9 @@
* Sets a JavaScript property on an object.
*/
void defineProperty(var obj, String property, var value) {
- JS('void', """Object.defineProperty(#, #,
- {value: #, enumerable: false, writable: true, configurable: true})""",
+ JS('void',
+ 'Object.defineProperty(#, #, '
+ '{value: #, enumerable: false, writable: true, configurable: true})',
obj,
property,
value);
@@ -205,21 +228,26 @@
String name,
var methods,
List arguments) {
+ // The tag is related to the class name. E.g. the dart:html class
+ // '_ButtonElement' has the tag 'HTMLButtonElement'. TODO(erikcorry): rename
+ // getTypeNameOf to getTypeTag.
String tag = getTypeNameOf(obj);
- var method = JS('var', '#[#]', methods, tag);
+ var hasOwnPropertyFunction = JS('var', 'Object.prototype.hasOwnProperty');
- if (method == null && _dynamicMetadata != null) {
- for (int i = 0; i < arrayLength(_dynamicMetadata); i++) {
- MetaInfo entry = arrayGet(_dynamicMetadata, i);
- if (JS('bool', '#', propertyGet(entry._set, tag))) {
- method = propertyGet(methods, entry._tag);
- if (method != null) break;
- }
+ var method = dynamicBindLookup(hasOwnPropertyFunction, tag, methods);
+ if (method == null) {
+ String secondTag = alternateTag(obj, tag);
+ if (secondTag != null) {
+ method = dynamicBindLookup(hasOwnPropertyFunction, secondTag, methods);
}
}
+ // If we didn't find the method then look up in the Dart Object class, using
+ // getTypeNameOf in case the minifier has renamed Object.
if (method == null) {
- method = propertyGet(methods, 'Object');
+ String nameOfObjectClass = getTypeNameOf(const Object());
+ method =
+ lookupDynamicClass(hasOwnPropertyFunction, methods, nameOfObjectClass);
}
var proto = JS('var', 'Object.getPrototypeOf(#)', obj);
@@ -239,13 +267,44 @@
proto, name, name);
}
- if (JS('bool', '!#.hasOwnProperty(#)', proto, name)) {
+ if (!callHasOwnProperty(hasOwnPropertyFunction, proto, name)) {
defineProperty(proto, name, method);
}
return JS('var', '#.apply(#, #)', method, obj, arguments);
}
+dynamicBindLookup(var hasOwnPropertyFunction, String tag, var methods) {
+ var method = lookupDynamicClass(hasOwnPropertyFunction, methods, tag);
+ // Look at the inheritance data, getting the class tags and using them
+ // to check the methods table for this method name.
+ if (method == null && _dynamicMetadata != null) {
+ for (int i = 0; i < arrayLength(_dynamicMetadata); i++) {
+ MetaInfo entry = arrayGet(_dynamicMetadata, i);
+ if (callHasOwnProperty(hasOwnPropertyFunction, entry._set, tag)) {
+ method =
+ lookupDynamicClass(hasOwnPropertyFunction, methods, entry._tag);
+ // Stop if we found it in the methods array.
+ if (method != null) break;
+ }
+ }
+ }
+ return method;
+}
+
+// For each method name and class inheritance subtree, we use an ordinary JS
+// object as a hash map to store the method for each class. Entries are added
+// in native_emitter.dart (see dynamicName). In order to avoid the class names
+// clashing with the method names on Object.prototype (needed for native
+// objects) we must always use hasOwnProperty.
+var lookupDynamicClass(var hasOwnPropertyFunction,
+ var methods,
+ String className) {
+ return callHasOwnProperty(hasOwnPropertyFunction, methods, className) ?
+ propertyGet(methods, className) :
+ null;
+}
+
/**
* Code for doing the dynamic dispatch on JavaScript prototypes that are not
* available at compile-time. Each property of a native Dart class
@@ -273,7 +332,9 @@
var methods = JS('var', '{}');
// If there is a method attached to the Dart Object class, use it as
// the method to call in case no method is registered for that type.
- var dartMethod = JS('var', 'Object.getPrototypeOf(#)[#]', const Object(), name);
+ var dartMethod = getPropertyFromPrototype(const Object(), name);
+ // Take the method from the Dart Object class if we didn't find it yet and it
+ // is there.
if (dartMethod != null) propertySet(methods, 'Object', dartMethod);
var bind = JS('var',
diff --git a/sdk/lib/_internal/compiler/implementation/lib/regexp_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/regexp_helper.dart
index 441512e..336664e 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/regexp_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/regexp_helper.dart
@@ -2,6 +2,8 @@
// 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 _js_helper;
+
List regExpExec(JSSyntaxRegExp regExp, String str) {
var nativeRegExp = regExpGetNative(regExp);
var result = JS('=List', r'#.exec(#)', nativeRegExp, str);
diff --git a/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart
index e3da2be..8c36382 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/string_helper.dart
@@ -2,6 +2,8 @@
// 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 _js_helper;
+
class StringMatch implements Match {
const StringMatch(int this.start,
String this.str,
diff --git a/sdk/lib/_internal/compiler/implementation/library_loader.dart b/sdk/lib/_internal/compiler/implementation/library_loader.dart
index 6a247e2..3472b3e 100644
--- a/sdk/lib/_internal/compiler/implementation/library_loader.dart
+++ b/sdk/lib/_internal/compiler/implementation/library_loader.dart
@@ -268,7 +268,22 @@
Script sourceScript = compiler.readScript(path, part);
CompilationUnitElement unit =
new CompilationUnitElement(sourceScript, library);
- compiler.withCurrentElement(unit, () => compiler.scanner.scan(unit));
+ compiler.withCurrentElement(unit, () {
+ compiler.scanner.scan(unit);
+ if (unit.partTag == null) {
+ bool wasDiagnosticEmitted = false;
+ compiler.withCurrentElement(library, () {
+ wasDiagnosticEmitted =
+ compiler.onDeprecatedFeature(part, 'missing part-of tag');
+ });
+ if (wasDiagnosticEmitted) {
+ compiler.reportMessage(
+ compiler.spanFromElement(unit),
+ MessageKind.MISSING_PART_OF_TAG.error([]),
+ api.Diagnostic.INFO);
+ }
+ }
+ });
}
/**
diff --git a/sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirror.dart b/sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirror.dart
index 8a68eb5..398d641 100644
--- a/sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirror.dart
+++ b/sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirror.dart
@@ -199,7 +199,7 @@
throw 'unimplemented';
}
- void onDeprecatedFeature(Spannable span, String feature) {
+ bool onDeprecatedFeature(Spannable span, String feature) {
// TODO(johnniwinther): implement this?
throw 'unimplemented';
}
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart
index 825069e..0d30ecd 100644
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart
@@ -34,6 +34,7 @@
class NativeEnqueuer {
/// Initial entry point to native enqueuer.
void processNativeClasses(Collection<LibraryElement> libraries) {}
+ void processNativeClassesInLibrary(LibraryElement library) {}
/// Notification of a main Enqueuer worklist element. For methods, adds
/// information from metadata attributes, and computes types instantiated due
@@ -452,7 +453,6 @@
String libraryName = uri.toString();
if (library.entryCompilationUnit.script.name.contains(
'dart/tests/compiler/dart2js_native')
- || libraryName == 'dart:isolate'
|| libraryName == 'dart:html'
|| libraryName == 'dart:html_common'
|| libraryName == 'dart:indexed_db'
@@ -794,19 +794,6 @@
Compiler compiler = builder.compiler;
FunctionElement element = builder.work.element;
NativeEmitter nativeEmitter = builder.emitter.nativeEmitter;
- // If what we're compiling is a getter named 'typeName' and the native
- // class is named 'DOMType', we generate a call to the typeNameOf
- // function attached on the isolate.
- // The DOM classes assume that their 'typeName' property, which is
- // not a JS property on the DOM types, returns the type name.
- if (element.name == const SourceString('typeName')
- && element.isGetter()
- && nativeEmitter.toNativeName(element.getEnclosingClass()) == 'DOMType') {
- Element helper =
- compiler.findHelper(const SourceString('getTypeNameOf'));
- builder.pushInvokeHelper1(helper, builder.localsHandler.readThis());
- builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit);
- }
HInstruction convertDartClosure(Element parameter, FunctionType type) {
HInstruction local = builder.localsHandler.readLocal(parameter);
@@ -823,14 +810,14 @@
// Check which pattern this native method follows:
// 1) foo() native;
- // hasBody = false, isRedirecting = false
+ // hasBody = false
// 2) foo() native "bar";
- // hasBody = false, isRedirecting = true, no longer supported.
+ // No longer supported, this is now done with @JSName('foo') and case 1.
// 3) foo() native "return 42";
- // hasBody = true, isRedirecting = false
+ // hasBody = true
bool hasBody = false;
assert(element.isNative());
- String nativeMethodName = element.nativeName();
+ String nativeMethodName = element.fixedBackendName();
if (nativeBody != null) {
LiteralString jsCode = nativeBody.asLiteralString();
String str = jsCode.dartString.slowToString();
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
index 6202dfb..e944505 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
@@ -413,6 +413,11 @@
}));
}
+ // TODO(johnniwinther): Remove this queue when resolution has been split into
+ // syntax and semantic resolution.
+ ClassElement currentlyResolvedClass;
+ Queue<ClassElement> pendingClassesToBeResolved = new Queue<ClassElement>();
+
/**
* Resolve the class [element].
*
@@ -425,6 +430,26 @@
* [:element.ensureResolved(compiler):].
*/
void resolveClass(ClassElement element) {
+ ClassElement previousResolvedClass = currentlyResolvedClass;
+ currentlyResolvedClass = element;
+ resolveClassInternal(element);
+ if (previousResolvedClass == null) {
+ while (!pendingClassesToBeResolved.isEmpty) {
+ pendingClassesToBeResolved.removeFirst().ensureResolved(compiler);
+ }
+ }
+ currentlyResolvedClass = previousResolvedClass;
+ }
+
+ void _ensureClassWillBeResolved(ClassElement element) {
+ if (currentlyResolvedClass == null) {
+ element.ensureResolved(compiler);
+ } else {
+ pendingClassesToBeResolved.add(element);
+ }
+ }
+
+ void resolveClassInternal(ClassElement element) {
if (!element.isPatch) {
compiler.withCurrentElement(element, () => measure(() {
assert(element.resolutionState == STATE_NOT_STARTED);
@@ -492,12 +517,42 @@
MessageKind.ILLEGAL_CONSTRUCTOR_MODIFIERS.error([mismatchedFlags]),
Diagnostic.ERROR);
}
+ checkConstructorNameHack(holder, member);
}
checkAbstractField(member);
checkValidOverride(member, cls.lookupSuperMember(member.name));
});
}
+ // TODO(ahe): Remove this method. It is only needed while we store
+ // constructor names as ClassName$id. Once we start storing
+ // constructors as just id, this will be caught by the general
+ // mechanism for duplicate members.
+ /// Check that a constructor name does not conflict with a member.
+ void checkConstructorNameHack(ClassElement holder, FunctionElement member) {
+ // If the name of the constructor is the same as the name of the
+ // class, there cannot be a problem.
+ if (member.name == holder.name) return;
+
+ SourceString name =
+ Elements.deconstructConstructorName(member.name, holder);
+
+ // If the name could not be deconstructed, this is is from a
+ // factory method from a deprecated interface implementation.
+ if (name == null) return;
+
+ Element otherMember = holder.lookupLocalMember(name);
+ if (otherMember != null) {
+ if (compiler.onDeprecatedFeature(member, 'conflicting constructor')) {
+ compiler.reportMessage(
+ compiler.spanFromElement(otherMember),
+ MessageKind.GENERIC.error(['This member conflicts with a'
+ ' constructor.']),
+ Diagnostic.INFO);
+ }
+ }
+ }
+
void checkAbstractField(Element member) {
// Only check for getters. The test can only fail if there is both a setter
// and a getter with the same name, and we only need to check each abstract
@@ -1153,7 +1208,8 @@
type = checkNoTypeArguments(element.computeType(compiler));
} else if (element.isClass()) {
ClassElement cls = element;
- cls.ensureResolved(compiler);
+ compiler.resolver._ensureClassWillBeResolved(cls);
+ element.computeType(compiler);
var arguments = new LinkBuilder<DartType>();
bool hashTypeArgumentMismatch = resolveTypeArguments(
node, cls.typeVariables, enclosingElement,
@@ -2070,7 +2126,7 @@
new VariableDefinitionsVisitor(compiler, node, this,
ElementKind.VARIABLE);
// Ensure that we set the type of the [VariableListElement] since it depends
- // the current scope. If the current scope is a [MethodScope] or
+ // on the current scope. If the current scope is a [MethodScope] or
// [BlockScope] it will not be available for the
// [VariableListElement.computeType] method.
if (node.type != null) {
@@ -2538,8 +2594,7 @@
TypeVariableElement variableElement = typeVariable.element;
if (typeNode.bound != null) {
DartType boundType = typeResolver.resolveTypeAnnotation(
- typeNode.bound, scope, element,
- onFailure: warning);
+ typeNode.bound, scope, element, onFailure: warning);
if (boundType != null && boundType.element == variableElement) {
// TODO(johnniwinther): Check for more general cycles, like
// [: <A extends B, B extends C, C extends B> :].
@@ -2612,14 +2667,22 @@
resolveTypeVariableBounds(node.typeParameters);
// Find super type.
- DartType supertype = visit(node.superclass);
- if (supertype != null && supertype.element.isExtendable()) {
- element.supertype = supertype;
- if (isBlackListed(supertype)) {
+ DartType supertype = null;
+ if (node.superclass != null) {
+ supertype = typeResolver.resolveTypeAnnotation(node.superclass, scope,
+ element, onFailure: error);
+ }
+ if (supertype != null) {
+ if (identical(supertype.kind, TypeKind.MALFORMED_TYPE)) {
+ // Error has already been reported.
+ } else if (!identical(supertype.kind, TypeKind.INTERFACE)) {
+ // TODO(johnniwinther): Handle dynamic.
+ error(node.superclass.typeName, MessageKind.CLASS_NAME_EXPECTED, []);
+ } else if (isBlackListed(supertype)) {
error(node.superclass, MessageKind.CANNOT_EXTEND, [supertype]);
+ } else {
+ element.supertype = supertype;
}
- } else if (supertype != null) {
- error(node.superclass, MessageKind.TYPE_NAME_EXPECTED);
}
final objectElement = compiler.objectClass;
if (!identical(element, objectElement) && element.supertype == null) {
@@ -2636,14 +2699,21 @@
for (Link<Node> link = node.interfaces.nodes;
!link.isEmpty;
link = link.tail) {
- DartType interfaceType = visit(link.head);
- if (interfaceType != null && interfaceType.element.isExtendable()) {
- interfaces = interfaces.prepend(interfaceType);
- if (isBlackListed(interfaceType)) {
- error(link.head, MessageKind.CANNOT_IMPLEMENT, [interfaceType]);
+ DartType interfaceType = typeResolver.resolveTypeAnnotation(
+ link.head, scope, element, onFailure: error);
+ if (interfaceType != null) {
+ if (identical(interfaceType.kind, TypeKind.MALFORMED_TYPE)) {
+ // Error has already been reported.
+ } else if (!identical(interfaceType.kind, TypeKind.INTERFACE)) {
+ // TODO(johnniwinther): Handle dynamic.
+ TypeAnnotation typeAnnotation = link.head;
+ error(typeAnnotation.typeName, MessageKind.CLASS_NAME_EXPECTED, []);
+ } else {
+ interfaces = interfaces.prepend(interfaceType);
+ if (isBlackListed(interfaceType)) {
+ error(link.head, MessageKind.CANNOT_IMPLEMENT, [interfaceType]);
+ }
}
- } else {
- error(link.head, MessageKind.TYPE_NAME_EXPECTED);
}
}
element.interfaces = interfaces;
@@ -2656,10 +2726,12 @@
return element.computeType(compiler);
}
+ // TODO(johnniwinther): Remove when default class is no longer supported.
DartType visitTypeAnnotation(TypeAnnotation node) {
return visit(node.typeName);
}
+ // TODO(johnniwinther): Remove when default class is no longer supported.
DartType visitIdentifier(Identifier node) {
Element element = scope.lookup(node.source);
if (element == null) {
@@ -2682,6 +2754,7 @@
return null;
}
+ // TODO(johnniwinther): Remove when default class is no longer supported.
DartType visitSend(Send node) {
Identifier prefix = node.receiver.asIdentifier();
if (prefix == null) {
@@ -2704,30 +2777,43 @@
}
void calculateAllSupertypes(ClassElement cls) {
- // TODO(karlklose): substitute type variables.
// TODO(karlklose): check if type arguments match, if a classelement occurs
// more than once in the supertypes.
if (cls.allSupertypes != null) return;
final DartType supertype = cls.supertype;
if (supertype != null) {
- ClassElement superElement = supertype.element;
- Link<DartType> superSupertypes = superElement.allSupertypes;
- assert(superSupertypes != null);
- Link<DartType> supertypes = superSupertypes.prepend(supertype);
+ var allSupertypes = new LinkBuilder<DartType>();
+ addAllSupertypes(allSupertypes, supertype);
for (Link<DartType> interfaces = cls.interfaces;
!interfaces.isEmpty;
interfaces = interfaces.tail) {
- ClassElement element = interfaces.head.element;
- Link<DartType> interfaceSupertypes = element.allSupertypes;
- assert(interfaceSupertypes != null);
- supertypes = supertypes.reversePrependAll(interfaceSupertypes);
- supertypes = supertypes.prepend(interfaces.head);
+ addAllSupertypes(allSupertypes, interfaces.head);
}
- cls.allSupertypes = supertypes;
+ cls.allSupertypes = allSupertypes.toLink();
} else {
assert(identical(cls, compiler.objectClass));
cls.allSupertypes = const Link<DartType>();
}
+ }
+
+ /**
+ * Adds [type] and all supertypes of [type] to [builder] while substituting
+ * type variables.
+ */
+ void addAllSupertypes(LinkBuilder<DartType> builder, InterfaceType type) {
+ builder.addLast(type);
+ Link<DartType> typeArguments = type.typeArguments;
+ ClassElement classElement = type.element;
+ Link<DartType> typeVariables = classElement.typeVariables;
+ Link<DartType> supertypes = classElement.allSupertypes;
+ assert(invariant(element, supertypes != null,
+ message: "Supertypes not computed on $classElement "
+ "during resolution of $element"));
+ while (!supertypes.isEmpty) {
+ DartType supertype = supertypes.head;
+ builder.addLast(supertype.subst(typeArguments, typeVariables));
+ supertypes = supertypes.tail;
+ }
}
/**
@@ -2811,7 +2897,7 @@
} else {
compiler.reportMessage(
compiler.spanFromNode(node),
- MessageKind.TYPE_NAME_EXPECTED.error([]),
+ MessageKind.CLASS_NAME_EXPECTED.error([]),
Diagnostic.ERROR);
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart b/sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart
index ea84f75..01f8e9e 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/array_based_scanner.dart
@@ -179,6 +179,5 @@
}
}
- // TODO(ahe): make class abstract instead of adding an abstract method.
- peek();
+ void unmatchedBeginGroup(BeginGroupToken begin);
}
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart b/sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart
index e7ceaf8..fc5e4df 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/class_element_parser.dart
@@ -105,6 +105,9 @@
listener.cancel('library prefix in named factory constructor not '
'implemented', node: send.receiver);
}
+ if (receiver.source != enclosingElement.name) {
+ listener.onDeprecatedFeature(receiver, 'interface factories');
+ }
return Elements.constructConstructorName(receiver.source,
selector.source);
}
@@ -137,6 +140,12 @@
FunctionExpression method = popNode();
pushNode(null);
SourceString name = getMethodNameHack(method.name);
+ Identifier singleIdentifierName = method.name.asIdentifier();
+ if (singleIdentifierName != null && singleIdentifierName.source == name) {
+ if (name != enclosingElement.name) {
+ listener.onDeprecatedFeature(method.name, 'interface factories');
+ }
+ }
ElementKind kind = ElementKind.FUNCTION;
Element memberElement =
new PartialFunctionElement(name, beginToken, null, endToken,
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/listener.dart b/sdk/lib/_internal/compiler/implementation/scanner/listener.dart
index 687cec8..b557d93 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/listener.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/listener.dart
@@ -1337,8 +1337,14 @@
Node receiver = popNode();
String tokenString = token.stringValue;
if (identical(tokenString, '.') || identical(tokenString, '..')) {
- if (argument is !Send) internalError(node: argument);
- if (argument.asSend().receiver != null) internalError(node: argument);
+ Send argumentSend = argument.asSend();
+ if (argumentSend == null) {
+ // TODO(ahe): The parser should diagnose this problem, not
+ // this listener.
+ listener.cancel('Syntax error: Expected an identifier.',
+ node: argument);
+ }
+ if (argumentSend.receiver != null) internalError(node: argument);
if (argument is SendSet) internalError(node: argument);
pushNode(argument.asSend().copyWithReceiver(receiver));
} else {
@@ -1369,8 +1375,9 @@
Node arg = popNode();
Node node = popNode();
Send send = node.asSend();
- if (send == null) internalError(node: node);
- if (!(send.isPropertyAccess || send.isIndex)) internalError(node: send);
+ if (send == null || !(send.isPropertyAccess || send.isIndex)) {
+ reportNotAssignable(node);
+ }
if (send.asSendSet() != null) internalError(node: send);
NodeList arguments;
if (send.isIndex) {
@@ -1384,6 +1391,12 @@
pushNode(new SendSet(send.receiver, send.selector, op, arguments));
}
+ void reportNotAssignable(Node node) {
+ // TODO(ahe): The parser should diagnose this problem, not this
+ // listener.
+ listener.cancel('Syntax error: Not assignable.', node: node);
+ }
+
void handleConditionalExpression(Token question, Token colon) {
Node elseExpression = popNode();
Node thenExpression = popNode();
@@ -1505,8 +1518,12 @@
void handleUnaryAssignmentExpression(Token token, bool isPrefix) {
Node node = popNode();
Send send = node.asSend();
- if (send == null) internalError(node: node);
- if (!(send.isPropertyAccess || send.isIndex)) internalError(node: send);
+ if (send == null) {
+ reportNotAssignable(node);
+ }
+ if (!(send.isPropertyAccess || send.isIndex)) {
+ reportNotAssignable(node);
+ }
if (send.asSendSet() != null) internalError(node: send);
Node argument = null;
if (send.isIndex) argument = send.arguments.head;
@@ -1817,8 +1834,8 @@
void internalError({Token token, Node node}) {
// TODO(ahe): This should call listener.internalError.
- listener.cancel('internal error', token: token, node: node);
- throw 'internal error';
+ Spannable spannable = (token == null) ? node : token;
+ throw new SpannableAssertionFailure(spannable, 'internal error in parser');
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/source_map_builder.dart b/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
index 498c9fb..a2cd6fc 100644
--- a/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
@@ -66,18 +66,22 @@
}
String build(SourceFile targetFile) {
+ StringBuffer mappingsBuffer = new StringBuffer();
+ entries.forEach((SourceMapEntry entry) => writeEntry(entry, targetFile,
+ mappingsBuffer));
StringBuffer buffer = new StringBuffer();
buffer.add('{\n');
buffer.add(' "version": 3,\n');
- buffer.add(' "mappings": "');
- entries.forEach((SourceMapEntry entry) => writeEntry(entry, targetFile, buffer));
- buffer.add('",\n');
+ buffer.add(' "sourceRoot": "",\n');
buffer.add(' "sources": ');
printStringListOn(sourceUrlList, buffer);
buffer.add(',\n');
buffer.add(' "names": ');
printStringListOn(sourceNameList, buffer);
- buffer.add('\n}\n');
+ buffer.add(',\n');
+ buffer.add(' "mappings": "');
+ buffer.add(mappingsBuffer);
+ buffer.add('"\n}\n');
return buffer.toString();
}
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index fcbc105..ebbec8c 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -1054,10 +1054,7 @@
if (node.hasEmptyBody()) return null;
ClassElement classElement = constructor.getEnclosingClass();
ConstructorBodyElement bodyElement;
- for (Link<Element> backendMembers = classElement.backendMembers;
- !backendMembers.isEmpty;
- backendMembers = backendMembers.tail) {
- Element backendMember = backendMembers.head;
+ for (Element backendMember in classElement.backendMembers) {
if (backendMember.isGenerativeConstructorBody()) {
ConstructorBodyElement body = backendMember;
if (body.constructor == constructor) {
@@ -2633,6 +2630,15 @@
push(result);
}
+ void pushInvokeHelper5(Element helper, HInstruction a0, HInstruction a1,
+ HInstruction a2, HInstruction a3, HInstruction a4) {
+ HInstruction reference = new HStatic(helper);
+ add(reference);
+ List<HInstruction> inputs = <HInstruction>[reference, a0, a1, a2, a3, a4];
+ HInstruction result = new HInvokeStatic(inputs);
+ push(result);
+ }
+
HForeign createForeign(String code, String type, List<HInstruction> inputs) {
return new HForeign(new LiteralDartString(code),
new LiteralDartString(type),
@@ -2740,8 +2746,8 @@
}
HInstruction instruction;
- if (RuntimeTypeInformation.hasTypeArguments(type) ||
- type.element.isTypeVariable()) {
+ if (type.element.isTypeVariable() ||
+ RuntimeTypeInformation.hasTypeArguments(type)) {
HInstruction typeInfo = getRuntimeTypeInfo(expression);
// TODO(karlklose): make isSubtype a HInstruction to enable
// optimizations?
@@ -3021,7 +3027,7 @@
// Call a helper method from the isolate library. The isolate
// library uses its own isolate structure, that encapsulates
// Leg's isolate.
- Element element = compiler.isolateLibrary.find(
+ Element element = compiler.isolateHelperLibrary.find(
const SourceString('_currentIsolate'));
if (element == null) {
compiler.cancel(
@@ -3041,7 +3047,7 @@
push(new HInvokeClosure(selector, <HInstruction>[pop()]));
} else {
// Call a helper method from the isolate library.
- Element element = compiler.isolateLibrary.find(
+ Element element = compiler.isolateHelperLibrary.find(
const SourceString('_callInIsolate'));
if (element == null) {
compiler.cancel(
@@ -3108,21 +3114,58 @@
}
generateSuperNoSuchMethodSend(Send node) {
+ Selector selector = elements.getSelector(node);
+ SourceString name = selector.name;
+
ClassElement cls = work.element.getEnclosingClass();
Element element = cls.lookupSuperMember(Compiler.NO_SUCH_METHOD);
+ if (element.enclosingElement.declaration != compiler.objectClass) {
+ // Register the call as dynamic if [:noSuchMethod:] on the super class
+ // is _not_ the default implementation from [:Object:].
+ compiler.enqueuer.codegen.registerDynamicInvocation(name, selector);
+ }
HStatic target = new HStatic(element);
add(target);
HInstruction self = localsHandler.readThis();
- Identifier identifier = node.selector.asIdentifier();
- String name = identifier.source.slowToString();
- // TODO(ahe): Add the arguments to this list.
- push(new HLiteralList([]));
- Constant nameConstant =
- constantSystem.createString(new DartString.literal(name), node);
+ Constant nameConstant = constantSystem.createString(
+ new DartString.literal(name.slowToString()), node);
+
+ String internalName = backend.namer.instanceMethodInvocationName(
+ currentLibrary, name, selector);
+ Constant internalNameConstant =
+ constantSystem.createString(new DartString.literal(internalName), node);
+
+ Element createInvocationMirror =
+ compiler.findHelper(Compiler.CREATE_INVOCATION_MIRROR);
+
+ var arguments = new List<HInstruction>();
+ addGenericSendArgumentsToList(node.arguments, arguments);
+ var argumentsInstruction = new HLiteralList(arguments);
+ add(argumentsInstruction);
+
+ var argumentNames = new List<HInstruction>();
+ for (SourceString argumentName in selector.namedArguments) {
+ Constant argumentNameConstant =
+ constantSystem.createString(new DartString.literal(
+ argumentName.slowToString()), node);
+ argumentNames.add(graph.addConstant(argumentNameConstant));
+ }
+ var argumentNamesInstruction = new HLiteralList(argumentNames);
+ add(argumentNamesInstruction);
+
+ Constant kindConstant =
+ constantSystem.createInt(selector.invocationMirrorKind);
+
+ pushInvokeHelper5(createInvocationMirror,
+ graph.addConstant(nameConstant),
+ graph.addConstant(internalNameConstant),
+ graph.addConstant(kindConstant),
+ argumentsInstruction,
+ argumentNamesInstruction);
+
var inputs = <HInstruction>[
target,
self,
- graph.addConstant(nameConstant),
pop()];
push(new HInvokeSuper(inputs));
}
@@ -3370,8 +3413,6 @@
return;
}
- // TODO(kasperl): Try to use the general inlining infrastructure for
- // inlining the identical function.
if (identical(element, compiler.identicalFunction)) {
pushWithPosition(new HIdentity(target, inputs[1], inputs[2]), node);
return;
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
index 6beaa0c..bf500a8 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/codegen.dart
@@ -1764,8 +1764,8 @@
node);
}
- String _fieldPropertyName(Element element) => element.isNative()
- ? element.nativeName()
+ String _fieldPropertyName(Element element) => element.hasFixedBackendName()
+ ? element.fixedBackendName()
: backend.namer.getName(element);
visitLocalGet(HLocalGet node) {
@@ -1777,13 +1777,17 @@
assignVariable(variableNames.getName(node.receiver), pop());
}
+ // TODO(sra): We could be more picky about when to inhibit renaming of locals
+ // - most JS strings don't contain free variables, or contain safe ones like
+ // 'Object'. JS strings like "#.length" and "#[#]" are perfectly safe for
+ // variable renaming. For now, be shy of any potential identifiers.
+ static final RegExp safeCodeRegExp = new RegExp(r'^[^_$a-zA-Z]*$');
+
visitForeign(HForeign node) {
- // TODO(sra): We could be a lot more picky about when to inhibit renaming of
- // locals - most JS strings don't contain free variables, or contain safe
- // ones like 'Object'. JS strings like "#.length" and "#[#]" are perfectly
- // safe for variable renaming.
- inhibitVariableMinification = true;
String code = node.code.slowToString();
+ if (!safeCodeRegExp.hasMatch(code)) {
+ inhibitVariableMinification = true;
+ }
List<HInstruction> inputs = node.inputs;
if (node.isJsStatement(types)) {
if (!inputs.isEmpty) {
@@ -1841,6 +1845,10 @@
assert(isGenerateAtUseSite(node));
generateConstant(node.constant);
DartType type = node.constant.computeType(compiler);
+ if (node.constant is ConstructedConstant) {
+ ConstantHandler handler = compiler.constantHandler;
+ handler.registerCompileTimeConstant(node.constant);
+ }
world.registerInstantiatedClass(type.element);
}
@@ -2347,15 +2355,13 @@
if (identical(element.kind, ElementKind.TYPE_VARIABLE)) {
compiler.unimplemented("visitIs for type variables",
instruction: node.expression);
- } else if (identical(element.kind, ElementKind.TYPEDEF)) {
- compiler.unimplemented("visitIs for typedefs",
- instruction: node.expression);
}
LibraryElement coreLibrary = compiler.coreLibrary;
ClassElement objectClass = compiler.objectClass;
HInstruction input = node.expression;
- if (identical(element, objectClass) || identical(element, compiler.dynamicClass)) {
+ if (identical(element, objectClass) ||
+ identical(element, compiler.dynamicClass)) {
// The constant folder also does this optimization, but we make
// it safe by assuming it may have not run.
push(newLiteralBool(true), node);
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
index 88718e1..290a357 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
@@ -2541,7 +2541,7 @@
HInstruction getCheck(int index) => inputs[index + 1];
int get checkCount => inputs.length - 1;
- bool hasArgumentChecks() => inputs.length >= 1;
+ bool hasArgumentChecks() => inputs.length > 1;
HType get guaranteedType => HType.BOOLEAN;
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index 036d1ac..b6ea705 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -207,7 +207,7 @@
}
HInstruction handleInterceptorCall(HInvokeDynamic node) {
- if (node is !HInvokeDynamicMethod) return;
+ if (node is !HInvokeDynamicMethod) return null;
HInstruction input = node.inputs[1];
if (input.isString(types)
&& node.selector.name == const SourceString('toString')) {
@@ -511,8 +511,10 @@
HInstruction visitIs(HIs node) {
DartType type = node.typeExpression;
Element element = type.element;
- if (identical(element.kind, ElementKind.TYPE_VARIABLE)) {
+ if (element.isTypeVariable()) {
compiler.unimplemented("visitIs for type variables");
+ } if (element.isTypedef()) {
+ return node;
}
HType expressionType = types[node.expression];
@@ -741,10 +743,8 @@
if (constantInterceptor == null) return node;
- ConstantHandler handler = compiler.constantHandler;
Constant constant = new ConstructedConstant(
constantInterceptor.computeType(compiler), <Constant>[]);
- handler.registerCompileTimeConstant(constant);
return graph.addConstant(constant);
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart b/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
index 0ffbdde..f310174 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
@@ -117,9 +117,9 @@
// Create a new node and move the children of the current node
// that are subtypes of the type of the new node below the new
// node in the hierarchy.
- PartialTypeTreeNode newNode = newNode(type);
+ PartialTypeTreeNode node = newNode(type);
if (!subtypes.isEmpty) {
- newNode.children = subtypes;
+ node.children = subtypes;
Link<PartialTypeTreeNode> remaining = const Link();
for (Link link = current.children; !link.isEmpty; link = link.tail) {
PartialTypeTreeNode child = link.head;
@@ -131,8 +131,8 @@
}
// Add the new node as a child node of the current node and return it.
- current.children = current.children.prepend(newNode);
- return newNode;
+ current.children = current.children.prepend(node);
+ return node;
}
// We found an exact match. No need to insert new nodes.
diff --git a/sdk/lib/_internal/compiler/implementation/universe/universe.dart b/sdk/lib/_internal/compiler/implementation/universe/universe.dart
index eff874c..446d835 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/universe.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/universe.dart
@@ -217,6 +217,25 @@
int get positionalArgumentCount => argumentCount - namedArgumentCount;
DartType get receiverType => null;
+ /**
+ * The member name for invocation mirrors created from this selector.
+ */
+ String get invocationMirrorMemberName =>
+ isSetter() ? '${name.slowToString()}=' : name.slowToString();
+
+ int get invocationMirrorKind {
+ const int METHOD = 0;
+ const int GETTER = 1;
+ const int SETTER = 2;
+ int kind = METHOD;
+ if (isGetter()) {
+ kind = GETTER;
+ } else if (isSetter()) {
+ kind = SETTER;
+ }
+ return kind;
+ }
+
bool applies(Element element, Compiler compiler)
=> appliesUntyped(element, compiler);
diff --git a/sdk/lib/_internal/compiler/implementation/warnings.dart b/sdk/lib/_internal/compiler/implementation/warnings.dart
index 9c07fd3..5f5b74e 100644
--- a/sdk/lib/_internal/compiler/implementation/warnings.dart
+++ b/sdk/lib/_internal/compiler/implementation/warnings.dart
@@ -187,8 +187,12 @@
static const CYCLIC_TYPE_VARIABLE = const MessageKind(
"cyclic reference to type variable #{1}");
- static const TYPE_NAME_EXPECTED = const MessageKind(
- "class or interface name expected");
+
+ static const CLASS_NAME_EXPECTED = const MessageKind(
+ "class name expected");
+
+ static const INTERFACE_TYPE_EXPECTED = const MessageKind(
+ "interface type expected");
static const CANNOT_EXTEND = const MessageKind(
"#{1} cannot be extended");
@@ -293,6 +297,9 @@
static const LIBRARY_NAME_MISMATCH = const MessageKind(
'Warning: expected part of library name "#{1}".');
+ static const MISSING_PART_OF_TAG = const MessageKind(
+ 'Note: This file has no part-of tag, but it is being used as a part.');
+
static const DUPLICATED_PART_OF = const MessageKind(
'Error: duplicated part-of directive.');
diff --git a/sdk/lib/_internal/dartdoc/bin/dartdoc.dart b/sdk/lib/_internal/dartdoc/bin/dartdoc.dart
index 1b45e3c..f289ed3 100755
--- a/sdk/lib/_internal/dartdoc/bin/dartdoc.dart
+++ b/sdk/lib/_internal/dartdoc/bin/dartdoc.dart
@@ -36,7 +36,8 @@
final argParser = new ArgParser();
final Path libPath = scriptDir.append('../../../../');
- Path pkgPath = scriptDir.append('../../../../pkg/');
+
+ Path pkgPath;
argParser.addFlag('no-code',
help: 'Do not include source code in the documentation.',
@@ -178,8 +179,21 @@
final entrypoints = <Path>[];
try {
final option = argParser.parse(args);
+
+ // This checks to see if the root of all entrypoints is the same.
+ // If it is not, then we display a warning, as package imports might fail.
+ var entrypointRoot;
for(final arg in option.rest) {
- entrypoints.add(new Path.fromNative(arg));
+ var entrypoint = new Path.fromNative(arg);
+ entrypoints.add(entrypoint);
+
+ if (entrypointRoot == null) {
+ entrypointRoot = entrypoint.directoryPath;
+ } else if (entrypointRoot.toNativePath() !=
+ entrypoint.directoryPath.toNativePath()) {
+ print('Warning: entrypoints are at different directories. "package:"'
+ ' imports may fail.');
+ }
}
} on FormatException catch (e) {
print(e.message);
@@ -193,6 +207,10 @@
print(argParser.getUsage());
return;
}
+
+ if (pkgPath == null) {
+ pkgPath = entrypoints[0].directoryPath.append('packages/');
+ }
cleanOutputDirectory(dartdoc.outputDir);
diff --git a/sdk/lib/_internal/dartdoc/lib/dartdoc.dart b/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
index ec86fd3..44c7d69 100644
--- a/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
+++ b/sdk/lib/_internal/dartdoc/lib/dartdoc.dart
@@ -712,7 +712,7 @@
/** Writes a linked navigation list item for the given type. */
void docTypeNavigation(ClassMirror type) {
var icon = 'interface';
- if (type.simpleName.endsWith('Exception')) {
+ if (isException(type)) {
icon = 'exception';
} else if (type.isClass) {
icon = 'class';
@@ -874,7 +874,7 @@
*/
void typeSpan(ClassMirror type) {
var icon = 'interface';
- if (type.simpleName.endsWith('Exception')) {
+ if (isException(type)) {
icon = 'exception';
} else if (type.isClass) {
icon = 'class';
diff --git a/sdk/lib/_internal/dartdoc/lib/src/client/client-live-nav.dart b/sdk/lib/_internal/dartdoc/lib/src/client/client-live-nav.dart
index 4405bab..9be17a3 100644
--- a/sdk/lib/_internal/dartdoc/lib/src/client/client-live-nav.dart
+++ b/sdk/lib/_internal/dartdoc/lib/src/client/client-live-nav.dart
@@ -66,7 +66,8 @@
final exceptions = [];
for (Map typeInfo in libraryInfo[TYPES]) {
- if (typeInfo[NAME].endsWith('Exception')) {
+ var name = typeInfo[NAME];
+ if (name.endsWith('Exception') || name.endsWith('Error')) {
exceptions.add(typeInfo);
} else {
types.add(typeInfo);
diff --git a/sdk/lib/_internal/dartdoc/test/dartdoc_test.dart b/sdk/lib/_internal/dartdoc/test/dartdoc_test.dart
index 143332b..b58195b 100644
--- a/sdk/lib/_internal/dartdoc/test/dartdoc_test.dart
+++ b/sdk/lib/_internal/dartdoc/test/dartdoc_test.dart
@@ -5,6 +5,8 @@
/// Unit tests for doc.
library dartdocTests;
+import 'dart:io';
+
// TODO(rnystrom): Use "package:" URL (#4968).
import '../lib/dartdoc.dart' as dd;
import '../lib/markdown.dart' as md;
@@ -116,4 +118,35 @@
'../../other/file.html'));
});
});
+
+ group('integration tests', () {
+ test('no entrypoints', () {
+ expect(_runDartdoc([]), completes);
+ });
+
+ test('library with no packages', () {
+ expect(_runDartdoc(
+ [new Path.fromNative('test/test_files/other_place/'
+ 'no_package_test_file.dart').toNativePath()]),
+ completes);
+ });
+
+ test('library with packages', () {
+ expect(_runDartdoc(
+ [new Path.fromNative('test/test_files/'
+ 'package_test_file.dart').toNativePath()]),
+ completes);
+ });
+
+ });
+}
+
+Future _runDartdoc(List<String> arguments) {
+ var dartBin = new Options().executable;
+ var dartdoc = 'bin/dartdoc.dart';
+ arguments.insertRange(0, 1, dartdoc);
+ return Process.run(dartBin, arguments)
+ .transform((result) {
+ expect(result.exitCode, 0);
+ });
}
diff --git a/sdk/lib/_internal/dartdoc/test/test_files/other_place/no_package_test_file.dart b/sdk/lib/_internal/dartdoc/test/test_files/other_place/no_package_test_file.dart
new file mode 100644
index 0000000..da52a19
--- /dev/null
+++ b/sdk/lib/_internal/dartdoc/test/test_files/other_place/no_package_test_file.dart
@@ -0,0 +1,5 @@
+library no_package_test;
+
+class NoPackageTestFile {
+
+}
diff --git a/sdk/lib/_internal/dartdoc/test/test_files/package_test_file.dart b/sdk/lib/_internal/dartdoc/test/test_files/package_test_file.dart
new file mode 100644
index 0000000..de09194
--- /dev/null
+++ b/sdk/lib/_internal/dartdoc/test/test_files/package_test_file.dart
@@ -0,0 +1,5 @@
+library package_test;
+
+class PackageTestFile {
+
+}
diff --git a/sdk/lib/_internal/dartdoc/test/test_files/packages/fake_package/fake_package.dart b/sdk/lib/_internal/dartdoc/test/test_files/packages/fake_package/fake_package.dart
new file mode 100644
index 0000000..0025791
--- /dev/null
+++ b/sdk/lib/_internal/dartdoc/test/test_files/packages/fake_package/fake_package.dart
@@ -0,0 +1,4 @@
+library fake_package;
+class FakePackage {
+
+}
diff --git a/sdk/lib/_internal/libraries.dart b/sdk/lib/_internal/libraries.dart
index fd8b5ab..29b3f36 100644
--- a/sdk/lib/_internal/libraries.dart
+++ b/sdk/lib/_internal/libraries.dart
@@ -115,6 +115,12 @@
category: "Internal",
documented: false,
platforms: DART2JS_PLATFORM),
+
+ "_isolate_helper": const LibraryInfo(
+ "_internal/compiler/implementation/lib/isolate_helper.dart",
+ category: "Internal",
+ documented: false,
+ platforms: DART2JS_PLATFORM),
};
/**
diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart
index eeb0c04..c18e174 100644
--- a/sdk/lib/core/errors.dart
+++ b/sdk/lib/core/errors.dart
@@ -245,3 +245,12 @@
const StackOverflowError();
String toString() => "Stack Overflow";
}
+
+/**
+ * Error thrown when a runtime error occurs.
+ */
+class RuntimeError implements Error {
+ final message;
+ RuntimeError(this.message);
+ String toString() => "RuntimeError: $message";
+}
diff --git a/sdk/lib/core/exceptions.dart b/sdk/lib/core/exceptions.dart
index afe3285..1a7c436 100644
--- a/sdk/lib/core/exceptions.dart
+++ b/sdk/lib/core/exceptions.dart
@@ -64,12 +64,3 @@
const IntegerDivisionByZeroException();
String toString() => "IntegerDivisionByZeroException";
}
-
-/**
- * Exception thrown when a runtime error occurs.
- */
-class RuntimeError implements Exception {
- final message;
- RuntimeError(this.message);
- String toString() => "RuntimeError: $message";
-}
diff --git a/sdk/lib/core/identical.dart b/sdk/lib/core/identical.dart
index 5de366f..5e8f782 100644
--- a/sdk/lib/core/identical.dart
+++ b/sdk/lib/core/identical.dart
@@ -5,4 +5,4 @@
/**
* Check whether two references are to the same object.
*/
-bool identical(Object a, Object b) => a === b;
+external bool identical(Object a, Object b);
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index d0de13d..fb99370 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -73,9 +73,12 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class AbstractWorkerEvents extends Events {
+ /// @docsEditable true
AbstractWorkerEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -84,7 +87,7 @@
/// @domName HTMLAnchorElement; @docsEditable true
-class AnchorElement extends Element implements Element native "*HTMLAnchorElement" {
+class AnchorElement extends Element native "*HTMLAnchorElement" {
///@docsEditable true
factory AnchorElement({String href}) {
@@ -229,7 +232,7 @@
/// @domName HTMLAppletElement; @docsEditable true
-class AppletElement extends Element implements Element native "*HTMLAppletElement" {
+class AppletElement extends Element native "*HTMLAppletElement" {
/// @domName HTMLAppletElement.align; @docsEditable true
String align;
@@ -313,23 +316,33 @@
void update() native;
}
+/// @docsEditable true
class ApplicationCacheEvents extends Events {
+ /// @docsEditable true
ApplicationCacheEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get cached => this['cached'];
+ /// @docsEditable true
EventListenerList get checking => this['checking'];
+ /// @docsEditable true
EventListenerList get downloading => this['downloading'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get noUpdate => this['noupdate'];
+ /// @docsEditable true
EventListenerList get obsolete => this['obsolete'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get updateReady => this['updateready'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -338,7 +351,7 @@
/// @domName HTMLAreaElement; @docsEditable true
-class AreaElement extends Element implements Element native "*HTMLAreaElement" {
+class AreaElement extends Element native "*HTMLAreaElement" {
///@docsEditable true
factory AreaElement() => document.$dom_createElement("area");
@@ -464,7 +477,7 @@
/// @domName HTMLBRElement; @docsEditable true
-class BRElement extends Element implements Element native "*HTMLBRElement" {
+class BRElement extends Element native "*HTMLBRElement" {
///@docsEditable true
factory BRElement() => document.$dom_createElement("br");
@@ -489,7 +502,7 @@
/// @domName HTMLBaseElement; @docsEditable true
-class BaseElement extends Element implements Element native "*HTMLBaseElement" {
+class BaseElement extends Element native "*HTMLBaseElement" {
///@docsEditable true
factory BaseElement() => document.$dom_createElement("base");
@@ -506,7 +519,7 @@
/// @domName HTMLBaseFontElement; @docsEditable true
-class BaseFontElement extends Element implements Element native "*HTMLBaseFontElement" {
+class BaseFontElement extends Element native "*HTMLBaseFontElement" {
/// @domName HTMLBaseFontElement.color; @docsEditable true
String color;
@@ -554,15 +567,21 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class BatteryManagerEvents extends Events {
+ /// @docsEditable true
BatteryManagerEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get chargingChange => this['chargingchange'];
+ /// @docsEditable true
EventListenerList get chargingTimeChange => this['chargingtimechange'];
+ /// @docsEditable true
EventListenerList get dischargingTimeChange => this['dischargingtimechange'];
+ /// @docsEditable true
EventListenerList get levelChange => this['levelchange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -610,7 +629,7 @@
/// @domName HTMLBodyElement; @docsEditable true
-class BodyElement extends Element implements Element native "*HTMLBodyElement" {
+class BodyElement extends Element native "*HTMLBodyElement" {
///@docsEditable true
factory BodyElement() => document.$dom_createElement("body");
@@ -635,33 +654,48 @@
String vLink;
}
+/// @docsEditable true
class BodyElementEvents extends ElementEvents {
+ /// @docsEditable true
BodyElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get beforeUnload => this['beforeunload'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get hashChange => this['hashchange'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get offline => this['offline'];
+ /// @docsEditable true
EventListenerList get online => this['online'];
+ /// @docsEditable true
EventListenerList get popState => this['popstate'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get storage => this['storage'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -670,7 +704,7 @@
/// @domName HTMLButtonElement; @docsEditable true
-class ButtonElement extends Element implements Element native "*HTMLButtonElement" {
+class ButtonElement extends Element native "*HTMLButtonElement" {
///@docsEditable true
factory ButtonElement() => document.$dom_createElement("button");
@@ -741,7 +775,7 @@
/// @domName HTMLCanvasElement
-class CanvasElement extends Element implements Element native "*HTMLCanvasElement" {
+class CanvasElement extends Element native "*HTMLCanvasElement" {
///@docsEditable true
factory CanvasElement({int width, int height}) {
@@ -751,12 +785,54 @@
return e;
}
+ /// The height of this canvas element in CSS pixels.
/// @domName HTMLCanvasElement.height; @docsEditable true
int height;
+ /// The width of this canvas element in CSS pixels.
/// @domName HTMLCanvasElement.width; @docsEditable true
int width;
+ /**
+ * Returns a data URI containing a representation of the image in the
+ * format specified by type (defaults to 'image/png').
+ *
+ * Data Uri format is as follow `data:[<MIME-type>][;charset=<encoding>][;base64],<data>`
+ *
+ * Optional parameter [quality] in the range of 0.0 and 1.0 can be used when requesting [type]
+ * 'image/jpeg' or 'image/webp'. If [quality] is not passed the default
+ * value is used. Note: the default value varies by browser.
+ *
+ * If the height or width of this canvas element is 0, then 'data:' is returned,
+ * representing no data.
+ *
+ * If the type requested is not 'image/png', and the returned value is
+ * 'data:image/png', then the requested type is not supported.
+ *
+ * Example usage:
+ *
+ * CanvasElement canvas = new CanvasElement();
+ * var ctx = canvas.context2d
+ * ..fillStyle = "rgb(200,0,0)"
+ * ..fillRect(10, 10, 55, 50);
+ * var dataUrl = canvas.toDataURL("image/jpeg", 0.95);
+ * // The Data Uri would look similar to
+ * // 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
+ * // AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
+ * // 9TXL0Y4OHwAAAABJRU5ErkJggg=='
+ * //Create a new image element from the data URI.
+ * var img = new ImageElement();
+ * img.src = dataUrl;
+ * document.body.children.add(img);
+ *
+ * See also:
+ *
+ * * [Data URI Scheme](http://en.wikipedia.org/wiki/Data_URI_scheme) from Wikipedia.
+ *
+ * * [HTMLCanvasElement](https://developer.mozilla.org/en-US/docs/DOM/HTMLCanvasElement) from MDN.
+ *
+ * * [toDataUrl](http://dev.w3.org/html5/spec/the-canvas-element.html#dom-canvas-todataurl) from W3C.
+ */
/// @domName HTMLCanvasElement.toDataURL; @docsEditable true
@JSName('toDataURL')
String toDataUrl(String type, [num quality]) native;
@@ -770,9 +846,47 @@
// BSD-style license that can be found in the LICENSE file.
+/**
+ * An opaque canvas object representing a gradient.
+ *
+ * Created by calling [createLinearGradient] or [createRadialGradient] on a
+ * [CanvasRenderingContext2D] object.
+ *
+ * Example usage:
+ *
+ * var canvas = new CanvasElement(width: 600, height: 600);
+ * var ctx = canvas.context2d;
+ * ctx.clearRect(0, 0, 600, 600);
+ * ctx.save();
+ * // Create radial gradient.
+ * CanvasGradient gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 600);
+ * gradient.addColorStop(0, '#000');
+ * gradient.addColorStop(1, 'rgb(255, 255, 255)');
+ * // Assign gradients to fill.
+ * ctx.fillStyle = gradient;
+ * // Draw a rectangle with a gradient fill.
+ * ctx.fillRect(0, 0, 600, 600);
+ * ctx.save();
+ * document.body.children.add(canvas);
+ *
+ * See also:
+ *
+ * * [CanvasGradient](https://developer.mozilla.org/en-US/docs/DOM/CanvasGradient) from MDN.
+ * * [CanvasGradient](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#canvasgradient) from whatwg.
+ * * [CanvasGradient](http://www.w3.org/TR/2010/WD-2dcontext-20100304/#canvasgradient) from W3C.
+ */
/// @domName CanvasGradient; @docsEditable true
class CanvasGradient native "*CanvasGradient" {
+ /**
+ * Adds a color stop to this gradient at the offset.
+ *
+ * The [offset] can range between 0.0 and 1.0.
+ *
+ * See also:
+ *
+ * * [Multiple Color Stops](https://developer.mozilla.org/en-US/docs/CSS/linear-gradient#Gradient_with_multiple_color_stops) from MDN.
+ */
/// @domName CanvasGradient.addColorStop; @docsEditable true
void addColorStop(num offset, String color) native;
}
@@ -781,6 +895,33 @@
// BSD-style license that can be found in the LICENSE file.
+/**
+ * An opaque object representing a pattern of image, canvas, or video.
+ *
+ * Created by calling [createPattern] on a [CanvasRenderingContext2D] object.
+ *
+ * Example usage:
+ *
+ * var canvas = new CanvasElement(width: 600, height: 600);
+ * var ctx = canvas.context2d;
+ * var img = new ImageElement();
+ * // Image src needs to be loaded before pattern is applied.
+ * img.on.load.add((event) {
+ * // When the image is loaded, create a pattern
+ * // from the ImageElement.
+ * CanvasPattern pattern = ctx.createPattern(img, 'repeat');
+ * ctx.rect(0, 0, canvas.width, canvas.height);
+ * ctx.fillStyle = pattern;
+ * ctx.fill();
+ * });
+ * img.src = "images/foo.jpg";
+ * document.body.children.add(canvas);
+ *
+ * See also:
+ * * [CanvasPattern](https://developer.mozilla.org/en-US/docs/DOM/CanvasPattern) from MDN.
+ * * [CanvasPattern](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#canvaspattern) from whatwg.
+ * * [CanvasPattern](http://www.w3.org/TR/2010/WD-2dcontext-20100304/#canvaspattern) from W3C.
+ */
/// @domName CanvasPattern; @docsEditable true
class CanvasPattern native "*CanvasPattern" {
}
@@ -789,9 +930,16 @@
// BSD-style license that can be found in the LICENSE file.
+/**
+ * A rendering context for a canvas element.
+ *
+ * This context is extended by [CanvasRenderingContext2D] and
+ * [WebGLRenderingContext].
+ */
/// @domName CanvasRenderingContext; @docsEditable true
class CanvasRenderingContext native "*CanvasRenderingContext" {
+ /// Reference to the canvas element to which this context belongs.
/// @domName CanvasRenderingContext.canvas; @docsEditable true
final CanvasElement canvas;
}
@@ -1336,7 +1484,7 @@
/// @domName HTMLContentElement; @docsEditable true
-class ContentElement extends Element implements Element native "*HTMLContentElement" {
+class ContentElement extends Element native "*HTMLContentElement" {
///@docsEditable true
factory ContentElement() => document.$dom_createElement("content");
@@ -5142,7 +5290,7 @@
/// @domName HTMLDListElement; @docsEditable true
-class DListElement extends Element implements Element native "*HTMLDListElement" {
+class DListElement extends Element native "*HTMLDListElement" {
///@docsEditable true
factory DListElement() => document.$dom_createElement("dl");
@@ -5156,7 +5304,7 @@
/// @domName HTMLDataListElement; @docsEditable true
-class DataListElement extends Element implements Element native "*HTMLDataListElement" {
+class DataListElement extends Element native "*HTMLDataListElement" {
///@docsEditable true
factory DataListElement() => document.$dom_createElement("datalist");
@@ -5354,9 +5502,12 @@
void _postMessage_2(message) native;
}
+/// @docsEditable true
class DedicatedWorkerContextEvents extends WorkerContextEvents {
+ /// @docsEditable true
DedicatedWorkerContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get message => this['message'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -5365,7 +5516,7 @@
/// @domName HTMLDetailsElement; @docsEditable true
-class DetailsElement extends Element implements Element native "*HTMLDetailsElement" {
+class DetailsElement extends Element native "*HTMLDetailsElement" {
///@docsEditable true
factory DetailsElement() => document.$dom_createElement("details");
@@ -5413,7 +5564,7 @@
/// @domName HTMLDirectoryElement; @docsEditable true
-class DirectoryElement extends Element implements Element native "*HTMLDirectoryElement" {
+class DirectoryElement extends Element native "*HTMLDirectoryElement" {
/// @domName HTMLDirectoryElement.compact; @docsEditable true
bool compact;
@@ -5548,8 +5699,29 @@
// BSD-style license that can be found in the LICENSE file.
+/**
+ * Represents an HTML <div> element.
+ *
+ * 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 block-level element, as opposed to [SpanElement],
+ * which is an inline-level element.
+ *
+ * Example usage:
+ *
+ * DivElement div = new DivElement();
+ * div.text = 'Here's my new DivElem
+ * document.body.elements.add(elem);
+ *
+ * See also:
+ *
+ * * [HTML <div> element](http://www.w3.org/TR/html-markup/div.html) from W3C.
+ * * [Block-level element](http://www.w3.org/TR/CSS2/visuren.html#block-boxes) from W3C.
+ * * [Inline-level element](http://www.w3.org/TR/CSS2/visuren.html#inline-boxes) from W3C.
+ */
/// @domName HTMLDivElement; @docsEditable true
-class DivElement extends Element implements Element native "*HTMLDivElement" {
+class DivElement extends Element native "*HTMLDivElement" {
///@docsEditable true
factory DivElement() => document.$dom_createElement("div");
@@ -5577,6 +5749,7 @@
DocumentEvents get on =>
new DocumentEvents(this);
+ /// Moved to [HtmlDocument].
/// @domName Document.body; @docsEditable true
@JSName('body')
Element $dom_body;
@@ -5587,6 +5760,7 @@
/// @domName Document.cookie; @docsEditable true
String cookie;
+ /// Returns the [Window] associated with the document.
/// @domName Document.defaultView; @docsEditable true
Window get window => _convertNativeToDart_Window(this._window);
@JSName('defaultView')
@@ -5599,6 +5773,7 @@
/// @domName Document.domain; @docsEditable true
final String domain;
+ /// Moved to [HtmlDocument].
/// @domName Document.head; @docsEditable true
@JSName('head')
final HeadElement $dom_head;
@@ -5606,6 +5781,7 @@
/// @domName Document.implementation; @docsEditable true
final DomImplementation implementation;
+ /// Moved to [HtmlDocument].
/// @domName Document.lastModified; @docsEditable true
@JSName('lastModified')
final String $dom_lastModified;
@@ -5617,6 +5793,7 @@
/// @domName Document.readyState; @docsEditable true
final String readyState;
+ /// Moved to [HtmlDocument].
/// @domName Document.referrer; @docsEditable true
@JSName('referrer')
final String $dom_referrer;
@@ -5625,39 +5802,48 @@
@JSName('selectedStylesheetSet')
String $dom_selectedStylesheetSet;
+ /// Moved to [HtmlDocument].
/// @domName Document.styleSheets; @docsEditable true
@JSName('styleSheets')
@Returns('_StyleSheetList') @Creates('_StyleSheetList')
final List<StyleSheet> $dom_styleSheets;
+ /// Moved to [HtmlDocument].
/// @domName Document.title; @docsEditable true
@JSName('title')
String $dom_title;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitFullscreenElement; @docsEditable true
@JSName('webkitFullscreenElement')
final Element $dom_webkitFullscreenElement;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitFullscreenEnabled; @docsEditable true
@JSName('webkitFullscreenEnabled')
final bool $dom_webkitFullscreenEnabled;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitHidden; @docsEditable true
@JSName('webkitHidden')
final bool $dom_webkitHidden;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitIsFullScreen; @docsEditable true
@JSName('webkitIsFullScreen')
final bool $dom_webkitIsFullScreen;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitPointerLockElement; @docsEditable true
@JSName('webkitPointerLockElement')
final Element $dom_webkitPointerLockElement;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitVisibilityState; @docsEditable true
@JSName('webkitVisibilityState')
final String $dom_webkitVisibilityState;
+ /// Use the [Range] constructor instead.
/// @domName Document.caretRangeFromPoint; @docsEditable true
@JSName('caretRangeFromPoint')
Range $dom_caretRangeFromPoint(int x, int y) native;
@@ -5669,6 +5855,7 @@
/// @domName Document.createDocumentFragment; @docsEditable true
DocumentFragment createDocumentFragment() native;
+ /// Deprecated: use new Element.tag(tagName) instead.
/// @domName Document.createElement; @docsEditable true
@JSName('createElement')
Element $dom_createElement(String tagName) native;
@@ -5697,10 +5884,12 @@
@JSName('createTouch')
Touch _$dom_createTouch_1(LocalWindow window, target, identifier, pageX, pageY, screenX, screenY, webkitRadiusX, webkitRadiusY, webkitRotationAngle, webkitForce) native;
+ /// Use the [TouchList] constructor isntead.
/// @domName Document.createTouchList; @docsEditable true
@JSName('createTouchList')
TouchList $dom_createTouchList() native;
+ /// Moved to [HtmlDocument].
/// @domName Document.elementFromPoint; @docsEditable true
@JSName('elementFromPoint')
Element $dom_elementFromPoint(int x, int y) native;
@@ -5712,6 +5901,7 @@
@JSName('getCSSCanvasContext')
CanvasRenderingContext $dom_getCssCanvasContext(String contextId, String name, int width, int height) native;
+ /// Deprecated: use query("#$elementId") instead.
/// @domName Document.getElementById; @docsEditable true
@JSName('getElementById')
Element $dom_getElementById(String elementId) native;
@@ -5746,23 +5936,28 @@
/// @domName Document.queryCommandValue; @docsEditable true
String queryCommandValue(String command) native;
+ /// Deprecated: renamed to the shorter name [query].
/// @domName Document.querySelector; @docsEditable true
@JSName('querySelector')
Element $dom_querySelector(String selectors) native;
+ /// Deprecated: use query("#$elementId") instead.
/// @domName Document.querySelectorAll; @docsEditable true
@JSName('querySelectorAll')
@Returns('NodeList') @Creates('NodeList')
List<Node> $dom_querySelectorAll(String selectors) native;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitCancelFullScreen; @docsEditable true
@JSName('webkitCancelFullScreen')
void $dom_webkitCancelFullScreen() native;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitExitFullscreen; @docsEditable true
@JSName('webkitExitFullscreen')
void $dom_webkitExitFullscreen() native;
+ /// Moved to [HtmlDocument].
/// @domName Document.webkitExitPointerLock; @docsEditable true
@JSName('webkitExitPointerLock')
void $dom_webkitExitPointerLock() native;
@@ -5830,15 +6025,21 @@
}
}
+/// @docsEditable true
class DocumentEvents extends ElementEvents {
+ /// @docsEditable true
DocumentEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get readyStateChange => this['readystatechange'];
+ /// @docsEditable true
EventListenerList get selectionChange => this['selectionchange'];
+ /// @docsEditable true
EventListenerList get pointerLockChange => this['webkitpointerlockchange'];
+ /// @docsEditable true
EventListenerList get pointerLockError => this['webkitpointerlockerror'];
}
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
@@ -5871,9 +6072,11 @@
factory DocumentFragment.svg(String svgContent) =>
_DocumentFragmentFactoryProvider.createDocumentFragment_svg(svgContent);
+ @deprecated
List<Element> get elements => this.children;
// TODO: The type of value should be Collection<Element>. See http://b/5392897
+ @deprecated
void set elements(value) {
this.children = value;
}
@@ -5979,12 +6182,12 @@
String get webkitdropzone => "";
String get webkitRegionOverflow => "";
Element get $m_firstElementChild {
- if (elements.length > 0) {
- return elements[0];
+ if (children.length > 0) {
+ return children[0];
}
return null;
}
- Element get $m_lastElementChild => elements.last;
+ Element get $m_lastElementChild => children.last;
Element get nextElementSibling => null;
Element get previousElementSibling => null;
Element get offsetParent => null;
@@ -7173,11 +7376,14 @@
/**
* Deprecated, use innerHtml instead.
*/
+ @deprecated
String get innerHTML => this.innerHtml;
+ @deprecated
void set innerHTML(String value) {
this.innerHtml = value;
}
+ @deprecated
void set elements(Collection<Element> value) {
this.children = value;
}
@@ -7185,6 +7391,7 @@
/**
* Deprecated, use [children] instead.
*/
+ @deprecated
List<Element> get elements => this.children;
/**
@@ -7325,7 +7532,7 @@
void _insertAdjacentNode(String where, Node node) {
switch (where.toLowerCase()) {
case 'beforebegin':
- this.parent.insertBefore(node, this);
+ this.parentNode.insertBefore(node, this);
break;
case 'afterbegin':
var first = this.nodes.length > 0 ? this.nodes[0] : null;
@@ -7335,7 +7542,7 @@
this.nodes.add(node);
break;
case 'afterend':
- this.parent.insertBefore(node, this.nextNode);
+ this.parentNode.insertBefore(node, this.nextNode);
break;
default:
throw new ArgumentError("Invalid position ${where}");
@@ -7628,98 +7835,145 @@
class ElementEvents extends Events {
ElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get beforeCopy => this['beforecopy'];
+ /// @docsEditable true
EventListenerList get beforeCut => this['beforecut'];
+ /// @docsEditable true
EventListenerList get beforePaste => this['beforepaste'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get change => this['change'];
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get contextMenu => this['contextmenu'];
+ /// @docsEditable true
EventListenerList get copy => this['copy'];
+ /// @docsEditable true
EventListenerList get cut => this['cut'];
+ /// @docsEditable true
EventListenerList get doubleClick => this['dblclick'];
+ /// @docsEditable true
EventListenerList get drag => this['drag'];
+ /// @docsEditable true
EventListenerList get dragEnd => this['dragend'];
+ /// @docsEditable true
EventListenerList get dragEnter => this['dragenter'];
+ /// @docsEditable true
EventListenerList get dragLeave => this['dragleave'];
+ /// @docsEditable true
EventListenerList get dragOver => this['dragover'];
+ /// @docsEditable true
EventListenerList get dragStart => this['dragstart'];
+ /// @docsEditable true
EventListenerList get drop => this['drop'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get input => this['input'];
+ /// @docsEditable true
EventListenerList get invalid => this['invalid'];
+ /// @docsEditable true
EventListenerList get keyDown => this['keydown'];
+ /// @docsEditable true
EventListenerList get keyPress => this['keypress'];
+ /// @docsEditable true
EventListenerList get keyUp => this['keyup'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get mouseDown => this['mousedown'];
+ /// @docsEditable true
EventListenerList get mouseMove => this['mousemove'];
+ /// @docsEditable true
EventListenerList get mouseOut => this['mouseout'];
+ /// @docsEditable true
EventListenerList get mouseOver => this['mouseover'];
+ /// @docsEditable true
EventListenerList get mouseUp => this['mouseup'];
+ /// @docsEditable true
EventListenerList get paste => this['paste'];
+ /// @docsEditable true
EventListenerList get reset => this['reset'];
+ /// @docsEditable true
EventListenerList get scroll => this['scroll'];
+ /// @docsEditable true
EventListenerList get search => this['search'];
+ /// @docsEditable true
EventListenerList get select => this['select'];
+ /// @docsEditable true
EventListenerList get selectStart => this['selectstart'];
+ /// @docsEditable true
EventListenerList get submit => this['submit'];
+ /// @docsEditable true
EventListenerList get touchCancel => this['touchcancel'];
+ /// @docsEditable true
EventListenerList get touchEnd => this['touchend'];
+ /// @docsEditable true
EventListenerList get touchEnter => this['touchenter'];
+ /// @docsEditable true
EventListenerList get touchLeave => this['touchleave'];
+ /// @docsEditable true
EventListenerList get touchMove => this['touchmove'];
+ /// @docsEditable true
EventListenerList get touchStart => this['touchstart'];
+ /// @docsEditable true
EventListenerList get transitionEnd => this['webkitTransitionEnd'];
+ /// @docsEditable true
EventListenerList get fullscreenChange => this['webkitfullscreenchange'];
+ /// @docsEditable true
EventListenerList get fullscreenError => this['webkitfullscreenerror'];
EventListenerList get mouseWheel {
@@ -7759,7 +8013,7 @@
/// @domName HTMLEmbedElement; @docsEditable true
-class EmbedElement extends Element implements Element native "*HTMLEmbedElement" {
+class EmbedElement extends Element native "*HTMLEmbedElement" {
///@docsEditable true
factory EmbedElement() => document.$dom_createElement("embed");
@@ -8093,13 +8347,18 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class EventSourceEvents extends Events {
+ /// @docsEditable true
EventSourceEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -8213,7 +8472,7 @@
/// @domName HTMLFieldSetElement; @docsEditable true
-class FieldSetElement extends Element implements Element native "*HTMLFieldSetElement" {
+class FieldSetElement extends Element native "*HTMLFieldSetElement" {
///@docsEditable true
factory FieldSetElement() => document.$dom_createElement("fieldset");
@@ -8554,19 +8813,27 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class FileReaderEvents extends Events {
+ /// @docsEditable true
FileReaderEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get loadEnd => this['loadend'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -8684,19 +8951,27 @@
void write(Blob data) native;
}
+/// @docsEditable true
class FileWriterEvents extends Events {
+ /// @docsEditable true
FileWriterEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get write => this['write'];
+ /// @docsEditable true
EventListenerList get writeEnd => this['writeend'];
+ /// @docsEditable true
EventListenerList get writeStart => this['writestart'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -8988,7 +9263,7 @@
/// @domName HTMLFontElement; @docsEditable true
-class FontElement extends Element implements Element native "*HTMLFontElement" {
+class FontElement extends Element native "*HTMLFontElement" {
/// @domName HTMLFontElement.color; @docsEditable true
String color;
@@ -9015,8 +9290,8 @@
return _FormDataFactoryProvider.createFormData(form);
}
- /// @domName FormData.append; @docsEditable true
- void append(String name, String value, String filename) native;
+ /// @domName DOMFormData.append; @docsEditable true
+ void append(String name, value, [String filename]) native;
}
// 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
@@ -9024,7 +9299,7 @@
/// @domName HTMLFormElement; @docsEditable true
-class FormElement extends Element implements Element native "*HTMLFormElement" {
+class FormElement extends Element native "*HTMLFormElement" {
///@docsEditable true
factory FormElement() => document.$dom_createElement("form");
@@ -9074,7 +9349,7 @@
/// @domName HTMLFrameElement; @docsEditable true
-class FrameElement extends Element implements Element native "*HTMLFrameElement" {
+class FrameElement extends Element native "*HTMLFrameElement" {
/// @domName HTMLFrameElement.contentWindow; @docsEditable true
Window get contentWindow => _convertNativeToDart_Window(this._contentWindow);
@@ -9121,7 +9396,7 @@
/// @domName HTMLFrameSetElement; @docsEditable true
-class FrameSetElement extends Element implements Element native "*HTMLFrameSetElement" {
+class FrameSetElement extends Element native "*HTMLFrameSetElement" {
/// @domName EventTarget.addEventListener, EventTarget.removeEventListener, EventTarget.dispatchEvent; @docsEditable true
FrameSetElementEvents get on =>
@@ -9134,33 +9409,48 @@
String rows;
}
+/// @docsEditable true
class FrameSetElementEvents extends ElementEvents {
+ /// @docsEditable true
FrameSetElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get beforeUnload => this['beforeunload'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get hashChange => this['hashchange'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get offline => this['offline'];
+ /// @docsEditable true
EventListenerList get online => this['online'];
+ /// @docsEditable true
EventListenerList get popState => this['popstate'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get storage => this['storage'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -9223,7 +9513,7 @@
/// @domName HTMLHRElement; @docsEditable true
-class HRElement extends Element implements Element native "*HTMLHRElement" {
+class HRElement extends Element native "*HTMLHRElement" {
///@docsEditable true
factory HRElement() => document.$dom_createElement("hr");
@@ -9265,7 +9555,7 @@
/// @domName HTMLHeadElement; @docsEditable true
-class HeadElement extends Element implements Element native "*HTMLHeadElement" {
+class HeadElement extends Element native "*HTMLHeadElement" {
///@docsEditable true
factory HeadElement() => document.$dom_createElement("head");
@@ -9279,7 +9569,7 @@
/// @domName HTMLHeadingElement; @docsEditable true
-class HeadingElement extends Element implements Element native "*HTMLHeadingElement" {
+class HeadingElement extends Element native "*HTMLHeadingElement" {
///@docsEditable true
factory HeadingElement.h1() => document.$dom_createElement("h1");
@@ -9646,7 +9936,7 @@
/// @domName HTMLHtmlElement; @docsEditable true
-class HtmlElement extends Element implements Element native "*HTMLHtmlElement" {
+class HtmlElement extends Element native "*HTMLHtmlElement" {
///@docsEditable true
factory HtmlElement() => document.$dom_createElement("html");
@@ -9731,9 +10021,27 @@
onComplete);
+ /**
+ * General constructor for any type of request (GET, POST, etc).
+ *
+ * This call is used in conjunction with [open]:
+ *
+ * var request = new HttpRequest();
+ * request.open('GET', 'http://dartlang.org')
+ * request.on.load.add((event) => print('Request complete'));
+ *
+ * is the (more verbose) equivalent of
+ *
+ * var request = new HttpRequest.get('http://dartlang.org', (event) => print('Request complete'));
+ */
///@docsEditable true
factory HttpRequest() => _HttpRequestFactoryProvider.createHttpRequest();
+ /**
+ * Get the set of [HttpRequestEvents] that this request can respond to.
+ * Usually used when adding an EventListener, such as in
+ * `document.window.on.keyDown.add((e) => print('keydown happened'))`.
+ */
/// @domName EventTarget.addEventListener, EventTarget.removeEventListener, EventTarget.dispatchEvent; @docsEditable true
HttpRequestEvents get on =>
new HttpRequestEvents(this);
@@ -9748,16 +10056,37 @@
static const int UNSENT = 0;
+ /** @domName XMLHttpRequest.readyState */
/// @domName XMLHttpRequest.readyState; @docsEditable true
final int readyState;
+ /**
+ * The data received as a reponse from the request.
+ *
+ * The data could be in the
+ * form of a [String], [ArrayBuffer], [Document], [Blob], or json (also a
+ * [String]). `null` indicates request failure.
+ */
/// @domName XMLHttpRequest.response; @docsEditable true
@Creates('ArrayBuffer|Blob|Document|=Object|=List|String|num')
final Object response;
+ /**
+ * The response in string form or `null` on failure.
+ */
/// @domName XMLHttpRequest.responseText; @docsEditable true
final String responseText;
+ /**
+ * [String] telling the server the desired response format.
+ *
+ * Default is `String`.
+ * Other options are one of 'arraybuffer', 'blob', 'document', 'json',
+ * 'text'. Some newer browsers will throw NS_ERROR_DOM_INVALID_ACCESS_ERR if
+ * `responseType` is set while performing a synchronous request.
+ *
+ * See also: [MDN responseType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType)
+ */
/// @domName XMLHttpRequest.responseType; @docsEditable true
String responseType;
@@ -9765,18 +10094,43 @@
@JSName('responseXML')
final Document responseXml;
+ /**
+ * The http result code from the request (200, 404, etc).
+ * See also: [Http Status Codes](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
+ */
/// @domName XMLHttpRequest.status; @docsEditable true
final int status;
+ /**
+ * The request response string (such as "200 OK").
+ * See also: [Http Status Codes](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
+ */
/// @domName XMLHttpRequest.statusText; @docsEditable true
final String statusText;
+ /**
+ * [EventTarget] that can hold listeners to track the progress of the request.
+ * The events fired will be members of [HttpRequestUploadEvents].
+ */
/// @domName XMLHttpRequest.upload; @docsEditable true
final HttpRequestUpload upload;
+ /**
+ * True if cross-site requests should use credentials such as cookies
+ * or authorization headers; false otherwise.
+ *
+ * This value is ignored for same-site requests.
+ */
/// @domName XMLHttpRequest.withCredentials; @docsEditable true
bool withCredentials;
+ /**
+ * Stop the current request.
+ *
+ * The request can only be stopped if readyState is `HEADERS_RECIEVED` or
+ * `LOADING`. If this method is not in the process of being sent, the method
+ * has no effect.
+ */
/// @domName XMLHttpRequest.abort; @docsEditable true
void abort() native;
@@ -9788,15 +10142,48 @@
@JSName('dispatchEvent')
bool $dom_dispatchEvent(Event evt) native;
+ /**
+ * Retrieve all the response headers from a request.
+ *
+ * `null` if no headers have been received. For multipart requests,
+ * `getAllResponseHeaders` will return the response headers for the current
+ * part of the request.
+ *
+ * See also [HTTP response headers](http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Responses)
+ * for a list of common response headers.
+ */
/// @domName XMLHttpRequest.getAllResponseHeaders; @docsEditable true
String getAllResponseHeaders() native;
+ /**
+ * Return the response header named `header`, or `null` if not found.
+ *
+ * See also [HTTP response headers](http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Responses)
+ * for a list of common response headers.
+ */
/// @domName XMLHttpRequest.getResponseHeader; @docsEditable true
String getResponseHeader(String header) native;
+ /**
+ * Specify the desired `url`, and `method` to use in making the request.
+ *
+ * By default the request is done asyncronously, with no user or password
+ * authentication information. If `async` is false, the request will be send
+ * synchronously.
+ *
+ * Calling `open` again on a currently active request is equivalent to
+ * calling `abort`.
+ */
/// @domName XMLHttpRequest.open; @docsEditable true
void open(String method, String url, [bool async, String user, String password]) native;
+ /**
+ * Specify a particular MIME type (such as `text/xml`) desired for the
+ * response.
+ *
+ * This value must be set before the request has been sent. See also the list
+ * of [common MIME types](http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types)
+ */
/// @domName XMLHttpRequest.overrideMimeType; @docsEditable true
void overrideMimeType(String override) native;
@@ -9804,29 +10191,90 @@
@JSName('removeEventListener')
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
+ /**
+ * Send the request with any given `data`.
+ *
+ * See also:
+ * [send() docs](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#send())
+ * from MDN.
+ */
/// @domName XMLHttpRequest.send; @docsEditable true
void send([data]) native;
+ /** Sets HTTP `header` to `value`. */
/// @domName XMLHttpRequest.setRequestHeader; @docsEditable true
void setRequestHeader(String header, String value) native;
}
+/**
+ * A class that supports listening for and dispatching events that can fire when
+ * making an HTTP request.
+ *
+ * Here's an example of adding an event handler that executes once an HTTP
+ * request has fully loaded:
+ *
+ * httpRequest.on.loadEnd.add((e) => myCustomLoadEndHandler(e));
+ *
+ * Each property of this class is a read-only pointer to an [EventListenerList].
+ * That list holds all of the [EventListener]s that have registered for that
+ * particular type of event that fires from an HttpRequest.
+ */
+/// @docsEditable true
class HttpRequestEvents extends Events {
+ /// @docsEditable true
HttpRequestEvents(EventTarget _ptr) : super(_ptr);
+ /**
+ * Event listeners to be notified when request has been aborted,
+ * generally due to calling `httpRequest.abort()`.
+ */
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /**
+ * Event listeners to be notified when a request has failed, such as when a
+ * cross-domain error occurred or the file wasn't found on the server.
+ */
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /**
+ * Event listeners to be notified once the request has completed
+ * *successfully*.
+ */
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /**
+ * Event listeners to be notified once the request has completed (on
+ * either success or failure).
+ */
+ /// @docsEditable true
EventListenerList get loadEnd => this['loadend'];
+ /**
+ * Event listeners to be notified when the request starts, once
+ * `httpRequest.send()` has been called.
+ */
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /**
+ * Event listeners to be notified when data for the request
+ * is being sent or loaded.
+ *
+ * Progress events are fired every 50ms or for every byte transmitted,
+ * whichever is less frequent.
+ */
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /**
+ * Event listeners to be notified every time the [HttpRequest]
+ * object's `readyState` changes values.
+ */
+ /// @docsEditable true
EventListenerList get readyStateChange => this['readystatechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -9892,19 +10340,27 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class HttpRequestUploadEvents extends Events {
+ /// @docsEditable true
HttpRequestUploadEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get loadEnd => this['loadend'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -9913,7 +10369,7 @@
/// @domName HTMLIFrameElement; @docsEditable true
-class IFrameElement extends Element implements Element native "*HTMLIFrameElement" {
+class IFrameElement extends Element native "*HTMLIFrameElement" {
///@docsEditable true
factory IFrameElement() => document.$dom_createElement("iframe");
@@ -10008,7 +10464,7 @@
/// @domName HTMLImageElement; @docsEditable true
-class ImageElement extends Element implements Element native "*HTMLImageElement" {
+class ImageElement extends Element native "*HTMLImageElement" {
///@docsEditable true
factory ImageElement({String src, int width, int height}) {
@@ -10081,8 +10537,31 @@
// BSD-style license that can be found in the LICENSE file.
-/// @domName HTMLInputElement; @docsEditable true
-class InputElement extends Element implements Element native "*HTMLInputElement" {
+/// @domName HTMLInputElement
+class InputElement extends Element implements
+ HiddenInputElement,
+ SearchInputElement,
+ TextInputElement,
+ UrlInputElement,
+ TelephoneInputElement,
+ EmailInputElement,
+ PasswordInputElement,
+ DateTimeInputElement,
+ DateInputElement,
+ MonthInputElement,
+ WeekInputElement,
+ TimeInputElement,
+ LocalDateTimeInputElement,
+ NumberInputElement,
+ RangeInputElement,
+ CheckboxInputElement,
+ RadioButtonInputElement,
+ FileUploadInputElement,
+ SubmitButtonInputElement,
+ ImageButtonInputElement,
+ ResetButtonInputElement,
+ ButtonInputElement
+ native "*HTMLInputElement" {
///@docsEditable true
factory InputElement({String type}) {
@@ -10268,11 +10747,480 @@
/// @domName HTMLInputElement.stepUp; @docsEditable true
void stepUp([int n]) native;
+
}
+
+// Interfaces representing the InputElement APIs which are supported
+// for the various types of InputElement.
+// From http://dev.w3.org/html5/spec/the-input-element.html#the-input-element.
+
+/**
+ * Exposes the functionality common between all InputElement types.
+ */
+abstract class InputElementBase implements Element {
+ /// @domName HTMLInputElement.autofocus
+ bool autofocus;
+
+ /// @domName HTMLInputElement.disabled
+ bool disabled;
+
+ /// @domName HTMLInputElement.incremental
+ bool incremental;
+
+ /// @domName HTMLInputElement.indeterminate
+ bool indeterminate;
+
+ /// @domName HTMLInputElement.labels
+ List<Node> get labels;
+
+ /// @domName HTMLInputElement.name
+ String name;
+
+ /// @domName HTMLInputElement.validationMessage
+ String get validationMessage;
+
+ /// @domName HTMLInputElement.validity
+ ValidityState get validity;
+
+ /// @domName HTMLInputElement.value
+ String value;
+
+ /// @domName HTMLInputElement.willValidate
+ bool get willValidate;
+
+ /// @domName HTMLInputElement.checkValidity
+ bool checkValidity();
+
+ /// @domName HTMLInputElement.setCustomValidity
+ void setCustomValidity(String error);
+}
+
+/**
+ * Hidden input which is not intended to be seen or edited by the user.
+ */
+abstract class HiddenInputElement implements Element {
+ factory HiddenInputElement() => new InputElement(type: 'hidden');
+}
+
+
+/**
+ * Base interface for all inputs which involve text editing.
+ */
+abstract class TextInputElementBase implements InputElementBase {
+ /// @domName HTMLInputElement.autocomplete
+ String autocomplete;
+
+ /// @domName HTMLInputElement.maxLength
+ int maxLength;
+
+ /// @domName HTMLInputElement.pattern
+ String pattern;
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.size
+ int size;
+
+ /// @domName HTMLInputElement.select
+ void select();
+
+ /// @domName HTMLInputElement.selectionDirection
+ String selectionDirection;
+
+ /// @domName HTMLInputElement.selectionEnd
+ int selectionEnd;
+
+ /// @domName HTMLInputElement.selectionStart
+ int selectionStart;
+
+ /// @domName HTMLInputElement.setSelectionRange
+ void setSelectionRange(int start, int end, [String direction]);
+}
+
+/**
+ * Similar to [TextInputElement], but on platforms where search is styled
+ * differently this will get the search style.
+ */
+abstract class SearchInputElement implements TextInputElementBase {
+ factory SearchInputElement() => new InputElement(type: 'search');
+
+ /// @domName HTMLInputElement.dirName;
+ String dirName;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * A basic text input editor control.
+ */
+abstract class TextInputElement implements TextInputElementBase {
+ factory TextInputElement() => new InputElement(type: 'text');
+
+ /// @domName HTMLInputElement.dirName;
+ String dirName;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * A control for editing an absolute URL.
+ */
+abstract class UrlInputElement implements TextInputElementBase {
+ factory UrlInputElement() => new InputElement(type: 'url');
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * Represents a control for editing a telephone number.
+ *
+ * This provides a single line of text with minimal formatting help since
+ * there is a wide variety of telephone numbers.
+ */
+abstract class TelephoneInputElement implements TextInputElementBase {
+ factory TelephoneInputElement() => new InputElement(type: 'tel');
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * An e-mail address or list of e-mail addresses.
+ */
+abstract class EmailInputElement implements TextInputElementBase {
+ factory EmailInputElement() => new InputElement(type: 'email');
+
+ /// @domName HTMLInputElement.autocomplete
+ String autocomplete;
+
+ /// @domName HTMLInputElement.autofocus
+ bool autofocus;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+
+ /// @domName HTMLInputElement.maxLength
+ int maxLength;
+
+ /// @domName HTMLInputElement.multiple;
+ bool multiple;
+
+ /// @domName HTMLInputElement.pattern
+ String pattern;
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.size
+ int size;
+}
+
+/**
+ * Text with no line breaks (sensitive information).
+ */
+abstract class PasswordInputElement implements TextInputElementBase {
+ factory PasswordInputElement() => new InputElement(type: 'password');
+}
+
+/**
+ * Base interface for all input element types which involve ranges.
+ */
+abstract class RangeInputElementBase implements InputElementBase {
+
+ /// @domName HTMLInputElement.list
+ Element get list;
+
+ /// @domName HTMLInputElement.max
+ String max;
+
+ /// @domName HTMLInputElement.min
+ String min;
+
+ /// @domName HTMLInputElement.step
+ String step;
+
+ /// @domName HTMLInputElement.valueAsNumber
+ num valueAsNumber;
+
+ /// @domName HTMLInputElement.stepDown
+ void stepDown([int n]);
+
+ /// @domName HTMLInputElement.stepUp
+ void stepUp([int n]);
+}
+
+/**
+ * A date and time (year, month, day, hour, minute, second, fraction of a
+ * second) with the time zone set to UTC.
+ */
+abstract class DateTimeInputElement implements RangeInputElementBase {
+ factory DateTimeInputElement() => new InputElement(type: 'datetime');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date (year, month, day) with no time zone.
+ */
+abstract class DateInputElement implements RangeInputElementBase {
+ factory DateInputElement() => new InputElement(type: 'date');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date consisting of a year and a month with no time zone.
+ */
+abstract class MonthInputElement implements RangeInputElementBase {
+ factory MonthInputElement() => new InputElement(type: 'month');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date consisting of a week-year number and a week number with no time zone.
+ */
+abstract class WeekInputElement implements RangeInputElementBase {
+ factory WeekInputElement() => new InputElement(type: 'week');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A time (hour, minute, seconds, fractional seconds) with no time zone.
+ */
+abstract class TimeInputElement implements RangeInputElementBase {
+ factory TimeInputElement() => new InputElement(type: 'time');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date and time (year, month, day, hour, minute, second, fraction of a
+ * second) with no time zone.
+ */
+abstract class LocalDateTimeInputElement implements RangeInputElementBase {
+ factory LocalDateTimeInputElement() =>
+ new InputElement(type: 'datetime-local');
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A numeric editor control.
+ */
+abstract class NumberInputElement implements RangeInputElementBase {
+ factory NumberInputElement() => new InputElement(type: 'number');
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * Similar to [NumberInputElement] but the browser may provide more optimal
+ * styling (such as a slider control).
+ */
+abstract class RangeInputElement implements RangeInputElementBase {
+ factory RangeInputElement() => new InputElement(type: 'range');
+}
+
+/**
+ * A boolean editor control.
+ *
+ * Note that if [indeterminate] is set then this control is in a third
+ * indeterminate state.
+ */
+abstract class CheckboxInputElement implements InputElementBase {
+ factory CheckboxInputElement() => new InputElement(type: 'checkbox');
+
+ /// @domName HTMLInputElement.checked
+ bool checked;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+
+/**
+ * A control that when used with other [ReadioButtonInputElement] controls
+ * forms a radio button group in which only one control can be checked at a
+ * time.
+ *
+ * Radio buttons are considered to be in the same radio button group if:
+ *
+ * * They are all of type 'radio'.
+ * * They all have either the same [FormElement] owner, or no owner.
+ * * Their name attributes contain the same name.
+ */
+abstract class RadioButtonInputElement implements InputElementBase {
+ factory RadioButtonInputElement() => new InputElement(type: 'radio');
+
+ /// @domName HTMLInputElement.checked
+ bool checked;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A control for picking files from the user's computer.
+ */
+abstract class FileUploadInputElement implements InputElementBase {
+ factory FileUploadInputElement() => new InputElement(type: 'file');
+
+ /// @domName HTMLInputElement.accept
+ String accept;
+
+ /// @domName HTMLInputElement.multiple
+ bool multiple;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.files
+ List<File> files;
+}
+
+/**
+ * A button, which when clicked, submits the form.
+ */
+abstract class SubmitButtonInputElement implements InputElementBase {
+ factory SubmitButtonInputElement() => new InputElement(type: 'submit');
+
+ /// @domName HTMLInputElement.formAction
+ String formAction;
+
+ /// @domName HTMLInputElement.formEnctype
+ String formEnctype;
+
+ /// @domName HTMLInputElement.formMethod
+ String formMethod;
+
+ /// @domName HTMLInputElement.formNoValidate
+ bool formNoValidate;
+
+ /// @domName HTMLInputElement.formTarget
+ String formTarget;
+}
+
+/**
+ * Either an image which the user can select a coordinate to or a form
+ * submit button.
+ */
+abstract class ImageButtonInputElement implements InputElementBase {
+ factory ImageButtonInputElement() => new InputElement(type: 'image');
+
+ /// @domName HTMLInputElement.alt
+ String alt;
+
+ /// @domName HTMLInputElement.formAction
+ String formAction;
+
+ /// @domName HTMLInputElement.formEnctype
+ String formEnctype;
+
+ /// @domName HTMLInputElement.formMethod
+ String formMethod;
+
+ /// @domName HTMLInputElement.formNoValidate
+ bool formNoValidate;
+
+ /// @domName HTMLInputElement.formTarget
+ String formTarget;
+
+ /// @domName HTMLInputElement.height
+ int height;
+
+ /// @domName HTMLInputElement.src
+ String src;
+
+ /// @domName HTMLInputElement.width
+ int width;
+}
+
+/**
+ * A button, which when clicked, resets the form.
+ */
+abstract class ResetButtonInputElement implements InputElementBase {
+ factory ResetButtonInputElement() => new InputElement(type: 'reset');
+}
+
+/**
+ * A button, with no default behavior.
+ */
+abstract class ButtonInputElement implements InputElementBase {
+ factory ButtonInputElement() => new InputElement(type: 'button');
+}
+
+
+/// @docsEditable true
class InputElementEvents extends ElementEvents {
+ /// @docsEditable true
InputElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get speechChange => this['webkitSpeechChange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -10778,7 +11726,7 @@
/// @domName HTMLKeygenElement; @docsEditable true
-class KeygenElement extends Element implements Element native "*HTMLKeygenElement" {
+class KeygenElement extends Element native "*HTMLKeygenElement" {
///@docsEditable true
factory KeygenElement() => document.$dom_createElement("keygen");
@@ -10829,7 +11777,7 @@
/// @domName HTMLLIElement; @docsEditable true
-class LIElement extends Element implements Element native "*HTMLLIElement" {
+class LIElement extends Element native "*HTMLLIElement" {
///@docsEditable true
factory LIElement() => document.$dom_createElement("li");
@@ -10846,7 +11794,7 @@
/// @domName HTMLLabelElement; @docsEditable true
-class LabelElement extends Element implements Element native "*HTMLLabelElement" {
+class LabelElement extends Element native "*HTMLLabelElement" {
///@docsEditable true
factory LabelElement() => document.$dom_createElement("label");
@@ -10866,7 +11814,7 @@
/// @domName HTMLLegendElement; @docsEditable true
-class LegendElement extends Element implements Element native "*HTMLLegendElement" {
+class LegendElement extends Element native "*HTMLLegendElement" {
///@docsEditable true
factory LegendElement() => document.$dom_createElement("legend");
@@ -10883,7 +11831,7 @@
/// @domName HTMLLinkElement; @docsEditable true
-class LinkElement extends Element implements Element native "*HTMLLinkElement" {
+class LinkElement extends Element native "*HTMLLinkElement" {
///@docsEditable true
factory LinkElement() => document.$dom_createElement("link");
@@ -11451,153 +12399,228 @@
}
+/// @docsEditable true
class LocalWindowEvents extends Events {
+ /// @docsEditable true
LocalWindowEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get beforeUnload => this['beforeunload'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get canPlay => this['canplay'];
+ /// @docsEditable true
EventListenerList get canPlayThrough => this['canplaythrough'];
+ /// @docsEditable true
EventListenerList get change => this['change'];
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get contextMenu => this['contextmenu'];
+ /// @docsEditable true
EventListenerList get doubleClick => this['dblclick'];
+ /// @docsEditable true
EventListenerList get deviceMotion => this['devicemotion'];
+ /// @docsEditable true
EventListenerList get deviceOrientation => this['deviceorientation'];
+ /// @docsEditable true
EventListenerList get drag => this['drag'];
+ /// @docsEditable true
EventListenerList get dragEnd => this['dragend'];
+ /// @docsEditable true
EventListenerList get dragEnter => this['dragenter'];
+ /// @docsEditable true
EventListenerList get dragLeave => this['dragleave'];
+ /// @docsEditable true
EventListenerList get dragOver => this['dragover'];
+ /// @docsEditable true
EventListenerList get dragStart => this['dragstart'];
+ /// @docsEditable true
EventListenerList get drop => this['drop'];
+ /// @docsEditable true
EventListenerList get durationChange => this['durationchange'];
+ /// @docsEditable true
EventListenerList get emptied => this['emptied'];
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get hashChange => this['hashchange'];
+ /// @docsEditable true
EventListenerList get input => this['input'];
+ /// @docsEditable true
EventListenerList get invalid => this['invalid'];
+ /// @docsEditable true
EventListenerList get keyDown => this['keydown'];
+ /// @docsEditable true
EventListenerList get keyPress => this['keypress'];
+ /// @docsEditable true
EventListenerList get keyUp => this['keyup'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get loadedData => this['loadeddata'];
+ /// @docsEditable true
EventListenerList get loadedMetadata => this['loadedmetadata'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get mouseDown => this['mousedown'];
+ /// @docsEditable true
EventListenerList get mouseMove => this['mousemove'];
+ /// @docsEditable true
EventListenerList get mouseOut => this['mouseout'];
+ /// @docsEditable true
EventListenerList get mouseOver => this['mouseover'];
+ /// @docsEditable true
EventListenerList get mouseUp => this['mouseup'];
+ /// @docsEditable true
EventListenerList get mouseWheel => this['mousewheel'];
+ /// @docsEditable true
EventListenerList get offline => this['offline'];
+ /// @docsEditable true
EventListenerList get online => this['online'];
+ /// @docsEditable true
EventListenerList get pageHide => this['pagehide'];
+ /// @docsEditable true
EventListenerList get pageShow => this['pageshow'];
+ /// @docsEditable true
EventListenerList get pause => this['pause'];
+ /// @docsEditable true
EventListenerList get play => this['play'];
+ /// @docsEditable true
EventListenerList get playing => this['playing'];
+ /// @docsEditable true
EventListenerList get popState => this['popstate'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get rateChange => this['ratechange'];
+ /// @docsEditable true
EventListenerList get reset => this['reset'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get scroll => this['scroll'];
+ /// @docsEditable true
EventListenerList get search => this['search'];
+ /// @docsEditable true
EventListenerList get seeked => this['seeked'];
+ /// @docsEditable true
EventListenerList get seeking => this['seeking'];
+ /// @docsEditable true
EventListenerList get select => this['select'];
+ /// @docsEditable true
EventListenerList get stalled => this['stalled'];
+ /// @docsEditable true
EventListenerList get storage => this['storage'];
+ /// @docsEditable true
EventListenerList get submit => this['submit'];
+ /// @docsEditable true
EventListenerList get suspend => this['suspend'];
+ /// @docsEditable true
EventListenerList get timeUpdate => this['timeupdate'];
+ /// @docsEditable true
EventListenerList get touchCancel => this['touchcancel'];
+ /// @docsEditable true
EventListenerList get touchEnd => this['touchend'];
+ /// @docsEditable true
EventListenerList get touchMove => this['touchmove'];
+ /// @docsEditable true
EventListenerList get touchStart => this['touchstart'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
+ /// @docsEditable true
EventListenerList get volumeChange => this['volumechange'];
+ /// @docsEditable true
EventListenerList get waiting => this['waiting'];
+ /// @docsEditable true
EventListenerList get animationEnd => this['webkitAnimationEnd'];
+ /// @docsEditable true
EventListenerList get animationIteration => this['webkitAnimationIteration'];
+ /// @docsEditable true
EventListenerList get animationStart => this['webkitAnimationStart'];
+ /// @docsEditable true
EventListenerList get transitionEnd => this['webkitTransitionEnd'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -11606,7 +12629,7 @@
/// @domName HTMLMapElement; @docsEditable true
-class MapElement extends Element implements Element native "*HTMLMapElement" {
+class MapElement extends Element native "*HTMLMapElement" {
///@docsEditable true
factory MapElement() => document.$dom_createElement("map");
@@ -11623,7 +12646,7 @@
/// @domName HTMLMarqueeElement; @docsEditable true
-class MarqueeElement extends Element implements Element native "*HTMLMarqueeElement" {
+class MarqueeElement extends Element native "*HTMLMarqueeElement" {
/// @domName HTMLMarqueeElement.behavior; @docsEditable true
String behavior;
@@ -11729,7 +12752,7 @@
/// @domName HTMLMediaElement; @docsEditable true
-class MediaElement extends Element implements Element native "*HTMLMediaElement" {
+class MediaElement extends Element native "*HTMLMediaElement" {
/// @domName EventTarget.addEventListener, EventTarget.removeEventListener, EventTarget.dispatchEvent; @docsEditable true
MediaElementEvents get on =>
@@ -11874,57 +12897,84 @@
void webkitGenerateKeyRequest(String keySystem, [Uint8Array initData]) native;
}
+/// @docsEditable true
class MediaElementEvents extends ElementEvents {
+ /// @docsEditable true
MediaElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get canPlay => this['canplay'];
+ /// @docsEditable true
EventListenerList get canPlayThrough => this['canplaythrough'];
+ /// @docsEditable true
EventListenerList get durationChange => this['durationchange'];
+ /// @docsEditable true
EventListenerList get emptied => this['emptied'];
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
+ /// @docsEditable true
EventListenerList get loadedData => this['loadeddata'];
+ /// @docsEditable true
EventListenerList get loadedMetadata => this['loadedmetadata'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get pause => this['pause'];
+ /// @docsEditable true
EventListenerList get play => this['play'];
+ /// @docsEditable true
EventListenerList get playing => this['playing'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get rateChange => this['ratechange'];
+ /// @docsEditable true
EventListenerList get seeked => this['seeked'];
+ /// @docsEditable true
EventListenerList get seeking => this['seeking'];
+ /// @docsEditable true
EventListenerList get show => this['show'];
+ /// @docsEditable true
EventListenerList get stalled => this['stalled'];
+ /// @docsEditable true
EventListenerList get suspend => this['suspend'];
+ /// @docsEditable true
EventListenerList get timeUpdate => this['timeupdate'];
+ /// @docsEditable true
EventListenerList get volumeChange => this['volumechange'];
+ /// @docsEditable true
EventListenerList get waiting => this['waiting'];
+ /// @docsEditable true
EventListenerList get keyAdded => this['webkitkeyadded'];
+ /// @docsEditable true
EventListenerList get keyError => this['webkitkeyerror'];
+ /// @docsEditable true
EventListenerList get keyMessage => this['webkitkeymessage'];
+ /// @docsEditable true
EventListenerList get needKey => this['webkitneedkey'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12143,9 +13193,12 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class MediaStreamEvents extends Events {
+ /// @docsEditable true
MediaStreamEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12202,13 +13255,18 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class MediaStreamTrackEvents extends Events {
+ /// @docsEditable true
MediaStreamTrackEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
+ /// @docsEditable true
EventListenerList get mute => this['mute'];
+ /// @docsEditable true
EventListenerList get unmute => this['unmute'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12259,11 +13317,15 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class MediaStreamTrackListEvents extends Events {
+ /// @docsEditable true
MediaStreamTrackListEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addTrack => this['addtrack'];
+ /// @docsEditable true
EventListenerList get removeTrack => this['removetrack'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12288,8 +13350,18 @@
// BSD-style license that can be found in the LICENSE file.
+/**
+ * An HTML <menu> element.
+ *
+ * A <menu> element represents an unordered list of menu commands.
+ *
+ * See also:
+ *
+ * * [Menu Element](https://developer.mozilla.org/en-US/docs/HTML/Element/menu) from MDN.
+ * * [Menu Element](http://www.w3.org/TR/html5/the-menu-element.html#the-menu-element) from the W3C.
+ */
/// @domName HTMLMenuElement; @docsEditable true
-class MenuElement extends Element implements Element native "*HTMLMenuElement" {
+class MenuElement extends Element native "*HTMLMenuElement" {
///@docsEditable true
factory MenuElement() => document.$dom_createElement("menu");
@@ -12394,9 +13466,12 @@
void start() native;
}
+/// @docsEditable true
class MessagePortEvents extends Events {
+ /// @docsEditable true
MessagePortEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get message => this['message'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12405,7 +13480,7 @@
/// @domName HTMLMetaElement; @docsEditable true
-class MetaElement extends Element implements Element native "*HTMLMetaElement" {
+class MetaElement extends Element native "*HTMLMetaElement" {
/// @domName HTMLMetaElement.content; @docsEditable true
String content;
@@ -12447,7 +13522,7 @@
/// @domName HTMLMeterElement; @docsEditable true
-class MeterElement extends Element implements Element native "*HTMLMeterElement" {
+class MeterElement extends Element native "*HTMLMeterElement" {
///@docsEditable true
factory MeterElement() => document.$dom_createElement("meter");
@@ -12480,7 +13555,7 @@
/// @domName HTMLModElement; @docsEditable true
-class ModElement extends Element implements Element native "*HTMLModElement" {
+class ModElement extends Element native "*HTMLModElement" {
/// @domName HTMLModElement.cite; @docsEditable true
String cite;
@@ -13157,9 +14232,9 @@
void remove() {
// TODO(jacobr): should we throw an exception if parent is already null?
// TODO(vsm): Use the native remove when available.
- if (this.parent != null) {
- final Node parent = this.parent;
- parent.$dom_removeChild(this);
+ if (this.parentNode != null) {
+ final Node parent = this.parentNode;
+ parentNode.$dom_removeChild(this);
}
}
@@ -13169,7 +14244,7 @@
*/
Node replaceWith(Node otherNode) {
try {
- final Node parent = this.parent;
+ final Node parent = this.parentNode;
parent.$dom_replaceChild(otherNode, this);
} catch (e) {
@@ -13250,9 +14325,12 @@
@JSName('ownerDocument')
final Document document;
+ /// @domName Node.parentElement; @docsEditable true
+ @JSName('parentElement')
+ final Element parent;
+
/// @domName Node.parentNode; @docsEditable true
- @JSName('parentNode')
- final Node parent;
+ final Node parentNode;
/// @domName Node.previousSibling; @docsEditable true
@JSName('previousSibling')
@@ -13564,17 +14642,24 @@
void show() native;
}
+/// @docsEditable true
class NotificationEvents extends Events {
+ /// @docsEditable true
NotificationEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get close => this['close'];
+ /// @docsEditable true
EventListenerList get display => this['display'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get show => this['show'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -13612,7 +14697,7 @@
/// @domName HTMLOListElement; @docsEditable true
-class OListElement extends Element implements Element native "*HTMLOListElement" {
+class OListElement extends Element native "*HTMLOListElement" {
///@docsEditable true
factory OListElement() => document.$dom_createElement("ol");
@@ -13635,7 +14720,7 @@
/// @domName HTMLObjectElement; @docsEditable true
-class ObjectElement extends Element implements Element native "*HTMLObjectElement" {
+class ObjectElement extends Element native "*HTMLObjectElement" {
///@docsEditable true
factory ObjectElement() => document.$dom_createElement("object");
@@ -13764,7 +14849,7 @@
/// @domName HTMLOptGroupElement; @docsEditable true
-class OptGroupElement extends Element implements Element native "*HTMLOptGroupElement" {
+class OptGroupElement extends Element native "*HTMLOptGroupElement" {
///@docsEditable true
factory OptGroupElement() => document.$dom_createElement("optgroup");
@@ -13781,7 +14866,7 @@
/// @domName HTMLOptionElement; @docsEditable true
-class OptionElement extends Element implements Element native "*HTMLOptionElement" {
+class OptionElement extends Element native "*HTMLOptionElement" {
///@docsEditable true
factory OptionElement([String data, String value, bool defaultSelected, bool selected]) {
@@ -13827,7 +14912,7 @@
/// @domName HTMLOutputElement; @docsEditable true
-class OutputElement extends Element implements Element native "*HTMLOutputElement" {
+class OutputElement extends Element native "*HTMLOutputElement" {
///@docsEditable true
factory OutputElement() => document.$dom_createElement("output");
@@ -13923,7 +15008,7 @@
/// @domName HTMLParagraphElement; @docsEditable true
-class ParagraphElement extends Element implements Element native "*HTMLParagraphElement" {
+class ParagraphElement extends Element native "*HTMLParagraphElement" {
///@docsEditable true
factory ParagraphElement() => document.$dom_createElement("p");
@@ -13937,7 +15022,7 @@
/// @domName HTMLParamElement; @docsEditable true
-class ParamElement extends Element implements Element native "*HTMLParamElement" {
+class ParamElement extends Element native "*HTMLParamElement" {
///@docsEditable true
factory ParamElement() => document.$dom_createElement("param");
@@ -14101,17 +15186,24 @@
void _startIce_2() native;
}
+/// @docsEditable true
class PeerConnection00Events extends Events {
+ /// @docsEditable true
PeerConnection00Events(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addStream => this['addstream'];
+ /// @docsEditable true
EventListenerList get connecting => this['connecting'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
+ /// @docsEditable true
EventListenerList get removeStream => this['removestream'];
+ /// @docsEditable true
EventListenerList get stateChange => this['statechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -14301,7 +15393,7 @@
/// @domName HTMLPreElement; @docsEditable true
-class PreElement extends Element implements Element native "*HTMLPreElement" {
+class PreElement extends Element native "*HTMLPreElement" {
///@docsEditable true
factory PreElement() => document.$dom_createElement("pre");
@@ -14335,7 +15427,7 @@
/// @domName HTMLProgressElement; @docsEditable true
-class ProgressElement extends Element implements Element native "*HTMLProgressElement" {
+class ProgressElement extends Element native "*HTMLProgressElement" {
///@docsEditable true
factory ProgressElement() => document.$dom_createElement("progress");
@@ -14376,7 +15468,7 @@
/// @domName HTMLQuoteElement; @docsEditable true
-class QuoteElement extends Element implements Element native "*HTMLQuoteElement" {
+class QuoteElement extends Element native "*HTMLQuoteElement" {
/// @domName HTMLQuoteElement.cite; @docsEditable true
String cite;
@@ -14654,15 +15746,21 @@
void send(data) native;
}
+/// @docsEditable true
class RtcDataChannelEvents extends Events {
+ /// @docsEditable true
RtcDataChannelEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get close => this['close'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -14859,21 +15957,30 @@
void _updateIce_3() native;
}
+/// @docsEditable true
class RtcPeerConnectionEvents extends Events {
+ /// @docsEditable true
RtcPeerConnectionEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addStream => this['addstream'];
+ /// @docsEditable true
EventListenerList get iceCandidate => this['icecandidate'];
+ /// @docsEditable true
EventListenerList get iceChange => this['icechange'];
+ /// @docsEditable true
EventListenerList get negotiationNeeded => this['negotiationneeded'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
+ /// @docsEditable true
EventListenerList get removeStream => this['removestream'];
+ /// @docsEditable true
EventListenerList get stateChange => this['statechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -15010,7 +16117,7 @@
/// @domName HTMLScriptElement; @docsEditable true
-class ScriptElement extends Element implements Element native "*HTMLScriptElement" {
+class ScriptElement extends Element native "*HTMLScriptElement" {
///@docsEditable true
factory ScriptElement() => document.$dom_createElement("script");
@@ -15098,7 +16205,7 @@
/// @domName HTMLSelectElement
-class SelectElement extends Element implements Element native "*HTMLSelectElement" {
+class SelectElement extends Element native "*HTMLSelectElement" {
///@docsEditable true
factory SelectElement() => document.$dom_createElement("select");
@@ -15200,7 +16307,7 @@
/// @domName HTMLShadowElement; @docsEditable true
-class ShadowElement extends Element implements Element native "*HTMLShadowElement" {
+class ShadowElement extends Element native "*HTMLShadowElement" {
/// @domName HTMLShadowElement.resetStyleInheritance; @docsEditable true
bool resetStyleInheritance;
@@ -15290,9 +16397,12 @@
final String name;
}
+/// @docsEditable true
class SharedWorkerContextEvents extends WorkerContextEvents {
+ /// @docsEditable true
SharedWorkerContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get connect => this['connect'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -15447,7 +16557,7 @@
/// @domName HTMLSourceElement; @docsEditable true
-class SourceElement extends Element implements Element native "*HTMLSourceElement" {
+class SourceElement extends Element native "*HTMLSourceElement" {
///@docsEditable true
factory SourceElement() => document.$dom_createElement("source");
@@ -15467,7 +16577,7 @@
/// @domName HTMLSpanElement; @docsEditable true
-class SpanElement extends Element implements Element native "*HTMLSpanElement" {
+class SpanElement extends Element native "*HTMLSpanElement" {
///@docsEditable true
factory SpanElement() => document.$dom_createElement("span");
@@ -15690,29 +16800,42 @@
void stop() native;
}
+/// @docsEditable true
class SpeechRecognitionEvents extends Events {
+ /// @docsEditable true
SpeechRecognitionEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get audioEnd => this['audioend'];
+ /// @docsEditable true
EventListenerList get audioStart => this['audiostart'];
+ /// @docsEditable true
EventListenerList get end => this['end'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get noMatch => this['nomatch'];
+ /// @docsEditable true
EventListenerList get result => this['result'];
+ /// @docsEditable true
EventListenerList get soundEnd => this['soundend'];
+ /// @docsEditable true
EventListenerList get soundStart => this['soundstart'];
+ /// @docsEditable true
EventListenerList get speechEnd => this['speechend'];
+ /// @docsEditable true
EventListenerList get speechStart => this['speechstart'];
+ /// @docsEditable true
EventListenerList get start => this['start'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -16180,7 +17303,7 @@
/// @domName HTMLStyleElement; @docsEditable true
-class StyleElement extends Element implements Element native "*HTMLStyleElement" {
+class StyleElement extends Element native "*HTMLStyleElement" {
///@docsEditable true
factory StyleElement() => document.$dom_createElement("style");
@@ -16249,7 +17372,7 @@
/// @domName HTMLTableCaptionElement; @docsEditable true
-class TableCaptionElement extends Element implements Element native "*HTMLTableCaptionElement" {
+class TableCaptionElement extends Element native "*HTMLTableCaptionElement" {
///@docsEditable true
factory TableCaptionElement() => document.$dom_createElement("caption");
@@ -16263,7 +17386,7 @@
/// @domName HTMLTableCellElement; @docsEditable true
-class TableCellElement extends Element implements Element native "*HTMLTableCellElement" {
+class TableCellElement extends Element native "*HTMLTableCellElement" {
///@docsEditable true
factory TableCellElement() => document.$dom_createElement("td");
@@ -16319,7 +17442,7 @@
/// @domName HTMLTableColElement; @docsEditable true
-class TableColElement extends Element implements Element native "*HTMLTableColElement" {
+class TableColElement extends Element native "*HTMLTableColElement" {
///@docsEditable true
factory TableColElement() => document.$dom_createElement("col");
@@ -16348,7 +17471,7 @@
/// @domName HTMLTableElement
-class TableElement extends Element implements Element native "*HTMLTableElement" {
+class TableElement extends Element native "*HTMLTableElement" {
///@docsEditable true
factory TableElement() => document.$dom_createElement("table");
@@ -16438,7 +17561,7 @@
/// @domName HTMLTableRowElement; @docsEditable true
-class TableRowElement extends Element implements Element native "*HTMLTableRowElement" {
+class TableRowElement extends Element native "*HTMLTableRowElement" {
///@docsEditable true
factory TableRowElement() => document.$dom_createElement("tr");
@@ -16479,7 +17602,7 @@
/// @domName HTMLTableSectionElement; @docsEditable true
-class TableSectionElement extends Element implements Element native "*HTMLTableSectionElement" {
+class TableSectionElement extends Element native "*HTMLTableSectionElement" {
/// @domName HTMLTableSectionElement.align; @docsEditable true
String align;
@@ -16529,7 +17652,7 @@
/// @domName HTMLTextAreaElement; @docsEditable true
-class TextAreaElement extends Element implements Element native "*HTMLTextAreaElement" {
+class TextAreaElement extends Element native "*HTMLTextAreaElement" {
///@docsEditable true
factory TextAreaElement() => document.$dom_createElement("textarea");
@@ -16693,9 +17816,12 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class TextTrackEvents extends Events {
+ /// @docsEditable true
TextTrackEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get cueChange => this['cuechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -16766,11 +17892,15 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class TextTrackCueEvents extends Events {
+ /// @docsEditable true
TextTrackCueEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get enter => this['enter'];
+ /// @docsEditable true
EventListenerList get exit => this['exit'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -17021,9 +18151,12 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class TextTrackListEvents extends Events {
+ /// @docsEditable true
TextTrackListEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addTrack => this['addtrack'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -17057,7 +18190,7 @@
/// @domName HTMLTitleElement; @docsEditable true
-class TitleElement extends Element implements Element native "*HTMLTitleElement" {
+class TitleElement extends Element native "*HTMLTitleElement" {
///@docsEditable true
factory TitleElement() => document.$dom_createElement("title");
@@ -17261,7 +18394,7 @@
/// @domName HTMLTrackElement; @docsEditable true
-class TrackElement extends Element implements Element native "*HTMLTrackElement" {
+class TrackElement extends Element native "*HTMLTrackElement" {
///@docsEditable true
factory TrackElement() => document.$dom_createElement("track");
@@ -17430,7 +18563,7 @@
/// @domName HTMLUListElement; @docsEditable true
-class UListElement extends Element implements Element native "*HTMLUListElement" {
+class UListElement extends Element native "*HTMLUListElement" {
///@docsEditable true
factory UListElement() => document.$dom_createElement("ul");
@@ -17852,7 +18985,7 @@
/// @domName HTMLUnknownElement; @docsEditable true
-class UnknownElement extends Element implements Element native "*HTMLUnknownElement" {
+class UnknownElement extends Element native "*HTMLUnknownElement" {
}
// 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
@@ -19405,15 +20538,21 @@
}
+/// @docsEditable true
class WebSocketEvents extends Events {
+ /// @docsEditable true
WebSocketEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get close => this['close'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -19539,9 +20678,12 @@
void terminate() native;
}
+/// @docsEditable true
class WorkerEvents extends AbstractWorkerEvents {
+ /// @docsEditable true
WorkerEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get message => this['message'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -19629,9 +20771,12 @@
this, this, this);
}
+/// @docsEditable true
class WorkerContextEvents extends Events {
+ /// @docsEditable true
WorkerContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -22064,7 +23209,7 @@
String _type;
/** The element we are watching for events to happen on. */
- Element _target;
+ EventTarget _target;
// The distance to shift from upper case alphabet Roman letters to lower case.
final int _ROMAN_ALPHABET_OFFSET = "a".charCodes[0] - "A".charCodes[0];
@@ -22123,7 +23268,7 @@
* General constructor, performs basic initialization for our improved
* KeyboardEvent controller.
*/
- _KeyboardEventController(Element target, String type) {
+ _KeyboardEventController(EventTarget target, String type) {
_callbacks = [];
_type = type;
_target = target;
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 9c67b2f..9f69c01 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -93,9 +93,12 @@
}
+/// @docsEditable true
class AbstractWorkerEvents extends Events {
+ /// @docsEditable true
AbstractWorkerEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -526,23 +529,33 @@
}
+/// @docsEditable true
class ApplicationCacheEvents extends Events {
+ /// @docsEditable true
ApplicationCacheEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get cached => this['cached'];
+ /// @docsEditable true
EventListenerList get checking => this['checking'];
+ /// @docsEditable true
EventListenerList get downloading => this['downloading'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get noUpdate => this['noupdate'];
+ /// @docsEditable true
EventListenerList get obsolete => this['obsolete'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get updateReady => this['updateready'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -909,15 +922,21 @@
}
+/// @docsEditable true
class BatteryManagerEvents extends Events {
+ /// @docsEditable true
BatteryManagerEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get chargingChange => this['chargingchange'];
+ /// @docsEditable true
EventListenerList get chargingTimeChange => this['chargingtimechange'];
+ /// @docsEditable true
EventListenerList get dischargingTimeChange => this['dischargingtimechange'];
+ /// @docsEditable true
EventListenerList get levelChange => this['levelchange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -1056,33 +1075,48 @@
}
+/// @docsEditable true
class BodyElementEvents extends ElementEvents {
+ /// @docsEditable true
BodyElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get beforeUnload => this['beforeunload'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get hashChange => this['hashchange'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get offline => this['offline'];
+ /// @docsEditable true
EventListenerList get online => this['online'];
+ /// @docsEditable true
EventListenerList get popState => this['popstate'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get storage => this['storage'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -6829,9 +6863,12 @@
}
+/// @docsEditable true
class DedicatedWorkerContextEvents extends WorkerContextEvents {
+ /// @docsEditable true
DedicatedWorkerContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get message => this['message'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -7327,15 +7364,21 @@
}
}
+/// @docsEditable true
class DocumentEvents extends ElementEvents {
+ /// @docsEditable true
DocumentEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get readyStateChange => this['readystatechange'];
+ /// @docsEditable true
EventListenerList get selectionChange => this['selectionchange'];
+ /// @docsEditable true
EventListenerList get pointerLockChange => this['webkitpointerlockchange'];
+ /// @docsEditable true
EventListenerList get pointerLockError => this['webkitpointerlockerror'];
}
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
@@ -7368,9 +7411,11 @@
factory DocumentFragment.svg(String svgContent) =>
_DocumentFragmentFactoryProvider.createDocumentFragment_svg(svgContent);
+ @deprecated
List<Element> get elements => this.children;
// TODO: The type of value should be Collection<Element>. See http://b/5392897
+ @deprecated
void set elements(value) {
this.children = value;
}
@@ -7473,12 +7518,12 @@
String get webkitdropzone => "";
String get webkitRegionOverflow => "";
Element get $m_firstElementChild {
- if (elements.length > 0) {
- return elements[0];
+ if (children.length > 0) {
+ return children[0];
}
return null;
}
- Element get $m_lastElementChild => elements.last;
+ Element get $m_lastElementChild => children.last;
Element get nextElementSibling => null;
Element get previousElementSibling => null;
Element get offsetParent => null;
@@ -8806,11 +8851,14 @@
/**
* Deprecated, use innerHtml instead.
*/
+ @deprecated
String get innerHTML => this.innerHtml;
+ @deprecated
void set innerHTML(String value) {
this.innerHtml = value;
}
+ @deprecated
void set elements(Collection<Element> value) {
this.children = value;
}
@@ -8818,6 +8866,7 @@
/**
* Deprecated, use [children] instead.
*/
+ @deprecated
List<Element> get elements => this.children;
/**
@@ -9266,103 +9315,153 @@
document.$dom_createElement(tag);
}
+/// @docsEditable true
class ElementEvents extends Events {
+ /// @docsEditable true
ElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get beforeCopy => this['beforecopy'];
+ /// @docsEditable true
EventListenerList get beforeCut => this['beforecut'];
+ /// @docsEditable true
EventListenerList get beforePaste => this['beforepaste'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get change => this['change'];
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get contextMenu => this['contextmenu'];
+ /// @docsEditable true
EventListenerList get copy => this['copy'];
+ /// @docsEditable true
EventListenerList get cut => this['cut'];
+ /// @docsEditable true
EventListenerList get doubleClick => this['dblclick'];
+ /// @docsEditable true
EventListenerList get drag => this['drag'];
+ /// @docsEditable true
EventListenerList get dragEnd => this['dragend'];
+ /// @docsEditable true
EventListenerList get dragEnter => this['dragenter'];
+ /// @docsEditable true
EventListenerList get dragLeave => this['dragleave'];
+ /// @docsEditable true
EventListenerList get dragOver => this['dragover'];
+ /// @docsEditable true
EventListenerList get dragStart => this['dragstart'];
+ /// @docsEditable true
EventListenerList get drop => this['drop'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get input => this['input'];
+ /// @docsEditable true
EventListenerList get invalid => this['invalid'];
+ /// @docsEditable true
EventListenerList get keyDown => this['keydown'];
+ /// @docsEditable true
EventListenerList get keyPress => this['keypress'];
+ /// @docsEditable true
EventListenerList get keyUp => this['keyup'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get mouseDown => this['mousedown'];
+ /// @docsEditable true
EventListenerList get mouseMove => this['mousemove'];
+ /// @docsEditable true
EventListenerList get mouseOut => this['mouseout'];
+ /// @docsEditable true
EventListenerList get mouseOver => this['mouseover'];
+ /// @docsEditable true
EventListenerList get mouseUp => this['mouseup'];
+ /// @docsEditable true
EventListenerList get mouseWheel => this['mousewheel'];
+ /// @docsEditable true
EventListenerList get paste => this['paste'];
+ /// @docsEditable true
EventListenerList get reset => this['reset'];
+ /// @docsEditable true
EventListenerList get scroll => this['scroll'];
+ /// @docsEditable true
EventListenerList get search => this['search'];
+ /// @docsEditable true
EventListenerList get select => this['select'];
+ /// @docsEditable true
EventListenerList get selectStart => this['selectstart'];
+ /// @docsEditable true
EventListenerList get submit => this['submit'];
+ /// @docsEditable true
EventListenerList get touchCancel => this['touchcancel'];
+ /// @docsEditable true
EventListenerList get touchEnd => this['touchend'];
+ /// @docsEditable true
EventListenerList get touchEnter => this['touchenter'];
+ /// @docsEditable true
EventListenerList get touchLeave => this['touchleave'];
+ /// @docsEditable true
EventListenerList get touchMove => this['touchmove'];
+ /// @docsEditable true
EventListenerList get touchStart => this['touchstart'];
+ /// @docsEditable true
EventListenerList get transitionEnd => this['webkitTransitionEnd'];
+ /// @docsEditable true
EventListenerList get fullscreenChange => this['webkitfullscreenchange'];
+ /// @docsEditable true
EventListenerList get fullscreenError => this['webkitfullscreenerror'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -9866,13 +9965,18 @@
}
+/// @docsEditable true
class EventSourceEvents extends Events {
+ /// @docsEditable true
EventSourceEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -10414,19 +10518,27 @@
}
+/// @docsEditable true
class FileReaderEvents extends Events {
+ /// @docsEditable true
FileReaderEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get loadEnd => this['loadend'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -10586,19 +10698,27 @@
}
+/// @docsEditable true
class FileWriterEvents extends Events {
+ /// @docsEditable true
FileWriterEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get write => this['write'];
+ /// @docsEditable true
EventListenerList get writeEnd => this['writeend'];
+ /// @docsEditable true
EventListenerList get writeStart => this['writestart'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -10994,7 +11114,7 @@
/** @domName DOMFormData.append */
- void append(String name, String value, String filename) native "DOMFormData_append_Callback";
+ void append(String name, value, [String filename]) native "DOMFormData_append_Callback";
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -11229,33 +11349,48 @@
}
+/// @docsEditable true
class FrameSetElementEvents extends ElementEvents {
+ /// @docsEditable true
FrameSetElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get beforeUnload => this['beforeunload'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get hashChange => this['hashchange'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get offline => this['offline'];
+ /// @docsEditable true
EventListenerList get online => this['online'];
+ /// @docsEditable true
EventListenerList get popState => this['popstate'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get storage => this['storage'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12032,21 +12167,30 @@
}
+/// @docsEditable true
class HttpRequestEvents extends Events {
+ /// @docsEditable true
HttpRequestEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get loadEnd => this['loadend'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get readyStateChange => this['readystatechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12130,19 +12274,27 @@
}
+/// @docsEditable true
class HttpRequestUploadEvents extends Events {
+ /// @docsEditable true
HttpRequestUploadEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get loadEnd => this['loadend'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -12472,11 +12624,32 @@
// 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.
-// WARNING: Do not edit - generated code.
-
/// @domName HTMLInputElement
-class InputElement extends _Element_Merged {
+class InputElement extends _Element_Merged implements
+ HiddenInputElement,
+ SearchInputElement,
+ TextInputElement,
+ UrlInputElement,
+ TelephoneInputElement,
+ EmailInputElement,
+ PasswordInputElement,
+ DateTimeInputElement,
+ DateInputElement,
+ MonthInputElement,
+ WeekInputElement,
+ TimeInputElement,
+ LocalDateTimeInputElement,
+ NumberInputElement,
+ RangeInputElement,
+ CheckboxInputElement,
+ RadioButtonInputElement,
+ FileUploadInputElement,
+ SubmitButtonInputElement,
+ ImageButtonInputElement,
+ ResetButtonInputElement,
+ ButtonInputElement
+ {
///@docsEditable true
factory InputElement({String type}) {
@@ -12932,9 +13105,477 @@
}
+
+// Interfaces representing the InputElement APIs which are supported
+// for the various types of InputElement.
+// From http://dev.w3.org/html5/spec/the-input-element.html#the-input-element.
+
+/**
+ * Exposes the functionality common between all InputElement types.
+ */
+abstract class InputElementBase implements Element {
+ /// @domName HTMLInputElement.autofocus
+ bool autofocus;
+
+ /// @domName HTMLInputElement.disabled
+ bool disabled;
+
+ /// @domName HTMLInputElement.incremental
+ bool incremental;
+
+ /// @domName HTMLInputElement.indeterminate
+ bool indeterminate;
+
+ /// @domName HTMLInputElement.labels
+ List<Node> get labels;
+
+ /// @domName HTMLInputElement.name
+ String name;
+
+ /// @domName HTMLInputElement.validationMessage
+ String get validationMessage;
+
+ /// @domName HTMLInputElement.validity
+ ValidityState get validity;
+
+ /// @domName HTMLInputElement.value
+ String value;
+
+ /// @domName HTMLInputElement.willValidate
+ bool get willValidate;
+
+ /// @domName HTMLInputElement.checkValidity
+ bool checkValidity();
+
+ /// @domName HTMLInputElement.setCustomValidity
+ void setCustomValidity(String error);
+}
+
+/**
+ * Hidden input which is not intended to be seen or edited by the user.
+ */
+abstract class HiddenInputElement implements Element {
+ factory HiddenInputElement() => new InputElement(type: 'hidden');
+}
+
+
+/**
+ * Base interface for all inputs which involve text editing.
+ */
+abstract class TextInputElementBase implements InputElementBase {
+ /// @domName HTMLInputElement.autocomplete
+ String autocomplete;
+
+ /// @domName HTMLInputElement.maxLength
+ int maxLength;
+
+ /// @domName HTMLInputElement.pattern
+ String pattern;
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.size
+ int size;
+
+ /// @domName HTMLInputElement.select
+ void select();
+
+ /// @domName HTMLInputElement.selectionDirection
+ String selectionDirection;
+
+ /// @domName HTMLInputElement.selectionEnd
+ int selectionEnd;
+
+ /// @domName HTMLInputElement.selectionStart
+ int selectionStart;
+
+ /// @domName HTMLInputElement.setSelectionRange
+ void setSelectionRange(int start, int end, [String direction]);
+}
+
+/**
+ * Similar to [TextInputElement], but on platforms where search is styled
+ * differently this will get the search style.
+ */
+abstract class SearchInputElement implements TextInputElementBase {
+ factory SearchInputElement() => new InputElement(type: 'search');
+
+ /// @domName HTMLInputElement.dirName;
+ String dirName;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * A basic text input editor control.
+ */
+abstract class TextInputElement implements TextInputElementBase {
+ factory TextInputElement() => new InputElement(type: 'text');
+
+ /// @domName HTMLInputElement.dirName;
+ String dirName;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * A control for editing an absolute URL.
+ */
+abstract class UrlInputElement implements TextInputElementBase {
+ factory UrlInputElement() => new InputElement(type: 'url');
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * Represents a control for editing a telephone number.
+ *
+ * This provides a single line of text with minimal formatting help since
+ * there is a wide variety of telephone numbers.
+ */
+abstract class TelephoneInputElement implements TextInputElementBase {
+ factory TelephoneInputElement() => new InputElement(type: 'tel');
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * An e-mail address or list of e-mail addresses.
+ */
+abstract class EmailInputElement implements TextInputElementBase {
+ factory EmailInputElement() => new InputElement(type: 'email');
+
+ /// @domName HTMLInputElement.autocomplete
+ String autocomplete;
+
+ /// @domName HTMLInputElement.autofocus
+ bool autofocus;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+
+ /// @domName HTMLInputElement.maxLength
+ int maxLength;
+
+ /// @domName HTMLInputElement.multiple;
+ bool multiple;
+
+ /// @domName HTMLInputElement.pattern
+ String pattern;
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.size
+ int size;
+}
+
+/**
+ * Text with no line breaks (sensitive information).
+ */
+abstract class PasswordInputElement implements TextInputElementBase {
+ factory PasswordInputElement() => new InputElement(type: 'password');
+}
+
+/**
+ * Base interface for all input element types which involve ranges.
+ */
+abstract class RangeInputElementBase implements InputElementBase {
+
+ /// @domName HTMLInputElement.list
+ Element get list;
+
+ /// @domName HTMLInputElement.max
+ String max;
+
+ /// @domName HTMLInputElement.min
+ String min;
+
+ /// @domName HTMLInputElement.step
+ String step;
+
+ /// @domName HTMLInputElement.valueAsNumber
+ num valueAsNumber;
+
+ /// @domName HTMLInputElement.stepDown
+ void stepDown([int n]);
+
+ /// @domName HTMLInputElement.stepUp
+ void stepUp([int n]);
+}
+
+/**
+ * A date and time (year, month, day, hour, minute, second, fraction of a
+ * second) with the time zone set to UTC.
+ */
+abstract class DateTimeInputElement implements RangeInputElementBase {
+ factory DateTimeInputElement() => new InputElement(type: 'datetime');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date (year, month, day) with no time zone.
+ */
+abstract class DateInputElement implements RangeInputElementBase {
+ factory DateInputElement() => new InputElement(type: 'date');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date consisting of a year and a month with no time zone.
+ */
+abstract class MonthInputElement implements RangeInputElementBase {
+ factory MonthInputElement() => new InputElement(type: 'month');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date consisting of a week-year number and a week number with no time zone.
+ */
+abstract class WeekInputElement implements RangeInputElementBase {
+ factory WeekInputElement() => new InputElement(type: 'week');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A time (hour, minute, seconds, fractional seconds) with no time zone.
+ */
+abstract class TimeInputElement implements RangeInputElementBase {
+ factory TimeInputElement() => new InputElement(type: 'time');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date and time (year, month, day, hour, minute, second, fraction of a
+ * second) with no time zone.
+ */
+abstract class LocalDateTimeInputElement implements RangeInputElementBase {
+ factory LocalDateTimeInputElement() =>
+ new InputElement(type: 'datetime-local');
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A numeric editor control.
+ */
+abstract class NumberInputElement implements RangeInputElementBase {
+ factory NumberInputElement() => new InputElement(type: 'number');
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * Similar to [NumberInputElement] but the browser may provide more optimal
+ * styling (such as a slider control).
+ */
+abstract class RangeInputElement implements RangeInputElementBase {
+ factory RangeInputElement() => new InputElement(type: 'range');
+}
+
+/**
+ * A boolean editor control.
+ *
+ * Note that if [indeterminate] is set then this control is in a third
+ * indeterminate state.
+ */
+abstract class CheckboxInputElement implements InputElementBase {
+ factory CheckboxInputElement() => new InputElement(type: 'checkbox');
+
+ /// @domName HTMLInputElement.checked
+ bool checked;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+
+/**
+ * A control that when used with other [ReadioButtonInputElement] controls
+ * forms a radio button group in which only one control can be checked at a
+ * time.
+ *
+ * Radio buttons are considered to be in the same radio button group if:
+ *
+ * * They are all of type 'radio'.
+ * * They all have either the same [FormElement] owner, or no owner.
+ * * Their name attributes contain the same name.
+ */
+abstract class RadioButtonInputElement implements InputElementBase {
+ factory RadioButtonInputElement() => new InputElement(type: 'radio');
+
+ /// @domName HTMLInputElement.checked
+ bool checked;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A control for picking files from the user's computer.
+ */
+abstract class FileUploadInputElement implements InputElementBase {
+ factory FileUploadInputElement() => new InputElement(type: 'file');
+
+ /// @domName HTMLInputElement.accept
+ String accept;
+
+ /// @domName HTMLInputElement.multiple
+ bool multiple;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.files
+ List<File> files;
+}
+
+/**
+ * A button, which when clicked, submits the form.
+ */
+abstract class SubmitButtonInputElement implements InputElementBase {
+ factory SubmitButtonInputElement() => new InputElement(type: 'submit');
+
+ /// @domName HTMLInputElement.formAction
+ String formAction;
+
+ /// @domName HTMLInputElement.formEnctype
+ String formEnctype;
+
+ /// @domName HTMLInputElement.formMethod
+ String formMethod;
+
+ /// @domName HTMLInputElement.formNoValidate
+ bool formNoValidate;
+
+ /// @domName HTMLInputElement.formTarget
+ String formTarget;
+}
+
+/**
+ * Either an image which the user can select a coordinate to or a form
+ * submit button.
+ */
+abstract class ImageButtonInputElement implements InputElementBase {
+ factory ImageButtonInputElement() => new InputElement(type: 'image');
+
+ /// @domName HTMLInputElement.alt
+ String alt;
+
+ /// @domName HTMLInputElement.formAction
+ String formAction;
+
+ /// @domName HTMLInputElement.formEnctype
+ String formEnctype;
+
+ /// @domName HTMLInputElement.formMethod
+ String formMethod;
+
+ /// @domName HTMLInputElement.formNoValidate
+ bool formNoValidate;
+
+ /// @domName HTMLInputElement.formTarget
+ String formTarget;
+
+ /// @domName HTMLInputElement.height
+ int height;
+
+ /// @domName HTMLInputElement.src
+ String src;
+
+ /// @domName HTMLInputElement.width
+ int width;
+}
+
+/**
+ * A button, which when clicked, resets the form.
+ */
+abstract class ResetButtonInputElement implements InputElementBase {
+ factory ResetButtonInputElement() => new InputElement(type: 'reset');
+}
+
+/**
+ * A button, with no default behavior.
+ */
+abstract class ButtonInputElement implements InputElementBase {
+ factory ButtonInputElement() => new InputElement(type: 'button');
+}
+
+
+/// @docsEditable true
class InputElementEvents extends ElementEvents {
+ /// @docsEditable true
InputElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get speechChange => this['webkitSpeechChange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -14353,153 +14994,228 @@
}
+/// @docsEditable true
class LocalWindowEvents extends Events {
+ /// @docsEditable true
LocalWindowEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get beforeUnload => this['beforeunload'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get canPlay => this['canplay'];
+ /// @docsEditable true
EventListenerList get canPlayThrough => this['canplaythrough'];
+ /// @docsEditable true
EventListenerList get change => this['change'];
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get contextMenu => this['contextmenu'];
+ /// @docsEditable true
EventListenerList get doubleClick => this['dblclick'];
+ /// @docsEditable true
EventListenerList get deviceMotion => this['devicemotion'];
+ /// @docsEditable true
EventListenerList get deviceOrientation => this['deviceorientation'];
+ /// @docsEditable true
EventListenerList get drag => this['drag'];
+ /// @docsEditable true
EventListenerList get dragEnd => this['dragend'];
+ /// @docsEditable true
EventListenerList get dragEnter => this['dragenter'];
+ /// @docsEditable true
EventListenerList get dragLeave => this['dragleave'];
+ /// @docsEditable true
EventListenerList get dragOver => this['dragover'];
+ /// @docsEditable true
EventListenerList get dragStart => this['dragstart'];
+ /// @docsEditable true
EventListenerList get drop => this['drop'];
+ /// @docsEditable true
EventListenerList get durationChange => this['durationchange'];
+ /// @docsEditable true
EventListenerList get emptied => this['emptied'];
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get hashChange => this['hashchange'];
+ /// @docsEditable true
EventListenerList get input => this['input'];
+ /// @docsEditable true
EventListenerList get invalid => this['invalid'];
+ /// @docsEditable true
EventListenerList get keyDown => this['keydown'];
+ /// @docsEditable true
EventListenerList get keyPress => this['keypress'];
+ /// @docsEditable true
EventListenerList get keyUp => this['keyup'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get loadedData => this['loadeddata'];
+ /// @docsEditable true
EventListenerList get loadedMetadata => this['loadedmetadata'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get mouseDown => this['mousedown'];
+ /// @docsEditable true
EventListenerList get mouseMove => this['mousemove'];
+ /// @docsEditable true
EventListenerList get mouseOut => this['mouseout'];
+ /// @docsEditable true
EventListenerList get mouseOver => this['mouseover'];
+ /// @docsEditable true
EventListenerList get mouseUp => this['mouseup'];
+ /// @docsEditable true
EventListenerList get mouseWheel => this['mousewheel'];
+ /// @docsEditable true
EventListenerList get offline => this['offline'];
+ /// @docsEditable true
EventListenerList get online => this['online'];
+ /// @docsEditable true
EventListenerList get pageHide => this['pagehide'];
+ /// @docsEditable true
EventListenerList get pageShow => this['pageshow'];
+ /// @docsEditable true
EventListenerList get pause => this['pause'];
+ /// @docsEditable true
EventListenerList get play => this['play'];
+ /// @docsEditable true
EventListenerList get playing => this['playing'];
+ /// @docsEditable true
EventListenerList get popState => this['popstate'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get rateChange => this['ratechange'];
+ /// @docsEditable true
EventListenerList get reset => this['reset'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get scroll => this['scroll'];
+ /// @docsEditable true
EventListenerList get search => this['search'];
+ /// @docsEditable true
EventListenerList get seeked => this['seeked'];
+ /// @docsEditable true
EventListenerList get seeking => this['seeking'];
+ /// @docsEditable true
EventListenerList get select => this['select'];
+ /// @docsEditable true
EventListenerList get stalled => this['stalled'];
+ /// @docsEditable true
EventListenerList get storage => this['storage'];
+ /// @docsEditable true
EventListenerList get submit => this['submit'];
+ /// @docsEditable true
EventListenerList get suspend => this['suspend'];
+ /// @docsEditable true
EventListenerList get timeUpdate => this['timeupdate'];
+ /// @docsEditable true
EventListenerList get touchCancel => this['touchcancel'];
+ /// @docsEditable true
EventListenerList get touchEnd => this['touchend'];
+ /// @docsEditable true
EventListenerList get touchMove => this['touchmove'];
+ /// @docsEditable true
EventListenerList get touchStart => this['touchstart'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
+ /// @docsEditable true
EventListenerList get volumeChange => this['volumechange'];
+ /// @docsEditable true
EventListenerList get waiting => this['waiting'];
+ /// @docsEditable true
EventListenerList get animationEnd => this['webkitAnimationEnd'];
+ /// @docsEditable true
EventListenerList get animationIteration => this['webkitAnimationIteration'];
+ /// @docsEditable true
EventListenerList get animationStart => this['webkitAnimationStart'];
+ /// @docsEditable true
EventListenerList get transitionEnd => this['webkitTransitionEnd'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -15029,57 +15745,84 @@
}
+/// @docsEditable true
class MediaElementEvents extends ElementEvents {
+ /// @docsEditable true
MediaElementEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get canPlay => this['canplay'];
+ /// @docsEditable true
EventListenerList get canPlayThrough => this['canplaythrough'];
+ /// @docsEditable true
EventListenerList get durationChange => this['durationchange'];
+ /// @docsEditable true
EventListenerList get emptied => this['emptied'];
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
+ /// @docsEditable true
EventListenerList get loadedData => this['loadeddata'];
+ /// @docsEditable true
EventListenerList get loadedMetadata => this['loadedmetadata'];
+ /// @docsEditable true
EventListenerList get loadStart => this['loadstart'];
+ /// @docsEditable true
EventListenerList get pause => this['pause'];
+ /// @docsEditable true
EventListenerList get play => this['play'];
+ /// @docsEditable true
EventListenerList get playing => this['playing'];
+ /// @docsEditable true
EventListenerList get progress => this['progress'];
+ /// @docsEditable true
EventListenerList get rateChange => this['ratechange'];
+ /// @docsEditable true
EventListenerList get seeked => this['seeked'];
+ /// @docsEditable true
EventListenerList get seeking => this['seeking'];
+ /// @docsEditable true
EventListenerList get show => this['show'];
+ /// @docsEditable true
EventListenerList get stalled => this['stalled'];
+ /// @docsEditable true
EventListenerList get suspend => this['suspend'];
+ /// @docsEditable true
EventListenerList get timeUpdate => this['timeupdate'];
+ /// @docsEditable true
EventListenerList get volumeChange => this['volumechange'];
+ /// @docsEditable true
EventListenerList get waiting => this['waiting'];
+ /// @docsEditable true
EventListenerList get keyAdded => this['webkitkeyadded'];
+ /// @docsEditable true
EventListenerList get keyError => this['webkitkeyerror'];
+ /// @docsEditable true
EventListenerList get keyMessage => this['webkitkeymessage'];
+ /// @docsEditable true
EventListenerList get needKey => this['webkitneedkey'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -15367,9 +16110,12 @@
}
+/// @docsEditable true
class MediaStreamEvents extends Events {
+ /// @docsEditable true
MediaStreamEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -15443,13 +16189,18 @@
}
+/// @docsEditable true
class MediaStreamTrackEvents extends Events {
+ /// @docsEditable true
MediaStreamTrackEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get ended => this['ended'];
+ /// @docsEditable true
EventListenerList get mute => this['mute'];
+ /// @docsEditable true
EventListenerList get unmute => this['unmute'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -15513,11 +16264,15 @@
}
+/// @docsEditable true
class MediaStreamTrackListEvents extends Events {
+ /// @docsEditable true
MediaStreamTrackListEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addTrack => this['addtrack'];
+ /// @docsEditable true
EventListenerList get removeTrack => this['removetrack'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -15663,9 +16418,12 @@
}
+/// @docsEditable true
class MessagePortEvents extends Events {
+ /// @docsEditable true
MessagePortEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get message => this['message'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -16540,9 +17298,9 @@
void remove() {
// TODO(jacobr): should we throw an exception if parent is already null?
// TODO(vsm): Use the native remove when available.
- if (this.parent != null) {
- final Node parent = this.parent;
- parent.$dom_removeChild(this);
+ if (this.parentNode != null) {
+ final Node parent = this.parentNode;
+ parentNode.$dom_removeChild(this);
}
}
@@ -16552,7 +17310,7 @@
*/
Node replaceWith(Node otherNode) {
try {
- final Node parent = this.parent;
+ final Node parent = this.parentNode;
parent.$dom_replaceChild(otherNode, this);
} catch (e) {
@@ -16635,8 +17393,12 @@
Document get document native "Node_ownerDocument_Getter";
+ /** @domName Node.parentElement */
+ Element get parent native "Node_parentElement_Getter";
+
+
/** @domName Node.parentNode */
- Node get parent native "Node_parentNode_Getter";
+ Node get parentNode native "Node_parentNode_Getter";
/** @domName Node.previousSibling */
@@ -17008,17 +17770,24 @@
}
+/// @docsEditable true
class NotificationEvents extends Events {
+ /// @docsEditable true
NotificationEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get close => this['close'];
+ /// @docsEditable true
EventListenerList get display => this['display'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get show => this['show'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -17782,17 +18551,24 @@
}
+/// @docsEditable true
class PeerConnection00Events extends Events {
+ /// @docsEditable true
PeerConnection00Events(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addStream => this['addstream'];
+ /// @docsEditable true
EventListenerList get connecting => this['connecting'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
+ /// @docsEditable true
EventListenerList get removeStream => this['removestream'];
+ /// @docsEditable true
EventListenerList get stateChange => this['statechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -18559,15 +19335,21 @@
}
+/// @docsEditable true
class RtcDataChannelEvents extends Events {
+ /// @docsEditable true
RtcDataChannelEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get close => this['close'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -18734,21 +19516,30 @@
}
+/// @docsEditable true
class RtcPeerConnectionEvents extends Events {
+ /// @docsEditable true
RtcPeerConnectionEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addStream => this['addstream'];
+ /// @docsEditable true
EventListenerList get iceCandidate => this['icecandidate'];
+ /// @docsEditable true
EventListenerList get iceChange => this['icechange'];
+ /// @docsEditable true
EventListenerList get negotiationNeeded => this['negotiationneeded'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
+ /// @docsEditable true
EventListenerList get removeStream => this['removestream'];
+ /// @docsEditable true
EventListenerList get stateChange => this['statechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -19360,9 +20151,12 @@
}
+/// @docsEditable true
class SharedWorkerContextEvents extends WorkerContextEvents {
+ /// @docsEditable true
SharedWorkerContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get connect => this['connect'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -19889,29 +20683,42 @@
}
+/// @docsEditable true
class SpeechRecognitionEvents extends Events {
+ /// @docsEditable true
SpeechRecognitionEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get audioEnd => this['audioend'];
+ /// @docsEditable true
EventListenerList get audioStart => this['audiostart'];
+ /// @docsEditable true
EventListenerList get end => this['end'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get noMatch => this['nomatch'];
+ /// @docsEditable true
EventListenerList get result => this['result'];
+ /// @docsEditable true
EventListenerList get soundEnd => this['soundend'];
+ /// @docsEditable true
EventListenerList get soundStart => this['soundstart'];
+ /// @docsEditable true
EventListenerList get speechEnd => this['speechend'];
+ /// @docsEditable true
EventListenerList get speechStart => this['speechstart'];
+ /// @docsEditable true
EventListenerList get start => this['start'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -21408,9 +22215,12 @@
}
+/// @docsEditable true
class TextTrackEvents extends Events {
+ /// @docsEditable true
TextTrackEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get cueChange => this['cuechange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -21541,11 +22351,15 @@
}
+/// @docsEditable true
class TextTrackCueEvents extends Events {
+ /// @docsEditable true
TextTrackCueEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get enter => this['enter'];
+ /// @docsEditable true
EventListenerList get exit => this['exit'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -21809,9 +22623,12 @@
}
+/// @docsEditable true
class TextTrackListEvents extends Events {
+ /// @docsEditable true
TextTrackListEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get addTrack => this['addtrack'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -24805,15 +25622,21 @@
}
+/// @docsEditable true
class WebSocketEvents extends Events {
+ /// @docsEditable true
WebSocketEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get close => this['close'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get message => this['message'];
+ /// @docsEditable true
EventListenerList get open => this['open'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -24878,9 +25701,12 @@
}
+/// @docsEditable true
class WorkerEvents extends AbstractWorkerEvents {
+ /// @docsEditable true
WorkerEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get message => this['message'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -24984,9 +25810,12 @@
}
+/// @docsEditable true
class WorkerContextEvents extends Events {
+ /// @docsEditable true
WorkerContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -27549,7 +28378,7 @@
String _type;
/** The element we are watching for events to happen on. */
- Element _target;
+ EventTarget _target;
// The distance to shift from upper case alphabet Roman letters to lower case.
final int _ROMAN_ALPHABET_OFFSET = "a".charCodes[0] - "A".charCodes[0];
@@ -27608,7 +28437,7 @@
* General constructor, performs basic initialization for our improved
* KeyboardEvent controller.
*/
- _KeyboardEventController(Element target, String type) {
+ _KeyboardEventController(EventTarget target, String type) {
_callbacks = [];
_type = type;
_target = target;
diff --git a/sdk/lib/html/docs/html_docs.json b/sdk/lib/html/docs/html_docs.json
index a88215d..de93b4a 100644
--- a/sdk/lib/html/docs/html_docs.json
+++ b/sdk/lib/html/docs/html_docs.json
@@ -205,19 +205,153 @@
},
"CanvasElement.dart":
{
-
+ " @JSName('toDataURL')":
+ [
+ " /**",
+ " * Returns a data URI containing a representation of the image in the ",
+ " * format specified by type (defaults to 'image/png'). ",
+ " * ",
+ " * Data Uri format is as follow `data:[<MIME-type>][;charset=<encoding>][;base64],<data>`",
+ " * ",
+ " * Optional parameter [quality] in the range of 0.0 and 1.0 can be used when requesting [type]",
+ " * 'image/jpeg' or 'image/webp'. If [quality] is not passed the default",
+ " * value is used. Note: the default value varies by browser.",
+ " * ",
+ " * If the height or width of this canvas element is 0, then 'data:' is returned,",
+ " * representing no data.",
+ " * ",
+ " * If the type requested is not 'image/png', and the returned value is ",
+ " * 'data:image/png', then the requested type is not supported.",
+ " * ",
+ " * Example usage:",
+ " * ",
+ " * CanvasElement canvas = new CanvasElement();",
+ " * var ctx = canvas.context2d",
+ " * ..fillStyle = \"rgb(200,0,0)\"",
+ " * ..fillRect(10, 10, 55, 50);",
+ " * var dataUrl = canvas.toDataURL(\"image/jpeg\", 0.95);",
+ " * // The Data Uri would look similar to",
+ " * // 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA",
+ " * // AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO",
+ " * // 9TXL0Y4OHwAAAABJRU5ErkJggg=='",
+ " * //Create a new image element from the data URI.",
+ " * var img = new ImageElement();",
+ " * img.src = dataUrl;",
+ " * document.body.children.add(img);",
+ " * ",
+ " * See also:",
+ " * ",
+ " * * [Data URI Scheme](http://en.wikipedia.org/wiki/Data_URI_scheme) from Wikipedia.",
+ " * ",
+ " * * [HTMLCanvasElement](https://developer.mozilla.org/en-US/docs/DOM/HTMLCanvasElement) from MDN.",
+ " * ",
+ " * * [toDataUrl](http://dev.w3.org/html5/spec/the-canvas-element.html#dom-canvas-todataurl) from W3C.",
+ " */"
+ ],
+ " int height;":
+ [
+ " /// The height of this canvas element in CSS pixels."
+ ],
+ " int width;":
+ [
+ " /// The width of this canvas element in CSS pixels."
+ ]
},
"CanvasGradient.dart":
{
-
+ " void addColorStop(num offset, String color) native;":
+ [
+ " /**",
+ " * Adds a color stop to this gradient at the offset.",
+ " *",
+ " * The [offset] can range between 0.0 and 1.0.",
+ " *",
+ " * See also:",
+ " *",
+ " * * [Multiple Color Stops](https://developer.mozilla.org/en-US/docs/CSS/linear-gradient#Gradient_with_multiple_color_stops) from MDN.",
+ " */"
+ ],
+ "class CanvasGradient native \"*CanvasGradient\" {":
+ [
+ "/**",
+ " * An opaque canvas object representing a gradient.",
+ " *",
+ " * Created by calling [createLinearGradient] or [createRadialGradient] on a",
+ " * [CanvasRenderingContext2D] object.",
+ " *",
+ " * Example usage:",
+ " *",
+ " * var canvas = new CanvasElement(width: 600, height: 600);",
+ " * var ctx = canvas.context2d;",
+ " * ctx.clearRect(0, 0, 600, 600);",
+ " * ctx.save();",
+ " * // Create radial gradient.",
+ " * CanvasGradient gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 600);",
+ " * gradient.addColorStop(0, '#000');",
+ " * gradient.addColorStop(1, 'rgb(255, 255, 255)');",
+ " * // Assign gradients to fill.",
+ " * ctx.fillStyle = gradient;",
+ " * // Draw a rectangle with a gradient fill.",
+ " * ctx.fillRect(0, 0, 600, 600);",
+ " * ctx.save();",
+ " * document.body.children.add(canvas);",
+ " *",
+ " * See also:",
+ " *",
+ " * * [CanvasGradient](https://developer.mozilla.org/en-US/docs/DOM/CanvasGradient) from MDN.",
+ " * * [CanvasGradient](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#canvasgradient) from whatwg.",
+ " * * [CanvasGradient](http://www.w3.org/TR/2010/WD-2dcontext-20100304/#canvasgradient) from W3C.",
+ " */"
+ ]
},
"CanvasPattern.dart":
{
-
+ "class CanvasPattern native \"*CanvasPattern\" {":
+ [
+ "/**",
+ " * An opaque object representing a pattern of image, canvas, or video.",
+ " *",
+ " * Created by calling [createPattern] on a [CanvasRenderingContext2D] object.",
+ " *",
+ " * Example usage:",
+ " *",
+ " * var canvas = new CanvasElement(width: 600, height: 600);",
+ " * var ctx = canvas.context2d;",
+ " * var img = new ImageElement();",
+ " * // Image src needs to be loaded before pattern is applied.",
+ " * img.on.load.add((event) {",
+ " * // When the image is loaded, create a pattern",
+ " * // from the ImageElement.",
+ " * CanvasPattern pattern = ctx.createPattern(img, 'repeat');",
+ " * ctx.rect(0, 0, canvas.width, canvas.height);",
+ " * ctx.fillStyle = pattern;",
+ " * ctx.fill();",
+ " * });",
+ " * img.src = \"images/foo.jpg\";",
+ " * document.body.children.add(canvas);",
+ " *",
+ " * See also:",
+ " * * [CanvasPattern](https://developer.mozilla.org/en-US/docs/DOM/CanvasPattern) from MDN.",
+ " * * [CanvasPattern](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#canvaspattern) from whatwg.",
+ " * * [CanvasPattern](http://www.w3.org/TR/2010/WD-2dcontext-20100304/#canvaspattern) from W3C.",
+ " */"
+ ]
},
"CanvasRenderingContext.dart":
{
-
+ " final CanvasElement canvas;":
+ [
+ " /// Reference to the canvas element to which this context belongs."
+ ],
+ "class CanvasRenderingContext native \"*CanvasRenderingContext\" {":
+ [
+ "/**",
+ " * A rendering context for a canvas element.",
+ " *",
+ " * This context is extended by [CanvasRenderingContext2D] and",
+ " * [WebGLRenderingContext].",
+ " */"
+ ]
},
"CanvasRenderingContext2D.dart":
{
@@ -481,7 +615,7 @@
},
"DivElement.dart":
{
- "class DivElement extends Element implements Element native \"*HTMLDivElement\" {":
+ "class DivElement extends Element native \"*HTMLDivElement\" {":
[
"/**",
" * Represents an HTML <div> element.",
@@ -508,63 +642,63 @@
},
"Document.dart":
{
- " @Returns('_NodeList') @Creates('_NodeList')":
+ " @JSName('querySelectorAll')":
[
" /// Deprecated: use query(\"#$elementId\") instead."
],
- " Element $dom_createElement(String tagName) native \"createElement\";":
+ " @JSName('createElement')":
[
" /// Deprecated: use new Element.tag(tagName) instead."
],
- " Element $dom_elementFromPoint(int x, int y) native \"elementFromPoint\";":
+ " @JSName('elementFromPoint')":
[
" /// Moved to [HtmlDocument]."
],
- " Element $dom_getElementById(String elementId) native \"getElementById\";":
+ " @JSName('getElementById')":
[
" /// Deprecated: use query(\"#$elementId\") instead."
],
- " Element $dom_querySelector(String selectors) native \"querySelector\";":
+ " @JSName('querySelector')":
[
" /// Deprecated: renamed to the shorter name [query]."
],
- " Element get $dom_webkitFullscreenElement => JS(\"Element\", \"#.webkitFullscreenElement\", this);":
+ " @JSName('webkitFullscreenElement')":
[
" /// Moved to [HtmlDocument]."
],
- " Element get $dom_webkitPointerLockElement => JS(\"Element\", \"#.webkitPointerLockElement\", this);":
+ " @JSName('webkitPointerLockElement')":
[
" /// Moved to [HtmlDocument]."
],
- " HeadElement get $dom_head => JS(\"HeadElement\", \"#.head\", this);":
+ " @JSName('head')":
[
" /// Moved to [HtmlDocument]."
],
- " List<StyleSheet> get $dom_styleSheets => JS(\"_StyleSheetList\", \"#.styleSheets\", this);":
+ " @JSName('styleSheets')":
[
" /// Moved to [HtmlDocument]."
],
- " Range $dom_caretRangeFromPoint(int x, int y) native \"caretRangeFromPoint\";":
+ " @JSName('caretRangeFromPoint')":
[
" /// Use the [Range] constructor instead."
],
- " String get $dom_lastModified => JS(\"String\", \"#.lastModified\", this);":
+ " @JSName('lastModified')":
[
" /// Moved to [HtmlDocument]."
],
- " String get $dom_referrer => JS(\"String\", \"#.referrer\", this);":
+ " @JSName('referrer')":
[
" /// Moved to [HtmlDocument]."
],
- " String get $dom_title => JS(\"String\", \"#.title\", this);":
+ " @JSName('title')":
[
" /// Moved to [HtmlDocument]."
],
- " String get $dom_webkitVisibilityState => JS(\"String\", \"#.webkitVisibilityState\", this);":
+ " @JSName('webkitVisibilityState')":
[
" /// Moved to [HtmlDocument]."
],
- " TouchList $dom_createTouchList() native \"createTouchList\";":
+ " @JSName('createTouchList')":
[
" /// Use the [TouchList] constructor isntead."
],
@@ -572,35 +706,35 @@
[
" /// Returns the [Window] associated with the document."
],
- " bool get $dom_webkitFullscreenEnabled => JS(\"bool\", \"#.webkitFullscreenEnabled\", this);":
+ " @JSName('webkitFullscreenEnabled')":
[
" /// Moved to [HtmlDocument]."
],
- " bool get $dom_webkitHidden => JS(\"bool\", \"#.webkitHidden\", this);":
+ " @JSName('webkitHidden')":
[
" /// Moved to [HtmlDocument]."
],
- " bool get $dom_webkitIsFullScreen => JS(\"bool\", \"#.webkitIsFullScreen\", this);":
+ " @JSName('webkitIsFullScreen')":
[
" /// Moved to [HtmlDocument]."
],
- " void $dom_webkitCancelFullScreen() native \"webkitCancelFullScreen\";":
+ " @JSName('webkitCancelFullScreen')":
[
" /// Moved to [HtmlDocument]."
],
- " void $dom_webkitExitFullscreen() native \"webkitExitFullscreen\";":
+ " @JSName('webkitExitFullscreen')":
[
" /// Moved to [HtmlDocument]."
],
- " void $dom_webkitExitPointerLock() native \"webkitExitPointerLock\";":
+ " @JSName('webkitExitPointerLock')":
[
" /// Moved to [HtmlDocument]."
],
- " void set $dom_body(Element value) {":
+ " @JSName('body')":
[
" /// Moved to [HtmlDocument]."
],
- " void set $dom_title(String value) {":
+ " @JSName('title')":
[
" /// Moved to [HtmlDocument]."
]
@@ -905,6 +1039,58 @@
" * [String]). `null` indicates request failure.",
" */"
],
+ " EventListenerList get abort => this['abort'];":
+ [
+ " /**",
+ " * Event listeners to be notified when request has been aborted,",
+ " * generally due to calling `httpRequest.abort()`.",
+ " */"
+ ],
+ " EventListenerList get error => this['error'];":
+ [
+ " /**",
+ " * Event listeners to be notified when a request has failed, such as when a",
+ " * cross-domain error occurred or the file wasn't found on the server.",
+ " */"
+ ],
+ " EventListenerList get load => this['load'];":
+ [
+ " /**",
+ " * Event listeners to be notified once the request has completed",
+ " * *successfully*.",
+ " */"
+ ],
+ " EventListenerList get loadEnd => this['loadend'];":
+ [
+ " /**",
+ " * Event listeners to be notified once the request has completed (on",
+ " * either success or failure).",
+ " */"
+ ],
+ " EventListenerList get loadStart => this['loadstart'];":
+ [
+ " /**",
+ " * Event listeners to be notified when the request starts, once",
+ " * `httpRequest.send()` has been called.",
+ " */"
+ ],
+ " EventListenerList get progress => this['progress'];":
+ [
+ " /**",
+ " * Event listeners to be notified when data for the request ",
+ " * is being sent or loaded.",
+ " *",
+ " * Progress events are fired every 50ms or for every byte transmitted,",
+ " * whichever is less frequent.",
+ " */"
+ ],
+ " EventListenerList get readyStateChange => this['readystatechange'];":
+ [
+ " /**",
+ " * Event listeners to be notified every time the [HttpRequest]",
+ " * object's `readyState` changes values.",
+ " */"
+ ],
" HttpRequestEvents get on =>":
[
" /**",
@@ -1050,6 +1236,22 @@
" void setRequestHeader(String header, String value) native;":
[
" /** Sets HTTP `header` to `value`. */"
+ ],
+ "class HttpRequestEvents extends Events {":
+ [
+ "/**",
+ " * A class that supports listening for and dispatching events that can fire when",
+ " * making an HTTP request. ",
+ " * ",
+ " * Here's an example of adding an event handler that executes once an HTTP",
+ " * request has fully loaded:",
+ " * ",
+ " * httpRequest.on.loadEnd.add((e) => myCustomLoadEndHandler(e));",
+ " *",
+ " * Each property of this class is a read-only pointer to an [EventListenerList].",
+ " * That list holds all of the [EventListener]s that have registered for that",
+ " * particular type of event that fires from an HttpRequest.",
+ " */"
]
},
"HttpRequestException.dart":
@@ -1286,7 +1488,7 @@
},
"MenuElement.dart":
{
- "class MenuElement extends Element implements Element native \"*HTMLMenuElement\" {":
+ "class MenuElement extends Element native \"*HTMLMenuElement\" {":
[
"/**",
" * An HTML <menu> element.",
@@ -2156,4 +2358,4 @@
{
}
-}
\ No newline at end of file
+}
diff --git a/sdk/lib/html/html_common/filtered_element_list.dart b/sdk/lib/html/html_common/filtered_element_list.dart
index b12ffdb..7486555 100644
--- a/sdk/lib/html/html_common/filtered_element_list.dart
+++ b/sdk/lib/html/html_common/filtered_element_list.dart
@@ -4,10 +4,22 @@
part of html_common;
+/**
+ * An indexable collection of a node's descendants in the document tree,
+ * filtered so that only elements are in the collection.
+ */
class FilteredElementList implements List {
final Node _node;
final List<Node> _childNodes;
+ /**
+ * Creates a collection of the elements that descend from a node.
+ *
+ * Example usage:
+ *
+ * var filteredElements = new FilteredElementList(query("#container"));
+ * // filteredElements is [a, b, c].
+ */
FilteredElementList(Node node): _childNodes = node.nodes, _node = node;
// We can't memoize this, since it's possible that children will be messed
diff --git a/sdk/lib/html/html_common/html_common.dart b/sdk/lib/html/html_common/html_common.dart
index 19d283a..18c63a9 100644
--- a/sdk/lib/html/html_common/html_common.dart
+++ b/sdk/lib/html/html_common/html_common.dart
@@ -9,3 +9,7 @@
part 'filtered_element_list.dart';
part 'lists.dart';
+
+// For annotating deprecated APIs.
+// TODO: remove once @deprecated is added to dart core.
+const deprecated = 0;
diff --git a/sdk/lib/html/html_common/html_common_dart2js.dart b/sdk/lib/html/html_common/html_common_dart2js.dart
index 53761f6..559f983 100644
--- a/sdk/lib/html/html_common/html_common_dart2js.dart
+++ b/sdk/lib/html/html_common/html_common_dart2js.dart
@@ -10,3 +10,7 @@
part 'conversions.dart';
part 'filtered_element_list.dart';
part 'lists.dart';
+
+// For annotating deprecated APIs.
+// TODO: remove once @deprecated is added to dart core.
+const deprecated = 0;
diff --git a/sdk/lib/html/idl/dart/dart.idl b/sdk/lib/html/idl/dart/dart.idl
index 1c97468..74f7e96 100644
--- a/sdk/lib/html/idl/dart/dart.idl
+++ b/sdk/lib/html/idl/dart/dart.idl
@@ -311,6 +311,9 @@
// Provide missing constructor signature.
Constructor([Optional] in HTMLFormElement form)]
interface DOMFormData {
+ [Suppressed] void append(in DOMString name, in DOMString value, in DOMString filename);
+ [Custom] void append(in DOMString name, in DOMString value);
+ [Custom] void append(in DOMString name, in Blob value, [Optional] in DOMString filename);
};
}
diff --git a/sdk/lib/html/scripts/htmleventgenerator.py b/sdk/lib/html/scripts/htmleventgenerator.py
index 2489d12..a99f8de 100644
--- a/sdk/lib/html/scripts/htmleventgenerator.py
+++ b/sdk/lib/html/scripts/htmleventgenerator.py
@@ -221,7 +221,9 @@
template_file = 'impl_%s.darttemplate' % events_class_name
template = (self._template_loader.TryLoad(template_file) or
'\n'
+ '/// @docsEditable true\n'
'class $CLASSNAME extends $SUPER {\n'
+ ' /// @docsEditable true\n'
' $CLASSNAME(EventTarget _ptr) : super(_ptr);\n'
'$!MEMBERS}\n')
@@ -248,6 +250,7 @@
if not full_event_name in custom_events:
implementation_events_members.Emit(
"\n"
+ " /// @docsEditable true\n"
" EventListenerList get $NAME => this['$DOM_NAME'];\n",
NAME=html_name,
DOM_NAME=dom_name)
diff --git a/sdk/lib/html/scripts/htmlrenamer.py b/sdk/lib/html/scripts/htmlrenamer.py
index f6d01cb..89b8273 100644
--- a/sdk/lib/html/scripts/htmlrenamer.py
+++ b/sdk/lib/html/scripts/htmlrenamer.py
@@ -146,7 +146,7 @@
'Node.cloneNode': 'clone',
'Node.nextSibling': 'nextNode',
'Node.ownerDocument': 'document',
- 'Node.parentNode': 'parent',
+ 'Node.parentElement': 'parent',
'Node.previousSibling': 'previousNode',
'Node.textContent': 'text',
'SvgElement.className': '$dom_svgClassName',
@@ -300,7 +300,6 @@
"Node.get:DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC",
"Node.get:NOTATION_NODE",
"Node.normalize",
- "Node.get:parentElement",
"Node.get:ATTRIBUTE_NODE",
"Node.get:ENTITY_NODE",
"Node.get:DOCUMENT_POSITION_CONTAINED_BY",
diff --git a/sdk/lib/html/scripts/systemhtml.py b/sdk/lib/html/scripts/systemhtml.py
index c0041cb..3a7abb5 100644
--- a/sdk/lib/html/scripts/systemhtml.py
+++ b/sdk/lib/html/scripts/systemhtml.py
@@ -48,7 +48,7 @@
'HTMLSelectElement.selectedOptions',
'HTMLTableElement.createTBody',
'IDBDatabase.transaction',
- 'KeyboardEvent.initKeyboardEvent',
+ 'KeyboardEvent.initKeyboardEvent',
'MouseEvent.offsetX',
'MouseEvent.offsetY',
'Navigator.language',
@@ -363,7 +363,8 @@
implements = self._backend.AdditionalImplementedInterfaces()
for parent in self._interface.parents:
parent_type_info = self._type_registry.TypeInfo(parent.type.id)
- if parent_type_info != base_type_info:
+ if parent_type_info.interface_name() != base_class and \
+ parent_type_info != base_type_info:
implements.append(parent_type_info.interface_name())
secure_base_name = self._backend.SecureBaseName(interface_name)
diff --git a/sdk/lib/html/src/KeyboardEventController.dart b/sdk/lib/html/src/KeyboardEventController.dart
index 33e2f63..f949b32 100644
--- a/sdk/lib/html/src/KeyboardEventController.dart
+++ b/sdk/lib/html/src/KeyboardEventController.dart
@@ -32,7 +32,7 @@
String _type;
/** The element we are watching for events to happen on. */
- Element _target;
+ EventTarget _target;
// The distance to shift from upper case alphabet Roman letters to lower case.
final int _ROMAN_ALPHABET_OFFSET = "a".charCodes[0] - "A".charCodes[0];
@@ -91,7 +91,7 @@
* General constructor, performs basic initialization for our improved
* KeyboardEvent controller.
*/
- _KeyboardEventController(Element target, String type) {
+ _KeyboardEventController(EventTarget target, String type) {
_callbacks = [];
_type = type;
_target = target;
diff --git a/sdk/lib/html/templates/html/impl/impl_DocumentFragment.darttemplate b/sdk/lib/html/templates/html/impl/impl_DocumentFragment.darttemplate
index a164c0d..b9b8cdb 100644
--- a/sdk/lib/html/templates/html/impl/impl_DocumentFragment.darttemplate
+++ b/sdk/lib/html/templates/html/impl/impl_DocumentFragment.darttemplate
@@ -29,9 +29,11 @@
factory $CLASSNAME.svg(String svgContent) =>
_$(CLASSNAME)FactoryProvider.createDocumentFragment_svg(svgContent);
+ @deprecated
List<Element> get elements => this.children;
// TODO: The type of value should be Collection<Element>. See http://b/5392897
+ @deprecated
void set elements(value) {
this.children = value;
}
@@ -139,12 +141,12 @@
String get webkitdropzone => "";
String get webkitRegionOverflow => "";
Element get $m_firstElementChild {
- if (elements.length > 0) {
- return elements[0];
+ if (children.length > 0) {
+ return children[0];
}
return null;
}
- Element get $m_lastElementChild => elements.last;
+ Element get $m_lastElementChild => children.last;
Element get nextElementSibling => null;
Element get previousElementSibling => null;
Element get offsetParent => null;
diff --git a/sdk/lib/html/templates/html/impl/impl_Element.darttemplate b/sdk/lib/html/templates/html/impl/impl_Element.darttemplate
index aab5f01..b253d2d 100644
--- a/sdk/lib/html/templates/html/impl/impl_Element.darttemplate
+++ b/sdk/lib/html/templates/html/impl/impl_Element.darttemplate
@@ -373,11 +373,14 @@
/**
* Deprecated, use innerHtml instead.
*/
+ @deprecated
String get innerHTML => this.innerHtml;
+ @deprecated
void set innerHTML(String value) {
this.innerHtml = value;
}
+ @deprecated
void set elements(Collection<Element> value) {
this.children = value;
}
@@ -385,6 +388,7 @@
/**
* Deprecated, use [children] instead.
*/
+ @deprecated
List<Element> get elements => this.children;
/**
@@ -545,7 +549,7 @@
void _insertAdjacentNode(String where, Node node) {
switch (where.toLowerCase()) {
case 'beforebegin':
- this.parent.insertBefore(node, this);
+ this.parentNode.insertBefore(node, this);
break;
case 'afterbegin':
var first = this.nodes.length > 0 ? this.nodes[0] : null;
@@ -555,7 +559,7 @@
this.nodes.add(node);
break;
case 'afterend':
- this.parent.insertBefore(node, this.nextNode);
+ this.parentNode.insertBefore(node, this.nextNode);
break;
default:
throw new ArgumentError("Invalid position ${where}");
diff --git a/sdk/lib/html/templates/html/impl/impl_HTMLInputElement.darttemplate b/sdk/lib/html/templates/html/impl/impl_HTMLInputElement.darttemplate
new file mode 100644
index 0000000..a4c4e9a
--- /dev/null
+++ b/sdk/lib/html/templates/html/impl/impl_HTMLInputElement.darttemplate
@@ -0,0 +1,498 @@
+// 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 $LIBRARYNAME;
+
+/// @domName $DOMNAME
+class $CLASSNAME$EXTENDS implements
+ HiddenInputElement,
+ SearchInputElement,
+ TextInputElement,
+ UrlInputElement,
+ TelephoneInputElement,
+ EmailInputElement,
+ PasswordInputElement,
+ DateTimeInputElement,
+ DateInputElement,
+ MonthInputElement,
+ WeekInputElement,
+ TimeInputElement,
+ LocalDateTimeInputElement,
+ NumberInputElement,
+ RangeInputElement,
+ CheckboxInputElement,
+ RadioButtonInputElement,
+ FileUploadInputElement,
+ SubmitButtonInputElement,
+ ImageButtonInputElement,
+ ResetButtonInputElement,
+ ButtonInputElement
+ $NATIVESPEC {
+$!MEMBERS
+}
+
+
+// Interfaces representing the InputElement APIs which are supported
+// for the various types of InputElement.
+// From http://dev.w3.org/html5/spec/the-input-element.html#the-input-element.
+
+/**
+ * Exposes the functionality common between all InputElement types.
+ */
+abstract class InputElementBase implements Element {
+ /// @domName HTMLInputElement.autofocus
+ bool autofocus;
+
+ /// @domName HTMLInputElement.disabled
+ bool disabled;
+
+ /// @domName HTMLInputElement.incremental
+ bool incremental;
+
+ /// @domName HTMLInputElement.indeterminate
+ bool indeterminate;
+
+ /// @domName HTMLInputElement.labels
+ List<Node> get labels;
+
+ /// @domName HTMLInputElement.name
+ String name;
+
+ /// @domName HTMLInputElement.validationMessage
+ String get validationMessage;
+
+ /// @domName HTMLInputElement.validity
+ ValidityState get validity;
+
+ /// @domName HTMLInputElement.value
+ String value;
+
+ /// @domName HTMLInputElement.willValidate
+ bool get willValidate;
+
+ /// @domName HTMLInputElement.checkValidity
+ bool checkValidity();
+
+ /// @domName HTMLInputElement.setCustomValidity
+ void setCustomValidity(String error);
+}
+
+/**
+ * Hidden input which is not intended to be seen or edited by the user.
+ */
+abstract class HiddenInputElement implements Element {
+ factory HiddenInputElement() => new InputElement(type: 'hidden');
+}
+
+
+/**
+ * Base interface for all inputs which involve text editing.
+ */
+abstract class TextInputElementBase implements InputElementBase {
+ /// @domName HTMLInputElement.autocomplete
+ String autocomplete;
+
+ /// @domName HTMLInputElement.maxLength
+ int maxLength;
+
+ /// @domName HTMLInputElement.pattern
+ String pattern;
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.size
+ int size;
+
+ /// @domName HTMLInputElement.select
+ void select();
+
+ /// @domName HTMLInputElement.selectionDirection
+ String selectionDirection;
+
+ /// @domName HTMLInputElement.selectionEnd
+ int selectionEnd;
+
+ /// @domName HTMLInputElement.selectionStart
+ int selectionStart;
+
+ /// @domName HTMLInputElement.setSelectionRange
+ void setSelectionRange(int start, int end, [String direction]);
+}
+
+/**
+ * Similar to [TextInputElement], but on platforms where search is styled
+ * differently this will get the search style.
+ */
+abstract class SearchInputElement implements TextInputElementBase {
+ factory SearchInputElement() => new InputElement(type: 'search');
+
+ /// @domName HTMLInputElement.dirName;
+ String dirName;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * A basic text input editor control.
+ */
+abstract class TextInputElement implements TextInputElementBase {
+ factory TextInputElement() => new InputElement(type: 'text');
+
+ /// @domName HTMLInputElement.dirName;
+ String dirName;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * A control for editing an absolute URL.
+ */
+abstract class UrlInputElement implements TextInputElementBase {
+ factory UrlInputElement() => new InputElement(type: 'url');
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * Represents a control for editing a telephone number.
+ *
+ * This provides a single line of text with minimal formatting help since
+ * there is a wide variety of telephone numbers.
+ */
+abstract class TelephoneInputElement implements TextInputElementBase {
+ factory TelephoneInputElement() => new InputElement(type: 'tel');
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+}
+
+/**
+ * An e-mail address or list of e-mail addresses.
+ */
+abstract class EmailInputElement implements TextInputElementBase {
+ factory EmailInputElement() => new InputElement(type: 'email');
+
+ /// @domName HTMLInputElement.autocomplete
+ String autocomplete;
+
+ /// @domName HTMLInputElement.autofocus
+ bool autofocus;
+
+ /// @domName HTMLInputElement.list;
+ Element get list;
+
+ /// @domName HTMLInputElement.maxLength
+ int maxLength;
+
+ /// @domName HTMLInputElement.multiple;
+ bool multiple;
+
+ /// @domName HTMLInputElement.pattern
+ String pattern;
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.size
+ int size;
+}
+
+/**
+ * Text with no line breaks (sensitive information).
+ */
+abstract class PasswordInputElement implements TextInputElementBase {
+ factory PasswordInputElement() => new InputElement(type: 'password');
+}
+
+/**
+ * Base interface for all input element types which involve ranges.
+ */
+abstract class RangeInputElementBase implements InputElementBase {
+
+ /// @domName HTMLInputElement.list
+ Element get list;
+
+ /// @domName HTMLInputElement.max
+ String max;
+
+ /// @domName HTMLInputElement.min
+ String min;
+
+ /// @domName HTMLInputElement.step
+ String step;
+
+ /// @domName HTMLInputElement.valueAsNumber
+ num valueAsNumber;
+
+ /// @domName HTMLInputElement.stepDown
+ void stepDown([int n]);
+
+ /// @domName HTMLInputElement.stepUp
+ void stepUp([int n]);
+}
+
+/**
+ * A date and time (year, month, day, hour, minute, second, fraction of a
+ * second) with the time zone set to UTC.
+ */
+abstract class DateTimeInputElement implements RangeInputElementBase {
+ factory DateTimeInputElement() => new InputElement(type: 'datetime');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date (year, month, day) with no time zone.
+ */
+abstract class DateInputElement implements RangeInputElementBase {
+ factory DateInputElement() => new InputElement(type: 'date');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date consisting of a year and a month with no time zone.
+ */
+abstract class MonthInputElement implements RangeInputElementBase {
+ factory MonthInputElement() => new InputElement(type: 'month');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date consisting of a week-year number and a week number with no time zone.
+ */
+abstract class WeekInputElement implements RangeInputElementBase {
+ factory WeekInputElement() => new InputElement(type: 'week');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A time (hour, minute, seconds, fractional seconds) with no time zone.
+ */
+abstract class TimeInputElement implements RangeInputElementBase {
+ factory TimeInputElement() => new InputElement(type: 'time');
+
+ /// @domName HTMLInputElement.valueAsDate
+ Date valueAsDate;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A date and time (year, month, day, hour, minute, second, fraction of a
+ * second) with no time zone.
+ */
+abstract class LocalDateTimeInputElement implements RangeInputElementBase {
+ factory LocalDateTimeInputElement() =>
+ new InputElement(type: 'datetime-local');
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A numeric editor control.
+ */
+abstract class NumberInputElement implements RangeInputElementBase {
+ factory NumberInputElement() => new InputElement(type: 'number');
+
+ /// @domName HTMLInputElement.placeholder
+ String placeholder;
+
+ /// @domName HTMLInputElement.readOnly
+ bool readOnly;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * Similar to [NumberInputElement] but the browser may provide more optimal
+ * styling (such as a slider control).
+ */
+abstract class RangeInputElement implements RangeInputElementBase {
+ factory RangeInputElement() => new InputElement(type: 'range');
+}
+
+/**
+ * A boolean editor control.
+ *
+ * Note that if [indeterminate] is set then this control is in a third
+ * indeterminate state.
+ */
+abstract class CheckboxInputElement implements InputElementBase {
+ factory CheckboxInputElement() => new InputElement(type: 'checkbox');
+
+ /// @domName HTMLInputElement.checked
+ bool checked;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+
+/**
+ * A control that when used with other [ReadioButtonInputElement] controls
+ * forms a radio button group in which only one control can be checked at a
+ * time.
+ *
+ * Radio buttons are considered to be in the same radio button group if:
+ *
+ * * They are all of type 'radio'.
+ * * They all have either the same [FormElement] owner, or no owner.
+ * * Their name attributes contain the same name.
+ */
+abstract class RadioButtonInputElement implements InputElementBase {
+ factory RadioButtonInputElement() => new InputElement(type: 'radio');
+
+ /// @domName HTMLInputElement.checked
+ bool checked;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+}
+
+/**
+ * A control for picking files from the user's computer.
+ */
+abstract class FileUploadInputElement implements InputElementBase {
+ factory FileUploadInputElement() => new InputElement(type: 'file');
+
+ /// @domName HTMLInputElement.accept
+ String accept;
+
+ /// @domName HTMLInputElement.multiple
+ bool multiple;
+
+ /// @domName HTMLInputElement.required
+ bool required;
+
+ /// @domName HTMLInputElement.files
+ List<File> files;
+}
+
+/**
+ * A button, which when clicked, submits the form.
+ */
+abstract class SubmitButtonInputElement implements InputElementBase {
+ factory SubmitButtonInputElement() => new InputElement(type: 'submit');
+
+ /// @domName HTMLInputElement.formAction
+ String formAction;
+
+ /// @domName HTMLInputElement.formEnctype
+ String formEnctype;
+
+ /// @domName HTMLInputElement.formMethod
+ String formMethod;
+
+ /// @domName HTMLInputElement.formNoValidate
+ bool formNoValidate;
+
+ /// @domName HTMLInputElement.formTarget
+ String formTarget;
+}
+
+/**
+ * Either an image which the user can select a coordinate to or a form
+ * submit button.
+ */
+abstract class ImageButtonInputElement implements InputElementBase {
+ factory ImageButtonInputElement() => new InputElement(type: 'image');
+
+ /// @domName HTMLInputElement.alt
+ String alt;
+
+ /// @domName HTMLInputElement.formAction
+ String formAction;
+
+ /// @domName HTMLInputElement.formEnctype
+ String formEnctype;
+
+ /// @domName HTMLInputElement.formMethod
+ String formMethod;
+
+ /// @domName HTMLInputElement.formNoValidate
+ bool formNoValidate;
+
+ /// @domName HTMLInputElement.formTarget
+ String formTarget;
+
+ /// @domName HTMLInputElement.height
+ int height;
+
+ /// @domName HTMLInputElement.src
+ String src;
+
+ /// @domName HTMLInputElement.width
+ int width;
+}
+
+/**
+ * A button, which when clicked, resets the form.
+ */
+abstract class ResetButtonInputElement implements InputElementBase {
+ factory ResetButtonInputElement() => new InputElement(type: 'reset');
+}
+
+/**
+ * A button, with no default behavior.
+ */
+abstract class ButtonInputElement implements InputElementBase {
+ factory ButtonInputElement() => new InputElement(type: 'button');
+}
+
diff --git a/sdk/lib/html/templates/html/impl/impl_Node.darttemplate b/sdk/lib/html/templates/html/impl/impl_Node.darttemplate
index 07e0609..85671be 100644
--- a/sdk/lib/html/templates/html/impl/impl_Node.darttemplate
+++ b/sdk/lib/html/templates/html/impl/impl_Node.darttemplate
@@ -153,9 +153,9 @@
void remove() {
// TODO(jacobr): should we throw an exception if parent is already null?
// TODO(vsm): Use the native remove when available.
- if (this.parent != null) {
- final Node parent = this.parent;
- parent.$dom_removeChild(this);
+ if (this.parentNode != null) {
+ final Node parent = this.parentNode;
+ parentNode.$dom_removeChild(this);
}
}
@@ -165,7 +165,7 @@
*/
Node replaceWith(Node otherNode) {
try {
- final Node parent = this.parent;
+ final Node parent = this.parentNode;
parent.$dom_replaceChild(otherNode, this);
} catch (e) {
diff --git a/sdk/lib/html/templates/html/impl/impl_SVGElement.darttemplate b/sdk/lib/html/templates/html/impl/impl_SVGElement.darttemplate
index 6d8826a..ef3305d 100644
--- a/sdk/lib/html/templates/html/impl/impl_SVGElement.darttemplate
+++ b/sdk/lib/html/templates/html/impl/impl_SVGElement.darttemplate
@@ -46,8 +46,10 @@
return _cssClassSet;
}
+ @deprecated
List<Element> get elements => new FilteredElementList(this);
+ @deprecated
void set elements(Collection<Element> value) {
final elements = this.elements;
elements.clear();
diff --git a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
index 30f6948..4d6beaf 100644
--- a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
+++ b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
@@ -301,13 +301,18 @@
// can't handle that. Move it back after dart2js is completely done.
var _transaction_fn; // Assigned one of the static methods.
+/// @docsEditable true
class DatabaseEvents extends Events {
+ /// @docsEditable true
DatabaseEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get versionChange => this['versionchange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -867,11 +872,15 @@
new OpenDBRequestEvents(this);
}
+/// @docsEditable true
class OpenDBRequestEvents extends RequestEvents {
+ /// @docsEditable true
OpenDBRequestEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get blocked => this['blocked'];
+ /// @docsEditable true
EventListenerList get upgradeNeeded => this['upgradeneeded'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -924,11 +933,15 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class RequestEvents extends Events {
+ /// @docsEditable true
RequestEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get success => this['success'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -977,13 +990,18 @@
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) native;
}
+/// @docsEditable true
class TransactionEvents extends Events {
+ /// @docsEditable true
TransactionEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get complete => this['complete'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -1024,9 +1042,12 @@
new VersionChangeRequestEvents(this);
}
+/// @docsEditable true
class VersionChangeRequestEvents extends RequestEvents {
+ /// @docsEditable true
VersionChangeRequestEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get blocked => this['blocked'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
diff --git a/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart b/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
index 2043e5c..e01edb1 100644
--- a/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
+++ b/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
@@ -194,13 +194,18 @@
}
+/// @docsEditable true
class DatabaseEvents extends Events {
+ /// @docsEditable true
DatabaseEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get versionChange => this['versionchange'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -784,11 +789,15 @@
}
+/// @docsEditable true
class OpenDBRequestEvents extends RequestEvents {
+ /// @docsEditable true
OpenDBRequestEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get blocked => this['blocked'];
+ /// @docsEditable true
EventListenerList get upgradeNeeded => this['upgradeneeded'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -848,11 +857,15 @@
}
+/// @docsEditable true
class RequestEvents extends Events {
+ /// @docsEditable true
RequestEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get success => this['success'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -910,13 +923,18 @@
}
+/// @docsEditable true
class TransactionEvents extends Events {
+ /// @docsEditable true
TransactionEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get complete => this['complete'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -972,9 +990,12 @@
}
+/// @docsEditable true
class VersionChangeRequestEvents extends RequestEvents {
+ /// @docsEditable true
VersionChangeRequestEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get blocked => this['blocked'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
diff --git a/sdk/lib/io/directory_impl.dart b/sdk/lib/io/directory_impl.dart
index ecf7959..c24bedb 100644
--- a/sdk/lib/io/directory_impl.dart
+++ b/sdk/lib/io/directory_impl.dart
@@ -66,7 +66,7 @@
}
}
if (future == null) {
- return new Future.immedidate(-1);
+ return new Future.immediate(-1);
} else {
return future;
}
diff --git a/sdk/lib/io/http.dart b/sdk/lib/io/http.dart
index 5c8a2f0..24cb85b 100644
--- a/sdk/lib/io/http.dart
+++ b/sdk/lib/io/http.dart
@@ -889,7 +889,22 @@
/**
* Set this property to [:true:] if this connection should
- * automatically follow redirects. The default is [:true:].
+ * automatically follow redirects. The default is
+ * [:true:].
+ *
+ * Automatic redirect will only happen for "GET" and "HEAD" requests
+ * and only for the status codes [:HttpStatus.MOVED_PERMANENTLY:]
+ * (301), [:HttpStatus.FOUND:] (302),
+ * [:HttpStatus.MOVED_TEMPORARILY:] (302, alias for
+ * [:HttpStatus.FOUND:]), [:HttpStatus.SEE_OTHER:] (303) and
+ * [:HttpStatus.TEMPORARY_REDIRECT:] (307). For
+ * [:HttpStatus.SEE_OTHER:] (303) autmatic redirect will also happen
+ * for "POST" requests with the method changed to "GET" when
+ * following the redirect.
+ *
+ * All headers added to the request will be added to the redirection
+ * request(s). However, any body send with the request will not be
+ * part of the redirection request(s).
*/
bool followRedirects;
@@ -909,9 +924,13 @@
/**
* Redirect this connection to a new URL. The default value for
* [method] is the method for the current request. The default value
- * for [url] is the value of the [:HttpStatus.LOCATION:] header of
+ * for [url] is the value of the [:HttpHeaders.LOCATION:] header of
* the current response. All body data must have been read from the
* current response before calling [redirect].
+ *
+ * All headers added to the request will be added to the redirection
+ * request(s). However, any body send with the request will not be
+ * part of the redirection request(s).
*/
void redirect([String method, Uri url]);
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 5672bb5..30fe2c1 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -832,7 +832,7 @@
}
bool _write(List<int> data, [bool copyBuffer = false]) {
- if (_isRequestDone) {
+ if (_isRequestDone || !_hasBody || _httpParser.upgrade) {
return _socket.outputStream.write(data, copyBuffer);
} else {
_bufferData(data, copyBuffer);
@@ -841,7 +841,7 @@
}
bool _writeFrom(List<int> data, [int offset, int len]) {
- if (_isRequestDone) {
+ if (_isRequestDone || !_hasBody || _httpParser.upgrade) {
return _socket.outputStream.writeFrom(data, offset, len);
} else {
if (offset == null) offset = 0;
@@ -895,7 +895,8 @@
void _onRequestReceived(String method,
String uri,
String version,
- _HttpHeaders headers) {
+ _HttpHeaders headers,
+ bool hasBody) {
_state = _HttpConnectionBase.ACTIVE;
// Create new request and response objects for this request.
_request = new _HttpRequest(this);
@@ -905,13 +906,16 @@
_response._protocolVersion = version;
_response._headResponse = method == "HEAD";
_response.persistentConnection = _httpParser.persistentConnection;
+ _hasBody = hasBody;
if (onRequestReceived != null) {
onRequestReceived(_request, _response);
}
+ _checkDone();
}
void _onDataReceived(List<int> data) {
_request._onDataReceived(data);
+ _checkDone();
}
void _checkDone() {
@@ -933,7 +937,7 @@
} else {
_state = _HttpConnectionBase.IDLE;
}
- } else if (_isResponseDone) {
+ } else if (_isResponseDone && _hasBody) {
// If the response is closed before the request is fully read
// close this connection. If there is buffered output
// (e.g. error response for invalid request where the server did
@@ -947,19 +951,20 @@
void _onDataEnd(bool close) {
// Start sending queued response if any.
- _writeBufferedResponse();
_state |= _HttpConnectionBase.REQUEST_DONE;
+ _writeBufferedResponse();
_request._onDataEnd();
+ _checkDone();
}
void _responseClosed() {
_state |= _HttpConnectionBase.RESPONSE_DONE;
- _checkDone();
}
HttpServer _server;
HttpRequest _request;
HttpResponse _response;
+ bool _hasBody = false;
// Buffer for data written before full response has been processed.
_BufferList _buffer;
@@ -1286,10 +1291,16 @@
String get reasonPhrase => _reasonPhrase;
bool get isRedirect {
- return statusCode == HttpStatus.MOVED_PERMANENTLY ||
- statusCode == HttpStatus.FOUND ||
- statusCode == HttpStatus.SEE_OTHER ||
- statusCode == HttpStatus.TEMPORARY_REDIRECT;
+ var method = _connection._request._method;
+ if (method == "GET" || method == "HEAD") {
+ return statusCode == HttpStatus.MOVED_PERMANENTLY ||
+ statusCode == HttpStatus.FOUND ||
+ statusCode == HttpStatus.SEE_OTHER ||
+ statusCode == HttpStatus.TEMPORARY_REDIRECT;
+ } else if (method == "POST") {
+ return statusCode == HttpStatus.SEE_OTHER;
+ }
+ return false;
}
List<Cookie> get cookies {
@@ -1314,7 +1325,8 @@
void _onResponseReceived(int statusCode,
String reasonPhrase,
String version,
- _HttpHeaders headers) {
+ _HttpHeaders headers,
+ bool hasBody) {
_statusCode = statusCode;
_reasonPhrase = reasonPhrase;
_headers = headers;
@@ -1324,7 +1336,6 @@
// Prepare for receiving data.
_headers._mutable = false;
_buffer = new _BufferList();
-
if (isRedirect && _connection.followRedirects) {
if (_connection._redirects == null ||
_connection._redirects.length < _connection.maxRedirects) {
@@ -1346,7 +1357,12 @@
}
// Drain body and redirect.
inputStream.onData = inputStream.read;
- _connection.redirect();
+ if (_statusCode == HttpStatus.SEE_OTHER &&
+ _connection._method == "POST") {
+ _connection.redirect("GET");
+ } else {
+ _connection.redirect();
+ }
} else {
throw new RedirectLimitExceededException(_connection._redirects);
}
@@ -1615,8 +1631,10 @@
void _onResponseReceived(int statusCode,
String reasonPhrase,
String version,
- _HttpHeaders headers) {
- _response._onResponseReceived(statusCode, reasonPhrase, version, headers);
+ _HttpHeaders headers,
+ bool hasBody) {
+ _response._onResponseReceived(
+ statusCode, reasonPhrase, version, headers, hasBody);
}
void _onDataReceived(List<int> data) {
@@ -2268,7 +2286,7 @@
void authorize(_Credentials credentials, HttpClientRequest request) {
// TODO(sgjesse): Implement!!!
- throw new UnsupportedOperationException();
+ throw new UnsupportedError("Digest authentication not yet supported");
}
String username;
diff --git a/sdk/lib/io/http_parser.dart b/sdk/lib/io/http_parser.dart
index 9dd569b..fbbd43d 100644
--- a/sdk/lib/io/http_parser.dart
+++ b/sdk/lib/io/http_parser.dart
@@ -326,13 +326,10 @@
case _State.RESPONSE_LINE_ENDING:
_expect(byte, _CharCode.LF);
_messageType == _MessageType.RESPONSE;
- _statusCode = parseInt(new String.fromCharCodes(_method_or_status_code));
+ _statusCode = parseInt(
+ new String.fromCharCodes(_method_or_status_code));
if (_statusCode < 100 || _statusCode > 599) {
throw new HttpParserException("Invalid response status code");
- } else {
- // Check whether this response will never have a body.
- _noMessageBody =
- _statusCode <= 199 || _statusCode == 204 || _statusCode == 304;
}
_state = _State.HEADER_START;
break;
@@ -443,34 +440,37 @@
if (_connectionUpgrade) {
_state = _State.UPGRADED;
}
+ var noBody;
if (_requestParser) {
+ noBody = _contentLength == 0;
requestStart(new String.fromCharCodes(_method_or_status_code),
new String.fromCharCodes(_uri_or_reason_phrase),
version,
- _headers);
+ _headers,
+ !noBody);
} else {
+ // Check whether this response will never have a body.
+ noBody = _contentLength == 0 ||
+ _statusCode <= 199 ||
+ _statusCode == HttpStatus.NO_CONTENT ||
+ _statusCode == HttpStatus.NOT_MODIFIED ||
+ _responseToMethod == "HEAD";
responseStart(_statusCode,
new String.fromCharCodes(_uri_or_reason_phrase),
version,
- _headers);
+ _headers,
+ !noBody);
}
- if (_state == _State.CANCELED) continue;
_method_or_status_code.clear();
_uri_or_reason_phrase.clear();
+ if (_state == _State.CANCELED) continue;
if (!_connectionUpgrade) {
- _method_or_status_code.clear();
- _uri_or_reason_phrase.clear();
- if (_chunked) {
+ if (noBody) {
+ _bodyEnd();
+ _reset();
+ } else if (_chunked) {
_state = _State.CHUNK_SIZE;
_remainingContent = 0;
- } else if (_contentLength == 0 ||
- (_messageType == _MessageType.RESPONSE &&
- (_noMessageBody || _responseToMethod == "HEAD"))) {
- // If there is no message body get ready to process the
- // next request.
- _bodyEnd();
- if (_state == _State.CANCELED) continue;
- _reset();
} else if (_contentLength > 0) {
_remainingContent = _contentLength;
_state = _State.BODY;
@@ -691,7 +691,6 @@
_connectionUpgrade = false;
_chunked = false;
- _noMessageBody = false;
_responseToMethod = null;
_remainingContent = null;
@@ -771,7 +770,6 @@
bool _connectionUpgrade;
bool _chunked;
- bool _noMessageBody;
String _responseToMethod; // Indicates the method used for the request.
int _remainingContent;
diff --git a/sdk/lib/io/io.dart b/sdk/lib/io/io.dart
index 7b0fbae..3900b61 100644
--- a/sdk/lib/io/io.dart
+++ b/sdk/lib/io/io.dart
@@ -29,6 +29,7 @@
#source('file.dart');
#source('file_impl.dart');
#source('http.dart');
+#source('http_headers.dart');
#source('http_impl.dart');
#source('http_parser.dart');
#source('http_session.dart');
diff --git a/sdk/lib/io/secure_server_socket.dart b/sdk/lib/io/secure_server_socket.dart
index 2ab290a..fb55196 100644
--- a/sdk/lib/io/secure_server_socket.dart
+++ b/sdk/lib/io/secure_server_socket.dart
@@ -13,24 +13,40 @@
* the certificate, such as "CN=localhost" or "CN=myserver.mydomain.com".
* The certificate is looked up in the NSS certificate database set by
* SecureSocket.setCertificateDatabase.
+ *
+ * To request or require that clients authenticate by providing an SSL (TLS)
+ * client certificate, set the optional parameters requestClientCertificate or
+ * requireClientCertificate to true. Require implies request, so one doesn't
+ * need to specify both. To check whether a client certificate was received,
+ * check SecureSocket.peerCertificate after connecting. If no certificate
+ * was received, the result will be null.
*/
factory SecureServerSocket(String bindAddress,
- int port,
- int backlog,
- String certificate_name) =>
- new _SecureServerSocket(bindAddress, port, backlog, certificate_name);
+ int port,
+ int backlog,
+ String certificate_name,
+ {bool requestClientCertificate: false,
+ bool requireClientCertificate: false}) {
+ return new _SecureServerSocket(bindAddress,
+ port,
+ backlog,
+ certificate_name,
+ requestClientCertificate,
+ requireClientCertificate);
+ }
}
class _SecureServerSocket implements SecureServerSocket {
_SecureServerSocket(String bindAddress,
- int port,
- int backlog,
- String certificate_name) {
- _socket = new ServerSocket(bindAddress, port, backlog);
- _socket.onConnection = this._onConnectionHandler;
- _certificate_name = certificate_name;
+ int port,
+ int backlog,
+ String this.certificate_name,
+ bool this.requestClientCertificate,
+ bool this.requireClientCertificate) {
+ socket = new ServerSocket(bindAddress, port, backlog);
+ socket.onConnection = this._onConnectionHandler;
}
void set onConnection(void callback(Socket connection)) {
@@ -38,19 +54,19 @@
}
void set onError(void callback(e)) {
- _socket.onError = callback;
+ socket.onError = callback;
}
/**
* Returns the port used by this socket.
*/
- int get port => _socket.port;
+ int get port => socket.port;
/**
* Closes the socket.
*/
void close() {
- _socket.close();
+ socket.close();
}
void _onConnectionHandler(Socket connection) {
@@ -59,19 +75,25 @@
throw new SocketIOException(
"SecureServerSocket with no onConnection callback connected to");
}
- if (_certificate_name == null) {
+ if (certificate_name == null) {
connection.close();
throw new SocketIOException(
"SecureServerSocket with server certificate not set connected to");
}
- var secure_connection = new _SecureSocket.server(connection.remoteHost,
- connection.remotePort,
- connection,
- _certificate_name);
+ var secure_connection = new _SecureSocket(
+ connection.remoteHost,
+ connection.remotePort,
+ certificate_name,
+ is_server: true,
+ socket: connection,
+ requestClientCertificate: requestClientCertificate,
+ requireClientCertificate: requireClientCertificate);
_onConnectionCallback(secure_connection);
}
- ServerSocket _socket;
+ ServerSocket socket;
var _onConnectionCallback;
- String _certificate_name;
+ final String certificate_name;
+ final bool requestClientCertificate;
+ final bool requireClientCertificate;
}
diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart
index 2721237..02742f7 100644
--- a/sdk/lib/io/secure_socket.dart
+++ b/sdk/lib/io/secure_socket.dart
@@ -11,9 +11,24 @@
/**
* Constructs a new secure client socket and connect it to the given
* host on the given port. The returned socket is not yet connected
- * but ready for registration of callbacks.
+ * but ready for registration of callbacks. If sendClientCertificate is
+ * set to true, the socket will send a client certificate if one is
+ * requested by the server. If clientCertificate is the nickname of
+ * a certificate in the certificate database, that certificate will be sent.
+ * If clientCertificate is null, which is the usual use case, an
+ * appropriate certificate will be searched for in the database and
+ * sent automatically, based on what the server says it will accept.
*/
- factory SecureSocket(String host, int port) => new _SecureSocket(host, port);
+ factory SecureSocket(String host,
+ int port,
+ {bool sendClientCertificate: false,
+ String certificateName}) {
+ return new _SecureSocket(host,
+ port,
+ certificateName,
+ is_server: false,
+ sendClientCertificate: sendClientCertificate);
+ }
/**
* Install a handler for unverifiable certificates. The handler can inspect
@@ -23,6 +38,14 @@
*/
void set onBadCertificate(bool callback(X509Certificate certificate));
+ /**
+ * Get the peerCertificate for a connected secure socket. For a server
+ * socket, this will return the client certificate, or null, if no
+ * client certificate was received. For a client socket, this
+ * will return the server's certificate.
+ */
+ X509Certificate get peerCertificate;
+
/**
* Initializes the NSS library with the path to a certificate database
* containing root certificates for verifying certificate paths on
@@ -89,45 +112,61 @@
static final int WRITE_ENCRYPTED = 3;
static final int NUM_BUFFERS = 4;
- int _count = 0;
- // Constructs a new secure client socket.
- factory _SecureSocket(String host, int port) =>
- new _SecureSocket.internal(host, port, false);
-
- // Constructs a new secure server socket, with the named server certificate.
- factory _SecureSocket.server(String host,
- int port,
- Socket socket,
- String certificateName) =>
- new _SecureSocket.internal(host, port, true, socket, certificateName);
-
- _SecureSocket.internal(String host,
- int port,
- bool is_server,
- [Socket socket,
- String certificateName])
- : _host = host,
- _port = port,
- _socket = socket,
- _certificateName = certificateName,
- _is_server = is_server,
- _secureFilter = new _SecureFilter() {
- if (_socket == null) {
- _socket = new Socket(host, port);
+ _SecureSocket(String this.host,
+ int requestedPort,
+ String this.certificateName,
+ {bool this.is_server,
+ Socket this.socket,
+ bool this.requestClientCertificate: false,
+ bool this.requireClientCertificate: false,
+ bool this.sendClientCertificate: false})
+ : secureFilter = new _SecureFilter() {
+ // Throw an ArgumentError if any field is invalid.
+ _verifyFields();
+ if (socket == null) {
+ socket = new Socket(host, requestedPort);
}
- _socket.onConnect = _secureConnectHandler;
- _socket.onData = _secureDataHandler;
- _socket.onClosed = _secureCloseHandler;
- _socket.onError = _secureErrorHandler;
- _secureFilter.init();
- _secureFilter.registerHandshakeCompleteCallback(_secureHandshakeCompleteHandler);
+ socket.onConnect = _secureConnectHandler;
+ socket.onData = _secureDataHandler;
+ socket.onClosed = _secureCloseHandler;
+ socket.onError = _secureErrorHandler;
+ secureFilter.init();
+ secureFilter.registerHandshakeCompleteCallback(
+ _secureHandshakeCompleteHandler);
}
- int get port => _socket.port;
+ void _verifyFields() {
+ if (host is! String) throw new ArgumentError(
+ "SecureSocket constructor: host is not a String");
+ assert(is_server is bool);
+ assert(socket == null || socket is Socket);
+ if (certificateName != null && certificateName is! String) {
+ throw new ArgumentError(
+ "SecureSocket constructor: certificateName is not null or a String");
+ }
+ if (certificateName == null && is_server) {
+ throw new ArgumentError(
+ "SecureSocket constructor: certificateName is null on a server");
+ }
+ if (requestClientCertificate is! bool) {
+ throw new ArgumentError(
+ "SecureSocket constructor: requestClientCertificate is not a bool");
+ }
+ if (requireClientCertificate is! bool) {
+ throw new ArgumentError(
+ "SecureSocket constructor: requireClientCertificate is not a bool");
+ }
+ if (sendClientCertificate is! bool) {
+ throw new ArgumentError(
+ "SecureSocket constructor: sendClientCertificate is not a bool");
+ }
+ }
- String get remoteHost => _socket.remoteHost;
+ int get port => socket.port;
- int get remotePort => _socket.remotePort;
+ String get remoteHost => socket.remoteHost;
+
+ int get remotePort => socket.remotePort;
void set onClosed(void callback()) {
if (_inputStream != null && callback != null) {
@@ -180,7 +219,7 @@
void set _onWrite(void callback()) {
_socketWriteHandler = callback;
// Reset the one-shot onWrite handler.
- _socket.onWrite = _secureWriteHandler;
+ socket.onWrite = _secureWriteHandler;
}
void set onBadCertificate(bool callback(X509Certificate certificate)) {
@@ -188,7 +227,7 @@
throw new SocketIOException(
"Callback provided to onBadCertificate is not a function or null");
}
- _secureFilter.registerBadCertificateCallback(callback);
+ secureFilter.registerBadCertificateCallback(callback);
}
InputStream get inputStream {
@@ -223,7 +262,7 @@
_closedWrite = true;
_writeEncryptedData();
if (_filterWriteEmpty) {
- _socket.close(true);
+ socket.close(true);
_socketClosedWrite = true;
if (_closedRead) {
close(false);
@@ -232,11 +271,11 @@
} else {
_closedWrite = true;
_closedRead = true;
- _socket.close(false);
+ socket.close(false);
_socketClosedWrite = true;
_socketClosedRead = true;
- _secureFilter.destroy();
- _secureFilter = null;
+ secureFilter.destroy();
+ secureFilter = null;
if (scheduledDataEvent != null) {
scheduledDataEvent.cancel();
}
@@ -253,7 +292,7 @@
if (_status != CONNECTED) {
return new List<int>(0);
}
- var buffer = _secureFilter.buffers[READ_PLAINTEXT];
+ var buffer = secureFilter.buffers[READ_PLAINTEXT];
_readEncryptedData();
int toRead = buffer.length;
if (len != null) {
@@ -284,7 +323,7 @@
}
int bytesRead = 0;
- var buffer = _secureFilter.buffers[READ_PLAINTEXT];
+ var buffer = secureFilter.buffers[READ_PLAINTEXT];
// TODO(whesse): Currently this fails if the if is turned into a while loop.
// Fix it so that it can loop and read more than one buffer's worth of data.
if (bytes > bytesRead) {
@@ -310,7 +349,7 @@
throw new SocketIOException("Writing to a closed socket");
}
if (_status != CONNECTED) return 0;
- var buffer = _secureFilter.buffers[WRITE_PLAINTEXT];
+ var buffer = secureFilter.buffers[WRITE_PLAINTEXT];
if (bytes > buffer.free) {
bytes = buffer.free;
}
@@ -322,9 +361,17 @@
return bytes;
}
+ X509Certificate get peerCertificate => secureFilter.peerCertificate;
+
void _secureConnectHandler() {
_connectPending = true;
- _secureFilter.connect(_host, _port, _is_server, _certificateName);
+ secureFilter.connect(host,
+ port,
+ is_server,
+ certificateName,
+ requestClientCertificate || requireClientCertificate,
+ requireClientCertificate,
+ sendClientCertificate);
_status = HANDSHAKE;
_secureHandshake();
}
@@ -338,7 +385,7 @@
_secureHandshake();
} else if (_status == CONNECTED &&
_socketWriteHandler != null &&
- _secureFilter.buffers[WRITE_PLAINTEXT].free > 0) {
+ secureFilter.buffers[WRITE_PLAINTEXT].free > 0) {
// We must be able to set onWrite from the onWrite callback.
var handler = _socketWriteHandler;
// Reset the one-shot handler.
@@ -420,10 +467,10 @@
void _secureHandshake() {
_readEncryptedData();
- _secureFilter.handshake();
+ secureFilter.handshake();
_writeEncryptedData();
- if (_secureFilter.buffers[WRITE_ENCRYPTED].length > 0) {
- _socket.onWrite = _secureWriteHandler;
+ if (secureFilter.buffers[WRITE_ENCRYPTED].length > 0) {
+ socket.onWrite = _secureWriteHandler;
}
}
@@ -434,7 +481,7 @@
_socketConnectHandler();
}
if (_socketWriteHandler != null) {
- _socket.onWrite = _secureWriteHandler;
+ socket.onWrite = _secureWriteHandler;
}
}
@@ -445,30 +492,30 @@
void _readEncryptedData() {
// Read from the socket, and push it through the filter as far as
// possible.
- var encrypted = _secureFilter.buffers[READ_ENCRYPTED];
- var plaintext = _secureFilter.buffers[READ_PLAINTEXT];
+ var encrypted = secureFilter.buffers[READ_ENCRYPTED];
+ var plaintext = secureFilter.buffers[READ_PLAINTEXT];
bool progress = true;
while (progress) {
progress = false;
// Do not try to read plaintext from the filter while handshaking.
if ((_status == CONNECTED) && plaintext.free > 0) {
- int bytes = _secureFilter.processBuffer(READ_PLAINTEXT);
+ int bytes = secureFilter.processBuffer(READ_PLAINTEXT);
if (bytes > 0) {
plaintext.length += bytes;
progress = true;
}
}
if (encrypted.length > 0) {
- int bytes = _secureFilter.processBuffer(READ_ENCRYPTED);
+ int bytes = secureFilter.processBuffer(READ_ENCRYPTED);
if (bytes > 0) {
encrypted.advanceStart(bytes);
progress = true;
}
}
if (!_socketClosedRead) {
- int bytes = _socket.readList(encrypted.data,
- encrypted.start + encrypted.length,
- encrypted.free);
+ int bytes = socket.readList(encrypted.data,
+ encrypted.start + encrypted.length,
+ encrypted.free);
if (bytes > 0) {
encrypted.length += bytes;
progress = true;
@@ -484,29 +531,29 @@
void _writeEncryptedData() {
if (_socketClosedWrite) return;
- var encrypted = _secureFilter.buffers[WRITE_ENCRYPTED];
- var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT];
+ var encrypted = secureFilter.buffers[WRITE_ENCRYPTED];
+ var plaintext = secureFilter.buffers[WRITE_PLAINTEXT];
while (true) {
if (encrypted.length > 0) {
// Write from the filter to the socket.
- int bytes = _socket.writeList(encrypted.data,
- encrypted.start,
- encrypted.length);
+ int bytes = socket.writeList(encrypted.data,
+ encrypted.start,
+ encrypted.length);
if (bytes == 0) {
// The socket has blocked while we have data to write.
// We must be notified when it becomes unblocked.
- _socket.onWrite = _secureWriteHandler;
+ socket.onWrite = _secureWriteHandler;
_filterWriteEmpty = false;
break;
}
encrypted.advanceStart(bytes);
} else {
- var plaintext = _secureFilter.buffers[WRITE_PLAINTEXT];
+ var plaintext = secureFilter.buffers[WRITE_PLAINTEXT];
if (plaintext.length > 0) {
- int plaintext_bytes = _secureFilter.processBuffer(WRITE_PLAINTEXT);
+ int plaintext_bytes = secureFilter.processBuffer(WRITE_PLAINTEXT);
plaintext.advanceStart(plaintext_bytes);
}
- int bytes = _secureFilter.processBuffer(WRITE_ENCRYPTED);
+ int bytes = secureFilter.processBuffer(WRITE_ENCRYPTED);
if (bytes <= 0) {
// We know the WRITE_ENCRYPTED buffer is empty, and the
// filter wrote zero bytes to it, so the filter must be empty.
@@ -557,11 +604,13 @@
bool get _socketClosed => _closedRead;
// _SecureSocket cannot extend _Socket and use _Socket's factory constructor.
- Socket _socket;
- String _host;
- int _port;
- bool _is_server;
- String _certificateName;
+ Socket socket;
+ final String host;
+ final bool is_server;
+ final String certificateName;
+ final bool requestClientCertificate;
+ final bool requireClientCertificate;
+ final bool sendClientCertificate;
var _status = NOT_CONNECTED;
bool _socketClosedRead = false; // The network socket is closed for reading.
@@ -580,7 +629,7 @@
Function _socketCloseHandler;
Timer scheduledDataEvent;
- _SecureFilter _secureFilter;
+ _SecureFilter secureFilter;
}
@@ -611,10 +660,14 @@
void connect(String hostName,
int port,
bool is_server,
- String certificateName);
+ String certificateName,
+ bool requestClientCertificate,
+ bool requireClientCertificate,
+ bool sendClientCertificate);
void destroy();
void handshake();
void init();
+ X509Certificate get peerCertificate;
int processBuffer(int bufferIndex);
void registerBadCertificateCallback(Function callback);
void registerHandshakeCompleteCallback(Function handshakeCompleteHandler);
diff --git a/sdk/lib/io/stdio.dart b/sdk/lib/io/stdio.dart
index e080526..246fe31 100644
--- a/sdk/lib/io/stdio.dart
+++ b/sdk/lib/io/stdio.dart
@@ -14,44 +14,9 @@
OutputStream _stderr;
-InputStream _getStdioInputStream() {
- switch (_StdIOUtils._getStdioHandleType(0)) {
- case _STDIO_HANDLE_TYPE_TERMINAL:
- case _STDIO_HANDLE_TYPE_PIPE:
- case _STDIO_HANDLE_TYPE_SOCKET:
- Socket s = new _Socket._internalReadOnly();
- _StdIOUtils._getStdioHandle(s, 0);
- s._closed = false;
- return s.inputStream;
- case _STDIO_HANDLE_TYPE_FILE:
- return new _FileInputStream.fromStdio(0);
- default:
- throw new FileIOException("Unsupported stdin type");
- }
-}
-
-
-OutputStream _getStdioOutputStream(int fd) {
- assert(fd == 1 || fd == 2);
- switch (_StdIOUtils._getStdioHandleType(fd)) {
- case _STDIO_HANDLE_TYPE_TERMINAL:
- case _STDIO_HANDLE_TYPE_PIPE:
- case _STDIO_HANDLE_TYPE_SOCKET:
- Socket s = new _Socket._internalWriteOnly();
- _StdIOUtils._getStdioHandle(s, fd);
- s._closed = false;
- return s.outputStream;
- case _STDIO_HANDLE_TYPE_FILE:
- return new _FileOutputStream.fromStdio(fd);
- default:
- throw new FileIOException("Unsupported stdin type");
- }
-}
-
-
InputStream get stdin {
if (_stdin == null) {
- _stdin = _getStdioInputStream();
+ _stdin = _StdIOUtils._getStdioInputStream();
}
return _stdin;
}
@@ -59,7 +24,7 @@
OutputStream get stdout {
if (_stdout == null) {
- _stdout = _getStdioOutputStream(1);
+ _stdout = _StdIOUtils._getStdioOutputStream(1);
}
return _stdout;
}
@@ -67,13 +32,13 @@
OutputStream get stderr {
if (_stderr == null) {
- _stderr = _getStdioOutputStream(2);
+ _stderr = _StdIOUtils._getStdioOutputStream(2);
}
return _stderr;
}
class _StdIOUtils {
- external static _getStdioHandle(Socket socket, int num);
- external static _getStdioHandleType(int num);
+ external static OutputStream _getStdioOutputStream(int fd);
+ external static InputStream _getStdioInputStream();
}
diff --git a/sdk/lib/io/websocket_impl.dart b/sdk/lib/io/websocket_impl.dart
index 1d0eb286..5d6f7d6 100644
--- a/sdk/lib/io/websocket_impl.dart
+++ b/sdk/lib/io/websocket_impl.dart
@@ -700,7 +700,6 @@
if (!_isWebSocketUpgrade(response)) {
_conn.detachSocket().socket.close();
throw new WebSocketException("Protocol upgrade failed");
- return;
}
// Connection upgrade successful.
diff --git a/sdk/lib/isolate/timer.dart b/sdk/lib/isolate/timer.dart
index d8da52f..1364239 100644
--- a/sdk/lib/isolate/timer.dart
+++ b/sdk/lib/isolate/timer.dart
@@ -5,45 +5,19 @@
abstract class Timer {
/**
* Creates a new timer. The [callback] callback is invoked after
- * [milliSeconds] milliseconds.
+ * [milliseconds] milliseconds.
*/
- factory Timer(int milliSeconds, void callback(Timer timer)) {
- if (_TimerFactory._factory == null) {
- throw new UnsupportedError("Timer interface not supported.");
- }
- return _TimerFactory._factory(milliSeconds, callback, false);
- }
+ external factory Timer(int milliseconds, void callback(Timer timer));
/**
* Creates a new repeating timer. The [callback] is invoked every
- * [milliSeconds] millisecond until cancelled.
+ * [milliseconds] millisecond until cancelled.
*/
- factory Timer.repeating(int milliSeconds, void callback(Timer timer)) {
- if (_TimerFactory._factory == null) {
- throw new UnsupportedError("Timer interface not supported.");
- }
- return _TimerFactory._factory(milliSeconds, callback, true);
- }
+ external factory Timer.repeating(int milliseconds,
+ void callback(Timer timer));
/**
* Cancels the timer.
*/
void cancel();
}
-
-// TODO(ajohnsen): Patch timer once we have support for patching named
-// factory constructors in the VM.
-
-typedef Timer _TimerFactoryClosure(int milliSeconds,
- void callback(Timer timer),
- bool repeating);
-
-class _TimerFactory {
- static _TimerFactoryClosure _factory;
-}
-
-// TODO(ahe): Warning: this is NOT called by Dartium. Instead, it sets
-// [_TimerFactory._factory] directly.
-void _setTimerFactoryClosure(_TimerFactoryClosure closure) {
- _TimerFactory._factory = closure;
-}
diff --git a/sdk/lib/scalarlist/byte_arrays.dart b/sdk/lib/scalarlist/byte_arrays.dart
index 476cc46..df3ab98 100644
--- a/sdk/lib/scalarlist/byte_arrays.dart
+++ b/sdk/lib/scalarlist/byte_arrays.dart
@@ -369,7 +369,7 @@
* not specified, it defaults to null, which indicates that the view extends
* to the end of the byte array.
*/
- external factory Int8List.view(ByteArray array, [int start, int length]);
+ external factory Int8List.view(ByteArray array, [int start = 0, int length]);
}
@@ -393,7 +393,7 @@
* not specified, it defaults to null, which indicates that the view extends
* to the end of the byte array.
*/
- external factory Uint8List.view(ByteArray array, [int start, int length]);
+ external factory Uint8List.view(ByteArray array, [int start = 0, int length]);
}
@@ -419,7 +419,7 @@
* extends to the end of the byte array.
*/
external factory Uint8ClampedList.view(ByteArray array,
- [int start, int length]);
+ [int start = 0, int length]);
}
@@ -453,7 +453,7 @@
* region does not contain an integral number of "int16s," or if it
* is not "int16-aligned."
*/
- external factory Int16List.view(ByteArray array, [int start, int length]);
+ external factory Int16List.view(ByteArray array, [int start = 0, int length]);
}
@@ -487,7 +487,8 @@
* region does not contain an integral number of "uint16s," or if it
* is not "uint16-aligned."
*/
- external factory Uint16List.view(ByteArray array, [int start, int length]);
+ external factory Uint16List.view(ByteArray array,
+ [int start = 0, int length]);
}
@@ -521,7 +522,7 @@
* region does not contain an integral number of "int32s," or if it
* is not "int32-aligned."
*/
- external factory Int32List.view(ByteArray array, [int start, int length]);
+ external factory Int32List.view(ByteArray array, [int start = 0, int length]);
}
@@ -555,7 +556,8 @@
* region does not contain an integral number of "uint32s," or if it
* is not "uint32-aligned."
*/
- external factory Uint32List.view(ByteArray array, [int start, int length]);
+ external factory Uint32List.view(ByteArray array,
+ [int start = 0, int length]);
}
@@ -589,7 +591,7 @@
* region does not contain an integral number of "int64s," or if it
* is not "int64-aligned."
*/
- external factory Int64List.view(ByteArray array, [int start, int length]);
+ external factory Int64List.view(ByteArray array, [int start = 0, int length]);
}
@@ -623,7 +625,8 @@
* region does not contain an integral number of "uint64s," or if it
* is not "uint64-aligned."
*/
- external factory Uint64List.view(ByteArray array, [int start, int length]);
+ external factory Uint64List.view(ByteArray array,
+ [int start = 0, int length]);
}
@@ -658,7 +661,8 @@
* region does not contain an integral number of "float32s," or if it
* is not "float32-aligned."
*/
- external factory Float32List.view(ByteArray array, [int start, int length]);
+ external factory Float32List.view(ByteArray array,
+ [int start = 0, int length]);
}
@@ -693,5 +697,6 @@
* region does not contain an integral number of "float64s," or if it
* is not "float64-aligned."
*/
- external factory Float64List.view(ByteArray array, [int start, int length]);
+ external factory Float64List.view(ByteArray array,
+ [int start = 0, int length]);
}
diff --git a/sdk/lib/svg/dart2js/svg_dart2js.dart b/sdk/lib/svg/dart2js/svg_dart2js.dart
index 4a87d20..5ec3b6e 100644
--- a/sdk/lib/svg/dart2js/svg_dart2js.dart
+++ b/sdk/lib/svg/dart2js/svg_dart2js.dart
@@ -901,87 +901,129 @@
final ElementInstance previousSibling;
}
+/// @docsEditable true
class ElementInstanceEvents extends Events {
+ /// @docsEditable true
ElementInstanceEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get beforeCopy => this['beforecopy'];
+ /// @docsEditable true
EventListenerList get beforeCut => this['beforecut'];
+ /// @docsEditable true
EventListenerList get beforePaste => this['beforepaste'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get change => this['change'];
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get contextMenu => this['contextmenu'];
+ /// @docsEditable true
EventListenerList get copy => this['copy'];
+ /// @docsEditable true
EventListenerList get cut => this['cut'];
+ /// @docsEditable true
EventListenerList get doubleClick => this['dblclick'];
+ /// @docsEditable true
EventListenerList get drag => this['drag'];
+ /// @docsEditable true
EventListenerList get dragEnd => this['dragend'];
+ /// @docsEditable true
EventListenerList get dragEnter => this['dragenter'];
+ /// @docsEditable true
EventListenerList get dragLeave => this['dragleave'];
+ /// @docsEditable true
EventListenerList get dragOver => this['dragover'];
+ /// @docsEditable true
EventListenerList get dragStart => this['dragstart'];
+ /// @docsEditable true
EventListenerList get drop => this['drop'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get input => this['input'];
+ /// @docsEditable true
EventListenerList get keyDown => this['keydown'];
+ /// @docsEditable true
EventListenerList get keyPress => this['keypress'];
+ /// @docsEditable true
EventListenerList get keyUp => this['keyup'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get mouseDown => this['mousedown'];
+ /// @docsEditable true
EventListenerList get mouseMove => this['mousemove'];
+ /// @docsEditable true
EventListenerList get mouseOut => this['mouseout'];
+ /// @docsEditable true
EventListenerList get mouseOver => this['mouseover'];
+ /// @docsEditable true
EventListenerList get mouseUp => this['mouseup'];
+ /// @docsEditable true
EventListenerList get mouseWheel => this['mousewheel'];
+ /// @docsEditable true
EventListenerList get paste => this['paste'];
+ /// @docsEditable true
EventListenerList get reset => this['reset'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get scroll => this['scroll'];
+ /// @docsEditable true
EventListenerList get search => this['search'];
+ /// @docsEditable true
EventListenerList get select => this['select'];
+ /// @docsEditable true
EventListenerList get selectStart => this['selectstart'];
+ /// @docsEditable true
EventListenerList get submit => this['submit'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -4933,8 +4975,10 @@
return _cssClassSet;
}
+ @deprecated
List<Element> get elements => new FilteredElementList(this);
+ @deprecated
void set elements(Collection<Element> value) {
final elements = this.elements;
elements.clear();
diff --git a/sdk/lib/svg/dartium/svg_dartium.dart b/sdk/lib/svg/dartium/svg_dartium.dart
index 66b9d73..620d78b 100644
--- a/sdk/lib/svg/dartium/svg_dartium.dart
+++ b/sdk/lib/svg/dartium/svg_dartium.dart
@@ -1171,87 +1171,129 @@
}
+/// @docsEditable true
class ElementInstanceEvents extends Events {
+ /// @docsEditable true
ElementInstanceEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get abort => this['abort'];
+ /// @docsEditable true
EventListenerList get beforeCopy => this['beforecopy'];
+ /// @docsEditable true
EventListenerList get beforeCut => this['beforecut'];
+ /// @docsEditable true
EventListenerList get beforePaste => this['beforepaste'];
+ /// @docsEditable true
EventListenerList get blur => this['blur'];
+ /// @docsEditable true
EventListenerList get change => this['change'];
+ /// @docsEditable true
EventListenerList get click => this['click'];
+ /// @docsEditable true
EventListenerList get contextMenu => this['contextmenu'];
+ /// @docsEditable true
EventListenerList get copy => this['copy'];
+ /// @docsEditable true
EventListenerList get cut => this['cut'];
+ /// @docsEditable true
EventListenerList get doubleClick => this['dblclick'];
+ /// @docsEditable true
EventListenerList get drag => this['drag'];
+ /// @docsEditable true
EventListenerList get dragEnd => this['dragend'];
+ /// @docsEditable true
EventListenerList get dragEnter => this['dragenter'];
+ /// @docsEditable true
EventListenerList get dragLeave => this['dragleave'];
+ /// @docsEditable true
EventListenerList get dragOver => this['dragover'];
+ /// @docsEditable true
EventListenerList get dragStart => this['dragstart'];
+ /// @docsEditable true
EventListenerList get drop => this['drop'];
+ /// @docsEditable true
EventListenerList get error => this['error'];
+ /// @docsEditable true
EventListenerList get focus => this['focus'];
+ /// @docsEditable true
EventListenerList get input => this['input'];
+ /// @docsEditable true
EventListenerList get keyDown => this['keydown'];
+ /// @docsEditable true
EventListenerList get keyPress => this['keypress'];
+ /// @docsEditable true
EventListenerList get keyUp => this['keyup'];
+ /// @docsEditable true
EventListenerList get load => this['load'];
+ /// @docsEditable true
EventListenerList get mouseDown => this['mousedown'];
+ /// @docsEditable true
EventListenerList get mouseMove => this['mousemove'];
+ /// @docsEditable true
EventListenerList get mouseOut => this['mouseout'];
+ /// @docsEditable true
EventListenerList get mouseOver => this['mouseover'];
+ /// @docsEditable true
EventListenerList get mouseUp => this['mouseup'];
+ /// @docsEditable true
EventListenerList get mouseWheel => this['mousewheel'];
+ /// @docsEditable true
EventListenerList get paste => this['paste'];
+ /// @docsEditable true
EventListenerList get reset => this['reset'];
+ /// @docsEditable true
EventListenerList get resize => this['resize'];
+ /// @docsEditable true
EventListenerList get scroll => this['scroll'];
+ /// @docsEditable true
EventListenerList get search => this['search'];
+ /// @docsEditable true
EventListenerList get select => this['select'];
+ /// @docsEditable true
EventListenerList get selectStart => this['selectstart'];
+ /// @docsEditable true
EventListenerList get submit => this['submit'];
+ /// @docsEditable true
EventListenerList get unload => this['unload'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -6471,8 +6513,10 @@
return _cssClassSet;
}
+ @deprecated
List<Element> get elements => new FilteredElementList(this);
+ @deprecated
void set elements(Collection<Element> value) {
final elements = this.elements;
elements.clear();
diff --git a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
index dbaef00..b58d552 100644
--- a/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
+++ b/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart
@@ -247,9 +247,12 @@
}
}
+/// @docsEditable true
class AudioContextEvents extends Events {
+ /// @docsEditable true
AudioContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get complete => this['complete'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -655,9 +658,12 @@
final int bufferSize;
}
+/// @docsEditable true
class ScriptProcessorNodeEvents extends Events {
+ /// @docsEditable true
ScriptProcessorNodeEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get audioProcess => this['audioprocess'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
diff --git a/sdk/lib/web_audio/dartium/web_audio_dartium.dart b/sdk/lib/web_audio/dartium/web_audio_dartium.dart
index ee711d3..d0b9b9e 100644
--- a/sdk/lib/web_audio/dartium/web_audio_dartium.dart
+++ b/sdk/lib/web_audio/dartium/web_audio_dartium.dart
@@ -387,9 +387,12 @@
}
+/// @docsEditable true
class AudioContextEvents extends Events {
+ /// @docsEditable true
AudioContextEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get complete => this['complete'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
@@ -1015,9 +1018,12 @@
}
+/// @docsEditable true
class ScriptProcessorNodeEvents extends Events {
+ /// @docsEditable true
ScriptProcessorNodeEvents(EventTarget _ptr) : super(_ptr);
+ /// @docsEditable true
EventListenerList get audioProcess => this['audioprocess'];
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
diff --git a/tests/co19/co19-compiler.status b/tests/co19/co19-compiler.status
index 6a88581..b8c6535 100644
--- a/tests/co19/co19-compiler.status
+++ b/tests/co19/co19-compiler.status
@@ -73,7 +73,48 @@
Language/13_Libraries_and_Scripts/1_Imports_A02_t16: Fail, OK
Language/13_Libraries_and_Scripts/1_Imports_A02_t17: Fail, OK
-Language/13_Libraries_and_Scripts/13_Libraries_and_Scripts_A05_t04: Fail, OK # contains syntax error
+
+# co19 issue 351 (use deprecated === and !==)
+LibTest/core/Expect/approxEquals_A04_t01: Fail
+LibTest/core/Expect/equals_A02_t01: Fail
+LibTest/core/Expect/identical_A02_t01: Fail
+LibTest/core/Expect/isFalse_A02_t01: Fail
+LibTest/core/Expect/isNotNull_A02_t01: Fail
+LibTest/core/Expect/isNull_A02_t01: Fail
+LibTest/core/Expect/isTrue_A02_t01: Fail
+LibTest/core/Expect/listEquals_A03_t01: Fail
+LibTest/core/Expect/notEquals_A02_t01: Fail
+LibTest/core/Expect/setEquals_A03_t01: Fail
+LibTest/core/Expect/stringEquals_A02_t01: Fail
+LibTest/core/Expect/throws_A01_t04: Fail
+LibTest/core/Future/chain_A02_t04: Fail
+LibTest/core/Future/chain_A02_t05: Fail
+LibTest/core/Future/transform_A02_t03: Fail
+LibTest/core/Future/transform_A02_t04: Fail
+LibTest/core/List/every_A04_t01: Fail
+LibTest/core/List/filter_A04_t01: Fail
+LibTest/core/List/forEach_A03_t01: Fail
+LibTest/core/List/List.from_A01_t01: Fail
+LibTest/core/List/operator_subscript_A01_t01: Fail
+LibTest/core/List/operator_subscript_A01_t02: Fail
+LibTest/core/List/operator_subscripted_assignment_A01_t02: Fail
+LibTest/core/List/operator_subscripted_assignment_A01_t01: Fail
+LibTest/core/List/setRange_A01_t01: Fail
+LibTest/core/Queue/addFirst_A01_t01: Fail
+LibTest/core/Set/intersection_A01_t03: Fail
+LibTest/core/String/toLowerCase_A01_t02: Fail
+LibTest/core/String/toUpperCase_A01_t02: Fail
+LibTest/core/String/trim_A01_t02: Fail
+LibTest/core/StringBuffer/add_A02_t01: Fail
+LibTest/core/StringBuffer/addAll_A02_t01: Fail
+LibTest/core/StringBuffer/clear_A02_t01: Fail
+
+
+# co19 issue 352 (use function expression with return type and name)
+LibTest/isolate/SendPort/send_A01_t01: Fail
+
+
+
Language/06_Functions/2_Formal_Parameters/2_Optional_Formals_A03_t02: Fail, OK # deprecated parameter syntax
Language/03_Overview/1_Scoping_A02_t28: Fail # language change 1031
@@ -90,5 +131,7 @@
Language/11_Expressions/22_Equality_A02_t03: Fail, OK # co19 issue 169
+
+
[ $runtime == drt && ($compiler == none || $compiler == frog) ]
*: Skip
diff --git a/tests/co19/co19-dart2dart.status b/tests/co19/co19-dart2dart.status
index 0f9d5f3..c36a20f 100644
--- a/tests/co19/co19-dart2dart.status
+++ b/tests/co19/co19-dart2dart.status
@@ -21,6 +21,17 @@
Language/15_Reference/1_Lexical_Rules_A01_t09: Fail # TODO(dart2dart-team): Please triage this failure.
Language/15_Reference/1_Lexical_Rules_A01_t11: Fail # TODO(dart2dart-team): Please triage this failure.
+Language/03_Overview/1_Scoping_A01_t39: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t01: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t02: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t03: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t04: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t05: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t01: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t02: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t03: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t04: Fail # http://dartbug.com/7202
+
# Calling unresolved class constructor:
Language/03_Overview/2_Privacy_A01_t19: Fail
@@ -263,13 +274,11 @@
Language/11_Expressions/11_Instance_Creation/2_Const_A06_t02: Fail # http://dartbug.com/5519
Language/11_Expressions/11_Instance_Creation/2_Const_A10_t01: Fail # inherited from VM
Language/11_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A05_t01: Fail # inherited from VM
-Language/11_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A05_t02: Fail # inherited from VM
Language/11_Expressions/15_Method_Invocation/1_Ordinary_Invocation_A05_t02: Fail # inherited from VM
Language/11_Expressions/15_Method_Invocation/1_Ordinary_Invocation_A05_t03: Fail # inherited from VM
Language/11_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t03: Fail # inherited from VM
Language/11_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t04: Fail # http://dartbug.com/5519
Language/11_Expressions/15_Method_Invocation/3_Static_Invocation_A03_t06: Fail # inherited from VM
-Language/11_Expressions/15_Method_Invocation/3_Static_Invocation_A04_t08: Fail # inherited from VM
Language/11_Expressions/17_Getter_Invocation_A02_t01: Fail # inherited from VM
Language/11_Expressions/18_Assignment_A05_t02: Fail # inherited from VM
Language/11_Expressions/18_Assignment_A05_t04: Fail, Pass, OK # Fails in minified, depends on method names.
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 9617c02..3ad85ec 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -68,7 +68,6 @@
Language/11_Expressions/18_Assignment_A05_t02: Fail # TODO(ahe): Please triage this failure.
Language/11_Expressions/18_Assignment_A05_t04: Fail # TODO(ahe): Please triage this failure.
Language/11_Expressions/18_Assignment_A05_t05: Fail # TODO(ahe): Please triage this failure.
-Language/11_Expressions/18_Assignment_A08_t04: Fail # TODO(ahe): Please triage this failure.
Language/11_Expressions/19_Conditional_A01_t14: Fail # TODO(ahe): Please triage this failure.
Language/11_Expressions/19_Conditional_A01_t15: Fail # TODO(ahe): Please triage this failure.
Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t14: Fail # TODO(ahe): Please triage this failure.
@@ -132,12 +131,8 @@
Language/13_Libraries_and_Scripts/1_Imports_A02_t14: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/1_Imports_A02_t16: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/1_Imports_A02_t17: Fail # TODO(ahe): Please triage this failure.
-Language/13_Libraries_and_Scripts/1_Imports_A02_t19: Fail # TODO(ahe): Please triage this failure.
-Language/13_Libraries_and_Scripts/1_Imports_A02_t27: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/1_Imports_A02_t28: Fail # TODO(ahe): Please triage this failure.
-Language/13_Libraries_and_Scripts/1_Imports_A03_t51: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/1_Imports_A05_t01: Fail # TODO(ahe): Please triage this failure.
-Language/13_Libraries_and_Scripts/2_Exports_A06_t02: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/3_Parts_A03_t02: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/4_Scripts_A01_t20: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/4_Scripts_A01_t21: Fail # TODO(ahe): Please triage this failure.
@@ -148,34 +143,19 @@
Language/13_Libraries_and_Scripts/5_URIs_A01_t21: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/5_URIs_A01_t24: Fail # TODO(ahe): Please triage this failure.
Language/13_Libraries_and_Scripts/5_URIs_A01_t25: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/3_Type_Declarations/1_Typedef_A07_t05: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/3_Type_Declarations/1_Typedef_A07_t06: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/3_Type_Declarations/1_Typedef_A07_t07: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A01_t04: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A01_t10: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A02_t02: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A02_t03: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A02_t04: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A02_t05: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A02_t06: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A02_t07: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A02_t08: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A02_t09: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A02_t10: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A03_t01: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A03_t02: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A03_t03: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A03_t04: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A03_t06: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A03_t07: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A03_t08: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A03_t09: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A03_t10: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A03_t11: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A03_t12: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A03_t13: Fail # TODO(ahe): Please triage this failure.
Language/14_Types/5_Function_Types_A04_t01: Fail # TODO(ahe): Please triage this failure.
-Language/14_Types/5_Function_Types_A06_t01: Fail # TODO(ahe): Please triage this failure.
Language/15_Reference/1_Lexical_Rules/1_Reserved_Words_A40_t04: Fail # TODO(ahe): Please triage this failure.
Language/15_Reference/1_Lexical_Rules_A01_t09: Fail # TODO(ahe): Please triage this failure.
Language/15_Reference/1_Lexical_Rules_A01_t11: Fail # TODO(ahe): Please triage this failure.
@@ -201,8 +181,6 @@
[ $compiler == dart2js && $unchecked ]
LibTest/core/Date/Date.fromMillisecondsSinceEpoch_A03_t01: Fail # TODO(ahe): Please triage this failure.
-Language/09_Generics/09_Generics_A05_t01: Fail # TODO(johnniwinther): Please triage this failure.
-
[ $compiler == dart2js && $runtime == jsshell ]
LibTest/core/double/round_A01_t01: Fail # TODO(ahe): Triage these tests.
LibTest/core/double/toStringAsExponential_A01_t07: Fail # TODO(ahe): Triage these tests.
@@ -273,13 +251,6 @@
LibTest/core/TypeError/srcType_A01_t01: Fail # TODO(ahe): Please triage this failure.
LibTest/core/TypeError/url_A01_t01: Fail # TODO(ahe): Please triage this failure.
-Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A04_t01: Fail # Checked mode failure for noSuchMethod type.
-Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A04_t02: Fail # Checked mode failure for noSuchMethod type.
-Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A04_t03: Fail # Checked mode failure for noSuchMethod type.
-Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A04_t04: Fail # Checked mode failure for noSuchMethod type.
-Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A06_t01: Fail # Checked mode failure for noSuchMethod type.
-Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A06_t02: Fail # Checked mode failure for noSuchMethod type.
-
Language/11_Expressions/11_Instance_Creation/1_New_A12_t02: Fail # http://dartbug.com/3970
@@ -586,9 +557,6 @@
Language/14_Types/5_Function_Types_A01_t05: Fail # http://dartbug.com/5022
Language/14_Types/5_Function_Types_A01_t06: Fail # http://dartbug.com/5022
Language/14_Types/5_Function_Types_A01_t07: Fail # http://dartbug.com/5022
-Language/14_Types/5_Function_Types_A01_t08: Fail # http://dartbug.com/5022
-Language/14_Types/5_Function_Types_A01_t09: Fail # http://dartbug.com/5022
-Language/14_Types/5_Function_Types_A01_t11: Fail # http://dartbug.com/5022
Language/13_Libraries_and_Scripts/4_Scripts_A03_t01: Fail # http://dartbug.com/5683
Language/13_Libraries_and_Scripts/4_Scripts_A03_t03: Fail # http://dartbug.com/5683
@@ -614,3 +582,14 @@
Language/14_Types/4_Interface_Types_A12_t10: Fail # http://dartbug.com/5020
Language/14_Types/4_Interface_Types_A12_t14: Fail # http://dartbug.com/5020
Language/14_Types/4_Interface_Types_A12_t16 :Fail # http://dartbug.com/5020
+
+Language/03_Overview/1_Scoping_A01_t39: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t01: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t02: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t03: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t04: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A06_t05: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t01: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t02: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t03: Fail # http://dartbug.com/7202
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t04: Fail # http://dartbug.com/7202
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index 753a249..ae33a54 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -6,155 +6,154 @@
Language/13_Libraries_and_Scripts/1_Imports_A02_t21: Crash # Dart issue 6060
Language/13_Libraries_and_Scripts/1_Imports_A02_t22: Crash # Dart issue 6060
-Language/03_Overview/1_Scoping_A02_t32: Fail # TODO(vm-team): Please triage this failure.
-Language/06_Functions/2_Formal_Parameters/2_Optional_Formals_A01_t10: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/3_Setters_A04_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/3_Setters_A04_t04: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/3_Setters_A04_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/4_Abstract_Instance_Members_A03_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/4_Abstract_Instance_Members_A03_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/4_Abstract_Instance_Members_A03_t04: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/4_Abstract_Instance_Members_A03_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/4_Abstract_Instance_Members_A04_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/4_Abstract_Instance_Members_A04_t06: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/6_Constructors/1_Generative_Constructors_A04_t15: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/08_Interfaces/5_Superinterfaces_A01_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/01_Constants_A03_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/01_Constants_A05_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/01_Constants_A06_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/01_Constants_A14_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/01_Constants_A20_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/01_Constants_A20_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t04: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t06: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/05_Strings_A02_t46: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/05_Strings_A02_t48: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/11_Instance_Creation/1_New_A02_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/11_Instance_Creation/1_New_A02_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/11_Instance_Creation/1_New_A09_t09: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/11_Instance_Creation/2_Const_A10_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A05_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/15_Method_Invocation/3_Static_Invocation_A04_t08: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/17_Getter_Invocation_A02_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/18_Assignment_A05_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/18_Assignment_A05_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/19_Conditional_A01_t10: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/19_Conditional_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/19_Conditional_A01_t12: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/19_Conditional_A01_t13: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/19_Conditional_A01_t14: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/19_Conditional_A01_t15: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t10: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t12: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t13: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t14: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t15: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/21_Bitwise_Expressions_A01_t12: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/21_Bitwise_Expressions_A01_t13: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/21_Bitwise_Expressions_A01_t14: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/21_Bitwise_Expressions_A01_t15: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/21_Bitwise_Expressions_A01_t16: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/21_Bitwise_Expressions_A01_t17: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/22_Equality_A01_t23: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/22_Equality_A01_t24: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/23_Relational_Expressions_A01_t18: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/23_Relational_Expressions_A01_t19: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/23_Relational_Expressions_A01_t20: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/23_Relational_Expressions_A01_t21: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/23_Relational_Expressions_A01_t22: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/23_Relational_Expressions_A01_t23: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/24_Shift_A01_t09: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/24_Shift_A01_t10: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/24_Shift_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/24_Shift_A01_t12: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/24_Shift_A01_t13: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/24_Shift_A01_t14: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/25_Additive_Expressions_A01_t07: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/25_Additive_Expressions_A01_t08: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/25_Additive_Expressions_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/25_Additive_Expressions_A01_t12: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/25_Additive_Expressions_A01_t13: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/25_Additive_Expressions_A01_t14: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/26_Multiplicative_Expressions_A01_t10: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/26_Multiplicative_Expressions_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/26_Multiplicative_Expressions_A01_t14: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/26_Multiplicative_Expressions_A01_t15: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/26_Multiplicative_Expressions_A01_t16: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/26_Multiplicative_Expressions_A01_t17: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t04: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t12: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t13: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t17: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t18: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t19: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t20: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t21: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/27_Unary_Expressions_A01_t22: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/28_Postfix_Expressions_A01_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/28_Postfix_Expressions_A01_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/28_Postfix_Expressions_A01_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/29_Assignable_Expressions_A01_t06: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/29_Assignable_Expressions_A01_t08: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/29_Assignable_Expressions_A01_t09: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/30_Identifier_Reference_A05_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/30_Identifier_Reference_A06_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/30_Identifier_Reference_A06_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/30_Identifier_Reference_A07_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/30_Identifier_Reference_A08_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/31_Type_Test_A01_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/11_Expressions/31_Type_Test_A01_t04: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/03_Variable_Declaration_A04_t07: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/04_Local_Function_Declaration_A02_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/06_For_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A01_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A02_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A02_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A02_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A03_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A03_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A04_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/09_Switch_A06_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/10_Try_A03_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/10_Try_A03_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/10_Try_A03_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/10_Try_A11_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/12_Statements/12_Labels_A01_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/13_Libraries_and_Scripts_A05_t04: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/1_Imports_A02_t29: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/1_Imports_A05_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/2_Exports_A04_t02: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/2_Exports_A04_t03: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/5_URIs_A01_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/5_URIs_A01_t04: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/5_URIs_A01_t05: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/5_URIs_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/5_URIs_A01_t14: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/5_URIs_A01_t15: Fail # TODO(vm-team): Please triage this failure.
-Language/13_Libraries_and_Scripts/5_URIs_A01_t21: Fail # TODO(vm-team): Please triage this failure.
-Language/14_Types/5_Function_Types_A04_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/14_Types/5_Function_Types_A06_t01: Fail # TODO(vm-team): Please triage this failure.
-Language/15_Reference/1_Lexical_Rules_A01_t09: Fail # TODO(vm-team): Please triage this failure.
-Language/15_Reference/1_Lexical_Rules_A01_t11: Fail # TODO(vm-team): Please triage this failure.
-LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A01_t01: Fail # TODO(vm-team): Please triage this failure.
-LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A02_t01: Fail # TODO(vm-team): Please triage this failure.
-LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A06_t01: Fail # TODO(vm-team): Please triage this failure.
-LibTest/core/double/parse_A02_t01: Fail # TODO(vm-team): Please triage this failure.
-LibTest/isolate/isolate_api/spawnUri_A01_t03: Fail # TODO(vm-team): Please triage this failure.
-LibTest/isolate/isolate_api/spawnUri_A01_t04: Fail # TODO(vm-team): Please triage this failure.
-LibTest/math/pow_A01_t01: Fail # TODO(vm-team): Please triage this failure.
-LibTest/math/pow_A11_t01: Fail # TODO(vm-team): Please triage this failure.
-LibTest/math/pow_A13_t01: Fail # TODO(vm-team): Please triage this failure.
-LibTest/math/sin_A01_t01: Fail # TODO(vm-team): Please triage this failure.
+Language/03_Overview/1_Scoping_A02_t32: Fail # Dart issue 6556
+Language/06_Functions/2_Formal_Parameters/2_Optional_Formals_A01_t10: Fail # co19 issue 348
+Language/07_Classes/3_Setters_A04_t01: Fail # Dart issue 5840
+Language/07_Classes/3_Setters_A04_t04: Fail # Dart issue 5840
+Language/07_Classes/3_Setters_A04_t05: Fail # Dart issue 5840
+Language/07_Classes/4_Abstract_Instance_Members_A03_t02: Fail # Dart issue 978
+Language/07_Classes/4_Abstract_Instance_Members_A03_t03: Fail # Dart issue 978
+Language/07_Classes/4_Abstract_Instance_Members_A03_t04: Fail # Dart issue 978
+Language/07_Classes/4_Abstract_Instance_Members_A03_t05: Fail # Dart issue 978
+Language/07_Classes/4_Abstract_Instance_Members_A04_t05: Fail # Dart issue 978
+Language/07_Classes/4_Abstract_Instance_Members_A04_t06: Fail # Dart issue 978
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A04_t15: Fail # Dart issue 6954
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t01: Fail # Dart issue 811
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t02: Fail # Dart issue 811
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t03: Fail # Dart issue 811
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t01: Fail # Dart issue 811
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t02: Fail # Dart issue 811
+Language/08_Interfaces/5_Superinterfaces_A01_t02: Fail # Dart issue 7245
+Language/11_Expressions/01_Constants_A03_t01: Fail # Dart issue 5214
+Language/11_Expressions/01_Constants_A05_t01: Fail # Dart issue 5832
+Language/11_Expressions/01_Constants_A06_t01: Fail # Dart issue 5214
+Language/11_Expressions/01_Constants_A14_t01: Fail # Dart issue 5214
+Language/11_Expressions/01_Constants_A20_t02: Fail # Dart issue 6556
+Language/11_Expressions/01_Constants_A20_t03: Fail # Dart issue 6556
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t04: Fail # Dart issue 7246
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t05: Fail # Dart issue 7246
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t06: Fail # Dart issue 7246
+Language/11_Expressions/05_Strings_A02_t46: Fail # Dart issue 4009
+Language/11_Expressions/05_Strings_A02_t48: Fail # Dart issue 4009
+Language/11_Expressions/11_Instance_Creation/1_New_A02_t03: Fail # Dart issue 3309
+Language/11_Expressions/11_Instance_Creation/1_New_A02_t05: Fail # Dart issue 7247
+Language/11_Expressions/11_Instance_Creation/1_New_A09_t09: Fail # Dart issue 1372
+Language/11_Expressions/11_Instance_Creation/2_Const_A10_t01: Fail # Dart issue 5775
+Language/11_Expressions/17_Getter_Invocation_A02_t01: Fail # Dart issue 6449
+Language/11_Expressions/18_Assignment_A05_t02: Fail # Dart issue 6449
+Language/11_Expressions/18_Assignment_A05_t04: Fail, OK # co19 issue 350.
+Language/11_Expressions/18_Assignment_A05_t05: Fail # Dart issue 6449
+Language/11_Expressions/19_Conditional_A01_t10: Fail # Dart issue 7251
+Language/11_Expressions/19_Conditional_A01_t11: Fail # Dart issue 7252
+Language/11_Expressions/19_Conditional_A01_t12: Fail # Dart issue 7251
+Language/11_Expressions/19_Conditional_A01_t13: Fail # Dart issue 7252
+Language/11_Expressions/19_Conditional_A01_t14: Fail # Dart issue 7251
+Language/11_Expressions/19_Conditional_A01_t15: Fail # Dart issue 7252
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t10: Fail # Dart issue 7251
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t11: Fail # Dart issue 7251
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t12: Fail # Dart issue 7251
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t13: Fail # Dart issue 7251
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t14: Fail # Dart issue 7251
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t15: Fail # Dart issue 7251
+Language/11_Expressions/21_Bitwise_Expressions_A01_t12: Fail # Dart issue 7251
+Language/11_Expressions/21_Bitwise_Expressions_A01_t13: Fail # Dart issue 7251
+Language/11_Expressions/21_Bitwise_Expressions_A01_t14: Fail # Dart issue 7251
+Language/11_Expressions/21_Bitwise_Expressions_A01_t15: Fail # Dart issue 7251
+Language/11_Expressions/21_Bitwise_Expressions_A01_t16: Fail # Dart issue 7251
+Language/11_Expressions/21_Bitwise_Expressions_A01_t17: Fail # Dart issue 7251
+Language/11_Expressions/22_Equality_A01_t23: Fail # Dart issue 7254
+Language/11_Expressions/22_Equality_A01_t24: Fail # Dart issue 7254
+Language/11_Expressions/23_Relational_Expressions_A01_t18: Fail # Dart issue 7256
+Language/11_Expressions/23_Relational_Expressions_A01_t19: Fail # Dart issue 7256
+Language/11_Expressions/23_Relational_Expressions_A01_t20: Fail # Dart issue 7256
+Language/11_Expressions/23_Relational_Expressions_A01_t21: Fail # Dart issue 7256
+Language/11_Expressions/23_Relational_Expressions_A01_t22: Fail # Dart issue 7256
+Language/11_Expressions/23_Relational_Expressions_A01_t23: Fail # Dart issue 7256
+Language/11_Expressions/24_Shift_A01_t09: Fail # Dart issue 7256
+Language/11_Expressions/24_Shift_A01_t10: Fail # Dart issue 7256
+Language/11_Expressions/24_Shift_A01_t11: Fail # Dart issue 7256
+Language/11_Expressions/24_Shift_A01_t12: Fail # Dart issue 7256
+Language/11_Expressions/24_Shift_A01_t13: Fail # Dart issue 7256
+Language/11_Expressions/24_Shift_A01_t14: Fail # Dart issue 7256
+Language/11_Expressions/25_Additive_Expressions_A01_t07: Fail # Dart issue 7256
+Language/11_Expressions/25_Additive_Expressions_A01_t08: Fail # Dart issue 7256
+Language/11_Expressions/25_Additive_Expressions_A01_t11: Fail # Dart issue 7256
+Language/11_Expressions/25_Additive_Expressions_A01_t12: Fail # Dart issue 7256
+Language/11_Expressions/25_Additive_Expressions_A01_t13: Fail # Dart issue 7256
+Language/11_Expressions/25_Additive_Expressions_A01_t14: Fail # Dart issue 7256
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t10: Fail # Dart issue 7256
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t11: Fail # Dart issue 7256
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t14: Fail # Dart issue 7256
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t15: Fail # Dart issue 7256
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t16: Fail # Dart issue 7256
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t17: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t02: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t04: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t05: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t11: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t12: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t13: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t17: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t18: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t19: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t20: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t21: Fail # Dart issue 7256
+Language/11_Expressions/27_Unary_Expressions_A01_t22: Fail # Dart issue 7256
+Language/11_Expressions/28_Postfix_Expressions_A01_t02: Fail # Dart issue 7256
+Language/11_Expressions/28_Postfix_Expressions_A01_t03: Fail # Dart issue 7256
+Language/11_Expressions/28_Postfix_Expressions_A01_t05: Fail # Dart issue 7256
+Language/11_Expressions/29_Assignable_Expressions_A01_t06: Fail # Dart issue 7256
+Language/11_Expressions/29_Assignable_Expressions_A01_t08: Fail # Dart issue 7256
+Language/11_Expressions/29_Assignable_Expressions_A01_t09: Fail # Dart issue 7256
+Language/11_Expressions/30_Identifier_Reference_A05_t02: Fail # Dart issue 2492
+Language/11_Expressions/30_Identifier_Reference_A06_t01: Fail # Dart issue 7257
+Language/11_Expressions/30_Identifier_Reference_A06_t02: Fail # Dart issue 7257
+Language/11_Expressions/30_Identifier_Reference_A07_t01: Fail # Dart issue 7257
+Language/11_Expressions/30_Identifier_Reference_A08_t02: Fail # Dart issue 5802
+Language/11_Expressions/31_Type_Test_A01_t02: Fail # Dart issue 7258
+Language/11_Expressions/31_Type_Test_A01_t04: Fail # Dart issue 7258
+Language/12_Statements/03_Variable_Declaration_A04_t07: Fail # Dart issue 7305
+Language/12_Statements/04_Local_Function_Declaration_A02_t02: Fail # Dart issue 5773
+Language/12_Statements/06_For_A01_t11: Fail # Dart issue 5675
+Language/12_Statements/09_Switch_A01_t02: Fail # Dart issue 2238
+Language/12_Statements/09_Switch_A02_t01: Fail # Dart issue 7306
+Language/12_Statements/09_Switch_A02_t02: Fail # Dart issue 7307
+Language/12_Statements/09_Switch_A02_t03: Fail # Dart issue 7308
+Language/12_Statements/09_Switch_A03_t01: Fail # Dart issue 7307
+Language/12_Statements/09_Switch_A03_t02: Fail # Dart issue 7307
+Language/12_Statements/09_Switch_A04_t01: Fail # Dart issue 6897
+Language/12_Statements/09_Switch_A06_t02: Fail # Dart issue 5837
+Language/12_Statements/10_Try_A03_t01: Fail # Dart issue 7311
+Language/12_Statements/10_Try_A03_t02: Fail # Dart issue 7311
+Language/12_Statements/10_Try_A03_t03: Fail # Dart issue 7311
+Language/12_Statements/10_Try_A11_t01: Fail # Dart issue 430
+Language/12_Statements/12_Labels_A01_t03: Fail # Dart issue 2238
+Language/13_Libraries_and_Scripts/13_Libraries_and_Scripts_A05_t04: Fail # Dart issue 5839
+Language/13_Libraries_and_Scripts/1_Imports_A02_t29: Fail # Dart issue 6783
+Language/13_Libraries_and_Scripts/1_Imports_A05_t01: Fail # Dart issue 3206
+Language/13_Libraries_and_Scripts/2_Exports_A04_t02: Fail # Dart issue 6134
+Language/13_Libraries_and_Scripts/2_Exports_A04_t03: Fail # Dart issue 6134
+Language/13_Libraries_and_Scripts/5_URIs_A01_t01: Fail # Dart issue 6352
+Language/13_Libraries_and_Scripts/5_URIs_A01_t04: Fail # Dart issue 7317
+Language/13_Libraries_and_Scripts/5_URIs_A01_t05: Fail # Dart issue 7317
+Language/13_Libraries_and_Scripts/5_URIs_A01_t11: Fail # Dart issue 6352
+Language/13_Libraries_and_Scripts/5_URIs_A01_t14: Fail # Dart issue 7317
+Language/13_Libraries_and_Scripts/5_URIs_A01_t15: Fail # Dart issue 7317
+Language/13_Libraries_and_Scripts/5_URIs_A01_t21: Fail # Dart issue 6352
+Language/14_Types/5_Function_Types_A04_t01: Fail # Dart issue 6921
+Language/14_Types/5_Function_Types_A06_t01: Fail # Dart issue 1604
+Language/15_Reference/1_Lexical_Rules_A01_t09: Fail # Dart issue 2687
+Language/15_Reference/1_Lexical_Rules_A01_t11: Fail # Dart issue 2687
+LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A01_t01: Fail # co19 issue 353
+LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A02_t01: Fail # co19 issue 353
+LibTest/core/RegExp/Pattern_semantics/firstMatch_CharacterClassEscape_A06_t01: Fail # co19 issue 353
+LibTest/core/double/parse_A02_t01: Fail # Dart issue 1929
+LibTest/isolate/isolate_api/spawnUri_A01_t03: Fail # Dart issue 5222
+LibTest/isolate/isolate_api/spawnUri_A01_t04: Fail # Dart issue 5222
+LibTest/math/pow_A01_t01: Fail # Dart issue 7318
+LibTest/math/pow_A11_t01: Fail # Dart issue 449
+LibTest/math/pow_A13_t01: Fail # Dart issue 449
+LibTest/math/sin_A01_t01: Fail # Dart issue 7318
Language/05_Variables/05_Variables_A05_t04: Fail # Dart issue 5881
Language/05_Variables/05_Variables_A05_t11: Fail # Dart issue 5885
diff --git a/tests/compiler/dart2js/deprecated_features_test.dart b/tests/compiler/dart2js/deprecated_features_test.dart
new file mode 100644
index 0000000..8cd931f
--- /dev/null
+++ b/tests/compiler/dart2js/deprecated_features_test.dart
@@ -0,0 +1,108 @@
+// 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.
+
+// Test that deprecated language features are diagnosed correctly.
+
+import '../../../sdk/lib/_internal/compiler/compiler.dart';
+import 'dart:uri';
+import '../../utils/dummy_compiler_test.dart' as dummy;
+
+main() {
+ StringBuffer messages = new StringBuffer();
+ void handler(Uri uri, int begin, int end, String message, Diagnostic kind) {
+ if (kind == Diagnostic.VERBOSE_INFO) return;
+ if (identical(kind.name, 'source map')) return;
+ if (uri == null) {
+ messages.add('$kind: $message\n');
+ } else {
+ Expect.equals('main:${uri.path}', '$uri');
+ String source = TEST_SOURCE[uri.path];
+ Expect.isNotNull(source);
+ messages.add('$begin<${source.substring(begin, end)}>:${uri.path}:'
+ '$kind: $message\n');
+ }
+ }
+
+ Future<String> provider(Uri uri) {
+ if (uri.scheme != "main") return dummy.provider(uri);
+ String source = TEST_SOURCE[uri.path];
+ Expect.isNotNull(source);
+ return (new Completer<String>()..complete(source)).future;
+ }
+
+ String code = compile(new Uri.fromComponents(scheme: 'main'),
+ new Uri.fromComponents(scheme: 'lib', path: '/'),
+ new Uri.fromComponents(scheme: 'package', path: '/'),
+ provider, handler).value;
+ if (code == null) {
+ throw 'Compilation failed: ${messages}';
+ }
+ Expect.stringEquals(
+ // This string is comprised of lines of the following format:
+ //
+ // offset<source>:path:kind: message
+ //
+ // "offset" is the character offset from the beginning of TEST_SOURCE.
+ // "source" is the substring of TEST_SOURCE that the compiler is
+ // indicating as erroneous.
+ // "path" is the URI path.
+ // "kind" is the result of calling toString on a [Diagnostic] object.
+ // "message" is the expected message as a [String]. This is a
+ // short-term solution and should eventually changed to include
+ // a symbolic reference to a MessageKind.
+ "0<#library('test');>::${deprecatedMessage('# tags')}\n"
+ "38<interface>::${deprecatedMessage('interface declarations')}\n"
+ "19<part 'part.dart';>::${deprecatedMessage('missing part-of tag')}\n"
+ "0<>:/part.dart:info: Note: This file has no part-of tag, but it is being"
+ " used as a part.\n"
+ "163<Fisk>::${deprecatedMessage('interface factories')}\n"
+
+ // TODO(ahe): Should be <Fisk.hest>.
+ "183<Fisk>::${deprecatedMessage('interface factories')}\n"
+
+ // TODO(ahe): Should be <bar>.
+ "109<Foo>::${deprecatedMessage('conflicting constructor')}\n"
+
+ "129<bar>::info: This member conflicts with a constructor.\n"
+ "200<Dynamic>::${deprecatedMessage('Dynamic')}\n"
+ "221<()>::${deprecatedMessage('getter parameters')}\n",
+ messages.toString());
+}
+
+deprecatedMessage(feature) {
+ return
+ "warning: Warning: deprecated language feature, $feature"
+ ", will be removed in a future Dart milestone.";
+}
+
+const Map<String, String> TEST_SOURCE =
+ const <String, String>{ '': """
+#library('test');
+
+part 'part.dart';
+
+interface Fisk default Foo {
+ Fisk();
+ Fisk.hest();
+}
+
+class Foo {
+ Foo.bar();
+ static bar() => new Foo.bar();
+ factory Fisk() {}
+ factory Fisk.hest() {}
+ Dynamic fisk;
+ get x() => null;
+}
+
+main() {
+ var a = Foo.bar();
+ var b = new Foo.bar();
+ new Fisk();
+ new Fisk.hest();
+}
+""",
+ // TODO(ahe): Why isn't this 'part.dart'? Why the leading slash?
+ '/part.dart': '',
+ };
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index 140cec2..883f02b 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -81,7 +81,7 @@
class Object {}
class Type {}
class Function {}
- class List<T> {}
+ class List<E> {}
abstract class Map<K,V> {}
class Closure {}
class Null {}
diff --git a/tests/compiler/dart2js/resolver_test.dart b/tests/compiler/dart2js/resolver_test.dart
index 2b1fac6..f6f87c5 100644
--- a/tests/compiler/dart2js/resolver_test.dart
+++ b/tests/compiler/dart2js/resolver_test.dart
@@ -628,6 +628,37 @@
Link<DartType> supertypes = aElement.allSupertypes;
Expect.equals(<String>['B', 'C', 'Object'].toString(),
asSortedStrings(supertypes).toString());
+
+ compiler = new MockCompiler();
+ compiler.parseScript("""class A<T> {}
+ class B<Z,W> extends A<int> implements I<Z,List<W>> {}
+ class I<X,Y> {}
+ class C extends B<bool,String> {}
+ main() { return new C(); }""");
+ mainElement = compiler.mainApp.find(MAIN);
+ compiler.resolver.resolve(mainElement);
+ Expect.equals(0, compiler.warnings.length);
+ Expect.equals(0, compiler.errors.length);
+ aElement = compiler.mainApp.find(buildSourceString("C"));
+ supertypes = aElement.allSupertypes;
+ // Object is once per inheritance path, that is from both A and I.
+ Expect.equals(<String>['A<int>', 'B<bool, String>', 'I<bool, List<String>>',
+ 'Object', 'Object'].toString(),
+ asSortedStrings(supertypes).toString());
+
+ compiler = new MockCompiler();
+ compiler.parseScript("""class A<T> {}
+ class D extends A<E> {}
+ class E extends D {}
+ main() { return new E(); }""");
+ mainElement = compiler.mainApp.find(MAIN);
+ compiler.resolver.resolve(mainElement);
+ Expect.equals(0, compiler.warnings.length);
+ Expect.equals(0, compiler.errors.length);
+ aElement = compiler.mainApp.find(buildSourceString("E"));
+ supertypes = aElement.allSupertypes;
+ Expect.equals(<String>['A<E>', 'D', 'Object'].toString(),
+ asSortedStrings(supertypes).toString());
}
testInitializers() {
diff --git a/tests/compiler/dart2js_extra/closure_capture2_test.dart b/tests/compiler/dart2js_extra/closure_capture2_test.dart
index b24fda9..21f91e4 100644
--- a/tests/compiler/dart2js_extra/closure_capture2_test.dart
+++ b/tests/compiler/dart2js_extra/closure_capture2_test.dart
@@ -24,8 +24,8 @@
}
closure1() {
- // f captures variable $0 which could yield to troubles with HForeign if we
- // don't mangle correctly.
+ // f captures variable $0 which once could yield to troubles with HForeign if
+ // we did not mangle correctly.
var $1 = 499;
// TODO(floitsch): remove name from functions.
var f = fun() { return $1; };
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index 9e569d2..e385dd9 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -23,6 +23,9 @@
[ $compiler == dart2js && $runtime == none ]
*: Fail, Pass # TODO(ahe): Triage these tests.
+[ $compiler == dart2js && $minified ]
+to_string_test: Fail # Issue 7179.
+
[ $jscl ]
timer_test: Fail, OK # Timer is only supported in browsers.
mirror_test: Fail, OK # Timer is only supported in browsers.
diff --git a/tests/compiler/dart2js_native/native_class_fields_test.dart b/tests/compiler/dart2js_native/native_class_fields_test.dart
new file mode 100644
index 0000000..8221eec
--- /dev/null
+++ b/tests/compiler/dart2js_native/native_class_fields_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2011, 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.
+
+// Verify that native fields on classes are not renamed by the minifier.
+class A native "*A" {
+ int myLongPropertyName;
+ int getValue;
+
+ int method(int z) => myLongPropertyName;
+}
+
+
+void setup() native r"""
+function getter() {
+ return ++this.getValue;
+}
+
+function setter(x) {
+ this.getValue += 10;
+}
+
+function A(){
+ var a = Object.create(
+ { constructor: { name: 'A'}},
+ { myLongPropertyName: { get: getter,
+ set: setter,
+ configurable: false,
+ writeable: false
+ }
+ });
+ a.getValue = 0;
+ return a;
+}
+
+makeA = function(){return new A;};
+""";
+
+A makeA() native { return new A(); }
+
+main() {
+ setup();
+ var a = makeA();
+ a.myLongPropertyName = 21;
+ int gotten = a.myLongPropertyName;
+ Expect.equals(11, gotten);
+
+ var a2 = makeA();
+ if (a2 is A) {
+ // Inside this 'if' the compiler knows that a2 is an A, so it is tempted
+ // to access myLongPropertyName directly, using its minified name. But
+ // renaming of native properties can only work using getters and setters
+ // that access the original name.
+ a2.myLongPropertyName = 21;
+ int gotten = a2.myLongPropertyName;
+ Expect.equals(11, gotten);
+ }
+}
+
diff --git a/tests/compiler/dart2js_native/native_field_name_test.dart b/tests/compiler/dart2js_native/native_field_name_test.dart
index d479d82..c1bb426 100644
--- a/tests/compiler/dart2js_native/native_field_name_test.dart
+++ b/tests/compiler/dart2js_native/native_field_name_test.dart
@@ -12,7 +12,7 @@
}
-// This code is inside the setup function, so the function names are not
+// This code is inside the setup function, so the function names are not
// accessible, but the makeA variable is global through the magic of JS scoping.
// The contents of this are of course not analyzable by the compiler.
void setup() native r"""
diff --git a/tests/compiler/dart2js_native/native_novel_html_test.dart b/tests/compiler/dart2js_native/native_novel_html_test.dart
new file mode 100644
index 0000000..485d2f6
--- /dev/null
+++ b/tests/compiler/dart2js_native/native_novel_html_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2011, 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 to see if novel HTML tags are interpreted as HTMLElement.
+
+class Element native "*HTMLElement" {
+ String dartMethod(int x) => 'dartMethod(${nativeMethod(x+1)})';
+ String nativeMethod(int x) native;
+}
+
+makeE() native;
+makeF() native;
+
+void setup() native """
+// A novel HTML element.
+function HTMLGoofyElement(){}
+HTMLGoofyElement.prototype.nativeMethod = function(a) {
+ return 'Goofy.nativeMethod(' + a + ')';
+};
+makeE = function(){return new HTMLGoofyElement};
+
+// A non-HTML element with a misleading name.
+function HTMLFakeyElement(){}
+HTMLFakeyElement.prototype.nativeMethod = function(a) {
+ return 'Fakey.nativeMethod(' + a + ')';
+};
+makeF = function(){return new HTMLFakeyElement};
+
+// Make the HTMLGoofyElement look like a real host object.
+var theRealObjectToString = Object.prototype.toString;
+Object.prototype.toString = function() {
+ if (this instanceof HTMLGoofyElement) return '[object HTMLGoofyElement]';
+ return theRealObjectToString.call(this);
+}
+""";
+
+
+main() {
+ setup();
+
+ var e = makeE();
+ Expect.equals('Goofy.nativeMethod(10)', e.nativeMethod(10));
+ Expect.equals('dartMethod(Goofy.nativeMethod(11))', e.dartMethod(10));
+
+ var f = makeF();
+ Expect.throws(() => f.nativeMethod(20), (e) => e is NoSuchMethodError,
+ 'fake HTML Element must not run Dart method on native class');
+ Expect.throws(() => f.dartMethod(20), (e) => e is NoSuchMethodError,
+ 'fake HTML Element must not run native method on native class');
+}
diff --git a/tests/corelib/bool_hashcode_test.dart b/tests/corelib/bool_hashcode_test.dart
new file mode 100644
index 0000000..63dbcb5
--- /dev/null
+++ b/tests/corelib/bool_hashcode_test.dart
@@ -0,0 +1,13 @@
+// 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.
+
+class BoolHashCodeTest {
+ static testMain() {
+ Expect.notEquals(true.hashCode, false.hashCode);
+ }
+}
+
+main() {
+ BoolHashCodeTest.testMain();
+}
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index f48942d..2843f20 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -56,6 +56,7 @@
*: Fail, Pass # TODO(ahe): Triage these tests.
[ $compiler == dart2js && $runtime == safari ]
+null_nosuchmethod_test: Fail # TODO(ahe): Please triage this failure.
core_runtime_types_test: Fail
[ $compiler == dart2js && $runtime == ie9 ]
diff --git a/tests/html/form_data_test.dart b/tests/html/form_data_test.dart
index 61f0147..6ea5381 100644
--- a/tests/html/form_data_test.dart
+++ b/tests/html/form_data_test.dart
@@ -29,10 +29,18 @@
test('appendTest', () {
var form = new FormData();
- form.append('test', '1', 'foo');
- form.append('username', 'Elmo', 'foo');
- form.append('address', '1 Sesame Street', 'foo');
+ form.append('test', '1');
+ form.append('username', 'Elmo');
+ form.append('address', '1 Sesame Street');
form.append('password', '123456', 'foo');
expect(form, isNotNull);
});
+
+ test('appendBlob', () {
+ var form = new FormData();
+ var blob = new Blob(
+ ['Indescribable... Indestructible! Nothing can stop it!'],
+ 'text/plain');
+ form.append('theBlob', blob, 'theBlob.txt');
+ });
}
diff --git a/tests/html/html.status b/tests/html/html.status
index 0326285..8825886 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -11,6 +11,11 @@
[ $runtime == chrome ]
contentelement_test: Fail # Issue 5445: not currently supported on chrome stable.
+input_element_test/date: Pass, Fail # Chrome stable does not support this input type.
+input_element_test/datetime: Fail # Chrome stable does not support this input type.
+input_element_test/datetime-local: Fail # Chrome stable does not support this input type.
+input_element_test/month: Fail # Chrome stable does not support this input type.
+input_element_test/week: Fail # Chrome stable does not support this input type.
[ $runtime == chrome || $runtime == drt]
audiobuffersourcenode_test: Pass, Fail # AudiobufferSourceNode is flaky on Chrome and Dartium.
@@ -75,6 +80,12 @@
indexeddb_3_test: Fail
indexeddb_4_test: Fail
inner_frame_test: Skip
+input_element_test/date: Fail # IE10 does not support this input type.
+input_element_test/datetime: Fail # IE10 does not support this input type.
+input_element_test/datetime-local: Fail # IE10 does not support this input type.
+input_element_test/month: Fail # IE10 does not support this input type.
+input_element_test/time: Fail # IE10 does not support this input type.
+input_element_test/week: Fail # IE10 does not support this input type.
isolates_test: Skip
localstorage_test: Fail
measurement_test: Fail, Pass
@@ -134,6 +145,14 @@
indexeddb_2_test: Fail
indexeddb_3_test: Fail
indexeddb_4_test: Fail
+input_element_test/date: Fail # IE9 does not support this input type.
+input_element_test/datetime: Fail # IE9 does not support this input type.
+input_element_test/datetime-local: Fail # IE9 does not support this input type.
+input_element_test/month: Fail # IE9 does not support this input type.
+input_element_test/range: Fail # IE9 does not support this input type.
+input_element_test/time: Fail # IE9 does not support this input type.
+input_element_test/week: Fail # IE9 does not support this input type.
+
messageevent_test: Fail
mutationobserver_test: Fail
postmessage_structured_test: Skip # BUG(5685): times out.
@@ -157,53 +176,39 @@
xhr_cross_origin_test: Fail # Issue 6016.
[ $runtime == safari ]
+# TODO(ahe, efortuna): These tests need to be retriaged now that we're testing
+# with Safari 6.
+audiocontext_test: Crash, Fail # TODO(ahe): Please triage this failure.
+svgelement_test/innerHtml: Crash, Fail, Pass # TODO(ahe): Please triage this failure.
+svgelement_test/outerHtml: Crash, Fail, Pass # TODO(ahe): Please triage this failure.
+element_test/elements: Crash, Fail # TODO(ahe): Please triage this failure.
+element_test/children: Crash, Fail, Pass # TODO(ahe): Please triage this failure.
+element_test/attributes: Pass, Crash, Fail # TODO(ahe): Please triage this failure.
performance_api_test: Fail # window.performance.timing not in Safari 6.
indexeddb_1_test: Fail # indexedDB not in Safari 6.
indexeddb_2_test: Fail # indexedDB not in Safari 6.
indexeddb_3_test: Fail # indexedDB not in Safari 6.
indexeddb_4_test: Fail # indexedDB not in Safari 6.
+input_element_test/date: Fail # Safari does not support this input type.
+input_element_test/datetime: Fail # Safari does not support this input type.
+input_element_test/datetime-local: Fail # Safari does not support this input type.
+input_element_test/month: Fail, Crash # Safari does not support this input type.
+input_element_test/range: Fail, Crash # TODO(efortuna): Please triage this failure.
+input_element_test/time: Fail, Crash # Safari does not support this input type.
+input_element_test/week: Fail, Crash # Safari does not support this input type.
fileapi_test: Fail # requestFileSystem not supported in Safari 6.
datalistelement_test: Fail # HTMLDataListElement not yet supported in Safari.
contentelement_test: Fail # Safari 6 does not support content element.
-# The following tests all fail in Safari 5.1. (NOT Mountain Lion). These will go
-# away after we upgrade the buildbots to Mountain Lion after M1.
+# TODO: The following tests need to be triaged to understand why they are
+# failing (They are expected to pass in Safari 6).
# TODO(efortuna): Make our test framework able to separate tests out by browser
# version.
-audiobuffersourcenode_test: Fail
-audiocontext_test: Fail
-blob_constructor_test: Fail
-canvas_pixel_array_type_alias_test: Fail
-canvas_test: Fail # createImageData not in Safari 5.
-canvasrenderingcontext2d_test: Fail
datalistelement_test: Pass,Fail
-document_test: Fail
-dom_constructors_test: Fail
-element_test/additionalConstructors: Fail
-element_test/attributes: Fail, Crash
-element_test/constructors: Fail
-element_test/elements: Crash
-element_test/eventListening: Fail
-element_test/_ElementList: Fail, Crash
-element_test/queryAll: Fail, Crash
-element_add_test: Fail
-element_constructor_1_test: Fail
-element_webkit_test: Fail
-exceptions_test: Fail
-instance_of_test: Fail
-mutationobserver_test: Fail
-native_gc_test: Fail
node_test: Skip # Issue 6457
-serialized_script_value_test: Fail
-svgelement2_test: Fail
-svgelement_test/constructors: Crash
-svgelement_test/css: Crash
-svgelement_test/additionalConstructors: Fail
-svgelement_test/elementget: Fail
-svgelement_test/elementset: Fail
-svgelement_test/innerHtml: Crash, Fail
-svgelement_test/outerHtml: Fail
-svgelement_test/svg: Fail
-url_test: Fail
+svgelement_test/elementset: Crash, Pass
+svgelement_test/elementget: Crash, Pass
+svgelement_test/css: Crash, Pass
+element_test/eventListening: Crash, Pass
[ $runtime == opera && $system == windows ]
htmlelement_test: Fail, Pass
@@ -269,6 +274,13 @@
indexeddb_4_test: Fail # FF disables indexedDB from file URLs.
# setup code fails. prepare. (DOM callback has errors) Caught [object Event]
inner_frame_test: Skip
+input_element_test/date: Fail # FF does not support this input type.
+input_element_test/datetime: Fail # FF does not support this input type.
+input_element_test/datetime-local: Fail # FF does not support this input type.
+input_element_test/month: Fail # FF does not support this input type.
+input_element_test/time: Fail # FF does not support this input type.
+input_element_test/week: Fail # FF does not support this input type.
+input_element_test/range: Fail # FF does not support this input type.
# Interfaces not implemented: SVGTests, SVGLangSpace, SVGExternalResourcesRequired, SVGStylable
svg_3_test: Fail
svgelement_test/additionalConstructors: Fail
diff --git a/tests/html/input_element_test.dart b/tests/html/input_element_test.dart
new file mode 100644
index 0000000..d8c80ce
--- /dev/null
+++ b/tests/html/input_element_test.dart
@@ -0,0 +1,154 @@
+library input_element_test;
+import '../../pkg/unittest/lib/unittest.dart';
+import '../../pkg/unittest/lib/html_individual_config.dart';
+import 'dart:html';
+
+main() {
+ useHtmlIndividualConfiguration();
+
+ test('hidden', () {
+ var e = new HiddenInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'hidden');
+ });
+
+ test('search', () {
+ var e = new SearchInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'search');
+ });
+
+ test('text', () {
+ var e = new TextInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'text');
+ });
+
+ test('url', () {
+ var e = new UrlInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'url');
+ });
+
+ test('telephone', () {
+ var e = new TelephoneInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'tel');
+ });
+
+ test('email', () {
+ var e = new EmailInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'email');
+ });
+
+ test('password', () {
+ var e = new PasswordInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'password');
+ });
+
+ group('datetime', () {
+ test('constructor', () {
+ var e = new DateTimeInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'datetime');
+ });
+ });
+
+ group('date', () {
+ test('constructor', () {
+ var e = new DateInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'date');
+ });
+ });
+
+ group('month', () {
+ test('constructor', () {
+ var e = new MonthInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'month');
+ });
+ });
+
+ group('week', () {
+ test('constructor', () {
+ var e = new WeekInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'week');
+ });
+ });
+
+ group('time', () {
+ test('constructor', () {
+ var e = new TimeInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'time');
+ });
+ });
+
+ group('datetime-local', () {
+ test('constructor', () {
+ var e = new LocalDateTimeInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'datetime-local');
+ });
+ });
+
+ test('number', () {
+ var e = new NumberInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'number');
+ });
+
+ group('range', () {
+ test('constructor', () {
+ var e = new RangeInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'range');
+ });
+ });
+
+ test('checkbox', () {
+ var e = new CheckboxInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'checkbox');
+ });
+
+ test('radio', () {
+ var e = new RadioButtonInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'radio');
+ });
+
+ test('file', () {
+ var e = new FileUploadInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'file');
+ });
+
+ test('submit', () {
+ var e = new SubmitButtonInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'submit');
+ });
+
+ test('image', () {
+ var e = new ImageButtonInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'image');
+ });
+
+ test('reset', () {
+ var e = new ResetButtonInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'reset');
+ });
+
+ test('button', () {
+ var e = new ButtonInputElement();
+ expect(e is InputElement, true);
+ expect(e.type, 'button');
+ });
+}
diff --git a/tests/html/unknownelement_test.dart b/tests/html/unknownelement_test.dart
index fda531e..5b1a8d2 100644
--- a/tests/html/unknownelement_test.dart
+++ b/tests/html/unknownelement_test.dart
@@ -54,7 +54,7 @@
switch (name) {
case 'get:y':
return map['y'];
- case 'set:y':
+ case 'set:y=':
map['y'] = args[0];
return;
}
diff --git a/tests/language/arithmetic_test.dart b/tests/language/arithmetic_test.dart
index 9c06276..5c36ddf 100644
--- a/tests/language/arithmetic_test.dart
+++ b/tests/language/arithmetic_test.dart
@@ -55,11 +55,6 @@
b = -1;
Expect.equals(1 << i, a ~/ b);
}
- for (int i = 0; i < 80; i++) {
- a = -1 << i;
- b = -1;
- Expect.equals(1 << i, a ~/ b);
- }
a = 22;
b = 4.0;
// Smi & double.
diff --git a/tests/language/invocation_mirror2_test.dart b/tests/language/invocation_mirror2_test.dart
new file mode 100644
index 0000000..de6fcc2
--- /dev/null
+++ b/tests/language/invocation_mirror2_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+class C {
+ var im;
+ noSuchMethod(im) => this.im = im;
+ flif() {}
+}
+
+main() {
+ var c = new C();
+ c.flif = 42;
+ Expect.equals('flif=', c.im.memberName);
+ Expect.equals(42, c.im.positionalArguments[0]);
+}
diff --git a/tests/language/language.status b/tests/language/language.status
index 08428a2..04878b6 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -35,9 +35,6 @@
const_init6_negative_test: Fail # Issue 811
super_first_constructor_test: Fail # Issue 1372.
-# Issue 1355
-call_operator_test: Fail
-
parameter_initializer6_negative_test: Fail # Issue 3502
named_parameters_aggregated_test/05: Fail # Compile-time error reported instead of static type warning.
@@ -52,12 +49,12 @@
compile_time_constant10_test/none: Fail # issue 5214
-invocation_mirror_test: Fail # issue 3326, 3622.
-no_such_method_test: Fail # issue 3326, 3622.
+invocation_mirror_test: Fail # issue 3326.
+invocation_mirror_indirect_test: Fail # Issue 3326
+super_call4_test: Fail # issue 3326.
export_cyclic_test: Fail, Crash # issue 6060
duplicate_export_negative_test: Fail # issue 6134
-invocation_mirror_indirect_test: Fail # Issue 3326
type_annotation_test/04: Fail # Issue 6970
type_annotation_test/06: Fail # Issue 6973
type_annotation_test/09: Fail # Issue 6973
@@ -267,6 +264,21 @@
try_catch_syntax_test/08: Fail
+# test issue 7298 (use deprecated === and !==)
+compile_time_constant8_test: Fail
+compile_time_constant_b_test: Fail
+compile_time_constant_d_test: Fail
+compile_time_constant_e_test: Fail
+licm_test: Fail
+typed_equality_test: Fail
+
+
+# test issue 7337 (reference unknown ID from static is warning, even when with import prefix)
+prefix12_negative_test: Fail
+prefix2_negative_test: Fail
+
+
+
#
# Add new dartc annotations above in alphabetical order
#
@@ -421,8 +433,6 @@
# external keyword is not yet supported by dart2js/dart2dart.
external_test/*: Skip
lazy_static3_test: Fail, OK # Issue 3558
-# Call operator is not supported by DartVM (see suppression above.)
-call_operator_test: Fail
# dart2js frontend doesn't even analyse problematic classes.
duplicate_implements_test/01: Fail
duplicate_implements_test/02: Fail
@@ -519,9 +529,11 @@
type_error_test: Fail, OK # VM bug: http://dartbug.com/5280
# This is a VM error when the compiled code is run.
-invocation_mirror_test: Fail # issue 3326, 3622.
-invocation_mirror_indirect_test: Fail # issue 3326, 3622.
+invocation_mirror_test: Fail # issue 3326.
+invocation_mirror_indirect_test: Fail # issue 3326.
+super_call4_test: Fail # issue 3326.
[ $compiler == dart2dart && $minified ]
import_core_prefix_test: Pass
prefix22_test: Pass
+invocation_mirror2_test: Fail
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 6160c80..0c88a52 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -2,9 +2,6 @@
# 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.
-[ $compiler == dart2js && $host_checked && $checked]
-new_expression_type_args_test/02: Crash # Issue 6931
-
[ $compiler == dart2js || $compiler == dart2dart ]
class_literal_test/01: Fail # Class literals are expression now; delete this test.
class_literal_test/02: Fail # Class literals are expression now; delete this test.
@@ -57,8 +54,6 @@
factory1_test/01: Fail
type_annotation_test/09: Fail # Named constructors interpreted as a type.
-super_call4_test: Fail # Badly generated noSuchMethod call.
-
[ $compiler == dart2js && $unchecked ]
default_factory2_test/01: Fail # type arguments on redirecting factory not implemented
type_variable_scope_test: Fail # type arguments on redirecting factory not implemented
@@ -95,7 +90,7 @@
new_expression_type_args_test/01: Fail # Wrongly reports compile-time error.
double_int_to_string_test: Fail # Issue 1533 (double/integer distinction)
mint_arithmetic_test: Fail # Issue 1533 (big integer arithmetic).
-arithmetic_test: Fail # http://dartbug.com/6627
+left_shift_test: Fail # Issue 1533
factory_redirection_test/01: Fail
factory_redirection_test/05: Fail
factory_redirection_test/07: Fail
@@ -329,9 +324,6 @@
# of failing.
const_factory_negative_test: Crash, Fail
-# Implement InvocationMirror and pass it to noSuchMethod.
-invocation_mirror_test: Fail
-
[ $compiler == dart2js && $mode == release ]
assign_top_method_negative_test: Crash
@@ -354,6 +346,10 @@
[ $compiler == dart2js && $runtime == safari ]
+execute_finally8_test: Fail # TODO(ahe): Please triage this failure.
+execute_finally9_test: Fail # TODO(ahe): Please triage this failure.
+null_access_error_test: Fail # TODO(ahe): Please triage this failure.
+string_interpolate_null_test: Fail # TODO(ahe): Please triage this failure.
arithmetic_test: Skip # BUG(3492): Times out.
call_through_null_getter_test: Fail # Expected: ObjectNotClosureException got: Instance of 'TypeError'
closure3_test: Fail # Uncaught error: Instance of 'TypeError'
diff --git a/tests/language/left_shift_test.dart b/tests/language/left_shift_test.dart
new file mode 100644
index 0000000..9cca5d3
--- /dev/null
+++ b/tests/language/left_shift_test.dart
@@ -0,0 +1,11 @@
+// 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.
+
+main() {
+ for (int i = 0; i < 80; i++) {
+ var a = -1 << i;
+ var b = -1;
+ Expect.equals(1 << i, a ~/ b);
+ }
+}
diff --git a/tests/language/megamorphic_no_such_method_test.dart b/tests/language/megamorphic_no_such_method_test.dart
new file mode 100644
index 0000000..56c3287
--- /dev/null
+++ b/tests/language/megamorphic_no_such_method_test.dart
@@ -0,0 +1,64 @@
+// 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.
+// Test program for correct optimizations related to types fo allocated lists.
+
+// Classes to induce polymorphism of degree 10.
+class A0 {
+ test() => 0;
+}
+
+class A1 {
+ test() => 1;
+}
+
+class A2 {
+ test() => 2;
+}
+
+class A3 {
+ test() => 3;
+}
+
+class A4 {
+ test() => 4;
+}
+
+class A5 {
+ test() => 5;
+}
+
+class A6 {
+ test() => 6;
+}
+
+class A7 {
+ test() => 7;
+}
+
+class A8 {
+ test() => 8;
+}
+
+class A9 {
+ test() => 9;
+}
+
+// Class with no test method.
+class B { }
+
+test(obj) {
+ return obj.test();
+}
+
+main() {
+ // Trigger optimization of 'test' function.
+ List list = [new A0(), new A1(), new A2(), new A3(), new A4(),
+ new A5(), new A6(), new A7(), new A8(), new A9()];
+ for (int i = 0; i < 1000; i++) {
+ for (var obj in list) {
+ test(obj);
+ }
+ }
+ Expect.throws(() => test(new B()), (e) => e is NoSuchMethodError);
+}
diff --git a/tests/language/super_call4_test.dart b/tests/language/super_call4_test.dart
index f5ce7c28..0093bda 100644
--- a/tests/language/super_call4_test.dart
+++ b/tests/language/super_call4_test.dart
@@ -6,8 +6,30 @@
// current class.
class C {
+ E e = new E();
+
bool noSuchMethod(InvocationMirror im) {
- return true;
+ if (im.memberName == 'foo') {
+ return im.positionalArguments.isEmpty &&
+ im.namedArguments.isEmpty &&
+ im.invokeOn(e);
+ }
+ if (im.memberName == 'bar') {
+ return im.positionalArguments.length == 1 &&
+ im.namedArguments.isEmpty &&
+ im.invokeOn(e);
+ }
+ if (im.memberName == 'baz') {
+ return im.positionalArguments.isEmpty &&
+ im.namedArguments.length == 1 &&
+ im.invokeOn(e);
+ }
+ if (im.memberName == 'boz') {
+ return im.positionalArguments.length == 1 &&
+ im.namedArguments.length == 1 &&
+ im.invokeOn(e);
+ }
+ return false;
}
}
@@ -15,12 +37,31 @@
bool noSuchMethod(InvocationMirror im) {
return false;
}
- test() {
+ test1() {
return super.foo();
}
+ test2() {
+ return super.bar(1);
+ }
+ test3() {
+ return super.baz(b: 2);
+ }
+ test4() {
+ return super.boz(1, c: 2);
+ }
+}
+
+class E {
+ bool foo() => true;
+ bool bar(int a) => a == 1;
+ bool baz({int b}) => b == 2;
+ bool boz(int a, {int c}) => a == 1 && c == 2;
}
main() {
var d = new D();
- Expect.isTrue(d.test());
+ Expect.isTrue(d.test1());
+ Expect.isTrue(d.test2());
+ Expect.isTrue(d.test3());
+ Expect.isTrue(d.test4());
}
diff --git a/tests/language/syntax_test.dart b/tests/language/syntax_test.dart
index 6c6821c..462bdbd 100644
--- a/tests/language/syntax_test.dart
+++ b/tests/language/syntax_test.dart
@@ -233,6 +233,10 @@
new Bad();
+ 1 + 2 = 1; /// 63: compile-time error
+ new SyntaxTest() = 1; /// 64: compile-time error
+ futureOf(null) = 1; /// 65: compile-time error
+
} catch (ex) {
// Swallowing exceptions. Any error should be a compile-time error
// which kills the current isolate.
diff --git a/tests/standalone/io/directory_error_test.dart b/tests/standalone/io/directory_error_test.dart
index cf90f91..fecb223 100644
--- a/tests/standalone/io/directory_error_test.dart
+++ b/tests/standalone/io/directory_error_test.dart
@@ -17,15 +17,10 @@
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Creation failed") != -1);
if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the path specified") != -1);
Expect.equals(3, e.osError.errorCode);
}
@@ -49,18 +44,11 @@
bool checkCreateTempInNonExistentFileException(e) {
Expect.isTrue(e is DirectoryIOException);
Expect.isTrue(e.osError != null);
- Expect.isTrue(e.toString().indexOf(
- "Creation of temporary directory failed") != -1);
if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the path specified") != -1);
Expect.equals(3, e.osError.errorCode);
}
@@ -84,16 +72,6 @@
bool checkDeleteNonExistentFileException(e) {
Expect.isTrue(e is DirectoryIOException);
Expect.isTrue(e.osError != null);
- Expect.isTrue(e.toString().indexOf("Deletion failed") != -1);
- if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the file specified") != -1);
- }
// File not not found has error code 2 on all supported platforms.
Expect.equals(2, e.osError.errorCode);
@@ -119,15 +97,10 @@
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Deletion failed") != -1);
if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the path specified") != -1);
Expect.equals(3, e.osError.errorCode);
}
@@ -153,15 +126,10 @@
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Directory listing failed") != -1);
if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the path specified") != -1);
Expect.equals(3, e.osError.errorCode);
}
diff --git a/tests/standalone/io/file_error_test.dart b/tests/standalone/io/file_error_test.dart
index 3c8d084..218879e 100644
--- a/tests/standalone/io/file_error_test.dart
+++ b/tests/standalone/io/file_error_test.dart
@@ -16,18 +16,8 @@
Expect.isTrue(e is FileIOException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf(str) != -1);
- if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the file specified") != -1);
- }
// File not not found has error code 2 on all supported platforms.
Expect.equals(2, e.osError.errorCode);
-
return true;
}
@@ -121,15 +111,10 @@
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Cannot create file") != -1);
if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
Expect.equals(2, e.osError.errorCode);
} else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the path specified") != -1);
Expect.equals(3, e.osError.errorCode);
}
@@ -162,15 +147,6 @@
Expect.isTrue(e is FileIOException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Cannot retrieve full path") != -1);
- if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the file specified") != -1);
- }
// File not not found has error code 2 on all supported platforms.
Expect.equals(2, e.osError.errorCode);
@@ -204,15 +180,6 @@
Expect.isTrue(e.osError != null);
Expect.isTrue(
e.toString().indexOf("Cannot retrieve directory for file") != -1);
- if (Platform.operatingSystem == "linux") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "macos") {
- Expect.isTrue(e.toString().indexOf("No such file or directory") != -1);
- } else if (Platform.operatingSystem == "windows") {
- Expect.isTrue(
- e.toString().indexOf(
- "The system cannot find the file specified") != -1);
- }
// File not not found has error code 2 on all supported platforms.
Expect.equals(2, e.osError.errorCode);
diff --git a/tests/standalone/io/http_parser_test.dart b/tests/standalone/io/http_parser_test.dart
index 72f4d8a..dafd9f9 100644
--- a/tests/standalone/io/http_parser_test.dart
+++ b/tests/standalone/io/http_parser_test.dart
@@ -40,7 +40,7 @@
void reset() {
httpParser = new _HttpParser.requestParser();
- httpParser.requestStart = (m, u, v, h) {
+ httpParser.requestStart = (m, u, v, h, b) {
method = m;
uri = u;
version = v;
@@ -60,7 +60,7 @@
Expect.equals(connectionClose, !httpParser.persistentConnection);
headersCompleteCalled = true;
};
- httpParser.responseStart = (s, r, v, h) {
+ httpParser.responseStart = (s, r, v, h, b) {
Expect.fail("Expected request");
};
httpParser.dataReceived = (List<int> data) {
@@ -126,7 +126,9 @@
void reset() {
httpParser = new _HttpParser.requestParser();
- httpParser.responseStart = (s, r) { Expect.fail("Expected request"); };
+ httpParser.responseStart = (s, r, v, h, b) {
+ Expect.fail("Expected request");
+ };
httpParser.error = (e) {
errorCalled = true;
};
@@ -185,10 +187,10 @@
if (responseToMethod != null) {
httpParser.responseToMethod = responseToMethod;
}
- httpParser.requestStart = (m, u, v, h) {
+ httpParser.requestStart = (m, u, v, h, b) {
Expect.fail("Expected response");
};
- httpParser.responseStart = (s, r, v, h) {
+ httpParser.responseStart = (s, r, v, h, b) {
statusCode = s;
reasonPhrase = r;
version = v;
@@ -276,7 +278,9 @@
void reset() {
httpParser = new _HttpParser.responseParser();
- httpParser.requestStart = (m, u) => Expect.fail("Expected response");
+ httpParser.requestStart = (m, u, v, h, b) {
+ Expect.fail("Expected response");
+ };
httpParser.error = (e) => errorCalled = true;
httpParser.closed = () { };
diff --git a/tests/standalone/io/http_redirect_test.dart b/tests/standalone/io/http_redirect_test.dart
index 6bae57e..1bad27f 100644
--- a/tests/standalone/io/http_redirect_test.dart
+++ b/tests/standalone/io/http_redirect_test.dart
@@ -87,6 +87,43 @@
}
);
+ // Setup redirect for 301 where POST should not redirect.
+ server.addRequestHandler(
+ (HttpRequest request) => request.path == "/301src",
+ (HttpRequest request, HttpResponse response) {
+ Expect.equals("POST", request.method);
+ response.headers.set(HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/301target");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.outputStream.close();
+ }
+ );
+ server.addRequestHandler(
+ (HttpRequest request) => request.path == "/301target",
+ (HttpRequest request, HttpResponse response) {
+ Expect.fail("Redirect of POST should not happen");
+ }
+ );
+
+ // Setup redirect for 303 where POST should turn into GET.
+ server.addRequestHandler(
+ (HttpRequest request) => request.path == "/303src",
+ (HttpRequest request, HttpResponse response) {
+ Expect.equals("POST", request.method);
+ response.headers.set(HttpHeaders.LOCATION,
+ "http://127.0.0.1:${server.port}/303target");
+ response.statusCode = HttpStatus.SEE_OTHER;
+ response.outputStream.close();
+ }
+ );
+ server.addRequestHandler(
+ (HttpRequest request) => request.path == "/303target",
+ (HttpRequest request, HttpResponse response) {
+ Expect.equals("GET", request.method);
+ response.outputStream.close();
+ }
+ );
+
return server;
}
@@ -212,6 +249,66 @@
conn.onError = (e) => Expect.fail("Error not expected ($e)");
}
+void testAutoRedirect301POST() {
+ HttpServer server = setupServer();
+ HttpClient client = new HttpClient();
+
+ var requestCount = 0;
+
+ void onRequest(HttpClientRequest request) {
+ requestCount++;
+ request.outputStream.close();
+ };
+
+ void onResponse(HttpClientResponse response) {
+ Expect.equals(HttpStatus.MOVED_PERMANENTLY, response.statusCode);
+ response.inputStream.onData =
+ () => Expect.fail("Response data not expected");
+ response.inputStream.onClosed = () {
+ Expect.equals(1, requestCount);
+ server.close();
+ client.shutdown();
+ };
+ };
+
+ HttpClientConnection conn =
+ client.postUrl(
+ new Uri.fromString("http://127.0.0.1:${server.port}/301src"));
+ conn.onRequest = onRequest;
+ conn.onResponse = onResponse;
+ conn.onError = (e) => Expect.fail("Error not expected ($e)");
+}
+
+void testAutoRedirect303POST() {
+ HttpServer server = setupServer();
+ HttpClient client = new HttpClient();
+
+ var requestCount = 0;
+
+ void onRequest(HttpClientRequest request) {
+ requestCount++;
+ request.outputStream.close();
+ };
+
+ void onResponse(HttpClientResponse response) {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ response.inputStream.onData =
+ () => Expect.fail("Response data not expected");
+ response.inputStream.onClosed = () {
+ Expect.equals(1, requestCount);
+ server.close();
+ client.shutdown();
+ };
+ };
+
+ HttpClientConnection conn =
+ client.postUrl(
+ new Uri.fromString("http://127.0.0.1:${server.port}/303src"));
+ conn.onRequest = onRequest;
+ conn.onResponse = onResponse;
+ conn.onError = (e) => Expect.fail("Error not expected ($e)");
+}
+
void testAutoRedirectLimit() {
HttpServer server = setupServer();
HttpClient client = new HttpClient();
@@ -254,6 +351,8 @@
testManualRedirectWithHeaders();
testAutoRedirect();
testAutoRedirectWithHeaders();
+ testAutoRedirect301POST();
+ testAutoRedirect303POST();
testAutoRedirectLimit();
testRedirectLoop();
}
diff --git a/tests/standalone/io/pkcert/cert9.db b/tests/standalone/io/pkcert/cert9.db
index 1c4913b..df2cf3e 100644
--- a/tests/standalone/io/pkcert/cert9.db
+++ b/tests/standalone/io/pkcert/cert9.db
Binary files differ
diff --git a/tests/standalone/io/pkcert/key4.db b/tests/standalone/io/pkcert/key4.db
index 6610e75..e529055 100644
--- a/tests/standalone/io/pkcert/key4.db
+++ b/tests/standalone/io/pkcert/key4.db
Binary files differ
diff --git a/tests/standalone/io/regress_7191_script.dart b/tests/standalone/io/regress_7191_script.dart
new file mode 100644
index 0000000..d90f0b5
--- /dev/null
+++ b/tests/standalone/io/regress_7191_script.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 'dart:io';
+import 'dart:isolate';
+
+main() {
+ // Open a port to make the script hang.
+ var port = new ReceivePort();
+ // Start sub-process when receiving data.
+ stdin.onData = () {
+ var data = stdin.read();
+ var options = new Options();
+ Process.start(options.executable, [options.script]).then((p) {
+ p.stdout.onData = p.stdout.read;
+ p.stderr.onData = p.stderr.read;
+ // When receiving data again, kill sub-process and exit.
+ stdin.onData = () {
+ var data = stdin.read();
+ Expect.listEquals([0], data);
+ p.kill();
+ p.onExit = exit;
+ };
+ // Close stdout. If handles are incorrectly inherited this will
+ // not actually close stdout and the test will hang.
+ stdout.close();
+ });
+ };
+}
diff --git a/tests/standalone/io/regress_7191_test.dart b/tests/standalone/io/regress_7191_test.dart
new file mode 100644
index 0000000..75284be
--- /dev/null
+++ b/tests/standalone/io/regress_7191_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.
+
+// Regression test for http://dartbug.com/7191.
+
+// Starts a sub-process which in turn starts another sub-process and then closes
+// its standard output. If handles are incorrectly inherited on Windows, this
+// will lead to a situation where the stdout of the first sub-process is never
+// closed which will make this test hang.
+
+import 'dart:io';
+import 'dart:isolate';
+
+main() {
+ var port = new ReceivePort();
+ var options = new Options();
+ var executable = options.executable;
+ var scriptDir = new Path.fromNative(options.script).directoryPath;
+ var script = scriptDir.append('regress_7191_script.dart').toNativePath();
+ Process.start(executable, [script]).then((process) {
+ process.stdin.write([0]);
+ process.stdout.onData = process.stdout.read;
+ process.stderr.onData = process.stderr.read;
+ process.stdout.onClosed = () {
+ process.stdin.write([0]);
+ };
+ process.onExit = (exitCode) => port.close();
+ });
+}
diff --git a/tests/standalone/io/secure_server_client_certificate_test.dart b/tests/standalone/io/secure_server_client_certificate_test.dart
new file mode 100644
index 0000000..261c716
--- /dev/null
+++ b/tests/standalone/io/secure_server_client_certificate_test.dart
@@ -0,0 +1,125 @@
+// 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 "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+
+void WriteAndClose(Socket socket, String message) {
+ var data = message.charCodes;
+ int written = 0;
+ void write() {
+ written += socket.writeList(data, written, data.length - written);
+ if (written < data.length) {
+ socket.onWrite = write;
+ } else {
+ socket.close(true);
+ }
+ }
+ write();
+}
+
+class SecureTestServer {
+ void onConnection(Socket connection) {
+ connection.onConnect = () {
+ numConnections++;
+ var certificate = connection.peerCertificate;
+ Expect.isTrue(certificate.subject.contains("CN="));
+ };
+ String received = "";
+ connection.onData = () {
+ received = received.concat(new String.fromCharCodes(connection.read()));
+ };
+ connection.onClosed = () {
+ Expect.isTrue(received.contains("Hello from client "));
+ String name = received.substring(received.indexOf("client ") + 7);
+ WriteAndClose(connection, "Welcome, client $name");
+ };
+ }
+
+ void errorHandlerServer(Exception e) {
+ Expect.fail("Server socket error $e");
+ }
+
+ int start() {
+ server = new SecureServerSocket(SERVER_ADDRESS,
+ 0,
+ 10,
+ "CN=$HOST_NAME",
+ requireClientCertificate: true);
+ Expect.isNotNull(server);
+ server.onConnection = onConnection;
+ server.onError = errorHandlerServer;
+ return server.port;
+ }
+
+ void stop() {
+ server.close();
+ }
+
+ int numConnections = 0;
+ SecureServerSocket server;
+}
+
+class SecureTestClient {
+ SecureTestClient(int this.port, String this.name) {
+ socket = new SecureSocket(HOST_NAME, port, sendClientCertificate: true);
+ socket.onConnect = this.onConnect;
+ socket.onData = () {
+ reply = reply.concat(new String.fromCharCodes(socket.read()));
+ };
+ socket.onClosed = done;
+ reply = "";
+ }
+
+ void onConnect() {
+ numRequests++;
+ WriteAndClose(socket, "Hello from client $name");
+ }
+
+ void done() {
+ Expect.equals("Welcome, client $name", reply);
+ numReplies++;
+ if (numReplies == CLIENT_NAMES.length) {
+ Expect.equals(numRequests, numReplies);
+ EndTest();
+ }
+ }
+
+ static int numRequests = 0;
+ static int numReplies = 0;
+
+ int port;
+ String name;
+ SecureSocket socket;
+ String reply;
+}
+
+Function EndTest;
+
+const CLIENT_NAMES = const ['able', 'baker', 'camera', 'donut', 'echo'];
+
+void main() {
+ ReceivePort keepAlive = new ReceivePort();
+ Path scriptDir = new Path.fromNative(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart',
+ useBuiltinRoots: false);
+
+ var server = new SecureTestServer();
+ int port = server.start();
+
+ EndTest = () {
+ Expect.equals(CLIENT_NAMES.length, server.numConnections);
+ server.stop();
+ keepAlive.close();
+ };
+
+ for (var x in CLIENT_NAMES) {
+ new SecureTestClient(port, x);
+ }
+}
diff --git a/tests/standalone/io/secure_server_test.dart b/tests/standalone/io/secure_server_test.dart
index 8941548..d27ca9a 100644
--- a/tests/standalone/io/secure_server_test.dart
+++ b/tests/standalone/io/secure_server_test.dart
@@ -101,7 +101,8 @@
Path scriptDir = new Path.fromNative(new Options().script).directoryPath;
Path certificateDatabase = scriptDir.append('pkcert');
SecureSocket.initialize(database: certificateDatabase.toNativePath(),
- password: 'dartdart');
+ password: 'dartdart',
+ useBuiltinRoots: false);
var server = new SecureTestServer();
int port = server.start();
diff --git a/tests/standalone/io/secure_session_resume_test.dart b/tests/standalone/io/secure_session_resume_test.dart
new file mode 100644
index 0000000..825351a
--- /dev/null
+++ b/tests/standalone/io/secure_session_resume_test.dart
@@ -0,0 +1,134 @@
+// 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 test tests TLS session resume, by making multiple client connections
+// on the same port to the same server, with a delay of 200 ms between them.
+// The unmodified secure_server_test creates all sessions simultaneously,
+// which means that no handshake completes and caches its keys in the session
+// cache in time for other connections to use it.
+//
+// Session resume is currently disabled - see issue
+// https://code.google.com/p/dart/issues/detail?id=7230
+
+import "dart:io";
+import "dart:isolate";
+
+const SERVER_ADDRESS = "127.0.0.1";
+const HOST_NAME = "localhost";
+
+void WriteAndClose(Socket socket, String message) {
+ var data = message.charCodes;
+ int written = 0;
+ void write() {
+ written += socket.writeList(data, written, data.length - written);
+ if (written < data.length) {
+ socket.onWrite = write;
+ } else {
+ socket.close(true);
+ }
+ }
+ write();
+}
+
+class SecureTestServer {
+ void onConnection(Socket connection) {
+ connection.onConnect = () {
+ numConnections++;
+ };
+ String received = "";
+ connection.onData = () {
+ received = received.concat(new String.fromCharCodes(connection.read()));
+ };
+ connection.onClosed = () {
+ Expect.isTrue(received.contains("Hello from client "));
+ String name = received.substring(received.indexOf("client ") + 7);
+ WriteAndClose(connection, "Welcome, client $name");
+ };
+ }
+
+ void errorHandlerServer(Exception e) {
+ Expect.fail("Server socket error $e");
+ }
+
+ int start() {
+ server = new SecureServerSocket(SERVER_ADDRESS, 0, 10, "CN=$HOST_NAME");
+ Expect.isNotNull(server);
+ server.onConnection = onConnection;
+ server.onError = errorHandlerServer;
+ return server.port;
+ }
+
+ void stop() {
+ server.close();
+ }
+
+ int numConnections = 0;
+ SecureServerSocket server;
+}
+
+class SecureTestClient {
+ SecureTestClient(int this.port, String this.name) {
+ socket = new SecureSocket(HOST_NAME, port);
+ socket.onConnect = this.onConnect;
+ socket.onData = () {
+ reply = reply.concat(new String.fromCharCodes(socket.read()));
+ };
+ socket.onClosed = done;
+ reply = "";
+ }
+
+ void onConnect() {
+ numRequests++;
+ WriteAndClose(socket, "Hello from client $name");
+ }
+
+ void done() {
+ Expect.equals("Welcome, client $name", reply);
+ numReplies++;
+ if (numReplies == CLIENT_NAMES.length) {
+ Expect.equals(numRequests, numReplies);
+ EndTest();
+ }
+ }
+
+ static int numRequests = 0;
+ static int numReplies = 0;
+
+ int port;
+ String name;
+ SecureSocket socket;
+ String reply;
+}
+
+Function EndTest;
+
+const CLIENT_NAMES = const ['able', 'baker'];
+
+void main() {
+ ReceivePort keepAlive = new ReceivePort();
+ Path scriptDir = new Path.fromNative(new Options().script).directoryPath;
+ Path certificateDatabase = scriptDir.append('pkcert');
+ SecureSocket.initialize(database: certificateDatabase.toNativePath(),
+ password: 'dartdart',
+ useBuiltinRoots: false);
+
+ var server = new SecureTestServer();
+ int port = server.start();
+
+ EndTest = () {
+ Expect.equals(CLIENT_NAMES.length, server.numConnections);
+ server.stop();
+ keepAlive.close();
+ };
+
+ int delay = 0;
+ int delay_between_connections = 300; // Milliseconds.
+
+ for (var x in CLIENT_NAMES) {
+ new Timer(delay, (_) {
+ new SecureTestClient(port, x);
+ });
+ delay += delay_between_connections;
+ }
+}
diff --git a/tests/standalone/io/windows_environment_script.dart b/tests/standalone/io/windows_environment_script.dart
new file mode 100644
index 0000000..d94a52c
--- /dev/null
+++ b/tests/standalone/io/windows_environment_script.dart
@@ -0,0 +1,12 @@
+// 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("dart:io");
+
+main() {
+ var scriptDir = Platform.environment['SCRIPTDIR'];
+ Expect.isTrue(scriptDir.contains('å'));
+ var str = new File('$scriptDir/funky.bat').readAsStringSync();
+ Expect.isTrue(str.contains('%~dp0'));
+}
diff --git a/tests/standalone/io/windows_environment_test.dart b/tests/standalone/io/windows_environment_test.dart
new file mode 100644
index 0000000..34dc2a2
--- /dev/null
+++ b/tests/standalone/io/windows_environment_test.dart
@@ -0,0 +1,27 @@
+// 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("dart:io");
+
+main() {
+ if (Platform.operatingSystem != 'windows') return;
+ var tempDir = new Directory('').createTempSync();
+ var funkyDir = new Directory("${tempDir.path}/å");
+ funkyDir.createSync();
+ var funkyFile = new File('${funkyDir.path}/funky.bat');
+ funkyFile.writeAsStringSync("""
+@echo off
+set SCRIPTDIR=%~dp0
+%1 %2
+ """);
+ var options = new Options();
+ var dart = options.executable;
+ var scriptDir = new Path.fromNative(options.script).directoryPath;
+ var script = scriptDir.append('windows_environment_script.dart');
+ Process.run('cmd',
+ ['/c', funkyFile.name, dart, script.toNativePath()]).then((p) {
+ Expect.equals(0, p.exitCode);
+ tempDir.deleteSync(recursive: true);
+ });
+}
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 1e89cdb..fa225d4 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -23,7 +23,6 @@
# This is expected as MacOS by default runs with a very low number
# of allowed open files ('ulimit -n' says something like 256).
io/socket_many_connections_test: Skip
-io/http_auth_test: Skip # Issue 7183
# These tests pass on MacOS 10.8.2 but fails on the buildbot machines
# that are running an earlier version of MacOS. The issue is that the
@@ -42,7 +41,8 @@
io/secure_server_stream_test: Pass, Crash, Timeout # Issue 6893
io/secure_server_test: Pass, Crash, Fail, Timeout # Issue 6893
io/secure_no_builtin_roots_test: Pass, Crash # Issue 7157
-io/secure_socket_bad_certificate_test: Pass, Crash, Timeout # Issue 7157
+io/secure_socket_bad_certificate_test: Pass, Crash, Fail, Timeout # Issue 7157
+io/http_shutdown_test: Pass, Fail, Timeout # Issue 7294
[ $compiler == none && $runtime == drt ]
io/*: Skip # Don't run tests using dart:io in the browser
diff --git a/tests/utils/dart2js_test.dart b/tests/utils/dart2js_test.dart
new file mode 100644
index 0000000..f7786fb
--- /dev/null
+++ b/tests/utils/dart2js_test.dart
@@ -0,0 +1,16 @@
+// 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.
+
+// Test to ensure that dart2js is free of warnings.
+
+// Test annotation to let the test framework know that this file
+// should analyze without any warnings.
+// @static-clean
+
+import '../../sdk/lib/_internal/compiler/implementation/dart2js.dart'
+ as dart2js;
+
+void main() {
+ // Do nothing.
+}
diff --git a/tests/utils/dummy_compiler_test.dart b/tests/utils/dummy_compiler_test.dart
index af4c06a..cd329ca 100644
--- a/tests/utils/dummy_compiler_test.dart
+++ b/tests/utils/dummy_compiler_test.dart
@@ -4,6 +4,8 @@
// Smoke test of the dart2js compiler API.
+library dummy_compiler;
+
import '../../sdk/lib/_internal/compiler/compiler.dart';
import 'dart:uri';
@@ -14,7 +16,7 @@
source = "main() {}";
} else if (uri.scheme == "lib") {
if (uri.path.endsWith("/core.dart")) {
- source = """#library('core');
+ source = """library core;
class Object {}
class Type {}
class bool {}
@@ -36,11 +38,19 @@
source = '';
} else if (uri.path.endsWith('interceptors.dart')) {
source = """class ObjectInterceptor {}
+ class JSArray {}
+ class JSString {}
+ class JSFunction {}
+ class JSInt {}
+ class JSDouble {}
+ class JSNumber {}
+ class JSNull {}
+ class JSBool {}
var getInterceptor;""";
} else if (uri.path.endsWith('js_helper.dart')) {
source = 'library jshelper; class JSInvocationMirror {}';
} else {
- source = "#library('lib');";
+ source = "library lib;";
}
} else {
throw "unexpected URI $uri";
diff --git a/tests/utils/utils.status b/tests/utils/utils.status
index d8b095d..e421ea3 100644
--- a/tests/utils/utils.status
+++ b/tests/utils/utils.status
@@ -10,8 +10,9 @@
recursive_import_test: Slow, Pass
[ $compiler == none && $runtime == drt ]
-dummy_compiler_test: Fail # http://dartbug.com/2264
+dummy_compiler_test: Skip # http://dartbug.com/7233
recursive_import_test: Fail # http://dartbug.com/2264
+dart2js_test: Fail # http://dartbug.com/2264
[ $compiler == dart2js && $browser ]
*: Skip
@@ -27,6 +28,3 @@
*: Skip
[ $compiler == dartc ]
-# dart2js issue 6870
-dummy_compiler_test: Fail, OK
-recursive_import_test: Fail, OK
diff --git a/tools/VERSION b/tools/VERSION
index 3c41a1a..0ade99d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
MAJOR 0
MINOR 2
-BUILD 8
-PATCH 2
+BUILD 9
+PATCH 0
diff --git a/tools/bots/compiler.py b/tools/bots/compiler.py
index c752f8d..9e88f2c 100644
--- a/tools/bots/compiler.py
+++ b/tools/bots/compiler.py
@@ -99,7 +99,7 @@
flags = [x for x in flags if not '=' in x]
return ('%s tests %s' % (name, ' '.join(flags))).strip()
-
+IsFirstTestStepCall = True
def TestStep(name, mode, system, compiler, runtime, targets, flags):
step_name = TestStepName(name, flags)
with bot.BuildStep(step_name, swallow_error=True):
@@ -121,11 +121,21 @@
'--use-sdk',
'--report'])
+ # TODO(ricow/kustermann): Issue 7339
+ if runtime == "safari":
+ cmd.append('--nobatch')
+
if user_test == 'yes':
cmd.append('--progress=color')
else:
cmd.extend(['--progress=buildbot', '-v'])
+ global IsFirstTestStepCall
+ if IsFirstTestStepCall:
+ IsFirstTestStepCall = False
+ else:
+ cmd.append('--append_flaky_log')
+
if flags:
cmd.extend(flags)
cmd.extend(targets)
diff --git a/tools/touch_version.py b/tools/touch_version.py
deleted file mode 100644
index 98841fe..0000000
--- a/tools/touch_version.py
+++ /dev/null
@@ -1,18 +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 python script "touches" the tools/VERSION file.
-
-import os
-import sys
-
-# Change into the dart directory as we want to be able to access the VERSION file
-# from a simple path.
-runtime_src = os.path.join(os.path.dirname(sys.argv[0]), os.pardir)
-os.chdir(runtime_src)
-
-if __name__ == '__main__':
- print 'Touching tools/VERSION.'
- os.utime(os.path.join('tools', 'VERSION'), None)
- sys.exit(0)
diff --git a/utils/pub/command_help.dart b/utils/pub/command_help.dart
index 7681f37..33ef705 100644
--- a/utils/pub/command_help.dart
+++ b/utils/pub/command_help.dart
@@ -7,11 +7,12 @@
import 'dart:io' as io;
import 'exit_codes.dart' as exit_codes;
import 'io.dart';
+import 'log.dart' as log;
import 'pub.dart';
/** Handles the `help` pub command. */
class HelpCommand extends PubCommand {
- String get description => "display help information for Pub";
+ String get description => "Display help information for Pub.";
String get usage => 'pub help [command]';
bool get requiresEntrypoint => false;
@@ -22,8 +23,8 @@
var name = commandOptions.rest[0];
var command = pubCommands[name];
if (command == null) {
- printError('Could not find a command named "$name".');
- printError('Run "pub help" to see available commands.');
+ log.error('Could not find a command named "$name".');
+ log.error('Run "pub help" to see available commands.');
io.exit(exit_codes.USAGE);
}
diff --git a/utils/pub/command_install.dart b/utils/pub/command_install.dart
index c14884e2..e5188f6 100644
--- a/utils/pub/command_install.dart
+++ b/utils/pub/command_install.dart
@@ -5,16 +5,17 @@
library command_install;
import 'entrypoint.dart';
+import 'log.dart' as log;
import 'pub.dart';
/** Handles the `install` pub command. */
class InstallCommand extends PubCommand {
- String get description => "install the current package's dependencies";
+ String get description => "Install the current package's dependencies.";
String get usage => "pub install";
Future onRun() {
return entrypoint.installDependencies().transform((_) {
- print("Dependencies installed!");
+ log.message("Dependencies installed!");
});
}
}
diff --git a/utils/pub/command_lish.dart b/utils/pub/command_lish.dart
index 3895232..f775b46 100644
--- a/utils/pub/command_lish.dart
+++ b/utils/pub/command_lish.dart
@@ -10,17 +10,16 @@
import '../../pkg/args/lib/args.dart';
import '../../pkg/http/lib/http.dart' as http;
-import 'pub.dart';
-import 'io.dart';
import 'git.dart' as git;
+import 'io.dart';
+import 'log.dart' as log;
import 'oauth2.dart' as oauth2;
+import 'pub.dart';
import 'validator.dart';
-// TODO(nweiz): Make "publish" the primary name for this command. See issue
-// 6949.
/// Handles the `lish` and `publish` pub commands.
class LishCommand extends PubCommand {
- final description = "publish the current package to pub.dartlang.org";
+ final description = "Publish the current package to pub.dartlang.org.";
final usage = "pub publish [options]";
final aliases = const ["lish", "lush"];
@@ -42,6 +41,7 @@
return Futures.wait([
client.get(server.resolve("/packages/versions/new.json")),
_filesToPublish.transform((files) {
+ log.fine('Archiving and publishing ${entrypoint.root}.');
return createTarGz(files, baseDir: entrypoint.root.dir);
}).chain(consumeInputStream),
_validate()
@@ -77,7 +77,7 @@
parsed['success']['message'] is! String) {
_invalidServerResponse(response);
}
- print(parsed['success']['message']);
+ log.message(parsed['success']['message']);
});
}).transformException((e) {
if (e is PubHttpException) {
@@ -97,13 +97,13 @@
throw errorMap['error']['message'];
}
} else if (e is oauth2.ExpirationException) {
- printError("Pub's authorization to upload packages has expired and "
+ log.error("Pub's authorization to upload packages has expired and "
"can't be automatically refreshed.");
return onRun();
} else if (e is oauth2.AuthorizationException) {
var message = "OAuth2 authorization failed";
if (e.description != null) message = "$message (${e.description})";
- printError("$message.");
+ log.error("$message.");
return oauth2.clearCredentials(cache).chain((_) => onRun());
} else {
throw e;
@@ -142,9 +142,8 @@
if (file == null || _BLACKLISTED_FILES.contains(basename(file))) {
return false;
}
- // TODO(nweiz): Since `file` is absolute, this will break if the package
- // itself is in a directory named "packages" (issue 7215).
- return !splitPath(file).some(_BLACKLISTED_DIRECTORIES.contains);
+ return !splitPath(relativeTo(file, rootDir))
+ .some(_BLACKLISTED_DIRECTORIES.contains);
}));
}
diff --git a/utils/pub/command_update.dart b/utils/pub/command_update.dart
index 16155e2..6995a6d 100644
--- a/utils/pub/command_update.dart
+++ b/utils/pub/command_update.dart
@@ -5,12 +5,13 @@
library command_update;
import 'entrypoint.dart';
+import 'log.dart' as log;
import 'pub.dart';
/** Handles the `update` pub command. */
class UpdateCommand extends PubCommand {
String get description =>
- "update the current package's dependencies to the latest versions";
+ "Update the current package's dependencies to the latest versions.";
String get usage => 'pub update [dependencies...]';
@@ -21,6 +22,6 @@
} else {
future = entrypoint.updateDependencies(commandOptions.rest);
}
- return future.transform((_) => print("Dependencies updated!"));
+ return future.transform((_) => log.message("Dependencies updated!"));
}
}
diff --git a/utils/pub/command_version.dart b/utils/pub/command_version.dart
index 464d1a2..5e2f005 100644
--- a/utils/pub/command_version.dart
+++ b/utils/pub/command_version.dart
@@ -8,7 +8,7 @@
/** Handles the `version` pub command. */
class VersionCommand extends PubCommand {
- String get description => 'print Pub version';
+ String get description => 'Print pub version.';
String get usage => 'pub version';
bool get requiresEntrypoint => false;
diff --git a/utils/pub/curl_client.dart b/utils/pub/curl_client.dart
index e1afc2d..6922b82 100644
--- a/utils/pub/curl_client.dart
+++ b/utils/pub/curl_client.dart
@@ -8,6 +8,7 @@
import '../../pkg/http/lib/http.dart' as http;
import 'io.dart';
+import 'log.dart' as log;
import 'utils.dart';
/// A drop-in replacement for [http.Client] that uses the `curl` command-line
@@ -30,12 +31,15 @@
/// Sends a request via `curl` and returns the response.
Future<http.StreamedResponse> send(http.BaseRequest request) {
+ log.fine("Sending Curl request $request");
+
var requestStream = request.finalize();
return withTempDir((tempDir) {
- var headerFile = new Path(tempDir).append("curl-headers").toNativePath();
+ var headerFile = join(tempDir, "curl-headers");
var arguments = _argumentsForRequest(request, headerFile);
+ log.process(executable, arguments);
var process;
- return Process.start(executable, arguments).chain((process_) {
+ return startProcess(executable, arguments).chain((process_) {
process = process_;
if (requestStream.closed) {
process.stdin.close();
@@ -114,6 +118,8 @@
Future _waitForHeaders(Process process, {bool expectBody}) {
var completer = new Completer();
process.onExit = (exitCode) {
+ log.io("Curl process exited with code $exitCode.");
+
if (exitCode == 0) {
completer.complete(null);
return;
@@ -122,6 +128,7 @@
chainToCompleter(consumeInputStream(process.stderr)
.transform((stderrBytes) {
var message = new String.fromCharCodes(stderrBytes);
+ log.fine('Got error reading headers from curl: $message');
if (exitCode == 47) {
throw new RedirectLimitExceededException([]);
} else {
diff --git a/utils/pub/entrypoint.dart b/utils/pub/entrypoint.dart
index d3af1fa..56007e0 100644
--- a/utils/pub/entrypoint.dart
+++ b/utils/pub/entrypoint.dart
@@ -6,12 +6,13 @@
import 'io.dart';
import 'lock_file.dart';
+import 'log.dart' as log;
import 'package.dart';
import 'root_source.dart';
import 'system_cache.dart';
+import 'utils.dart';
import 'version.dart';
import 'version_solver.dart';
-import 'utils.dart';
/**
* Pub operates over a directed graph of dependencies that starts at a root
@@ -87,6 +88,7 @@
if (!exists) return new Future.immediate(null);
// TODO(nweiz): figure out when to actually delete the directory, and when
// we can just re-use the existing symlink.
+ log.fine("Deleting package directory for ${id.name} before install.");
return deleteDir(packageDir);
}).chain((_) {
if (id.source.shouldCache) {
@@ -165,26 +167,18 @@
* warning message and act as though the file doesn't exist.
*/
Future<LockFile> _loadLockFile() {
- var completer = new Completer<LockFile>();
var lockFilePath = join(root.dir, 'pubspec.lock');
- var future = readTextFile(lockFilePath);
- future.handleException((_) {
- // If we failed to load the lockfile but it does exist, something's
- // probably wrong and we should notify the user.
- fileExists(lockFilePath).transform((exists) {
- if (!exists) return;
- printError("Error reading pubspec.lock: ${future.exception}");
- }).then((_) {
- completer.complete(new LockFile.empty());
- });
+ log.fine("Loading lockfile.");
+ return fileExists(lockFilePath).chain((exists) {
+ if (!exists) {
+ log.fine("No lock file at $lockFilePath, creating empty one.");
+ return new Future<LockFile>.immediate(new LockFile.empty());
+ }
- return true;
+ return readTextFile(lockFilePath).transform((text) =>
+ new LockFile.parse(text, cache.sources));
});
-
- future.then((text) =>
- completer.complete(new LockFile.parse(text, cache.sources)));
- return completer.future;
}
/**
@@ -196,7 +190,9 @@
if (id.source is! RootSource) lockFile.packages[id.name] = id;
}
- return writeTextFile(join(root.dir, 'pubspec.lock'), lockFile.serialize());
+ var lockFilePath = join(root.dir, 'pubspec.lock');
+ log.fine("Saving lockfile.");
+ return writeTextFile(lockFilePath, lockFile.serialize());
}
/**
diff --git a/utils/pub/git.dart b/utils/pub/git.dart
index 80901a9..6defde3 100644
--- a/utils/pub/git.dart
+++ b/utils/pub/git.dart
@@ -8,6 +8,7 @@
library git;
import 'io.dart';
+import 'log.dart' as log;
import 'utils.dart';
/// Tests whether or not the git command-line app is available for use.
@@ -57,6 +58,7 @@
return null;
});
}).transform((command) {
+ log.fine('Determined git command $command.');
_gitCommandCache = command;
return command;
});
diff --git a/utils/pub/hosted_source.dart b/utils/pub/hosted_source.dart
index 8315acd..ba5119d 100644
--- a/utils/pub/hosted_source.dart
+++ b/utils/pub/hosted_source.dart
@@ -11,6 +11,7 @@
// TODO(nweiz): Make this import better.
import '../../pkg/http/lib/http.dart' as http;
import 'io.dart';
+import 'log.dart' as log;
import 'package.dart';
import 'pubspec.dart';
import 'source.dart';
@@ -73,7 +74,7 @@
var fullUrl = "$url/packages/$name/versions/${id.version}.tar.gz";
- print('Downloading $id...');
+ log.message('Downloading $id...');
// Download and extract the archive to a temp directory.
var tempDir;
diff --git a/utils/pub/io.dart b/utils/pub/io.dart
index 968f354..0ecc7b2 100644
--- a/utils/pub/io.dart
+++ b/utils/pub/io.dart
@@ -13,83 +13,50 @@
// TODO(nweiz): Make this import better.
import '../../pkg/http/lib/http.dart' as http;
-import 'utils.dart';
import 'curl_client.dart';
+import 'log.dart' as log;
+import 'path.dart' as path;
+import 'utils.dart';
bool _isGitInstalledCache;
/// The cached Git command.
String _gitCommandCache;
-/** Gets the current working directory. */
-String get currentWorkingDir => new File('.').fullPathSync();
-
final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?");
/**
- * Prints the given string to `stderr` on its own line.
- */
-void printError(value) {
- stderr.writeString(value.toString());
- stderr.writeString('\n');
-}
-
-
-/**
* Joins a number of path string parts into a single path. Handles
* platform-specific path separators. Parts can be [String], [Directory], or
* [File] objects.
*/
String join(part1, [part2, part3, part4]) {
- final parts = sanitizePath(part1).split('/');
+ part1 = _getPath(part1);
+ if (part2 != null) part2 = _getPath(part2);
+ if (part3 != null) part3 = _getPath(part3);
+ if (part4 != null) part4 = _getPath(part4);
- for (final part in [part2, part3, part4]) {
- if (part == null) continue;
-
- for (final piece in _getPath(part).split('/')) {
- if (piece == '..' && parts.length > 0 &&
- parts.last != '.' && parts.last != '..') {
- parts.removeLast();
- } else if (piece != '') {
- if (parts.length > 0 && parts.last == '.') {
- parts.removeLast();
- }
- parts.add(piece);
- }
- }
- }
-
- return Strings.join(parts, Platform.pathSeparator);
-}
-
-/// Splits [path] into its individual components.
-List<String> splitPath(path) => sanitizePath(path).split('/');
-
-/**
- * Gets the basename, the file name without any leading directory path, for
- * [file], which can either be a [String], [File], or [Directory].
- */
-// TODO(rnystrom): Copied from file_system (so that we don't have to add
-// file_system to the SDK). Should unify.
-String basename(file) {
- file = sanitizePath(file);
-
- int lastSlash = file.lastIndexOf('/', file.length);
- if (lastSlash == -1) {
- return file;
+ // TODO(nweiz): Don't use "?part" in path.dart.
+ if (part4 != null) {
+ return path.join(part1, part2, part3, part4);
+ } else if (part3 != null) {
+ return path.join(part1, part2, part3);
+ } else if (part2 != null) {
+ return path.join(part1, part2);
} else {
- return file.substring(lastSlash + 1);
+ return path.join(part1);
}
}
-/**
- * Gets the the leading directory path for [file], which can either be a
- * [String], [File], or [Directory].
- */
-// TODO(nweiz): Copied from file_system (so that we don't have to add
-// file_system to the SDK). Should unify.
+/// Gets the basename, the file name without any leading directory path, for
+/// [file], which can either be a [String], [File], or [Directory].
+String basename(file) => path.basename(_getPath(file));
+
+// TODO(nweiz): move this into path.dart.
+/// Gets the the leading directory path for [file], which can either be a
+/// [String], [File], or [Directory].
String dirname(file) {
- file = sanitizePath(file);
+ file = _sanitizePath(file);
int lastSlash = file.lastIndexOf('/', file.length);
if (lastSlash == -1) {
@@ -99,10 +66,21 @@
}
}
+// TODO(nweiz): move this into path.dart.
+/// Splits [path] into its individual components.
+List<String> splitPath(path) => _sanitizePath(path).split('/');
+
/// Returns whether or not [entry] is nested somewhere within [dir]. This just
/// performs a path comparison; it doesn't look at the actual filesystem.
-bool isBeneath(entry, dir) =>
- sanitizePath(entry).startsWith('${sanitizePath(dir)}/');
+bool isBeneath(entry, dir) {
+ var relative = relativeTo(entry, dir);
+ return !path.isAbsolute(relative) && splitPath(relative)[0] != '..';
+}
+
+// TODO(nweiz): move this into path.dart.
+/// Returns the path to [target] from [base].
+String relativeTo(target, base) =>
+ new path.Builder(root: base).relative(target);
/**
* Asynchronously determines if [path], which can be a [String] file path, a
@@ -122,7 +100,10 @@
* the result.
*/
Future<bool> fileExists(file) {
- return new File(_getPath(file)).exists();
+ var path = _getPath(file);
+ return log.ioAsync("Seeing if file $path exists.",
+ new File(path).exists(),
+ (exists) => "File $path ${exists ? 'exists' : 'does not exist'}.");
}
/**
@@ -130,7 +111,17 @@
* a [File].
*/
Future<String> readTextFile(file) {
- return new File(_getPath(file)).readAsString(Encoding.UTF_8);
+ var path = _getPath(file);
+ return log.ioAsync("Reading text file $path.",
+ new File(path).readAsString(Encoding.UTF_8),
+ (contents) {
+ // Sanity check: don't spew a huge file.
+ if (contents.length < 1024 * 1024) {
+ return "Read $path. Contents:\n$contents";
+ } else {
+ return "Read ${contents.length} characters from $path.";
+ }
+ });
}
/**
@@ -138,10 +129,21 @@
* [contents] to it. Completes when the file is written and closed.
*/
Future<File> writeTextFile(file, String contents) {
- file = new File(_getPath(file));
+ var path = _getPath(file);
+ file = new File(path);
+
+ // Sanity check: don't spew a huge file.
+ log.io("Writing ${contents.length} characters to text file $path.");
+ if (contents.length < 1024 * 1024) {
+ log.fine("Contents:\n$contents");
+ }
+
return file.open(FileMode.WRITE).chain((opened) {
return opened.writeString(contents).chain((ignore) {
- return opened.close().transform((ignore) => file);
+ return opened.close().transform((_) {
+ log.fine("Wrote text file $path.");
+ return file;
+ });
});
});
}
@@ -151,7 +153,9 @@
* [Future] that completes when the deletion is done.
*/
Future<File> deleteFile(file) {
- return new File(_getPath(file)).delete();
+ var path = _getPath(file);
+ return log.ioAsync("delete file $path",
+ new File(path).delete());
}
/// Writes [stream] to a new file at [path], which may be a [String] or a
@@ -160,12 +164,15 @@
Future<File> createFileFromStream(InputStream stream, path) {
path = _getPath(path);
+ log.io("Creating $path from stream.");
+
var completer = new Completer<File>();
var file = new File(path);
var outputStream = file.openOutputStream();
stream.pipe(outputStream);
outputStream.onClosed = () {
+ log.fine("Created $path from stream.");
completer.complete(file);
};
@@ -178,7 +185,11 @@
}
completeError(error) {
- if (!completer.isComplete) completer.completeException(error, stackTrace);
+ if (!completer.isComplete) {
+ completer.completeException(error, stackTrace);
+ } else {
+ log.fine("Got error after stream was closed: $error");
+ }
}
stream.onError = completeError;
@@ -193,7 +204,8 @@
*/
Future<Directory> createDir(dir) {
dir = _getDirectory(dir);
- return dir.create();
+ return log.ioAsync("create directory ${dir.path}",
+ dir.create());
}
/**
@@ -203,10 +215,15 @@
*/
Future<Directory> ensureDir(path) {
path = _getPath(path);
+ log.fine("Ensuring directory $path exists.");
if (path == '.') return new Future.immediate(new Directory('.'));
return dirExists(path).chain((exists) {
- if (exists) return new Future.immediate(new Directory(path));
+ if (exists) {
+ log.fine("Directory $path already exists.");
+ return new Future.immediate(new Directory(path));
+ }
+
return ensureDir(dirname(path)).chain((_) {
var completer = new Completer<Directory>();
var future = createDir(path);
@@ -214,7 +231,10 @@
if (error is! DirectoryIOException) return false;
// Error 17 means the directory already exists (or 183 on Windows).
if (error.osError.errorCode != 17 &&
- error.osError.errorCode != 183) return false;
+ error.osError.errorCode != 183) {
+ log.fine("Got 'already exists' error when creating directory.");
+ return false;
+ }
completer.complete(_getDirectory(path));
return true;
@@ -233,7 +253,8 @@
*/
Future<Directory> createTempDir([dir = '']) {
dir = _getDirectory(dir);
- return dir.createTemp();
+ return log.ioAsync("create temp directory ${dir.path}",
+ dir.createTemp());
}
/**
@@ -242,7 +263,9 @@
*/
Future<Directory> deleteDir(dir) {
dir = _getDirectory(dir);
- return dir.delete(recursive: true);
+
+ return _attemptRetryable(() => log.ioAsync("delete directory ${dir.path}",
+ dir.delete(recursive: true)));
}
/**
@@ -257,12 +280,17 @@
final contents = <String>[];
dir = _getDirectory(dir);
+ log.io("Listing directory ${dir.path}.");
var lister = dir.list(recursive: recursive);
lister.onDone = (done) {
// TODO(rnystrom): May need to sort here if it turns out onDir and onFile
// aren't guaranteed to be called in a certain order. So far, they seem to.
- if (done) completer.complete(contents);
+ if (done) {
+ log.fine("Listed directory ${dir.path}:\n"
+ "${Strings.join(contents, '\n')}");
+ completer.complete(contents);
+ }
};
// TODO(nweiz): remove this when issue 4061 is fixed.
@@ -293,7 +321,10 @@
*/
Future<bool> dirExists(dir) {
dir = _getDirectory(dir);
- return dir.exists();
+ return log.ioAsync("Seeing if directory ${dir.path} exists.",
+ dir.exists(),
+ (exists) => "Directory ${dir.path} "
+ "${exists ? 'exists' : 'does not exist'}.");
}
/**
@@ -317,29 +348,42 @@
/// the destination directory.
Future<Directory> renameDir(from, String to) {
from = _getDirectory(from);
+ log.io("Renaming directory ${from.path} to $to.");
- if (Platform.operatingSystem != 'windows') return from.rename(to);
+ return _attemptRetryable(() => from.rename(to)).transform((dir) {
+ log.fine("Renamed directory ${from.path} to $to.");
+ return dir;
+ });
+}
- // On Windows, we sometimes get failures where the directory is still in use
- // when we try to move it. To be a bit more resilient, we wait and retry a
- // few times.
+/// On Windows, we sometimes get failures where the directory is still in use
+/// when we try to do something with it. This is usually because the OS hasn't
+/// noticed yet that a process using that directory has closed. To be a bit
+/// more resilient, we wait and retry a few times.
+///
+/// Takes a [callback] which returns a future for the operation being attempted.
+/// If that future completes with an error, it will slepp and then [callback]
+/// will be invoked again to retry the operation. It will try a few times before
+/// giving up.
+Future _attemptRetryable(Future callback()) {
+ // Only do lame retry logic on Windows.
+ if (Platform.operatingSystem != 'windows') return callback();
+
var attempts = 0;
- attemptRename(_) {
+ makeAttempt(_) {
attempts++;
- return from.rename(to).transformException((e) {
+ return callback().transformException((e) {
if (attempts >= 10) {
- throw 'Could not move directory "${from.path}" to "$to". Gave up '
- 'after $attempts attempts.';
+ throw 'Could not complete operation. Gave up after $attempts attempts.';
}
// Wait a bit and try again.
- return sleep(500).chain(attemptRename);
+ log.fine("Operation failed, retrying (attempt $attempts).");
+ return sleep(500).chain(makeAttempt);
});
-
- return from;
}
- return attemptRename(null);
+ return makeAttempt(null);
}
/**
@@ -351,6 +395,8 @@
from = _getPath(from);
to = _getPath(to);
+ log.fine("Create symlink $from -> $to.");
+
var command = 'ln';
var args = ['-s', from, to];
@@ -382,15 +428,15 @@
// See if the package has a "lib" directory.
from = join(from, 'lib');
return dirExists(from).chain((exists) {
+ log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'.");
if (exists) return createSymlink(from, to);
// It's OK for the self link (i.e. the root package) to not have a lib
// directory since it may just be a leaf application that only has
// code in bin or web.
if (!isSelfLink) {
- printError(
- 'Warning: Package "$name" does not have a "lib" directory so you '
- 'will not be able to import any libraries from it.');
+ log.warning('Warning: Package "$name" does not have a "lib" directory so '
+ 'you will not be able to import any libraries from it.');
}
return new Future.immediate(to);
@@ -399,52 +445,25 @@
/// Given [entry] which may be a [String], [File], or [Directory] relative to
/// the current working directory, returns its full canonicalized path.
-String getFullPath(entry) {
- var path = _getPath(entry);
-
- // Don't do anything if it's already absolute.
- if (isAbsolute(path)) return path;
-
- // Using Path.join here instead of File().fullPathSync() because the former
- // does not require an actual file to exist at that path.
- return new Path.fromNative(currentWorkingDir).join(new Path(path))
- .toNativePath();
-}
+String getFullPath(entry) => path.absolute(_getPath(entry));
/// Returns whether or not [entry] is an absolute path.
-bool isAbsolute(entry) => _splitAbsolute(entry).first != null;
+bool isAbsolute(entry) => path.isAbsolute(_getPath(entry));
-/// Splits [entry] into two components: the absolute path prefix and the
-/// remaining path. Takes into account Windows' quirky absolute paths syntaxes.
-Pair<String, String> _splitAbsolute(entry) {
- var path = _getPath(entry);
-
- if (Platform.operatingSystem != 'windows') {
- return !path.startsWith('/') ? new Pair(null, path)
- : new Pair('/', path.substring(1));
- }
-
- // An absolute path on Windows is either UNC (two leading backslashes),
- // or a drive letter followed by a colon and a slash.
- var match = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])').firstMatch(path);
- return match == null ? new Pair(null, path)
- : new Pair(match.group(0), path.substring(match.end));
-}
-
-/// Resolves [path] relative to the location of pub.dart.
-String relativeToPub(String path) {
+/// Resolves [target] relative to the location of pub.dart.
+String relativeToPub(String target) {
var scriptPath = new File(new Options().script).fullPathSync();
// Walk up until we hit the "util(s)" directory. This lets us figure out where
// we are if this function is called from pub.dart, or one of the tests,
// which also live under "utils", or from the SDK where pub is in "util".
- var utilDir = new Path.fromNative(scriptPath).directoryPath;
- while (utilDir.filename != 'utils' && utilDir.filename != 'util') {
- if (utilDir.filename == '') throw 'Could not find path to pub.';
- utilDir = utilDir.directoryPath;
+ var utilDir = dirname(scriptPath);
+ while (basename(utilDir) != 'utils' && basename(utilDir) != 'util') {
+ if (basename(utilDir) == '') throw 'Could not find path to pub.';
+ utilDir = dirname(utilDir);
}
- return utilDir.append('pub').append(path).canonicalize().toNativePath();
+ return path.normalize(join(utilDir, 'pub', target));
}
/// A StringInputStream reading from stdin.
@@ -506,6 +525,9 @@
: _inner = inner == null ? new http.Client() : inner;
Future<http.StreamedResponse> send(http.BaseRequest request) {
+ log.io("Sending HTTP request $request.");
+ // TODO(rnystrom): Log request body when it's available and plaintext.
+
// TODO(nweiz): remove this when issue 4061 is fixed.
var stackTrace;
try {
@@ -517,6 +539,9 @@
// TODO(nweiz): Ideally the timeout would extend to reading from the
// response input stream, but until issue 3657 is fixed that's not feasible.
return timeout(_inner.send(request).chain((streamedResponse) {
+ log.fine("Got response ${streamedResponse.statusCode} "
+ "${streamedResponse.reasonPhrase}.");
+
var status = streamedResponse.statusCode;
// 401 responses should be handled by the OAuth2 client. It's very
// unlikely that they'll be returned by non-OAuth2 requests.
@@ -557,7 +582,18 @@
sink.markEndOfStream();
completer.complete(null);
};
- source.onData = () => sink.write(source.read());
+ source.onData = () {
+ // Even if the sink is closed and we aren't going to do anything with more
+ // data, we still need to drain it from source to work around issue 7218.
+ var data = source.read();
+ try {
+ if (!sink.closed) sink.write(data);
+ } on StreamException catch (e, stackTrace) {
+ // Ignore an exception to work around issue 4222.
+ log.io("Writing to an unclosed ListInputStream caused exception $e\n"
+ "$stackTrace");
+ }
+ };
// TODO(nweiz): propagate this error to the sink. See issue 3657.
source.onError = (e) { throw e; };
return completer.future;
@@ -605,6 +641,16 @@
return completer.future;
}
+/// Wrap an InputStream in a ListInputStream. This eagerly drains the [source]
+/// input stream. This is useful for spawned processes which will not exit until
+/// their output streams have been drained.
+/// TODO(rnystrom): We should use this logic anywhere we spawn a process.
+InputStream wrapInputStream(InputStream source) {
+ var sink = new ListInputStream();
+ pipeInputToInput(source, sink);
+ return sink;
+}
+
/// Spawns and runs the process located at [executable], passing in [args].
/// Returns a [Future] that will complete with the results of the process after
/// it has ended.
@@ -622,9 +668,13 @@
if (!lines.isEmpty && lines.last == "") lines.removeLast();
return lines;
}
- return new PubProcessResult(toLines(result.stdout),
+
+ var pubResult = new PubProcessResult(toLines(result.stdout),
toLines(result.stderr),
result.exitCode);
+
+ log.processResult(executable, pubResult);
+ return pubResult;
});
}
@@ -636,7 +686,30 @@
/// the inherited variables.
Future<Process> startProcess(String executable, List<String> args,
{workingDir, Map<String, String> environment}) =>
- _doProcess(Process.start, executable, args, workingDir, environment);
+ _doProcess(Process.start, executable, args, workingDir, environment)
+ .transform((process) => new _WrappedProcess(process));
+
+/// A wrapper around [Process] that buffers the stdout and stderr to avoid
+/// running into issue 7218.
+class _WrappedProcess implements Process {
+ final Process _process;
+ final InputStream stderr;
+ final InputStream stdout;
+
+ OutputStream get stdin => _process.stdin;
+
+ void set onExit(void callback(int exitCode)) {
+ _process.onExit = callback;
+ }
+
+ _WrappedProcess(Process process)
+ : _process = process,
+ stderr = wrapInputStream(process.stderr),
+ stdout = wrapInputStream(process.stdout);
+
+ bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) =>
+ _process.kill(signal);
+}
/// Calls [fn] with appropriately modified arguments. [fn] should have the same
/// signature as [Process.start], except that the returned [Future] may have a
@@ -663,34 +736,11 @@
environment.forEach((key, value) => options.environment[key] = value);
}
+ log.process(executable, args);
+
return fn(executable, args, options);
}
-/// Closes [response] while ignoring the body of [request]. Returns a Future
-/// that completes once the response is closed.
-///
-/// Due to issue 6984, it's necessary to drain the request body before closing
-/// the response.
-Future closeHttpResponse(HttpRequest request, HttpResponse response) {
- // TODO(nweiz): remove this when issue 4061 is fixed.
- var stackTrace;
- try {
- throw "";
- } catch (_, localStackTrace) {
- stackTrace = localStackTrace;
- }
-
- var completer = new Completer();
- request.inputStream.onError = (e) =>
- completer.completeException(e, stackTrace);
- request.inputStream.onData = request.inputStream.read;
- request.inputStream.onClosed = () {
- response.outputStream.close();
- completer.complete(null);
- };
- return completer.future;
-}
-
/**
* Wraps [input] to provide a timeout. If [input] completes before
* [milliseconds] have passed, then the return value completes in the same way.
@@ -731,7 +781,10 @@
tempDir = dir;
return fn(tempDir.path);
});
- future.onComplete((_) => tempDir.delete(recursive: true));
+ future.onComplete((_) {
+ log.fine('Cleaning up temp directory ${tempDir.path}.');
+ deleteDir(tempDir);
+ });
return future;
}
@@ -804,12 +857,14 @@
Future<bool> extractTarGz(InputStream stream, destination) {
destination = _getPath(destination);
+ log.fine("Extracting .tar.gz stream to $destination.");
+
if (Platform.operatingSystem == "windows") {
return _extractTarGzWindows(stream, destination);
}
var completer = new Completer<int>();
- var processFuture = Process.start("tar",
+ var processFuture = startProcess("tar",
["--extract", "--gunzip", "--directory", destination]);
processFuture.then((process) {
process.onExit = completer.complete;
@@ -822,7 +877,12 @@
return true;
});
- return completer.future.transform((exitCode) => exitCode == 0);
+ return completer.future.transform((exitCode) {
+ log.fine("Extracted .tar.gz stream to $destination. Exit code $exitCode.");
+ // TODO(rnystrom): Does anything check this result value? If not, it should
+ // throw on a bad exit code.
+ return exitCode == 0;
+ });
}
Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
@@ -862,7 +922,7 @@
}).chain((files) {
var tarFile;
for (var file in files) {
- if (new Path(file).extension == 'tar') {
+ if (path.extension(file) == '.tar') {
tarFile = file;
break;
}
@@ -879,7 +939,7 @@
'${Strings.join(result.stderr, "\n")}';
}
- // Clean up the temp directory.
+ log.fine('Clean up 7zip temp directory ${tempDir.path}.');
// TODO(rnystrom): Should also delete this if anything fails.
return deleteDir(tempDir);
}).transform((_) => true);
@@ -890,19 +950,23 @@
/// considered to be [baseDir], which defaults to the current working directory.
/// Returns an [InputStream] that will emit the contents of the archive.
InputStream createTarGz(List contents, {baseDir}) {
+ var buffer = new StringBuffer();
+ buffer.add('Creating .tag.gz stream containing:\n');
+ contents.forEach(buffer.add);
+ log.fine(buffer.toString());
+
// TODO(nweiz): Propagate errors to the returned stream (including non-zero
// exit codes). See issue 3657.
var stream = new ListInputStream();
- if (baseDir == null) baseDir = currentWorkingDir;
+ if (baseDir == null) baseDir = path.current;
baseDir = getFullPath(baseDir);
contents = contents.map((entry) {
entry = getFullPath(entry);
if (!isBeneath(entry, baseDir)) {
throw 'Entry $entry is not inside $baseDir.';
}
- return new Path.fromNative(entry).relativeTo(new Path.fromNative(baseDir))
- .toNativePath();
+ return relativeTo(entry, baseDir);
});
if (Platform.operatingSystem != "windows") {
@@ -913,7 +977,12 @@
// file and pass them in via --files-from for tar and -i@filename for 7zip.
startProcess("tar", args).then((process) {
pipeInputToInput(process.stdout, stream);
- process.stderr.pipe(stderr, close: false);
+
+ // Drain and discard 7zip's stderr. 7zip writes its normal output to
+ // stderr. We don't want to show that since it's meaningless.
+ // TODO(rnystrom): Should log this and display it if an actual error
+ // occurs.
+ consumeInputStream(process.stderr);
});
return stream;
}
@@ -939,7 +1008,11 @@
args = ["a", "unused", "-tgzip", "-so", tarFile];
return startProcess(command, args);
}).chain((process) {
- process.stderr.pipe(stderr, close: false);
+ // Drain and discard 7zip's stderr. 7zip writes its normal output to
+ // stderr. We don't want to show that since it's meaningless.
+ // TODO(rnystrom): Should log this and display it if an actual error
+ // occurs.
+ consumeInputStream(process.stderr);
return pipeInputToInput(process.stdout, stream);
});
});
@@ -955,7 +1028,7 @@
const PubHttpException(this.response);
String toString() => 'HTTP error ${response.statusCode}: '
- '${response.reasonPhrase}';
+ '${response.reasonPhrase}';
}
/**
@@ -996,7 +1069,7 @@
/// Gets the path string for [entry], normalizing backslashes to forward slashes
/// on Windows.
-String sanitizePath(entry) {
+String _sanitizePath(entry) {
entry = _getPath(entry);
if (Platform.operatingSystem != 'windows') return entry;
@@ -1010,6 +1083,24 @@
'${split.last.replaceAll('\\', '/')}';
}
+// TODO(nweiz): Add something like this to path.dart.
+/// Splits [entry] into two components: the absolute path prefix and the
+/// remaining path. Takes into account Windows' quirky absolute paths syntaxes.
+Pair<String, String> _splitAbsolute(entry) {
+ var path = _getPath(entry);
+
+ if (Platform.operatingSystem != 'windows') {
+ return !path.startsWith('/') ? new Pair(null, path)
+ : new Pair('/', path.substring(1));
+ }
+
+ // An absolute path on Windows is either UNC (two leading backslashes),
+ // or a drive letter followed by a colon and a slash.
+ var match = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])').firstMatch(path);
+ return match == null ? new Pair(null, path)
+ : new Pair(match.group(0), path.substring(match.end));
+}
+
/**
* Gets a [Directory] for [entry], which can either already be one, or be a
* [String].
diff --git a/utils/pub/log.dart b/utils/pub/log.dart
new file mode 100644
index 0000000..029f72c
--- /dev/null
+++ b/utils/pub/log.dart
@@ -0,0 +1,228 @@
+// 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.
+
+/// Message logging.
+library log;
+
+import 'dart:io';
+import 'io.dart';
+
+typedef LogFn(Entry entry);
+final Map<Level, LogFn> _loggers = new Map<Level, LogFn>();
+
+/// The list of recorded log messages. Will only be recorded if
+/// [recordTranscript()] is called.
+List<Entry> _transcript;
+
+/// An enum type for defining the different logging levels. By default, [ERROR]
+/// and [WARNING] messages are printed to sterr. [MESSAGE] messages are printed
+/// to stdout, and others are ignored.
+class Level {
+ /// An error occurred and an operation could not be completed. Usually shown
+ /// to the user on stderr.
+ static const ERROR = const Level._("ERR ");
+
+ /// Something unexpected happened, but the program was able to continue,
+ /// though possibly in a degraded fashion.
+ static const WARNING = const Level._("WARN");
+
+ /// A message intended specifically to be shown to the user.
+ static const MESSAGE = const Level._("MSG ");
+
+ /// Some interaction with the external world occurred, such as a network
+ /// operation, process spawning, or file IO.
+ static const IO = const Level._("IO ");
+
+ /// Fine-grained and verbose additional information. Can be used to provide
+ /// program state context for other logs (such as what pub was doing when an
+ /// IO operation occurred) or just more detail for an operation.
+ static const FINE = const Level._("FINE");
+
+ const Level._(this.name);
+ final String name;
+
+ String toString() => name;
+ int get hashCode => name.hashCode;
+}
+
+/// A single log entry.
+class Entry {
+ final Level level;
+ final List<String> lines;
+
+ Entry(this.level, this.lines);
+}
+
+/// Logs [message] at [Level.ERROR].
+void error(message) => write(Level.ERROR, message);
+
+/// Logs [message] at [Level.WARNING].
+void warning(message) => write(Level.WARNING, message);
+
+/// Logs [message] at [Level.MESSAGE].
+void message(message) => write(Level.MESSAGE, message);
+
+/// Logs [message] at [Level.IO].
+void io(message) => write(Level.IO, message);
+
+/// Logs [message] at [Level.FINE].
+void fine(message) => write(Level.FINE, message);
+
+/// Logs [message] at [level].
+void write(Level level, message) {
+ if (_loggers.isEmpty) showNormal();
+
+ var lines = message.toString().split(NEWLINE_PATTERN);
+ var entry = new Entry(level, lines);
+
+ var logFn = _loggers[level];
+ if (logFn != null) logFn(entry);
+
+ if (_transcript != null) _transcript.add(entry);
+}
+
+/// Logs an asynchronous IO operation. Logs [startMessage] before the operation
+/// starts, then when [operation] completes, invokes [endMessage] with the
+/// completion value and logs the result of that. Returns a future that
+/// completes after the logging is done.
+///
+/// If [endMessage] is omitted, then logs "Begin [startMessage]" before the
+/// operation and "End [startMessage]" after it.
+Future ioAsync(String startMessage, Future operation,
+ [String endMessage(value)]) {
+ if (endMessage == null) {
+ io("Begin $startMessage.");
+ } else {
+ io(startMessage);
+ }
+
+ return operation.transform((result) {
+ if (endMessage == null) {
+ io("End $startMessage.");
+ } else {
+ io(endMessage(result));
+ }
+ return result;
+ });
+}
+
+/// Logs the spawning of an [executable] process with [arguments] at [IO]
+/// level.
+void process(String executable, List<String> arguments) {
+ io("Spawning $executable ${Strings.join(arguments, ' ')}");
+}
+
+/// Logs the results of running [executable].
+void processResult(String executable, PubProcessResult result) {
+ // Log it all as one message so that it shows up as a single unit in the logs.
+ var buffer = new StringBuffer();
+ buffer.add("Finished $executable. Exit code ${result.exitCode}.");
+
+ dumpOutput(String name, List<String> output) {
+ if (output.length == 0) {
+ buffer.add("Nothing output on $name.");
+ } else {
+ buffer.add("$name:");
+ var numLines = 0;
+ for (var line in output) {
+ if (++numLines > 1000) {
+ buffer.add('[${output.length - 1000}] more lines of output '
+ 'truncated...]');
+ break;
+ }
+
+ buffer.add(line);
+ }
+ }
+ }
+
+ dumpOutput("stdout", result.stdout);
+ dumpOutput("stderr", result.stderr);
+
+ io(buffer.toString());
+}
+
+/// Enables recording of log entries.
+void recordTranscript() {
+ _transcript = <Entry>[];
+}
+
+/// If [recordTranscript()] was called, then prints the previously recorded log
+/// transcript to stderr.
+void dumpTranscript() {
+ if (_transcript == null) return;
+
+ stderr.writeString('---- Log transcript ----\n');
+ for (var entry in _transcript) {
+ _logToStderrWithLabel(entry);
+ }
+ stderr.writeString('---- End log transcript ----\n');
+}
+
+/// Sets the verbosity to "normal", which shows errors, warnings, and messages.
+void showNormal() {
+ _loggers[Level.ERROR] = _logToStderr;
+ _loggers[Level.WARNING] = _logToStderr;
+ _loggers[Level.MESSAGE] = _logToStdout;
+ _loggers[Level.IO] = null;
+ _loggers[Level.FINE] = null;
+}
+
+/// Sets the verbosity to "io", which shows errors, warnings, messages, and IO
+/// event logs.
+void showIO() {
+ _loggers[Level.ERROR] = _logToStderrWithLabel;
+ _loggers[Level.WARNING] = _logToStderrWithLabel;
+ _loggers[Level.MESSAGE] = _logToStdoutWithLabel;
+ _loggers[Level.IO] = _logToStderrWithLabel;
+ _loggers[Level.FINE] = null;
+}
+
+/// Sets the verbosity to "all", which logs ALL the things.
+void showAll() {
+ _loggers[Level.ERROR] = _logToStderrWithLabel;
+ _loggers[Level.WARNING] = _logToStderrWithLabel;
+ _loggers[Level.MESSAGE] = _logToStdoutWithLabel;
+ _loggers[Level.IO] = _logToStderrWithLabel;
+ _loggers[Level.FINE] = _logToStderrWithLabel;
+}
+
+/// Log function that prints the message to stdout.
+void _logToStdout(Entry entry) {
+ _logToStream(stdout, entry, showLabel: false);
+}
+
+/// Log function that prints the message to stdout with the level name.
+void _logToStdoutWithLabel(Entry entry) {
+ _logToStream(stdout, entry, showLabel: true);
+}
+
+/// Log function that prints the message to stderr.
+void _logToStderr(Entry entry) {
+ _logToStream(stderr, entry, showLabel: false);
+}
+
+/// Log function that prints the message to stderr with the level name.
+void _logToStderrWithLabel(Entry entry) {
+ _logToStream(stderr, entry, showLabel: true);
+}
+
+void _logToStream(OutputStream stream, Entry entry, {bool showLabel}) {
+ bool firstLine = true;
+ for (var line in entry.lines) {
+ if (showLabel) {
+ if (firstLine) {
+ stream.writeString(entry.level.name);
+ stream.writeString(': ');
+ } else {
+ stream.writeString(' | ');
+ }
+ }
+
+ stream.writeString(line);
+ stream.writeString('\n');
+
+ firstLine = false;
+ }
+}
diff --git a/utils/pub/oauth2.dart b/utils/pub/oauth2.dart
index c7a87fd..78cd4c2 100644
--- a/utils/pub/oauth2.dart
+++ b/utils/pub/oauth2.dart
@@ -10,6 +10,7 @@
// TODO(nweiz): Make this a "package:" URL, or something nicer than this.
import '../../pkg/oauth2/lib/oauth2.dart';
import 'io.dart';
+import 'log.dart' as log;
import 'system_cache.dart';
import 'utils.dart';
@@ -100,14 +101,24 @@
/// filesystem if possible. If the credentials can't be loaded for any reason,
/// the returned [Future] will complete to null.
Future<Credentials> _loadCredentials(SystemCache cache) {
- if (_credentials != null) return new Future.immediate(_credentials);
- return fileExists(_credentialsFile(cache)).chain((credentialsExist) {
- if (!credentialsExist) return new Future.immediate(null);
+ log.fine('Loading OAuth2 credentials.');
+
+ if (_credentials != null) {
+ log.fine('Using already-loaded credentials.');
+ return new Future.immediate(_credentials);
+ }
+
+ var path = _credentialsFile(cache);
+ return fileExists(path).chain((credentialsExist) {
+ if (!credentialsExist) {
+ log.fine('No credentials found at $path.');
+ return new Future.immediate(null);
+ }
return readTextFile(_credentialsFile(cache)).transform((credentialsJson) {
var credentials = new Credentials.fromJson(credentialsJson);
if (credentials.isExpired && !credentials.canRefresh) {
- printError("Pub's authorization to upload packages has expired and "
+ log.error("Pub's authorization to upload packages has expired and "
"can't be automatically refreshed.");
return null; // null means re-authorize
}
@@ -115,8 +126,7 @@
return credentials;
});
}).transformException((e) {
- printError('Warning: could not load the saved OAuth2 credentials:'
- ' $e\n'
+ log.error('Warning: could not load the saved OAuth2 credentials: $e\n'
'Obtaining new credentials...');
return null; // null means re-authorize
});
@@ -125,10 +135,11 @@
/// Save the user's OAuth2 credentials to the in-memory cache and the
/// filesystem.
Future _saveCredentials(SystemCache cache, Credentials credentials) {
+ log.fine('Saving OAuth2 credentials.');
_credentials = credentials;
- var credentialsFile = _credentialsFile(cache);
- return ensureDir(dirname(credentialsFile)).chain((_) =>
- writeTextFile(credentialsFile, credentials.toJson()));
+ var path = _credentialsFile(cache);
+ return ensureDir(dirname(path)).chain((_) =>
+ writeTextFile(path, credentials.toJson()));
}
/// The path to the file in which the user's OAuth2 credentials are stored.
@@ -161,18 +172,16 @@
server.addRequestHandler((request) => request.path == "/",
(request, response) {
chainToCompleter(new Future.immediate(null).chain((_) {
- print('Authorization received, processing...');
+ log.message('Authorization received, processing...');
var queryString = request.queryString;
if (queryString == null) queryString = '';
response.statusCode = 302;
response.headers.set('location', 'http://pub.dartlang.org/authorized');
- return Futures.wait([
- closeHttpResponse(request, response),
- grant.handleAuthorizationResponse(queryToMap(queryString))
- ]);
- }).transform((results) {
+ response.outputStream.close();
+ return grant.handleAuthorizationResponse(queryToMap(queryString));
+ }).transform((client) {
server.close();
- return results[1];
+ return client;
}), completer);
});
server.listen('127.0.0.1', 0);
@@ -180,13 +189,14 @@
var authUrl = grant.getAuthorizationUrl(
new Uri.fromString('http://localhost:${server.port}'), scopes: _scopes);
- print('Pub needs your authorization to upload packages on your behalf.\n'
- 'In a web browser, go to $authUrl\n'
- 'Then click "Allow access".\n\n'
- 'Waiting for your authorization...');
+ log.message(
+ 'Pub needs your authorization to upload packages on your behalf.\n'
+ 'In a web browser, go to $authUrl\n'
+ 'Then click "Allow access".\n\n'
+ 'Waiting for your authorization...');
return completer.future.transform((client) {
- print('Successfully authorized.\n');
+ log.message('Successfully authorized.\n');
return client;
});
}
\ No newline at end of file
diff --git a/utils/pub/path.dart b/utils/pub/path.dart
new file mode 100644
index 0000000..d803fd1
--- /dev/null
+++ b/utils/pub/path.dart
@@ -0,0 +1,535 @@
+// 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.
+
+/// A comprehensive, cross-platform path manipulation library.
+library path;
+
+import 'dart:io' as io;
+
+/// An internal builder for the current OS so we can provide a straight
+/// functional interface and not require users to create one.
+final _builder = new Builder();
+
+/// Gets the path to the current working directory.
+String get current => new io.Directory.current().path;
+
+/// Gets the path separator for the current platform. On Mac and Linux, this
+/// is `/`. On Windows, it's `\`.
+String get separator => _builder.separator;
+
+/// Converts [path] to an absolute path by resolving it relative to the current
+/// working directory. If [path] is already an absolute path, just returns it.
+///
+/// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt
+String absolute(String path) => join(current, path);
+
+/// Gets the part of [path] after the last separator.
+///
+/// path.basename('path/to/foo.dart'); // -> 'foo.dart'
+/// path.basename('path/to'); // -> 'to'
+String basename(String path) => _builder.basename(path);
+
+/// Gets the part of [path] after the last separator, and without any trailing
+/// file extension.
+///
+/// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
+String basenameWithoutExtension(String path) =>
+ _builder.basenameWithoutExtension(path);
+
+/// Gets the file extension of [path]: the portion of [basename] from the last
+/// `.` to the end (including the `.` itself).
+///
+/// path.extension('path/to/foo.dart'); // -> '.dart'
+/// path.extension('path/to/foo'); // -> ''
+/// path.extension('path.to/foo'); // -> ''
+/// path.extension('path/to/foo.dart.js'); // -> '.js'
+///
+/// If the file name starts with a `.`, then that is not considered the
+/// extension:
+///
+/// path.extension('~/.bashrc'); // -> ''
+/// path.extension('~/.notes.txt'); // -> '.txt'
+String extension(String path) => _builder.extension(path);
+
+/// Returns `true` if [path] is an absolute path and `false` if it is a
+/// relative path. On POSIX systems, absolute paths start with a `/` (forward
+/// slash). On Windows, an absolute path starts with `\\`, or a drive letter
+/// followed by `:/` or `:\`.
+bool isAbsolute(String path) => _builder.isAbsolute(path);
+
+/// Returns `true` if [path] is a relative path and `false` if it is absolute.
+/// On POSIX systems, absolute paths start with a `/` (forward slash). On
+/// Windows, an absolute path starts with `\\`, or a drive letter followed by
+/// `:/` or `:\`.
+bool isRelative(String path) => _builder.isRelative(path);
+
+/// Joins the given path parts into a single path using the current platform's
+/// [separator]. Example:
+///
+/// path.join('path', 'to', 'foo'); // -> 'path/to/foo'
+///
+/// If any part ends in a path separator, then a redundant separator will not
+/// be added:
+///
+/// path.join('path/', 'to', 'foo'); // -> 'path/to/foo
+///
+/// If a part is an absolute path, then anything before that will be ignored:
+///
+/// path.join('path', '/to', 'foo'); // -> '/to/foo'
+String join(String part1, [String part2, String part3, String part4,
+ String part5, String part6, String part7, String part8]) {
+ if (!?part2) return _builder.join(part1);
+ if (!?part3) return _builder.join(part1, part2);
+ if (!?part4) return _builder.join(part1, part2, part3);
+ if (!?part5) return _builder.join(part1, part2, part3, part4);
+ if (!?part6) return _builder.join(part1, part2, part3, part4, part5);
+ if (!?part7) return _builder.join(part1, part2, part3, part4, part5, part6);
+ if (!?part8) return _builder.join(part1, part2, part3, part4, part5, part6,
+ part7);
+ return _builder.join(part1, part2, part3, part4, part5, part6, part7, part8);
+}
+
+/// Normalizes [path], simplifying it by handling `..`, and `.`, and
+/// removing redundant path separators whenever possible.
+///
+/// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
+String normalize(String path) => _builder.normalize(path);
+
+/// Attempts to convert [path] to an equivalent relative path from the current
+/// directory.
+///
+/// // Given current directory is /root/path:
+/// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
+/// path.relative('/root/other.dart'); // -> '../other.dart'
+///
+/// Since there is no relative path from one drive letter to another on Windows,
+/// this will return an absolute path in that case.
+///
+/// // Given current directory is C:\home:
+/// path.relative(r'D:\other'); // -> 'D:\other'
+String relative(String path) => _builder.relative(path);
+
+/// Removes a trailing extension from the last part of [path].
+///
+/// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
+String withoutExtension(String path) => _builder.withoutExtension(path);
+
+/// An instantiable class for manipulating paths. Unlike the top-level
+/// functions, this lets you explicitly select what platform the paths will use.
+class Builder {
+ /// Creates a new path builder for the given style and root directory.
+ ///
+ /// If [style] is omitted, it uses the host operating system's path style. If
+ /// [root] is omitted, it defaults to the current working directory. If [root]
+ /// is relative, it is considered relative to the current working directory.
+ factory Builder({Style style, String root}) {
+ if (style == null) {
+ if (io.Platform.operatingSystem == 'windows') {
+ style = Style.windows;
+ } else {
+ style = Style.posix;
+ }
+ }
+
+ if (root == null) root = current;
+
+ return new Builder._(style, root);
+ }
+
+ Builder._(this.style, this.root);
+
+ /// The style of path that this builder works with.
+ final Style style;
+
+ /// The root directory that relative paths will be relative to.
+ final String root;
+
+ /// Gets the path separator for the builder's [style]. On Mac and Linux,
+ /// this is `/`. On Windows, it's `\`.
+ String get separator => style.separator;
+
+ /// Gets the part of [path] after the last separator on the builder's
+ /// platform.
+ ///
+ /// builder.basename('path/to/foo.dart'); // -> 'foo.dart'
+ /// builder.basename('path/to'); // -> 'to'
+ String basename(String path) => _parse(path).basename;
+
+ /// Gets the part of [path] after the last separator on the builder's
+ /// platform, and without any trailing file extension.
+ ///
+ /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
+ String basenameWithoutExtension(String path) =>
+ _parse(path).basenameWithoutExtension;
+
+ /// Gets the file extension of [path]: the portion of [basename] from the last
+ /// `.` to the end (including the `.` itself).
+ ///
+ /// builder.extension('path/to/foo.dart'); // -> '.dart'
+ /// builder.extension('path/to/foo'); // -> ''
+ /// builder.extension('path.to/foo'); // -> ''
+ /// builder.extension('path/to/foo.dart.js'); // -> '.js'
+ ///
+ /// If the file name starts with a `.`, then it is not considered an
+ /// extension:
+ ///
+ /// builder.extension('~/.bashrc'); // -> ''
+ /// builder.extension('~/.notes.txt'); // -> '.txt'
+ String extension(String path) => _parse(path).extension;
+
+ /// Returns `true` if [path] is an absolute path and `false` if it is a
+ /// relative path. On POSIX systems, absolute paths start with a `/` (forward
+ /// slash). On Windows, an absolute path starts with `\\`, or a drive letter
+ /// followed by `:/` or `:\`.
+ bool isAbsolute(String path) => _parse(path).isAbsolute;
+
+ /// Returns `true` if [path] is a relative path and `false` if it is absolute.
+ /// On POSIX systems, absolute paths start with a `/` (forward slash). On
+ /// Windows, an absolute path starts with `\\`, or a drive letter followed by
+ /// `:/` or `:\`.
+ bool isRelative(String path) => !isAbsolute(path);
+
+ /// Joins the given path parts into a single path. Example:
+ ///
+ /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo'
+ ///
+ /// If any part ends in a path separator, then a redundant separator will not
+ /// be added:
+ ///
+ /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo
+ ///
+ /// If a part is an absolute path, then anything before that will be ignored:
+ ///
+ /// builder.join('path', '/to', 'foo'); // -> '/to/foo'
+ ///
+ String join(String part1, [String part2, String part3, String part4,
+ String part5, String part6, String part7, String part8]) {
+ var buffer = new StringBuffer();
+ var needsSeparator = false;
+
+ addPart(condition, part) {
+ if (!condition) return;
+
+ if (this.isAbsolute(part)) {
+ // An absolute path discards everything before it.
+ buffer.clear();
+ buffer.add(part);
+ } else {
+ if (part.length > 0 && style.separatorPattern.hasMatch(part[0])) {
+ // The part starts with a separator, so we don't need to add one.
+ } else if (needsSeparator) {
+ buffer.add(separator);
+ }
+
+ buffer.add(part);
+ }
+
+ // Unless this part ends with a separator, we'll need to add one before
+ // the next part.
+ needsSeparator = part.length > 0 &&
+ !style.separatorPattern.hasMatch(part[part.length - 1]);
+ }
+
+ addPart(true, part1);
+ addPart(?part2, part2);
+ addPart(?part3, part3);
+ addPart(?part4, part4);
+ addPart(?part5, part5);
+ addPart(?part6, part6);
+ addPart(?part7, part7);
+ addPart(?part8, part8);
+
+ return buffer.toString();
+ }
+
+ /// Normalizes [path], simplifying it by handling `..`, and `.`, and
+ /// removing redundant path separators whenever possible.
+ ///
+ /// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
+ String normalize(String path) {
+ if (path == '') return path;
+
+ var parsed = _parse(path);
+ parsed.normalize();
+ return parsed.toString();
+ }
+
+ /// Creates a new path by appending the given path parts to the [root].
+ /// Equivalent to [join()] with [root] as the first argument. Example:
+ ///
+ /// var builder = new Builder(root: 'root');
+ /// builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo'
+ String resolve(String part1, [String part2, String part3, String part4,
+ String part5, String part6, String part7]) {
+ if (!?part2) return join(root, part1);
+ if (!?part3) return join(root, part1, part2);
+ if (!?part4) return join(root, part1, part2, part3);
+ if (!?part5) return join(root, part1, part2, part3, part4);
+ if (!?part6) return join(root, part1, part2, part3, part4, part5);
+ if (!?part7) return join(root, part1, part2, part3, part4, part5, part6);
+ return join(root, part1, part2, part3, part4, part5, part6, part7);
+ }
+
+ /// Attempts to convert [path] to an equivalent relative path relative to
+ /// [root].
+ ///
+ /// var builder = new Builder(root: '/root/path');
+ /// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
+ /// builder.relative('/root/other.dart'); // -> '../other.dart'
+ ///
+ /// Since there is no relative path from one drive letter to another on
+ /// Windows, this will return an absolute path in that case.
+ ///
+ /// var builder = new Builder(root: r'C:\home');
+ /// builder.relative(r'D:\other'); // -> 'D:\other'
+ String relative(String path) {
+ if (path == '') return '.';
+
+ // If the base path is relative, resolve it relative to the current
+ // directory.
+ var base = root;
+ if (this.isRelative(base)) base = absolute(base);
+
+ // If the given path is relative, resolve it relative to the base.
+ if (this.isRelative(path)) return this.normalize(path);
+
+ var baseParsed = _parse(base)..normalize();
+ var pathParsed = _parse(path)..normalize();
+
+ // If the root prefixes don't match (for example, different drive letters
+ // on Windows), then there is no relative path, so just return the absolute
+ // one.
+ // TODO(rnystrom): Drive letters are case-insentive on Windows. Should
+ // handle "C:\" and "c:\" being the same root.
+ if (baseParsed.root != pathParsed.root) return pathParsed.toString();
+
+ // Strip off their common prefix.
+ while (baseParsed.parts.length > 0 && pathParsed.parts.length > 0 &&
+ baseParsed.parts[0] == pathParsed.parts[0]) {
+ baseParsed.parts.removeAt(0);
+ baseParsed.separators.removeAt(0);
+ pathParsed.parts.removeAt(0);
+ pathParsed.separators.removeAt(0);
+ }
+
+ // If there are any directories left in the root path, we need to walk up
+ // out of them.
+ pathParsed.parts.insertRange(0, baseParsed.parts.length, '..');
+ pathParsed.separators.insertRange(0, baseParsed.parts.length,
+ style.separator);
+
+ // Corner case: the paths completely collapsed.
+ if (pathParsed.parts.length == 0) return '.';
+
+ // Make it relative.
+ pathParsed.root = '';
+ pathParsed.removeTrailingSeparator();
+
+ return pathParsed.toString();
+ }
+
+ /// Removes a trailing extension from the last part of [path].
+ ///
+ /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
+ String withoutExtension(String path) {
+ var parsed = _parse(path);
+ if (parsed.hasTrailingSeparator) return parsed.toString();
+
+ if (!parsed.parts.isEmpty) {
+ parsed.parts[parsed.parts.length - 1] = parsed.basenameWithoutExtension;
+ }
+
+ return parsed.toString();
+ }
+
+ _ParsedPath _parse(String path) {
+ var before = path;
+
+ // Remove the root prefix, if any.
+ var root = style.getRoot(path);
+ if (root != null) path = path.substring(root.length);
+
+ // Split the parts on path separators.
+ var parts = [];
+ var separators = [];
+ var start = 0;
+ for (var match in style.separatorPattern.allMatches(path)) {
+ parts.add(path.substring(start, match.start));
+ separators.add(match[0]);
+ start = match.end;
+ }
+
+ // Add the final part, if any.
+ if (start < path.length) {
+ parts.add(path.substring(start));
+ separators.add('');
+ }
+
+ return new _ParsedPath(style, root, parts, separators);
+ }
+}
+
+/// An enum type describing a "flavor" of path.
+class Style {
+ /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths
+ /// start with "/". Used by UNIX, Linux, Mac OS X, and others.
+ static final posix = new Style._('posix', '/', '/', '/');
+
+ /// Windows paths use "\" (backslash) as separators. Absolute paths start with
+ /// a drive letter followed by a colon (example, "C:") or two backslashes
+ /// ("\\") for UNC paths.
+ // TODO(rnystrom): The UNC root prefix should include the drive name too, not
+ // just the "\\".
+ static final windows = new Style._('windows', '\\', r'[/\\]',
+ r'\\\\|[a-zA-Z]:[/\\]');
+
+ Style._(this.name, this.separator, String separatorPattern,
+ String rootPattern)
+ : separatorPattern = new RegExp(separatorPattern),
+ _rootPattern = new RegExp('^$rootPattern');
+
+ /// The name of this path style. Will be "posix" or "windows".
+ final String name;
+
+ /// The path separator for this style. On POSIX, this is `/`. On Windows,
+ /// it's `\`.
+ final String separator;
+
+ /// The [Pattern] that can be used to match a separator for a path in this
+ /// style. Windows allows both "/" and "\" as path separators even though
+ /// "\" is the canonical one.
+ final Pattern separatorPattern;
+
+ /// The [Pattern] that can be used to match the root prefix of an absolute
+ /// path in this style.
+ final Pattern _rootPattern;
+
+ /// Gets the root prefix of [path] if path is absolute. If [path] is relative,
+ /// returns `null`.
+ String getRoot(String path) {
+ var match = _rootPattern.firstMatch(path);
+ if (match == null) return null;
+ return match[0];
+ }
+
+ String toString() => name;
+}
+
+// TODO(rnystrom): Make this public?
+class _ParsedPath {
+ /// The [Style] that was used to parse this path.
+ Style style;
+
+ /// The absolute root portion of the path, or `null` if the path is relative.
+ /// On POSIX systems, this will be `null` or "/". On Windows, it can be
+ /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
+ /// letters.
+ String root;
+
+ /// The path-separated parts of the path. All but the last will be
+ /// directories.
+ List<String> parts;
+
+ /// The path separators following each part. The last one will be an empty
+ /// string unless the path ends with a trailing separator.
+ List<String> separators;
+
+ /// The file extension of the last part, or "" if it doesn't have one.
+ String get extension => _splitExtension()[1];
+
+ /// `true` if the path ends with a trailing separator.
+ bool get hasTrailingSeparator {
+ if (separators.length == 0) return false;
+ return separators[separators.length - 1] != '';
+ }
+
+ /// `true` if this is an absolute path.
+ bool get isAbsolute => root != null;
+
+ _ParsedPath(this.style, this.root, this.parts, this.separators);
+
+ String get basename {
+ if (parts.length == 0) return extension;
+ if (hasTrailingSeparator) return '';
+ return parts.last;
+ }
+
+ String get basenameWithoutExtension => _splitExtension()[0];
+
+ void removeTrailingSeparator() {
+ if (separators.length > 0) {
+ separators[separators.length - 1] = '';
+ }
+ }
+
+ void normalize() {
+ // Handle '.', '..', and empty parts.
+ var leadingDoubles = 0;
+ var newParts = [];
+ for (var part in parts) {
+ if (part == '.' || part == '') {
+ // Do nothing. Ignore it.
+ } else if (part == '..') {
+ // Pop the last part off.
+ if (newParts.length > 0) {
+ newParts.removeLast();
+ } else {
+ // Backed out past the beginning, so preserve the "..".
+ leadingDoubles++;
+ }
+ } else {
+ newParts.add(part);
+ }
+ }
+
+ // A relative path can back out from the start directory.
+ if (!isAbsolute) {
+ newParts.insertRange(0, leadingDoubles, '..');
+ }
+
+ // If we collapsed down to nothing, do ".".
+ if (newParts.length == 0 && !isAbsolute) {
+ newParts.add('.');
+ }
+
+ // Canonicalize separators.
+ var newSeparators = [];
+ newSeparators.insertRange(0, newParts.length, style.separator);
+
+ parts = newParts;
+ separators = newSeparators;
+
+ removeTrailingSeparator();
+ }
+
+ String toString() {
+ var builder = new StringBuffer();
+ if (root != null) builder.add(root);
+ for (var i = 0; i < parts.length; i++) {
+ builder.add(parts[i]);
+ builder.add(separators[i]);
+ }
+
+ return builder.toString();
+ }
+
+ /// Splits the last part of the path into a two-element list. The first is
+ /// the name of the file without any extension. The second is the extension
+ /// or "" if it has none.
+ List<String> _splitExtension() {
+ if (parts.isEmpty) return ['', ''];
+ if (hasTrailingSeparator) return ['', ''];
+
+ var file = parts.last;
+ if (file == '..') return ['..', ''];
+
+ var lastDot = file.lastIndexOf('.');
+
+ // If there is no dot, or it's the first character, like '.bashrc', it
+ // doesn't count.
+ if (lastDot <= 0) return [file, ''];
+
+ return [file.substring(0, lastDot), file.substring(lastDot)];
+ }
+}
diff --git a/utils/pub/pub.dart b/utils/pub/pub.dart
index 1daf20d..835a09d 100644
--- a/utils/pub/pub.dart
+++ b/utils/pub/pub.dart
@@ -18,7 +18,9 @@
import 'command_version.dart';
import 'entrypoint.dart';
import 'exit_codes.dart' as exit_codes;
+import 'log.dart' as log;
import 'package.dart';
+import 'path.dart' as path;
import 'pubspec.dart';
import 'source.dart';
import 'source_registry.dart';
@@ -54,10 +56,21 @@
ArgParser get pubArgParser {
var parser = new ArgParser();
parser.addFlag('help', abbr: 'h', negatable: false,
- help: 'Prints this usage information');
+ help: 'Print this usage information.');
parser.addFlag('version', negatable: false,
- help: 'Prints the version of Pub');
- parser.addFlag('trace', help: 'Prints a stack trace when an error occurs');
+ help: 'Print pub version.');
+ parser.addFlag('trace',
+ help: 'Print debugging information when an error occurs.');
+ parser.addOption('verbosity',
+ help: 'Control output verbosity.',
+ allowed: ['normal', 'io', 'all'],
+ allowedHelp: {
+ 'normal': 'Errors, warnings, and user messages are shown.',
+ 'io': 'IO operations are also shown.',
+ 'all': 'All output including internal tracing messages are shown.'
+ });
+ parser.addFlag('verbose', abbr: 'v', negatable: false,
+ help: 'Shortcut for "--verbosity=all"');
return parser;
}
@@ -66,8 +79,8 @@
try {
globalOptions = pubArgParser.parse(new Options().arguments);
} on FormatException catch (e) {
- printError(e.message);
- printError('Run "pub help" to see available options.');
+ log.error(e.message);
+ log.error('Run "pub help" to see available options.');
exit(exit_codes.USAGE);
}
@@ -81,6 +94,24 @@
return;
}
+ if (globalOptions['trace']) {
+ log.recordTranscript();
+ }
+
+ switch (globalOptions['verbosity']) {
+ case 'normal': log.showNormal(); break;
+ case 'io': log.showIO(); break;
+ case 'all': log.showAll(); break;
+ default:
+ // No specific verbosity given, so check for the shortcut.
+ if (globalOptions['verbose']) {
+ log.showAll();
+ } else {
+ log.showNormal();
+ }
+ break;
+ }
+
// TODO(nweiz): Have a fallback for this this out automatically once 1145 is
// fixed.
var sdkDir = Platform.environment['DART_SDK'];
@@ -99,8 +130,8 @@
// Select the command.
var command = pubCommands[globalOptions.rest[0]];
if (command == null) {
- printError('Could not find a command named "${globalOptions.rest[0]}".');
- printError('Run "pub help" to see available commands.');
+ log.error('Could not find a command named "${globalOptions.rest[0]}".');
+ log.error('Run "pub help" to see available commands.');
exit(exit_codes.USAGE);
return;
}
@@ -112,16 +143,16 @@
/** Displays usage information for the app. */
void printUsage([String description = 'Pub is a package manager for Dart.']) {
- print(description);
- print('');
- print('Usage: pub command [arguments]');
- print('');
- print('Global options:');
- print(pubArgParser.getUsage());
- print('');
+ // Build up a buffer so it shows up as a single log entry.
+ var buffer = new StringBuffer();
+ buffer.add(description);
+ buffer.add('\n\n');
+ buffer.add('Usage: pub command [arguments]\n\n');
+ buffer.add('Global options:\n');
+ buffer.add('${pubArgParser.getUsage()}\n\n');
// Show the commands sorted.
- print('Available commands:');
+ buffer.add('Available commands:\n');
// TODO(rnystrom): A sorted map would be nice.
int length = 0;
@@ -136,15 +167,17 @@
names.sort((a, b) => a.compareTo(b));
for (var name in names) {
- print(' ${padRight(name, length)} ${pubCommands[name].description}');
+ buffer.add(' ${padRight(name, length)} '
+ '${pubCommands[name].description}\n');
}
- print('');
- print('Use "pub help [command]" for more information about a command.');
+ buffer.add('\n');
+ buffer.add('Use "pub help [command]" for more information about a command.');
+ log.message(buffer.toString());
}
void printVersion() {
- print('Pub $pubVersion');
+ log.message('Pub $pubVersion');
}
abstract class PubCommand {
@@ -187,8 +220,8 @@
try {
commandOptions = commandParser.parse(commandArgs);
} on FormatException catch (e) {
- printError(e.message);
- printError('Use "pub help" for more information.');
+ log.error(e.message);
+ log.error('Use "pub help" for more information.');
exit(exit_codes.USAGE);
}
@@ -203,9 +236,10 @@
message = message.substring("Exception: ".length);
}
- printError(message);
+ log.error(message);
if (globalOptions['trace'] && trace != null) {
- printError(trace);
+ log.error(trace);
+ log.dumpTranscript();
}
exit(_chooseExitCode(error));
@@ -216,7 +250,7 @@
// TODO(rnystrom): Will eventually need better logic to walk up
// subdirectories until we hit one that looks package-like. For now, just
// assume the cwd is it.
- future = Entrypoint.load(currentWorkingDir, cache);
+ future = Entrypoint.load(path.current, cache);
}
future = future.chain((entrypoint) {
@@ -237,10 +271,10 @@
future.handleException((e) {
if (e is PubspecNotFoundException && e.name == null) {
e = 'Could not find a file named "pubspec.yaml" in the directory '
- '$currentWorkingDir.';
+ '${path.current}.';
} else if (e is PubspecHasNoNameException && e.name == null) {
e = 'pubspec.yaml is missing the required "name" field (e.g. "name: '
- '${basename(currentWorkingDir)}").';
+ '${basename(path.current)}").';
}
handleError(e, future.stackTrace);
@@ -261,15 +295,17 @@
/** Displays usage information for this command. */
void printUsage([String description]) {
if (description == null) description = this.description;
- print(description);
- print('');
- print('Usage: $usage');
+
+ var buffer = new StringBuffer();
+ buffer.add('$description\n\nUsage: $usage');
var commandUsage = commandParser.getUsage();
if (!commandUsage.isEmpty) {
- print('');
- print(commandUsage);
+ buffer.add('\n');
+ buffer.add(commandUsage);
}
+
+ log.message(buffer.toString());
}
/// Returns the appropriate exit code for [exception], falling back on 1 if no
diff --git a/utils/pub/system_cache.dart b/utils/pub/system_cache.dart
index 932cbbb..ce913e0 100644
--- a/utils/pub/system_cache.dart
+++ b/utils/pub/system_cache.dart
@@ -10,6 +10,7 @@
import 'hosted_source.dart';
import 'io.dart';
import 'io.dart' as io show createTempDir;
+import 'log.dart' as log;
import 'package.dart';
import 'sdk_source.dart';
import 'source.dart';
@@ -102,6 +103,7 @@
/// Delete's the system cache's internal temp directory.
Future deleteTempDir() {
+ log.fine('Clean up system cache temp directory $tempDir.');
return dirExists(tempDir).chain((exists) {
if (!exists) return new Future.immediate(null);
return deleteDir(tempDir);
diff --git a/utils/pub/validator.dart b/utils/pub/validator.dart
index 584a214..3e491a9 100644
--- a/utils/pub/validator.dart
+++ b/utils/pub/validator.dart
@@ -5,9 +5,12 @@
library validator;
import 'entrypoint.dart';
+import 'log.dart' as log;
import 'io.dart';
import 'system_cache.dart';
import 'utils.dart';
+import 'validator/lib.dart';
+import 'validator/license.dart';
import 'validator/name.dart';
import 'validator/pubspec_field.dart';
@@ -38,6 +41,8 @@
static Future<Pair<List<String>, List<String>>> runAll(
Entrypoint entrypoint) {
var validators = [
+ new LibValidator(entrypoint),
+ new LicenseValidator(entrypoint),
new NameValidator(entrypoint),
new PubspecFieldValidator(entrypoint)
];
@@ -52,19 +57,19 @@
var warnings = flatten(validators.map((validator) => validator.warnings));
if (!errors.isEmpty) {
- printError("== Errors:");
+ log.error("== Errors:");
for (var error in errors) {
- printError("* $error");
+ log.error("* $error");
}
- printError("");
+ log.error("");
}
if (!warnings.isEmpty) {
- printError("== Warnings:");
+ log.warning("== Warnings:");
for (var warning in warnings) {
- printError("* $warning");
+ log.warning("* $warning");
}
- printError("");
+ log.warning("");
}
return new Pair<List<String>, List<String>>(errors, warnings);
diff --git a/utils/pub/validator/lib.dart b/utils/pub/validator/lib.dart
new file mode 100644
index 0000000..64f44c6
--- /dev/null
+++ b/utils/pub/validator/lib.dart
@@ -0,0 +1,43 @@
+// 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 lib_validator;
+
+import 'dart:io';
+
+import '../entrypoint.dart';
+import '../io.dart';
+import '../system_cache.dart';
+import '../utils.dart';
+import '../validator.dart';
+
+// TODO(nweiz): When issue 7196 is fixed, complain about non-Dart files in lib.
+/// A validator that checks that libraries in "lib/" (and not "lib/src/") exist
+/// and are well-formed.
+class LibValidator extends Validator {
+ LibValidator(Entrypoint entrypoint)
+ : super(entrypoint);
+
+ Future validate() {
+ var libDir = join(entrypoint.root.dir, "lib");
+ return dirExists(libDir).chain((libDirExists) {
+ if (!libDirExists) {
+ errors.add('Your package must have a "lib/" directory so users have '
+ 'something to import.');
+ return new Future.immediate(null);
+ }
+
+ return listDir(libDir).transform((files) {
+ files = files.map((file) => relativeTo(file, libDir));
+ if (files.isEmpty) {
+ errors.add('The "lib/" directory may not be empty so users have '
+ 'something to import');
+ } else if (files.length == 1 && files.first == "src") {
+ errors.add('The "lib/" directory must contain something other than '
+ '"src/" so users have something to import');
+ }
+ });
+ });
+ }
+}
diff --git a/utils/pub/validator/license.dart b/utils/pub/validator/license.dart
new file mode 100644
index 0000000..46a1c40
--- /dev/null
+++ b/utils/pub/validator/license.dart
@@ -0,0 +1,28 @@
+// 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 pubspec_field_validator;
+
+import '../entrypoint.dart';
+import '../io.dart';
+import '../system_cache.dart';
+import '../validator.dart';
+
+/// A validator that checks that a LICENSE-like file exists.
+class LicenseValidator extends Validator {
+ LicenseValidator(Entrypoint entrypoint)
+ : super(entrypoint);
+
+ Future validate() {
+ return listDir(entrypoint.root.dir).transform((files) {
+ var licenseLike = new RegExp(
+ r"^([a-zA-Z0-9]+[-_])?(LICENSE|COPYING)(\..*)?$");
+ if (files.map(basename).some(licenseLike.hasMatch)) return;
+
+ errors.add("Your package must have a COPYING or LICENSE file containing "
+ "an open-source license. For more details, see "
+ "http://pub.dartlang.org/doc/pub-lish.html.");
+ });
+ }
+}
diff --git a/utils/pub/validator/name.dart b/utils/pub/validator/name.dart
index 93c08ae..ecc34e4 100644
--- a/utils/pub/validator/name.dart
+++ b/utils/pub/validator/name.dart
@@ -8,6 +8,7 @@
import '../entrypoint.dart';
import '../io.dart';
+import '../path.dart' as path;
import '../validator.dart';
/// Dart reserved words, from the Dart spec.
@@ -31,11 +32,10 @@
return listDir(libDir, recursive: true);
}).transform((files) {
for (var file in files) {
- // TODO(nweiz): Since `file` is absolute, this will break if the package
- // itself is in a directory named "src" (issue 7215).
+ file = relativeTo(file, libDir);
if (splitPath(file).contains("src")) continue;
- if (new Path(file).extension != 'dart') continue;
- var libName = new Path(basename(file)).filenameWithoutExtension;
+ if (path.extension(file) != '.dart') continue;
+ var libName = path.basenameWithoutExtension(file);
_checkName(libName, 'The name of "$file", "$libName",');
}
});
diff --git a/utils/pub/version_solver.dart b/utils/pub/version_solver.dart
index 31baa9c..1a84aa2 100644
--- a/utils/pub/version_solver.dart
+++ b/utils/pub/version_solver.dart
@@ -40,6 +40,7 @@
import 'dart:json';
import 'dart:math';
import 'lock_file.dart';
+import 'log.dart' as log;
import 'package.dart';
import 'pubspec.dart';
import 'root_source.dart';
@@ -59,7 +60,7 @@
*/
Future<List<PackageId>> resolveVersions(SourceRegistry sources, Package root,
LockFile lockFile) {
- print('Resolving dependencies...');
+ log.message('Resolving dependencies...');
return new VersionSolver(sources, root, lockFile).solve();
}
@@ -267,6 +268,8 @@
}
Future process(VersionSolver solver) {
+ log.fine("Changing $package to version $version.");
+
var dependency = solver.getDependency(package);
var oldVersion = dependency.version;
solver.setVersion(package, version);
@@ -418,6 +421,8 @@
AddConstraint(this.depender, this.ref);
Future process(VersionSolver solver) {
+ log.fine("Adding $depender's constraint $ref.");
+
var dependency = solver.getDependency(ref.name);
var oldDependency = dependency.clone();
dependency.placeConstraint(depender, ref);
@@ -449,6 +454,8 @@
RemoveConstraint(this.depender, this.dependent);
Future process(VersionSolver solver) {
+ log.fine("Removing $depender's constraint ($_removed) on $dependent.");
+
var dependency = solver.getDependency(dependent);
var oldDependency = dependency.clone();
_removed = dependency.removeConstraint(depender);
@@ -468,6 +475,8 @@
UnlockPackage(this.package);
Future process(VersionSolver solver) {
+ log.fine("Unlocking ${package.name}.");
+
solver.lockFile.packages.remove(package.name);
return solver.getBestVersion(package).transform((best) {
if (best == null) return null;
diff --git a/utils/tests/pub/oauth2_test.dart b/utils/tests/pub/oauth2_test.dart
index e739a96..32e4a75 100644
--- a/utils/tests/pub/oauth2_test.dart
+++ b/utils/tests/pub/oauth2_test.dart
@@ -15,7 +15,7 @@
import '../../pub/utils.dart';
main() {
- setUp(() => dir(appPath, [libPubspec("test_pkg", "1.0.0")]).scheduleCreate());
+ setUp(() => normalPackage.scheduleCreate());
test('with no credentials.json, authenticates and saves credentials.json',
() {
@@ -27,7 +27,7 @@
expect(request.headers.value('authorization'),
equals('Bearer access token'));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
pub.kill();
@@ -46,7 +46,7 @@
expect(request.headers.value('authorization'),
equals('Bearer access token'));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
pub.kill();
@@ -83,7 +83,7 @@
expect(request.headers.value('authorization'),
equals('Bearer new access token'));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
pub.shouldExit();
@@ -111,7 +111,7 @@
expect(request.headers.value('authorization'),
equals('Bearer new access token'));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
pub.kill();
@@ -135,7 +135,7 @@
expect(request.headers.value('authorization'),
equals('Bearer new access token'));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
pub.kill();
diff --git a/utils/tests/pub/path/path_posix_test.dart b/utils/tests/pub/path/path_posix_test.dart
new file mode 100644
index 0000000..a34c32d
--- /dev/null
+++ b/utils/tests/pub/path/path_posix_test.dart
@@ -0,0 +1,284 @@
+// 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 path_test;
+
+import 'dart:io' as io;
+
+import '../../../../pkg/unittest/lib/unittest.dart';
+import '../../../pub/path.dart' as path;
+
+main() {
+ var builder = new path.Builder(style: path.Style.posix, root: '/root/path');
+
+ if (new path.Builder().style == path.Style.posix) {
+ group('absolute', () {
+ expect(path.absolute('a/b.txt'), path.join(path.current, 'a/b.txt'));
+ expect(path.absolute('/a/b.txt'), '/a/b.txt');
+ });
+ }
+
+ test('separator', () {
+ expect(builder.separator, '/');
+ });
+
+ test('extension', () {
+ expect(builder.extension(''), '');
+ expect(builder.extension('foo.dart'), '.dart');
+ expect(builder.extension('foo.dart.js'), '.js');
+ expect(builder.extension('a.b/c'), '');
+ expect(builder.extension('a.b/c.d'), '.d');
+ expect(builder.extension('~/.bashrc'), '');
+ expect(builder.extension(r'a.b\c'), r'.b\c');
+ });
+
+ test('basename', () {
+ expect(builder.basename(''), '');
+ expect(builder.basename('a'), 'a');
+ expect(builder.basename('a/b'), 'b');
+ expect(builder.basename('a/b/c'), 'c');
+ expect(builder.basename('a/b.c'), 'b.c');
+ expect(builder.basename('a/'), '');
+ expect(builder.basename('a/.'), '.');
+ expect(builder.basename(r'a/b\c'), r'b\c');
+ });
+
+ test('basenameWithoutExtension', () {
+ expect(builder.basenameWithoutExtension(''), '');
+ expect(builder.basenameWithoutExtension('a'), 'a');
+ expect(builder.basenameWithoutExtension('a/b'), 'b');
+ expect(builder.basenameWithoutExtension('a/b/c'), 'c');
+ expect(builder.basenameWithoutExtension('a/b.c'), 'b');
+ expect(builder.basenameWithoutExtension('a/'), '');
+ expect(builder.basenameWithoutExtension('a/.'), '.');
+ expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c');
+ expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc');
+ expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d');
+ });
+
+ test('isAbsolute', () {
+ expect(builder.isAbsolute(''), false);
+ expect(builder.isAbsolute('a'), false);
+ expect(builder.isAbsolute('a/b'), false);
+ expect(builder.isAbsolute('/a'), true);
+ expect(builder.isAbsolute('/a/b'), true);
+ expect(builder.isAbsolute('~'), false);
+ expect(builder.isAbsolute('.'), false);
+ expect(builder.isAbsolute('../a'), false);
+ expect(builder.isAbsolute('C:/a'), false);
+ expect(builder.isAbsolute(r'C:\a'), false);
+ expect(builder.isAbsolute(r'\\a'), false);
+ });
+
+ test('isRelative', () {
+ expect(builder.isRelative(''), true);
+ expect(builder.isRelative('a'), true);
+ expect(builder.isRelative('a/b'), true);
+ expect(builder.isRelative('/a'), false);
+ expect(builder.isRelative('/a/b'), false);
+ expect(builder.isRelative('~'), true);
+ expect(builder.isRelative('.'), true);
+ expect(builder.isRelative('../a'), true);
+ expect(builder.isRelative('C:/a'), true);
+ expect(builder.isRelative(r'C:\a'), true);
+ expect(builder.isRelative(r'\\a'), true);
+ });
+
+ group('join', () {
+ test('allows up to eight parts', () {
+ expect(builder.join('a'), 'a');
+ expect(builder.join('a', 'b'), 'a/b');
+ expect(builder.join('a', 'b', 'c'), 'a/b/c');
+ expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d');
+ expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e');
+ expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f');
+ expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g');
+ expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'),
+ 'a/b/c/d/e/f/g/h');
+ });
+
+ test('does not add separator if a part ends in one', () {
+ expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d');
+ expect(builder.join('a\\', 'b'), r'a\/b');
+ });
+
+ test('ignores parts before an absolute path', () {
+ expect(builder.join('a', '/b', '/c', 'd'), '/c/d');
+ expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d');
+ expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d');
+ });
+ });
+
+ group('normalize', () {
+ test('simple cases', () {
+ expect(builder.normalize(''), '');
+ expect(builder.normalize('.'), '.');
+ expect(builder.normalize('..'), '..');
+ expect(builder.normalize('a'), 'a');
+ expect(builder.normalize('/'), '/');
+ expect(builder.normalize(r'\'), r'\');
+ });
+
+ test('collapses redundant separators', () {
+ expect(builder.normalize(r'a/b/c'), r'a/b/c');
+ expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d');
+ });
+
+ test('does not collapse separators for other platform', () {
+ expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c');
+ });
+
+ test('eliminates "." parts', () {
+ expect(builder.normalize('./'), '.');
+ expect(builder.normalize('/.'), '/');
+ expect(builder.normalize('/./'), '/');
+ expect(builder.normalize('./.'), '.');
+ expect(builder.normalize('a/./b'), 'a/b');
+ expect(builder.normalize('a/.b/c'), 'a/.b/c');
+ expect(builder.normalize('a/././b/./c'), 'a/b/c');
+ expect(builder.normalize('././a'), 'a');
+ expect(builder.normalize('a/./.'), 'a');
+ });
+
+ test('eliminates ".." parts', () {
+ expect(builder.normalize('..'), '..');
+ expect(builder.normalize('../'), '..');
+ expect(builder.normalize('../../..'), '../../..');
+ expect(builder.normalize('../../../'), '../../..');
+ expect(builder.normalize('/..'), '/');
+ expect(builder.normalize('/../../..'), '/');
+ expect(builder.normalize('/../../../a'), '/a');
+ expect(builder.normalize('a/..'), '.');
+ expect(builder.normalize('a/b/..'), 'a');
+ expect(builder.normalize('a/../b'), 'b');
+ expect(builder.normalize('a/./../b'), 'b');
+ expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d');
+ expect(builder.normalize('a/b/../../../../c'), '../../c');
+ });
+
+ test('does not walk before root on absolute paths', () {
+ expect(builder.normalize('..'), '..');
+ expect(builder.normalize('../'), '..');
+ expect(builder.normalize('/..'), '/');
+ expect(builder.normalize('a/..'), '.');
+ expect(builder.normalize('a/b/..'), 'a');
+ expect(builder.normalize('a/../b'), 'b');
+ expect(builder.normalize('a/./../b'), 'b');
+ expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d');
+ expect(builder.normalize('a/b/../../../../c'), '../../c');
+ });
+
+ test('removes trailing separators', () {
+ expect(builder.normalize('./'), '.');
+ expect(builder.normalize('.//'), '.');
+ expect(builder.normalize('a/'), 'a');
+ expect(builder.normalize('a/b/'), 'a/b');
+ expect(builder.normalize('a/b///'), 'a/b');
+ });
+ });
+
+ group('relative', () {
+ group('from absolute root', () {
+ test('given absolute path in root', () {
+ expect(builder.relative('/'), '../..');
+ expect(builder.relative('/root'), '..');
+ expect(builder.relative('/root/path'), '.');
+ expect(builder.relative('/root/path/a'), 'a');
+ expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt');
+ expect(builder.relative('/root/a/b.txt'), '../a/b.txt');
+ });
+
+ test('given absolute path outside of root', () {
+ expect(builder.relative('/a/b'), '../../a/b');
+ expect(builder.relative('/root/path/a'), 'a');
+ expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt');
+ expect(builder.relative('/root/a/b.txt'), '../a/b.txt');
+ });
+
+ test('given relative path', () {
+ // The path is considered relative to the root, so it basically just
+ // normalizes.
+ expect(builder.relative(''), '.');
+ expect(builder.relative('.'), '.');
+ expect(builder.relative('a'), 'a');
+ expect(builder.relative('a/b.txt'), 'a/b.txt');
+ expect(builder.relative('../a/b.txt'), '../a/b.txt');
+ expect(builder.relative('a/./b/../c.txt'), 'a/c.txt');
+ });
+ });
+
+ group('from relative root', () {
+ var r = new path.Builder(style: path.Style.posix, root: 'foo/bar');
+
+ // These tests rely on the current working directory, so don't do the
+ // right thing if you run them on the wrong platform.
+ if (io.Platform.operatingSystem != 'windows') {
+ test('given absolute path', () {
+ var b = new path.Builder(style: path.Style.posix);
+ expect(r.relative('/'), b.join(b.relative('/'), '../..'));
+ expect(r.relative('/a/b'), b.join(b.relative('/'), '../../a/b'));
+ });
+ }
+
+ test('given relative path', () {
+ // The path is considered relative to the root, so it basically just
+ // normalizes.
+ expect(r.relative(''), '.');
+ expect(r.relative('.'), '.');
+ expect(r.relative('..'), '..');
+ expect(r.relative('a'), 'a');
+ expect(r.relative('a/b.txt'), 'a/b.txt');
+ expect(r.relative('../a/b.txt'), '../a/b.txt');
+ expect(r.relative('a/./b/../c.txt'), 'a/c.txt');
+ });
+ });
+
+ test('from a root with extension', () {
+ var r = new path.Builder(style: path.Style.posix, root: '/dir.ext');
+ expect(r.relative('/dir.ext/file'), 'file');
+ });
+ });
+
+ group('resolve', () {
+ test('allows up to seven parts', () {
+ expect(builder.resolve('a'), '/root/path/a');
+ expect(builder.resolve('a', 'b'), '/root/path/a/b');
+ expect(builder.resolve('a', 'b', 'c'), '/root/path/a/b/c');
+ expect(builder.resolve('a', 'b', 'c', 'd'), '/root/path/a/b/c/d');
+ expect(builder.resolve('a', 'b', 'c', 'd', 'e'), '/root/path/a/b/c/d/e');
+ expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'),
+ '/root/path/a/b/c/d/e/f');
+ expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'),
+ '/root/path/a/b/c/d/e/f/g');
+ });
+
+ test('does not add separator if a part ends in one', () {
+ expect(builder.resolve('a/', 'b', 'c/', 'd'), '/root/path/a/b/c/d');
+ expect(builder.resolve(r'a\', 'b'), r'/root/path/a\/b');
+ });
+
+ test('ignores parts before an absolute path', () {
+ expect(builder.resolve('a', '/b', '/c', 'd'), '/c/d');
+ expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'/root/path/a/c:\b/c/d');
+ expect(builder.resolve('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d');
+ });
+ });
+
+ test('withoutExtension', () {
+ expect(builder.withoutExtension(''), '');
+ expect(builder.withoutExtension('a'), 'a');
+ expect(builder.withoutExtension('.a'), '.a');
+ expect(builder.withoutExtension('a.b'), 'a');
+ expect(builder.withoutExtension('a/b.c'), 'a/b');
+ expect(builder.withoutExtension('a/b.c.d'), 'a/b.c');
+ expect(builder.withoutExtension('a/'), 'a/');
+ expect(builder.withoutExtension('a/b/'), 'a/b/');
+ expect(builder.withoutExtension('a/.'), 'a/.');
+ expect(builder.withoutExtension('a/.b'), 'a/.b');
+ expect(builder.withoutExtension('a.b/c'), 'a.b/c');
+ expect(builder.withoutExtension(r'a.b\c'), r'a');
+ expect(builder.withoutExtension(r'a/b\c'), r'a/b\c');
+ expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c');
+ });
+}
diff --git a/utils/tests/pub/path/path_test.dart b/utils/tests/pub/path/path_test.dart
new file mode 100644
index 0000000..26812a1
--- /dev/null
+++ b/utils/tests/pub/path/path_test.dart
@@ -0,0 +1,59 @@
+// 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 all_test;
+
+import 'dart:io' as io;
+
+import '../../../../pkg/unittest/lib/unittest.dart';
+import '../../../pub/path.dart' as path;
+
+main() {
+ group('path.Style', () {
+ test('name', () {
+ expect(path.Style.posix.name, 'posix');
+ expect(path.Style.windows.name, 'windows');
+ });
+
+ test('separator', () {
+ expect(path.Style.posix.separator, '/');
+ expect(path.Style.windows.separator, '\\');
+ });
+
+ test('toString()', () {
+ expect(path.Style.posix.toString(), 'posix');
+ expect(path.Style.windows.toString(), 'windows');
+ });
+ });
+
+ group('new Builder()', () {
+ test('uses the given root directory', () {
+ var builder = new path.Builder(root: '/a/b/c');
+ expect(builder.root, '/a/b/c');
+ });
+
+ test('uses the given style', () {
+ var builder = new path.Builder(style: path.Style.windows);
+ expect(builder.style, path.Style.windows);
+ });
+
+ test('uses the current working directory if root is omitted', () {
+ var builder = new path.Builder();
+ expect(builder.root, new io.Directory.current().path);
+ });
+
+ test('uses the host OS if style is omitted', () {
+ var builder = new path.Builder();
+ if (io.Platform.operatingSystem == 'windows') {
+ expect(builder.style, path.Style.windows);
+ } else {
+ expect(builder.style, path.Style.posix);
+ }
+ });
+ });
+
+ test('current', () {
+ expect(path.current, new io.Directory.current().path);
+ });
+}
diff --git a/utils/tests/pub/path/path_windows_test.dart b/utils/tests/pub/path/path_windows_test.dart
new file mode 100644
index 0000000..1861a67
--- /dev/null
+++ b/utils/tests/pub/path/path_windows_test.dart
@@ -0,0 +1,311 @@
+// 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 path_test;
+
+import 'dart:io' as io;
+
+import '../../../../pkg/unittest/lib/unittest.dart';
+import '../../../pub/path.dart' as path;
+
+main() {
+ var builder = new path.Builder(style: path.Style.windows,
+ root: r'C:\root\path');
+
+ if (new path.Builder().style == path.Style.windows) {
+ group('absolute', () {
+ expect(path.absolute(r'a\b.txt'), path.join(path.current, r'a\b.txt'));
+ expect(path.absolute(r'C:\a\b.txt'), r'C:\a\b.txt');
+ expect(path.absolute(r'\\a\b.txt'), r'\\a\b.txt');
+ });
+ }
+
+ group('separator', () {
+ expect(builder.separator, '\\');
+ });
+
+ test('extension', () {
+ expect(builder.extension(''), '');
+ expect(builder.extension('foo.dart'), '.dart');
+ expect(builder.extension('foo.dart.js'), '.js');
+ expect(builder.extension(r'a.b\c'), '');
+ expect(builder.extension('a.b/c.d'), '.d');
+ expect(builder.extension(r'~\.bashrc'), '');
+ expect(builder.extension(r'a.b/c'), r'');
+ });
+
+ test('basename', () {
+ expect(builder.basename(r''), '');
+ expect(builder.basename(r'a'), 'a');
+ expect(builder.basename(r'a\b'), 'b');
+ expect(builder.basename(r'a\b\c'), 'c');
+ expect(builder.basename(r'a\b.c'), 'b.c');
+ expect(builder.basename(r'a\'), '');
+ expect(builder.basename(r'a/'), '');
+ expect(builder.basename(r'a\.'), '.');
+ expect(builder.basename(r'a\b/c'), r'c');
+ });
+
+ test('basenameWithoutExtension', () {
+ expect(builder.basenameWithoutExtension(''), '');
+ expect(builder.basenameWithoutExtension('a'), 'a');
+ expect(builder.basenameWithoutExtension(r'a\b'), 'b');
+ expect(builder.basenameWithoutExtension(r'a\b\c'), 'c');
+ expect(builder.basenameWithoutExtension(r'a\b.c'), 'b');
+ expect(builder.basenameWithoutExtension(r'a\'), '');
+ expect(builder.basenameWithoutExtension(r'a\.'), '.');
+ expect(builder.basenameWithoutExtension(r'a\b/c'), r'c');
+ expect(builder.basenameWithoutExtension(r'a\.bashrc'), '.bashrc');
+ expect(builder.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d');
+ });
+
+ test('isAbsolute', () {
+ expect(builder.isAbsolute(''), false);
+ expect(builder.isAbsolute('a'), false);
+ expect(builder.isAbsolute(r'a\b'), false);
+ expect(builder.isAbsolute(r'\a'), false);
+ expect(builder.isAbsolute(r'\a\b'), false);
+ expect(builder.isAbsolute('~'), false);
+ expect(builder.isAbsolute('.'), false);
+ expect(builder.isAbsolute(r'..\a'), false);
+ expect(builder.isAbsolute(r'a:/a\b'), true);
+ expect(builder.isAbsolute(r'D:/a/b'), true);
+ expect(builder.isAbsolute(r'c:\'), true);
+ expect(builder.isAbsolute(r'B:\'), true);
+ expect(builder.isAbsolute(r'c:\a'), true);
+ expect(builder.isAbsolute(r'C:\a'), true);
+ expect(builder.isAbsolute(r'\\a'), true);
+ expect(builder.isAbsolute(r'\\'), true);
+ });
+
+ test('isRelative', () {
+ expect(builder.isRelative(''), true);
+ expect(builder.isRelative('a'), true);
+ expect(builder.isRelative(r'a\b'), true);
+ expect(builder.isRelative(r'\a'), true);
+ expect(builder.isRelative(r'\a\b'), true);
+ expect(builder.isRelative('~'), true);
+ expect(builder.isRelative('.'), true);
+ expect(builder.isRelative(r'..\a'), true);
+ expect(builder.isRelative(r'a:/a\b'), false);
+ expect(builder.isRelative(r'D:/a/b'), false);
+ expect(builder.isRelative(r'c:\'), false);
+ expect(builder.isRelative(r'B:\'), false);
+ expect(builder.isRelative(r'c:\a'), false);
+ expect(builder.isRelative(r'C:\a'), false);
+ expect(builder.isRelative(r'\\a'), false);
+ expect(builder.isRelative(r'\\'), false);
+ });
+
+ group('join', () {
+ test('allows up to eight parts', () {
+ expect(builder.join('a'), 'a');
+ expect(builder.join('a', 'b'), r'a\b');
+ expect(builder.join('a', 'b', 'c'), r'a\b\c');
+ expect(builder.join('a', 'b', 'c', 'd'), r'a\b\c\d');
+ expect(builder.join('a', 'b', 'c', 'd', 'e'), r'a\b\c\d\e');
+ expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), r'a\b\c\d\e\f');
+ expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g');
+ expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'),
+ r'a\b\c\d\e\f\g\h');
+ });
+
+ test('does not add separator if a part ends or begins in one', () {
+ expect(builder.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d');
+ expect(builder.join('a/', 'b'), r'a/b');
+ expect(builder.join('a', '/b'), 'a/b');
+ expect(builder.join('a', r'\b'), r'a\b');
+ });
+
+ test('ignores parts before an absolute path', () {
+ expect(builder.join('a', '/b', '/c', 'd'), r'a/b/c\d');
+ expect(builder.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d');
+ expect(builder.join('a', r'\\b', r'\\c', 'd'), r'\\c\d');
+ });
+ });
+
+ group('normalize', () {
+ test('simple cases', () {
+ expect(builder.normalize(''), '');
+ expect(builder.normalize('.'), '.');
+ expect(builder.normalize('..'), '..');
+ expect(builder.normalize('a'), 'a');
+ expect(builder.normalize('C:/'), r'C:/');
+ expect(builder.normalize(r'C:\'), r'C:\');
+ expect(builder.normalize(r'\\'), r'\\');
+ });
+
+ test('collapses redundant separators', () {
+ expect(builder.normalize(r'a\b\c'), r'a\b\c');
+ expect(builder.normalize(r'a\\b\\\c\\\\d'), r'a\b\c\d');
+ });
+
+ test('eliminates "." parts', () {
+ expect(builder.normalize(r'.\'), '.');
+ expect(builder.normalize(r'c:\.'), r'c:\');
+ expect(builder.normalize(r'B:\.\'), r'B:\');
+ expect(builder.normalize(r'\\.'), r'\\');
+ expect(builder.normalize(r'\\.\'), r'\\');
+ expect(builder.normalize(r'.\.'), '.');
+ expect(builder.normalize(r'a\.\b'), r'a\b');
+ expect(builder.normalize(r'a\.b\c'), r'a\.b\c');
+ expect(builder.normalize(r'a\./.\b\.\c'), r'a\b\c');
+ expect(builder.normalize(r'.\./a'), 'a');
+ expect(builder.normalize(r'a/.\.'), 'a');
+ });
+
+ test('eliminates ".." parts', () {
+ expect(builder.normalize('..'), '..');
+ expect(builder.normalize(r'..\'), '..');
+ expect(builder.normalize(r'..\..\..'), r'..\..\..');
+ expect(builder.normalize(r'../..\..\'), r'..\..\..');
+ // TODO(rnystrom): Is this how Python handles absolute paths on Windows?
+ expect(builder.normalize(r'\\..'), r'\\');
+ expect(builder.normalize(r'\\..\..\..'), r'\\');
+ expect(builder.normalize(r'\\..\../..\a'), r'\\a');
+ expect(builder.normalize(r'c:\..'), r'c:\');
+ expect(builder.normalize(r'A:/..\..\..'), r'A:/');
+ expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a');
+ expect(builder.normalize(r'a\..'), '.');
+ expect(builder.normalize(r'a\b\..'), 'a');
+ expect(builder.normalize(r'a\..\b'), 'b');
+ expect(builder.normalize(r'a\.\..\b'), 'b');
+ expect(builder.normalize(r'a\b\c\..\..\d\e\..'), r'a\d');
+ expect(builder.normalize(r'a\b\..\..\..\..\c'), r'..\..\c');
+ });
+
+ test('removes trailing separators', () {
+ expect(builder.normalize(r'.\'), '.');
+ expect(builder.normalize(r'.\\'), '.');
+ expect(builder.normalize(r'a/'), 'a');
+ expect(builder.normalize(r'a\b\'), r'a\b');
+ expect(builder.normalize(r'a\b\\\'), r'a\b');
+ });
+
+ test('normalizes separators', () {
+ expect(builder.normalize(r'a/b\c'), r'a\b\c');
+ });
+ });
+
+ group('relative', () {
+ group('from absolute root', () {
+ test('given absolute path in root', () {
+ expect(builder.relative(r'C:\'), r'..\..');
+ expect(builder.relative(r'C:\root'), '..');
+ expect(builder.relative(r'C:\root\path'), '.');
+ expect(builder.relative(r'C:\root\path\a'), 'a');
+ expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt');
+ expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt');
+ });
+
+ test('given absolute path outside of root', () {
+ expect(builder.relative(r'C:\a\b'), r'..\..\a\b');
+ expect(builder.relative(r'C:\root\path\a'), 'a');
+ expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt');
+ expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt');
+ });
+
+ test('given absolute path on different drive', () {
+ expect(builder.relative(r'D:\a\b'), r'D:\a\b');
+ });
+
+ test('given relative path', () {
+ // The path is considered relative to the root, so it basically just
+ // normalizes.
+ expect(builder.relative(''), '.');
+ expect(builder.relative('.'), '.');
+ expect(builder.relative('a'), 'a');
+ expect(builder.relative(r'a\b.txt'), r'a\b.txt');
+ expect(builder.relative(r'..\a\b.txt'), r'..\a\b.txt');
+ expect(builder.relative(r'a\.\b\..\c.txt'), r'a\c.txt');
+ });
+ });
+
+ group('from relative root', () {
+ var r = new path.Builder(style: path.Style.windows, root: r'foo\bar');
+
+ // These tests rely on the current working directory, so don't do the
+ // right thing if you run them on the wrong platform.
+ if (io.Platform.operatingSystem == 'windows') {
+ test('given absolute path', () {
+ var b = new path.Builder(style: path.Style.windows);
+ // TODO(rnystrom): Use a path method here to get the root prefix
+ // when one exists.
+ var drive = path.current.substring(0, 3);
+ expect(r.relative(drive), b.join(b.relative(drive), r'..\..'));
+ expect(r.relative(b.join(drive, r'a\b')),
+ b.join(b.relative(drive), r'..\..\a\b'));
+
+ // TODO(rnystrom): Test behavior when drive letters differ.
+ });
+ }
+
+ test('given relative path', () {
+ // The path is considered relative to the root, so it basically just
+ // normalizes.
+ expect(r.relative(''), '.');
+ expect(r.relative('.'), '.');
+ expect(r.relative('..'), '..');
+ expect(r.relative('a'), 'a');
+ expect(r.relative(r'a\b.txt'), r'a\b.txt');
+ expect(r.relative(r'..\a/b.txt'), r'..\a\b.txt');
+ expect(r.relative(r'a\./b\../c.txt'), r'a\c.txt');
+ });
+ });
+
+ test('from a root with extension', () {
+ var r = new path.Builder(style: path.Style.windows, root: r'C:\dir.ext');
+ expect(r.relative(r'C:\dir.ext\file'), 'file');
+ });
+
+ test('given absolute with different root prefix', () {
+ expect(builder.relative(r'D:\a\b'), r'D:\a\b');
+ expect(builder.relative(r'\\a\b'), r'\\a\b');
+ });
+ });
+
+ group('resolve', () {
+ test('allows up to seven parts', () {
+ expect(builder.resolve('a'), r'C:\root\path\a');
+ expect(builder.resolve('a', 'b'), r'C:\root\path\a\b');
+ expect(builder.resolve('a', 'b', 'c'), r'C:\root\path\a\b\c');
+ expect(builder.resolve('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d');
+ expect(builder.resolve('a', 'b', 'c', 'd', 'e'),
+ r'C:\root\path\a\b\c\d\e');
+ expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'),
+ r'C:\root\path\a\b\c\d\e\f');
+ expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'),
+ r'C:\root\path\a\b\c\d\e\f\g');
+ });
+
+ test('does not add separator if a part ends in one', () {
+ expect(builder.resolve(r'a\', 'b', r'c\', 'd'), r'C:\root\path\a\b\c\d');
+ expect(builder.resolve('a/', 'b'), r'C:\root\path\a/b');
+ });
+
+ test('ignores parts before an absolute path', () {
+ expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\root\path\a/b/c\d');
+ expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'c:\b\c\d');
+ expect(builder.resolve('a', r'\\b', r'\\c', 'd'), r'\\c\d');
+ });
+ });
+
+ test('withoutExtension', () {
+ expect(builder.withoutExtension(''), '');
+ expect(builder.withoutExtension('a'), 'a');
+ expect(builder.withoutExtension('.a'), '.a');
+ expect(builder.withoutExtension('a.b'), 'a');
+ expect(builder.withoutExtension(r'a\b.c'), r'a\b');
+ expect(builder.withoutExtension(r'a\b.c.d'), r'a\b.c');
+ expect(builder.withoutExtension(r'a\'), r'a\');
+ expect(builder.withoutExtension(r'a\b\'), r'a\b\');
+ expect(builder.withoutExtension(r'a\.'), r'a\.');
+ expect(builder.withoutExtension(r'a\.b'), r'a\.b');
+ expect(builder.withoutExtension(r'a.b\c'), r'a.b\c');
+ expect(builder.withoutExtension(r'a/b.c/d'), r'a/b.c/d');
+ expect(builder.withoutExtension(r'a\b/c'), r'a\b/c');
+ expect(builder.withoutExtension(r'a\b/c.d'), r'a\b/c');
+ expect(builder.withoutExtension(r'a.b/c'), r'a.b/c');
+ });
+}
diff --git a/utils/tests/pub/pub.status b/utils/tests/pub/pub.status
index 83a5ac5..de0fba4 100644
--- a/utils/tests/pub/pub.status
+++ b/utils/tests/pub/pub.status
@@ -10,6 +10,6 @@
[ $runtime == drt || $runtime == dartium || $runtime == opera ]
*: Skip
-# TODO(rnystrom): Figure out why this is failing or timing out (#7211).
-[ $system == windows ]
-pub_lish_test: Skip
+[ $system == macos ]
+validator_test: Fail # Issue 7330
+
diff --git a/utils/tests/pub/pub_lish_test.dart b/utils/tests/pub/pub_lish_test.dart
index 1bfa3d6..a947601 100644
--- a/utils/tests/pub/pub_lish_test.dart
+++ b/utils/tests/pub/pub_lish_test.dart
@@ -13,7 +13,7 @@
void handleUploadForm(ScheduledServer server, [Map body]) {
server.handle('GET', '/packages/versions/new.json', (request, response) {
- return server.url.chain((url) {
+ return server.url.transform((url) {
expect(request.headers.value('authorization'),
equals('Bearer access token'));
@@ -29,7 +29,7 @@
response.headers.contentType = new ContentType("application", "json");
response.outputStream.writeString(JSON.stringify(body));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
});
}
@@ -38,16 +38,16 @@
server.handle('POST', '/upload', (request, response) {
// TODO(nweiz): Once a multipart/form-data parser in Dart exists, validate
// that the request body is correctly formatted. See issue 6952.
- return server.url.chain((url) {
+ return server.url.transform((url) {
response.statusCode = 302;
response.headers.set('location', url.resolve('/create').toString());
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
});
}
main() {
- setUp(() => dir(appPath, [libPubspec("test_pkg", "1.0.0")]).scheduleCreate());
+ setUp(() => normalPackage.scheduleCreate());
test('archives and uploads a package', () {
var server = new ScheduledServer();
@@ -60,7 +60,7 @@
response.outputStream.writeString(JSON.stringify({
'success': {'message': 'Package test_pkg 1.0.0 uploaded!'}
}));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextLine(), equals('Package test_pkg 1.0.0 uploaded!'));
@@ -84,7 +84,7 @@
response.outputStream.writeString(JSON.stringify({
'error': {'message': 'your token sucks'}
}));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('OAuth2 authorization failed (your '
@@ -145,7 +145,7 @@
response.outputStream.writeString(JSON.stringify({
'success': {'message': 'Package test_pkg 1.0.0 uploaded!'}
}));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
pub.shouldExit(0);
@@ -165,7 +165,7 @@
response.outputStream.writeString(JSON.stringify({
'error': {'message': 'your request sucked'}
}));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('your request sucked'));
@@ -181,7 +181,7 @@
server.handle('GET', '/packages/versions/new.json', (request, response) {
response.outputStream.writeString('{not json');
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -307,7 +307,7 @@
server.handle('POST', '/upload', (request, response) {
// don't set the location header
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('Failed to upload the package.'));
@@ -328,7 +328,7 @@
response.outputStream.writeString(JSON.stringify({
'error': {'message': 'Your package was too boring.'}
}));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('Your package was too boring.'));
@@ -346,7 +346,7 @@
server.handle('GET', '/create', (request, response) {
response.outputStream.writeString('{not json');
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -367,7 +367,7 @@
server.handle('GET', '/create', (request, response) {
response.statusCode = 400;
response.outputStream.writeString(JSON.stringify(body));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
@@ -387,7 +387,7 @@
var body = {'success': 'Your package was awesome.'};
server.handle('GET', '/create', (request, response) {
response.outputStream.writeString(JSON.stringify(body));
- return closeHttpResponse(request, response);
+ response.outputStream.close();
});
expectLater(pub.nextErrLine(), equals('Invalid server response:'));
diff --git a/utils/tests/pub/pub_test.dart b/utils/tests/pub/pub_test.dart
index aa5454a..4ad65ad 100644
--- a/utils/tests/pub/pub_test.dart
+++ b/utils/tests/pub/pub_test.dart
@@ -15,16 +15,23 @@
Usage: pub command [arguments]
Global options:
- -h, --help Prints this usage information
- --version Prints the version of Pub
- --[no-]trace Prints a stack trace when an error occurs
+ -h, --help Print this usage information.
+ --version Print pub version.
+ --[no-]trace Print debugging information when an error occurs.
+ --verbosity Control output verbosity.
+
+ [all] All output including internal tracing messages are shown.
+ [io] IO operations are also shown.
+ [normal] Errors, warnings, and user messages are shown.
+
+ -v, --verbose Shortcut for "--verbosity=all"
Available commands:
- help display help information for Pub
- install install the current package's dependencies
- publish publish the current package to pub.dartlang.org
- update update the current package's dependencies to the latest versions
- version print Pub version
+ help Display help information for Pub.
+ install Install the current package's dependencies.
+ publish Publish the current package to pub.dartlang.org.
+ update Update the current package's dependencies to the latest versions.
+ version Print pub version.
Use "pub help [command]" for more information about a command.
""";
@@ -75,13 +82,36 @@
exitCode: 64);
});
- test('an unknown help command displays an error message', () {
- runPub(args: ['help', 'quylthulg'],
- error: '''
- Could not find a command named "quylthulg".
- Run "pub help" to see available commands.
- ''',
- exitCode: 64);
+ group('help', () {
+ test('shows help for a command', () {
+ runPub(args: ['help', 'install'],
+ output: '''
+ Install the current package's dependencies.
+
+ Usage: pub install
+ ''');
+ });
+
+ test('shows help for a command', () {
+ runPub(args: ['help', 'publish'],
+ output: '''
+ Publish the current package to pub.dartlang.org.
+
+ Usage: pub publish [options]
+ --server The package server to which to upload this package
+ (defaults to "https://pub.dartlang.org")
+ ''');
+ });
+
+ test('an unknown help command displays an error message', () {
+ runPub(args: ['help', 'quylthulg'],
+ error: '''
+ Could not find a command named "quylthulg".
+ Run "pub help" to see available commands.
+ ''',
+ exitCode: 64);
+ });
+
});
test('displays the current version', () =>
diff --git a/utils/tests/pub/test_pub.dart b/utils/tests/pub/test_pub.dart
index 433c14c..aa8e879 100644
--- a/utils/tests/pub/test_pub.dart
+++ b/utils/tests/pub/test_pub.dart
@@ -23,6 +23,7 @@
import '../../pub/git_source.dart';
import '../../pub/hosted_source.dart';
import '../../pub/io.dart';
+import '../../pub/path.dart' as path;
import '../../pub/sdk_source.dart';
import '../../pub/system_cache.dart';
import '../../pub/utils.dart';
@@ -108,7 +109,7 @@
} catch (e) {
response.statusCode = 404;
response.contentLength = 0;
- closeHttpResponse(request, response);
+ response.outputStream.close();
return;
}
@@ -117,14 +118,14 @@
response.statusCode = 200;
response.contentLength = data.length;
response.outputStream.write(data);
- closeHttpResponse(request, response);
+ response.outputStream.close();
});
future.handleException((e) {
print("Exception while handling ${request.uri}: $e");
response.statusCode = 500;
response.reasonPhrase = e.message;
- closeHttpResponse(request, response);
+ response.outputStream.close();
});
};
_server.listen("127.0.0.1", 0);
@@ -221,6 +222,15 @@
/** Converts [value] into a YAML string. */
String yaml(value) => JSON.stringify(value);
+/// Describes a package that passes all validation.
+Descriptor get normalPackage => dir(appPath, [
+ libPubspec("test_pkg", "1.0.0"),
+ file("LICENSE", "Eh, do what you want."),
+ dir("lib", [
+ file("test_pkg.dart", "int i = 1;")
+ ])
+]);
+
/**
* Describes a file named `pubspec.yaml` with the given YAML-serialized
* [contents], which should be a serializable object.
@@ -527,15 +537,15 @@
// If an error occurs during testing, delete the sandbox, throw the error so
// that the test framework sees it, then finally call asyncDone so that the
// test framework knows we're done doing asynchronous stuff.
- var future = _runScheduled(createdSandboxDir, _scheduledOnException)
+ var subFuture = _runScheduled(createdSandboxDir, _scheduledOnException)
.chain((_) => cleanup());
- future.handleException((e) {
+ subFuture.handleException((e) {
print("Exception while cleaning up: $e");
- print(future.stackTrace);
- registerException(error, future.stackTrace);
+ print(subFuture.stackTrace);
+ registerException(error, subFuture.stackTrace);
return true;
});
- future.then((_) => registerException(error, future.stackTrace));
+ subFuture.then((_) => registerException(error, future.stackTrace));
return true;
});
@@ -546,10 +556,10 @@
/// Get the path to the root "util/test/pub" directory containing the pub tests.
String get testDirectory {
- var dir = new Path.fromNative(new Options().script);
- while (dir.filename != 'pub') dir = dir.directoryPath;
+ var dir = new Options().script;
+ while (basename(dir) != 'pub') dir = dirname(dir);
- return new File(dir.toNativePath()).fullPathSync();
+ return getFullPath(dir);
}
/**
@@ -639,14 +649,14 @@
// Find the main pub entrypoint.
var pubPath = fs.joinPaths(testDirectory, '../../pub/pub.dart');
- var dartArgs =
- ['--enable-type-checks', '--enable-asserts', pubPath, '--trace'];
+ var dartArgs = ['--checked', pubPath, '--trace'];
dartArgs.addAll(args);
var environment = {
'PUB_CACHE': pathInSandbox(cachePath),
'DART_SDK': pathInSandbox(sdkPath)
};
+
if (tokenEndpoint != null) {
environment['_PUB_TEST_TOKEN_ENDPOINT'] = tokenEndpoint.toString();
}
@@ -1268,7 +1278,7 @@
});
}
-/// A matcher that matches a Pair.
+/// A matcher that matches a Pair.
Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) =>
new _PairMatcher(firstMatcher, lastMatcher);
diff --git a/utils/tests/pub/validator_test.dart b/utils/tests/pub/validator_test.dart
index a975c3e..d9877eb 100644
--- a/utils/tests/pub/validator_test.dart
+++ b/utils/tests/pub/validator_test.dart
@@ -12,6 +12,8 @@
import '../../pub/entrypoint.dart';
import '../../pub/io.dart';
import '../../pub/validator.dart';
+import '../../pub/validator/lib.dart';
+import '../../pub/validator/license.dart';
import '../../pub/validator/name.dart';
import '../../pub/validator/pubspec_field.dart';
@@ -27,19 +29,49 @@
expectLater(schedulePackageValidation(fn), pairOf(isEmpty, isNot(isEmpty)));
}
-Validator pubspecField(Entrypoint entrypoint) =>
- new PubspecFieldValidator(entrypoint);
+Validator lib(Entrypoint entrypoint) => new LibValidator(entrypoint);
+
+Validator license(Entrypoint entrypoint) => new LicenseValidator(entrypoint);
Validator name(Entrypoint entrypoint) => new NameValidator(entrypoint);
+Validator pubspecField(Entrypoint entrypoint) =>
+ new PubspecFieldValidator(entrypoint);
+
+void scheduleNormalPackage() => normalPackage.scheduleCreate();
+
main() {
group('should consider a package valid if it', () {
+ setUp(scheduleNormalPackage);
+
test('looks normal', () {
dir(appPath, [libPubspec("test_pkg", "1.0.0")]).scheduleCreate();
+ expectNoValidationError(license);
expectNoValidationError(pubspecField);
run();
});
+ test('has a COPYING file', () {
+ file(join(appPath, 'LICENSE'), '').scheduleDelete();
+ file(join(appPath, 'COPYING'), '').scheduleCreate();
+ expectNoValidationError(license);
+ run();
+ });
+
+ test('has a prefixed LICENSE file', () {
+ file(join(appPath, 'LICENSE'), '').scheduleDelete();
+ file(join(appPath, 'MIT_LICENSE'), '').scheduleCreate();
+ expectNoValidationError(license);
+ run();
+ });
+
+ test('has a suffixed LICENSE file', () {
+ file(join(appPath, 'LICENSE'), '').scheduleDelete();
+ file(join(appPath, 'LICENSE.md'), '').scheduleCreate();
+ expectNoValidationError(license);
+ run();
+ });
+
test('has "authors" instead of "author"', () {
var package = package("test_pkg", "1.0.0");
package["authors"] = [package.remove("author")];
@@ -59,9 +91,22 @@
expectNoValidationError(name);
run();
});
+
+ test('has a non-Dart file in lib', () {
+ dir(appPath, [
+ libPubspec("test_pkg", "1.0.0"),
+ dir("lib", [
+ file("thing.txt", "woo hoo")
+ ])
+ ]).scheduleCreate();
+ expectNoValidationError(lib);
+ run();
+ });
});
group('should consider a package invalid if it', () {
+ setUp(scheduleNormalPackage);
+
test('is missing the "homepage" field', () {
var package = package("test_pkg", "1.0.0");
package.remove("homepage");
@@ -135,6 +180,12 @@
run();
});
+ test('has no LICENSE file', () {
+ file(join(appPath, 'LICENSE'), '').scheduleDelete();
+ expectValidationError(license);
+ run();
+ });
+
test('has an empty package name', () {
dir(appPath, [libPubspec("", "1.0.0")]).scheduleCreate();
expectValidationError(name);
@@ -200,5 +251,28 @@
expectValidationError(name);
run();
});
+
+ test('has no lib directory', () {
+ dir(join(appPath, "lib")).scheduleDelete();
+ expectValidationError(lib);
+ run();
+ });
+
+ test('has an empty lib directory', () {
+ file(join(appPath, "lib", "test_pkg.dart"), '').scheduleDelete();
+ expectValidationError(lib);
+ run();
+ });
+
+ test('has a lib directory containing only src', () {
+ file(join(appPath, "lib", "test_pkg.dart"), '').scheduleDelete();
+ dir(appPath, [
+ dir("lib", [
+ dir("src", [file("test_pkg.dart", "int i = 0;")])
+ ])
+ ]).scheduleCreate();
+ expectValidationError(lib);
+ run();
+ });
});
}