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..0b10b38 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(); + }); }); }