From 977977e6ea5360c30d22e7e1b2b03b559cb71ff1 Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Wed, 5 Oct 2011 05:00:35 +0000 Subject: [PATCH] Initial checkin. git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@9 260f80e4-7a28-3924-810f-c04153c831b5 --- compiler/README | 2 + compiler/api.dart | 4 + compiler/build.xml | 201 + compiler/codereview.settings | 4 + compiler/dart-compiler.gyp | 213 + compiler/dartc.mf | 3 + compiler/dartc.xml | 250 ++ compiler/dartium.gyp | 67 + compiler/eclipse.workspace/README.txt | 60 + compiler/eclipse.workspace/dartc/.classpath | 19 + compiler/eclipse.workspace/dartc/.project | 58 + .../eclipse.workspace/dartc/deps/README.txt | 5 + compiler/eclipse.workspace/tests/.classpath | 16 + compiler/eclipse.workspace/tests/.project | 128 + .../tests/SharedTests.launch | 20 + .../tests/TestSharedTests.launch | 20 + .../tests/dartc_jscomp_suites.launch | 23 + .../tests/dartc_tests_suites.launch | 23 + compiler/generate_source_list.py | 117 + .../com/google/dart/compiler/Backend.java | 59 + .../dart/compiler/CommandLineOptions.java | 358 ++ .../dart/compiler/CompilerConfiguration.java | 90 + .../dart/compiler/DartArtifactProvider.java | 67 + .../dart/compiler/DartCompilationError.java | 295 ++ .../dart/compiler/DartCompilationPhase.java | 25 + .../google/dart/compiler/DartCompiler.java | 1092 +++++ .../dart/compiler/DartCompilerContext.java | 133 + .../dart/compiler/DartCompilerErrorCode.java | 219 + .../dart/compiler/DartCompilerListener.java | 25 + .../compiler/DartCompilerMainContext.java | 183 + ...ateStubGeneratorCompilerConfiguration.java | 22 + .../com/google/dart/compiler/DartSource.java | 21 + .../DefaultCompilerConfiguration.java | 208 + .../compiler/DefaultDartArtifactProvider.java | 232 ++ .../compiler/DefaultDartCompilerListener.java | 103 + .../dart/compiler/DefaultErrorFormatter.java | 29 + .../dart/compiler/DefaultLibrarySource.java | 197 + .../DelegatingCompilerConfiguration.java | 103 + .../google/dart/compiler/DeltaAnalyzer.java | 215 + .../com/google/dart/compiler/ErrorCode.java | 16 + .../google/dart/compiler/ErrorFormatter.java | 16 + .../compiler/InternalCompilerException.java | 15 + .../com/google/dart/compiler/LibraryDeps.java | 189 + .../dart/compiler/LibraryDepsVisitor.java | 160 + .../google/dart/compiler/LibrarySource.java | 37 + .../dart/compiler/PrettyErrorFormatter.java | 143 + .../java/com/google/dart/compiler/Source.java | 45 + .../com/google/dart/compiler/SourceDelta.java | 57 + .../google/dart/compiler/SystemLibrary.java | 70 + .../dart/compiler/SystemLibraryManager.java | 314 ++ .../dart/compiler/UnitTestBatchRunner.java | 61 + .../google/dart/compiler/UrlDartSource.java | 54 + .../dart/compiler/UrlLibrarySource.java | 48 + .../com/google/dart/compiler/UrlSource.java | 194 + .../dart/compiler/ast/DartArrayAccess.java | 65 + .../dart/compiler/ast/DartArrayLiteral.java | 45 + .../dart/compiler/ast/DartAssertion.java | 58 + .../compiler/ast/DartBinaryExpression.java | 84 + .../google/dart/compiler/ast/DartBlock.java | 51 + .../dart/compiler/ast/DartBooleanLiteral.java | 40 + .../dart/compiler/ast/DartBreakStatement.java | 34 + .../google/dart/compiler/ast/DartCase.java | 55 + .../dart/compiler/ast/DartCatchBlock.java | 60 + .../google/dart/compiler/ast/DartClass.java | 188 + .../dart/compiler/ast/DartClassMember.java | 33 + .../google/dart/compiler/ast/DartComment.java | 64 + .../dart/compiler/ast/DartConditional.java | 55 + .../google/dart/compiler/ast/DartContext.java | 27 + .../compiler/ast/DartContinueStatement.java | 34 + .../dart/compiler/ast/DartDeclaration.java | 33 + .../google/dart/compiler/ast/DartDefault.java | 30 + .../dart/compiler/ast/DartDirective.java | 11 + .../compiler/ast/DartDoWhileStatement.java | 47 + .../dart/compiler/ast/DartDoubleLiteral.java | 40 + .../dart/compiler/ast/DartEmptyStatement.java | 26 + .../dart/compiler/ast/DartExprStmt.java | 38 + .../dart/compiler/ast/DartExpression.java | 21 + .../google/dart/compiler/ast/DartField.java | 95 + .../compiler/ast/DartFieldDefinition.java | 57 + .../dart/compiler/ast/DartForInStatement.java | 78 + .../dart/compiler/ast/DartForStatement.java | 76 + .../dart/compiler/ast/DartFunction.java | 68 + .../compiler/ast/DartFunctionExpression.java | 80 + .../ast/DartFunctionObjectInvocation.java | 46 + .../compiler/ast/DartFunctionTypeAlias.java | 83 + .../dart/compiler/ast/DartGotoStatement.java | 51 + .../dart/compiler/ast/DartIdentifier.java | 86 + .../dart/compiler/ast/DartIfStatement.java | 59 + .../compiler/ast/DartImportDirective.java | 51 + .../dart/compiler/ast/DartInitializer.java | 74 + .../dart/compiler/ast/DartIntegerLiteral.java | 46 + .../dart/compiler/ast/DartInvocation.java | 48 + .../google/dart/compiler/ast/DartLabel.java | 73 + .../compiler/ast/DartLibraryDirective.java | 38 + .../google/dart/compiler/ast/DartLiteral.java | 24 + .../dart/compiler/ast/DartMapLiteral.java | 45 + .../compiler/ast/DartMapLiteralEntry.java | 47 + .../compiler/ast/DartMethodDefinition.java | 128 + .../compiler/ast/DartMethodInvocation.java | 79 + .../dart/compiler/ast/DartModVisitor.java | 190 + .../compiler/ast/DartNamedExpression.java | 56 + .../dart/compiler/ast/DartNativeBlock.java | 31 + .../compiler/ast/DartNativeDirective.java | 38 + .../dart/compiler/ast/DartNewExpression.java | 69 + .../google/dart/compiler/ast/DartNode.java | 167 + .../dart/compiler/ast/DartNodeTraverser.java | 441 ++ .../dart/compiler/ast/DartNullLiteral.java | 33 + .../dart/compiler/ast/DartParameter.java | 126 + .../compiler/ast/DartParameterizedNode.java | 53 + .../ast/DartParenthesizedExpression.java | 43 + .../dart/compiler/ast/DartPlainVisitor.java | 148 + .../dart/compiler/ast/DartPropertyAccess.java | 83 + .../DartRedirectConstructorInvocation.java | 63 + .../compiler/ast/DartResourceDirective.java | 38 + .../compiler/ast/DartReturnStatement.java | 48 + .../compiler/ast/DartSourceDirective.java | 38 + .../dart/compiler/ast/DartStatement.java | 14 + .../compiler/ast/DartStringInterpolation.java | 64 + .../dart/compiler/ast/DartStringLiteral.java | 40 + .../ast/DartSuperConstructorInvocation.java | 74 + .../compiler/ast/DartSuperExpression.java | 48 + .../dart/compiler/ast/DartSwitchMember.java | 41 + .../compiler/ast/DartSwitchStatement.java | 49 + .../ast/DartSyntheticErrorExpression.java | 39 + .../ast/DartSyntheticErrorStatement.java | 39 + .../dart/compiler/ast/DartThisExpression.java | 33 + .../dart/compiler/ast/DartThrowStatement.java | 48 + .../compiler/ast/DartToSourceVisitor.java | 995 +++++ .../dart/compiler/ast/DartTryStatement.java | 62 + .../dart/compiler/ast/DartTypeExpression.java | 39 + .../dart/compiler/ast/DartTypeNode.java | 67 + .../dart/compiler/ast/DartTypeParameter.java | 51 + .../dart/compiler/ast/DartTypedLiteral.java | 64 + .../compiler/ast/DartUnaryExpression.java | 82 + .../google/dart/compiler/ast/DartUnit.java | 152 + .../ast/DartUnqualifiedInvocation.java | 46 + .../dart/compiler/ast/DartVariable.java | 64 + .../compiler/ast/DartVariableStatement.java | 63 + .../dart/compiler/ast/DartVisitable.java | 19 + .../google/dart/compiler/ast/DartVisitor.java | 622 +++ .../dart/compiler/ast/DartWhileStatement.java | 47 + .../dart/compiler/ast/ElementReference.java | 19 + .../google/dart/compiler/ast/LibraryNode.java | 42 + .../google/dart/compiler/ast/LibraryUnit.java | 451 +++ .../google/dart/compiler/ast/Modifiers.java | 102 + .../backend/common/AbstractBackend.java | 15 + .../backend/common/TypeHeuristic.java | 47 + .../common/TypeHeuristicImplementation.java | 1165 ++++++ .../compiler/backend/dart/DartBackend.java | 119 + .../doc/DartDocumentationGenerator.java | 246 ++ .../backend/doc/DartDocumentationVisitor.java | 805 ++++ .../backend/doc/ElementNameComparator.java | 18 + .../compiler/backend/doc/LinkInformation.java | 42 + .../isolate/DartIsolateStubGenerator.java | 754 ++++ .../backend/js/AbstractJsBackend.java | 467 +++ .../backend/js/BasicOptimizationStrategy.java | 458 +++ .../dart/compiler/backend/js/Cloner.java | 253 ++ .../compiler/backend/js/ClosureJsAst.java | 76 + .../backend/js/ClosureJsAstTranslator.java | 716 ++++ .../compiler/backend/js/ClosureJsBackend.java | 537 +++ .../backend/js/ClosureJsCodingConvention.java | 104 + .../dart/compiler/backend/js/DartMangler.java | 138 + .../compiler/backend/js/DollarMangler.java | 411 ++ .../backend/js/GenerateJavascriptAST.java | 3583 +++++++++++++++++ .../backend/js/GenerateNamesAndScopes.java | 189 + .../backend/js/JavascriptBackend.java | 298 ++ .../js/JsConstructExpressionVisitor.java | 114 + .../backend/js/JsFirstExpressionVisitor.java | 134 + .../compiler/backend/js/JsNameProvider.java | 86 + .../dart/compiler/backend/js/JsNamer.java | 25 + .../compiler/backend/js/JsNormalizer.java | 112 + .../backend/js/JsParserException.java | 92 + .../backend/js/JsPrecedenceVisitor.java | 317 ++ .../compiler/backend/js/JsPrettyNamer.java | 127 + .../backend/js/JsRequiresSemiVisitor.java | 158 + .../backend/js/JsReservedIdentifiers.java | 223 + .../backend/js/JsSourceGenerationVisitor.java | 39 + .../js/JsToStringGenerationVisitor.java | 1390 +++++++ .../backend/js/NoOptimizationStrategy.java | 80 + .../backend/js/NormalizedVisitor.java | 21 + .../dart/compiler/backend/js/Normalizer.java | 924 +++++ .../backend/js/OptimizationStrategy.java | 42 + .../backend/js/RuntimeTypeInjector.java | 802 ++++ .../compiler/backend/js/ScopeRootInfo.java | 528 +++ .../backend/js/TranslationContext.java | 62 + .../backend/js/TraversalContextProvider.java | 40 + .../js/UncheckedJsParserException.java | 21 + .../backend/js/ast/CanBooleanEval.java | 15 + .../compiler/backend/js/ast/HasArguments.java | 15 + .../compiler/backend/js/ast/HasCondition.java | 15 + .../dart/compiler/backend/js/ast/HasName.java | 14 + .../backend/js/ast/JsArrayAccess.java | 68 + .../backend/js/ast/JsArrayLiteral.java | 67 + .../backend/js/ast/JsBinaryOperation.java | 107 + .../backend/js/ast/JsBinaryOperator.java | 120 + .../dart/compiler/backend/js/ast/JsBlock.java | 44 + .../backend/js/ast/JsBooleanLiteral.java | 53 + .../dart/compiler/backend/js/ast/JsBreak.java | 46 + .../dart/compiler/backend/js/ast/JsCase.java | 39 + .../dart/compiler/backend/js/ast/JsCatch.java | 66 + .../compiler/backend/js/ast/JsCatchScope.java | 75 + .../backend/js/ast/JsConditional.java | 78 + .../compiler/backend/js/ast/JsContext.java | 27 + .../compiler/backend/js/ast/JsContinue.java | 46 + .../compiler/backend/js/ast/JsDebugger.java | 25 + .../compiler/backend/js/ast/JsDefault.java | 28 + .../compiler/backend/js/ast/JsDoWhile.java | 54 + .../dart/compiler/backend/js/ast/JsEmpty.java | 26 + .../compiler/backend/js/ast/JsExprStmt.java | 36 + .../compiler/backend/js/ast/JsExpression.java | 52 + .../dart/compiler/backend/js/ast/JsFor.java | 105 + .../dart/compiler/backend/js/ast/JsForIn.java | 71 + .../compiler/backend/js/ast/JsFunction.java | 225 ++ .../backend/js/ast/JsGlobalBlock.java | 19 + .../dart/compiler/backend/js/ast/JsIf.java | 65 + .../compiler/backend/js/ast/JsInvocation.java | 62 + .../dart/compiler/backend/js/ast/JsLabel.java | 52 + .../compiler/backend/js/ast/JsLiteral.java | 14 + .../compiler/backend/js/ast/JsModVisitor.java | 189 + .../dart/compiler/backend/js/ast/JsName.java | 114 + .../compiler/backend/js/ast/JsNameRef.java | 120 + .../dart/compiler/backend/js/ast/JsNew.java | 61 + .../dart/compiler/backend/js/ast/JsNode.java | 51 + .../backend/js/ast/JsNullLiteral.java | 46 + .../backend/js/ast/JsNumberLiteral.java | 53 + .../backend/js/ast/JsObjectLiteral.java | 66 + .../compiler/backend/js/ast/JsOperator.java | 32 + .../compiler/backend/js/ast/JsParameter.java | 41 + .../backend/js/ast/JsPostfixOperation.java | 42 + .../backend/js/ast/JsPrefixOperation.java | 69 + .../compiler/backend/js/ast/JsProgram.java | 172 + .../backend/js/ast/JsProgramFragment.java | 34 + .../backend/js/ast/JsPropertyInitializer.java | 56 + .../compiler/backend/js/ast/JsRegExp.java | 64 + .../compiler/backend/js/ast/JsReturn.java | 48 + .../compiler/backend/js/ast/JsRootScope.java | 50 + .../dart/compiler/backend/js/ast/JsScope.java | 298 ++ .../compiler/backend/js/ast/JsStatement.java | 22 + .../backend/js/ast/JsStringLiteral.java | 53 + .../compiler/backend/js/ast/JsSwitch.java | 47 + .../backend/js/ast/JsSwitchMember.java | 24 + .../compiler/backend/js/ast/JsThisRef.java | 51 + .../dart/compiler/backend/js/ast/JsThrow.java | 48 + .../dart/compiler/backend/js/ast/JsTry.java | 59 + .../backend/js/ast/JsUnaryOperation.java | 53 + .../backend/js/ast/JsUnaryOperator.java | 78 + .../backend/js/ast/JsValueLiteral.java | 24 + .../dart/compiler/backend/js/ast/JsVars.java | 115 + .../compiler/backend/js/ast/JsVisitable.java | 19 + .../compiler/backend/js/ast/JsVisitor.java | 433 ++ .../dart/compiler/backend/js/ast/JsWhile.java | 52 + .../compiler/backend/js/ast/NodeKind.java | 50 + .../dart/compiler/common/AbstractNode.java | 82 + .../compiler/common/GenerateSourceMap.java | 85 + .../dart/compiler/common/HasSourceInfo.java | 55 + .../dart/compiler/common/HasSymbol.java | 16 + .../com/google/dart/compiler/common/Name.java | 125 + .../dart/compiler/common/NameFactory.java | 226 ++ .../dart/compiler/common/SourceInfo.java | 59 + .../dart/compiler/common/SourceMapping.java | 52 + .../google/dart/compiler/common/Symbol.java | 16 + .../compiler/metrics/CompilerMetrics.java | 247 ++ .../dart/compiler/metrics/DartEventType.java | 63 + .../dart/compiler/metrics/JvmMetrics.java | 249 ++ .../metrics/SpeedTracerEventType.java | 33 + .../google/dart/compiler/metrics/Tracer.java | 959 +++++ .../dart/compiler/parser/AbstractParser.java | 136 + .../parser/CommentPreservingParser.java | 151 + .../parser/CompletionHooksParserBase.java | 449 +++ .../dart/compiler/parser/DartParser.java | 3520 ++++++++++++++++ .../dart/compiler/parser/DartScanner.java | 1388 +++++++ .../parser/DartScannerParserContext.java | 194 + .../dart/compiler/parser/ParserContext.java | 128 + .../google/dart/compiler/parser/Token.java | 259 ++ .../compiler/resolver/AbstractElement.java | 74 + .../dart/compiler/resolver/ClassElement.java | 50 + .../resolver/ClassElementImplementation.java | 366 ++ .../dart/compiler/resolver/ClassScope.java | 51 + .../compiler/resolver/ConstructorElement.java | 14 + .../ConstructorElementImplementation.java | 55 + .../compiler/resolver/CoreTypeProvider.java | 48 + .../CoreTypeProviderImplementation.java | 161 + .../resolver/CyclicDeclarationException.java | 21 + .../DuplicatedInterfaceException.java | 30 + .../compiler/resolver/DynamicElement.java | 17 + .../DynamicElementImplementation.java | 251 ++ .../dart/compiler/resolver/Element.java | 26 + .../dart/compiler/resolver/ElementKind.java | 38 + .../dart/compiler/resolver/Elements.java | 212 + .../compiler/resolver/EnclosingElement.java | 13 + .../dart/compiler/resolver/FieldElement.java | 17 + .../resolver/FieldElementImplementation.java | 92 + .../resolver/FunctionAliasElement.java | 20 + .../FunctionAliasElementImplementation.java | 60 + .../dart/compiler/resolver/LabelElement.java | 12 + .../resolver/LabelElementImplementation.java | 32 + .../compiler/resolver/LibraryElement.java | 17 + .../LibraryElementImplementation.java | 72 + .../dart/compiler/resolver/MemberBuilder.java | 459 +++ .../dart/compiler/resolver/MethodElement.java | 22 + .../resolver/MethodElementImplementation.java | 128 + .../compiler/resolver/ResolutionContext.java | 295 ++ .../resolver/ResolutionErrorListener.java | 15 + .../compiler/resolver/ResolveVisitor.java | 115 + .../dart/compiler/resolver/Resolver.java | 1268 ++++++ .../google/dart/compiler/resolver/Scope.java | 90 + .../dart/compiler/resolver/SuperElement.java | 13 + .../resolver/SuperElementImplementation.java | 29 + .../compiler/resolver/SupertypeResolver.java | 100 + .../resolver/TopLevelElementBuilder.java | 157 + .../resolver/TypeVariableElement.java | 25 + .../TypeVariableElementImplementation.java | 71 + .../compiler/resolver/VariableElement.java | 15 + .../VariableElementImplementation.java | 70 + .../dart/compiler/resolver/VoidElement.java | 34 + .../testing/TestCompilerConfiguration.java | 112 + .../compiler/testing/TestCompilerContext.java | 152 + .../testing/TestDartArtifactProvider.java | 38 + .../compiler/testing/TestLibrarySource.java | 143 + .../dart/compiler/type/AbstractType.java | 11 + .../dart/compiler/type/DynamicType.java | 24 + .../type/DynamicTypeImplementation.java | 126 + .../dart/compiler/type/FunctionAliasType.java | 15 + .../type/FunctionAliasTypeImplementation.java | 36 + .../dart/compiler/type/FunctionType.java | 38 + .../type/FunctionTypeImplementation.java | 185 + .../dart/compiler/type/InterfaceType.java | 41 + .../type/InterfaceTypeImplementation.java | 167 + .../com/google/dart/compiler/type/Type.java | 26 + .../dart/compiler/type/TypeAnalyzer.java | 1558 +++++++ .../google/dart/compiler/type/TypeKind.java | 27 + .../dart/compiler/type/TypeVariable.java | 34 + .../type/TypeVariableImplementation.java | 74 + .../com/google/dart/compiler/type/Types.java | 428 ++ .../google/dart/compiler/type/VoidType.java | 46 + .../compiler/util/AbstractTextOutput.java | 132 + .../google/dart/compiler/util/AstUtil.java | 208 + .../dart/compiler/util/DartSourceString.java | 73 + .../dart/compiler/util/DefaultTextOutput.java | 32 + .../com/google/dart/compiler/util/Hack.java | 16 + .../com/google/dart/compiler/util/Lists.java | 296 ++ .../com/google/dart/compiler/util/Maps.java | 160 + .../com/google/dart/compiler/util/Paths.java | 100 + .../google/dart/compiler/util/TextOutput.java | 37 + .../dart/runner/BundleLibrarySource.java | 62 + .../com/google/dart/runner/DartRunner.java | 407 ++ .../dart/runner/JavaScriptLauncher.java | 20 + .../com/google/dart/runner/RhinoLauncher.java | 221 + .../com/google/dart/runner/RunnerError.java | 19 + .../com/google/dart/runner/RunnerFlag.java | 17 + .../com/google/dart/runner/TestRunner.java | 109 + .../com/google/dart/runner/V8Launcher.java | 283 ++ .../dart/compiler/AbstractSourceFileTest.java | 56 + .../compiler/CodeCompletionParseTest.java | 86 + .../dart/compiler/CompilerTestCase.java | 248 ++ .../compiler/DartCompilerListenerTest.java | 93 + .../dart/compiler/DartLibrarySourceTest.java | 38 + .../google/dart/compiler/DartSourceTest.java | 52 + .../dart/compiler/DeltaAnalyzerTest.java | 107 + .../com/google/dart/compiler/DeltaBench.java | 102 + .../com/google/dart/compiler/IdeTest.java | 243 ++ .../com/google/dart/compiler/IdeTests.java | 23 + .../dart/compiler/MockArtifactProvider.java | 92 + .../compiler/MockBundleLibrarySource.java | 181 + .../dart/compiler/MockLibrarySource.java | 91 + .../com/google/dart/compiler/SourceTest.java | 39 + .../compiler/SystemLibraryManagerTest.java | 85 + .../google/dart/compiler/ast/AstTests.java | 23 + .../compiler/ast/DartToSourceVisitorTest.java | 83 + .../TypeHeuristicImplementationTest.java | 937 +++++ .../testArrayIncompatibleArrayOperator.dart | 4 + .../common/testCombinedExpressions.dart | 4 + .../common/testCompatibleBinaryOp.dart | 4 + .../backend/common/testCompatibleFields.dart | 4 + .../backend/common/testCompatibleMethods.dart | 4 + .../common/testIncompatibleBinaryOp1.dart | 4 + .../common/testIncompatibleBinaryOp2.dart | 4 + .../common/testIncompatibleFields.dart | 4 + .../common/testIncompatibleFields2.dart | 4 + .../testIncompatibleFieldsWithGetter.dart | 4 + .../testIncompatibleFieldsWithSetter.dart | 4 + .../common/testIncompatibleLogicalOp.dart | 4 + .../common/testIncompatibleMethods.dart | 4 + .../js/ClosureJsCodingConventionTest.java | 30 + .../compiler/backend/js/ComparingVisitor.java | 367 ++ .../dart/compiler/backend/js/ExprOptTest.java | 30 + .../backend/js/FlatteningVisitor.java | 53 + .../backend/js/JavaScriptStringTest.java | 53 + .../backend/js/JsArrayExprOptTest.java | 93 + .../compiler/backend/js/JsBackendTests.java | 37 + .../backend/js/JsBinaryExprOptTest.java | 192 + .../backend/js/JsClosureExprOptTest.java | 97 + .../js/JsCompoundBinaryExprOptTest.java | 202 + .../backend/js/JsConstExprOptTest.java | 51 + .../backend/js/JsConstructorOptTest.java | 144 + .../backend/js/JsFieldAccessOptTest.java | 69 + .../dart/compiler/backend/js/JsScopeTest.java | 81 + .../backend/js/JsUnaryExprOptTest.java | 73 + .../dart/compiler/backend/js/RttTest.java | 78 + .../compiler/backend/js/SnippetTestCase.java | 160 + .../compiler/backend/js/testClosureOpt.dart | 66 + .../backend/js/testCompoundBinaryExprOpt.dart | 141 + .../backend/js/testConstantExprOpt.dart | 41 + .../backend/js/testConstructorOptTest.dart | 34 + .../backend/js/testFieldAccessExprOpt.dart | 85 + .../compiler/backend/js/testListExprOpt.dart | 51 + .../backend/js/testListSubTypeExprOpt.dart | 39 + .../backend/js/testLiteralExpressions.dart | 95 + .../compiler/backend/js/testRuntimeTypes.dart | 41 + .../backend/js/testUnaryDecIncExprOpt.dart | 70 + .../common/ApplicationSourceFileTest.dart | 8 + .../dart/compiler/common/CommonTests.java | 26 + .../common/GenerateSourceMapTest.java | 308 ++ .../common/LibrarySourceFileTest.dart | 8 + .../common/LibrarySourceFileTest.java | 121 + .../dart/compiler/common/NameFactoryTest.java | 127 + .../google/dart/compiler/common/NameTest.java | 145 + .../dart/compiler/common/NameTestCase.java | 37 + .../dart/compiler/end2end/BasicOptTest.java | 17 + .../dart/compiler/end2end/BasicTest.dart | 6 + .../dart/compiler/end2end/BasicTest.java | 19 + .../dart/compiler/end2end/BasicTest_app.dart | 5 + .../compiler/end2end/End2EndOptTests.java | 24 + .../compiler/end2end/End2EndTestCase.java | 122 + .../dart/compiler/end2end/End2EndTests.java | 27 + .../compiler/end2end/MainMethodOptTest.java | 17 + .../dart/compiler/end2end/MainMethodTest.java | 130 + .../compiler/end2end/NamedParameterTest.dart | 221 + .../compiler/end2end/NamedParameterTest.java | 51 + .../dart/compiler/end2end/NativeTest.dart | 62 + .../dart/compiler/end2end/NativeTest.js | 25 + .../dart/compiler/end2end/NativeTestLib.dart | 6 + .../end2end/RedirectedConstructorTest.dart | 26 + .../inc/IncrementalCompilationTest.java | 725 ++++ .../dart/compiler/end2end/inc/my.app.dart | 14 + .../google/dart/compiler/end2end/inc/my.dart | 62 + .../compiler/end2end/inc/my.merged.app.dart | 13 + .../dart/compiler/end2end/inc/my.no5ref.dart | 50 + .../compiler/end2end/inc/my.nuke5.app.dart | 13 + .../dart/compiler/end2end/inc/mybase.dart | 8 + .../compiler/end2end/inc/mybase.no5ref.dart | 7 + .../dart/compiler/end2end/inc/myother0.dart | 18 + .../end2end/inc/myother0.fillthehole.dart | 24 + .../inc/myother0.fillthemethodhole.dart | 24 + .../end2end/inc/myother0.fillthenothole.dart | 24 + .../inc/myother0.globalfunctionchange.dart | 19 + .../end2end/inc/myother0.globalvarchange.dart | 18 + .../end2end/inc/myother0.newstaticmethod.dart | 19 + .../inc/myother0.returntypechange.dart | 18 + .../compiler/end2end/inc/myother1.change.dart | 10 + .../dart/compiler/end2end/inc/myother1.dart | 11 + .../compiler/end2end/inc/myother2.change.dart | 14 + .../dart/compiler/end2end/inc/myother2.dart | 15 + .../dart/compiler/end2end/inc/myother3.dart | 7 + .../end2end/inc/myother3.newstaticfield.dart | 8 + .../dart/compiler/end2end/inc/myother34.dart | 10 + .../compiler/end2end/inc/myother4.change.dart | 8 + .../dart/compiler/end2end/inc/myother4.dart | 7 + .../end2end/inc/myother4.newstaticfield.dart | 8 + .../compiler/end2end/inc/myother5.change.dart | 10 + .../dart/compiler/end2end/inc/myother5.dart | 9 + .../compiler/end2end/inc/myother6.change.dart | 13 + .../dart/compiler/end2end/inc/myother6.dart | 12 + .../end2end/inc/myother6.removeclass.dart | 8 + .../dart/compiler/end2end/inc/some.dart | 8 + .../compiler/end2end/inc/some.intfchange.dart | 8 + .../dart/compiler/end2end/inc/some.lib.dart | 7 + .../compiler/end2end/inc/some.newmethod.dart | 9 + .../end2end/inc/someimpl.bodychange.dart | 10 + .../compiler/end2end/inc/someimpl.change.dart | 11 + .../dart/compiler/end2end/inc/someimpl.dart | 10 + .../compiler/end2end/inc/someimpl.lib.dart | 7 + .../compiler/parser/AbstractParserTest.java | 165 + .../parser/BadCommentNegativeTest.dart | 4 + .../dart/compiler/parser/CPParserTest.java | 71 + .../dart/compiler/parser/CatchFinally.dart | 119 + .../compiler/parser/ClassesInterfaces.dart | 127 + .../dart/compiler/parser/CommentTest.java | 105 + .../google/dart/compiler/parser/Comments.dart | 7 + .../compiler/parser/DartASTValidator.java | 637 +++ .../compiler/parser/DartParserRunner.java | 198 + .../dart/compiler/parser/DietParserTest.java | 22 + .../dart/compiler/parser/Directives.dart | 16 + .../dart/compiler/parser/Directives2.dart | 11 + .../parser/ErrorMessageLocationTest.java | 38 + .../FactoryInitializersNegativeTest.dart | 12 + .../compiler/parser/FormalParameters.dart | 10 + .../compiler/parser/FunctionInterfaces.dart | 28 + .../dart/compiler/parser/FunctionTypes.dart | 21 + .../dart/compiler/parser/GenericTypedef.dart | 5 + .../dart/compiler/parser/GenericTypes.dart | 15 + .../compiler/parser/LibraryParserTest.java | 190 + .../compiler/parser/ListObjectLiterals.dart | 32 + .../compiler/parser/MethodSignatures.dart | 20 + .../compiler/parser/NegativeParserTest.java | 45 + .../dart/compiler/parser/NewWithPrefix.dart | 27 + .../compiler/parser/ParserEventsTest.java | 756 ++++ .../compiler/parser/ParserRoundTripTest.java | 78 + .../dart/compiler/parser/ParserTests.java | 31 + .../parser/RedirectedConstructor.dart | 34 + .../google/dart/compiler/parser/Shifting.dart | 15 + .../dart/compiler/parser/StringBuffer.dart | 88 + .../google/dart/compiler/parser/Strings.dart | 33 + .../parser/StringsErrorsNegativeTest.dart | 12 + .../dart/compiler/parser/SuperCalls.dart | 29 + .../dart/compiler/parser/SyntaxTest.java | 175 + .../dart/compiler/parser/TerminationTest.java | 125 + .../compiler/parser/TerminationTests.java | 22 + .../google/dart/compiler/parser/TopLevel.dart | 13 + .../google/dart/compiler/parser/TryCatch.dart | 46 + .../compiler/parser/TryCatchNegative.dart | 9 + .../compiler/parser/ValidatingSyntaxTest.java | 90 + .../google/dart/compiler/parser/VoidTest.java | 82 + .../BadNamedConstructorNegativeTest.dart | 7 + .../ClassExtendsInterfaceNegativeTest.dart | 12 + .../ClassImplementsClassNegativeTest.dart | 12 + ...mplementsUnknownInterfaceNegativeTest.dart | 10 + ...onstRedirectedConstructorNegativeTest.dart | 11 + .../resolver/ConstSuperNegativeTest1.dart | 14 + .../resolver/ConstSuperNegativeTest2.dart | 13 + .../resolver/ConstSuperNegativeTest3.dart | 17 + ...stVariableInitializationNegativeTest1.dart | 12 + ...stVariableInitializationNegativeTest2.dart | 12 + ...clicRedirectedConstructorNegativeTest.dart | 9 + .../resolver/Initializer1NegativeTest.dart | 9 + .../resolver/Initializer2NegativeTest.dart | 10 + .../resolver/Initializer3NegativeTest.dart | 14 + .../resolver/Initializer4NegativeTest.dart | 11 + .../resolver/Initializer5NegativeTest.dart | 10 + .../resolver/Initializer6NegativeTest.dart | 10 + .../resolver/NameShadowNegativeTest1.dart | 10 + .../resolver/NameShadowNegativeTest10.dart | 11 + .../resolver/NameShadowNegativeTest11.dart | 10 + .../resolver/NameShadowNegativeTest2.dart | 11 + .../resolver/NameShadowNegativeTest4.dart | 10 + .../resolver/NameShadowNegativeTest5.dart | 10 + .../resolver/NameShadowNegativeTest6.dart | 10 + .../resolver/NameShadowNegativeTest7.dart | 11 + .../resolver/NameShadowNegativeTest8.dart | 11 + .../resolver/NameShadowNegativeTest9.dart | 10 + .../resolver/NegativeResolverTest.java | 189 + .../ParameterInitializerNegativeTest1.dart | 10 + .../ParameterInitializerNegativeTest2.dart | 12 + .../ParameterInitializerNegativeTest3.dart | 10 + .../dart/compiler/resolver/ResolverTest.java | 360 ++ .../compiler/resolver/ResolverTestCase.java | 193 + .../dart/compiler/resolver/ResolverTests.java | 28 + .../StaticInstanceCallNegativeTest.dart | 12 + .../StaticSuperFieldNegativeTest.dart | 15 + .../StaticSuperGetterNegativeTest.dart | 15 + .../StaticSuperMethodNegativeTest.dart | 15 + ...aticToInstanceInvocationNegativeTest1.dart | 10 + ...aticToInstanceInvocationNegativeTest2.dart | 10 + .../UnresolvedSuperFieldNegativeTest.dart | 14 + .../dart/compiler/type/FunctionTypeTest.java | 113 + .../dart/compiler/type/TypeAnalyzerBench.java | 182 + .../dart/compiler/type/TypeAnalyzerTest.java | 1622 ++++++++ .../google/dart/compiler/type/TypeTest.java | 123 + .../dart/compiler/type/TypeTestCase.java | 179 + .../google/dart/compiler/type/TypeTests.java | 26 + .../compiler/type/class_with_methods.dart | 17 + .../compiler/type/class_with_operators.dart | 61 + .../compiler/type/class_with_supertypes.dart | 27 + .../type/class_with_type_parameter.dart | 13 + .../type/classes_with_properties.dart | 35 + .../dart/compiler/type/covariant_class.dart | 43 + .../type/generic_class_with_supertypes.dart | 20 + .../google/dart/compiler/type/interfaces.dart | 13 + .../type/named_function_type_alias.dart | 5 + .../google/dart/compiler/util/PathsTest.java | 132 + .../google/dart/compiler/util/UtilTests.java | 24 + .../google/dart/compiler/vm/DartOptTests.java | 26 + .../com/google/dart/compiler/vm/DartTest.java | 18 + .../google/dart/compiler/vm/DartTests.java | 26 + .../dart/compiler/vm/ImportedDartOptTest.java | 23 + .../dart/compiler/vm/ImportedLibOptTest.java | 17 + .../google/dart/compiler/vm/LibOptTests.java | 27 + .../com/google/dart/compiler/vm/LibTest.java | 18 + .../com/google/dart/compiler/vm/LibTests.java | 23 + .../com/google/dart/compiler/vm/VmTest.java | 77 + .../google/dart/corelib/SharedTestCase.java | 294 ++ .../com/google/dart/corelib/SharedTests.java | 113 + .../google/dart/corelib/TestSharedTests.java | 40 + .../com/google/dart/runner/AllTests.java | 18 + .../google/dart/runner/TestRunnerTest.java | 22 + compiler/lib/clock.dart | 25 + compiler/lib/corelib.dart | 41 + compiler/lib/corelib_impl.dart | 47 + compiler/lib/error.dart | 18 + compiler/lib/implementation/array.dart | 269 ++ compiler/lib/implementation/array.js | 42 + compiler/lib/implementation/arrays.dart | 66 + compiler/lib/implementation/bool.dart | 19 + compiler/lib/implementation/bool.js | 27 + compiler/lib/implementation/collections.dart | 43 + compiler/lib/implementation/core.dart | 46 + compiler/lib/implementation/core.js | 474 +++ .../date_time_implementation.dart | 171 + .../date_time_implementation.js | 80 + compiler/lib/implementation/isolate.dart | 195 + compiler/lib/implementation/isolate.js | 536 +++ .../implementation/isolate_serialization.dart | 264 ++ compiler/lib/implementation/math_natives.dart | 26 + compiler/lib/implementation/math_natives.js | 56 + compiler/lib/implementation/number.dart | 66 + compiler/lib/implementation/number.js | 153 + compiler/lib/implementation/object.js | 7 + compiler/lib/implementation/print.js | 14 + compiler/lib/implementation/regexp.dart | 135 + compiler/lib/implementation/regexp.js | 65 + compiler/lib/implementation/rtt.js | 200 + compiler/lib/implementation/string.dart | 230 ++ compiler/lib/implementation/string.js | 141 + compiler/lib/implementation/string_base.dart | 23 + .../lib/implementation/string_buffer.dart | 84 + .../time_zone_implementation.dart | 35 + compiler/lib/implementation/type_token.dart | 11 + compiler/lib/object.dart | 23 + compiler/lib/print.dart | 19 + compiler/scripts/build_dartc_for_perf_metrics | 65 + compiler/scripts/compiler_compare.sh | 191 + compiler/scripts/compiler_metrics.sh | 127 + compiler/scripts/compiler_series_test.sh | 226 ++ compiler/scripts/dartc.sh | 15 + compiler/scripts/dartc_build_wrapper.py | 55 + compiler/scripts/dartc_metrics.sh | 71 + compiler/scripts/dartc_run.sh | 64 + compiler/scripts/dartc_size.sh | 92 + compiler/scripts/dartc_test.sh | 31 + compiler/scripts/dartc_wrapper.py | 28 + compiler/scripts/generate_my_projects.py | 61 + compiler/scripts/metrics_math.sh | 59 + compiler/scripts/sample_metrics.sh | 18 + compiler/tests/dart/dart.status | 31 + compiler/tests/dart/src/TemplateTest.dart | 9 + compiler/tests/dart/testcfg.py | 8 + compiler/tests/dartc/dartc.status | 35 + compiler/tests/dartc/testcfg.py | 97 + 638 files changed, 73883 insertions(+) create mode 100644 compiler/README create mode 100644 compiler/api.dart create mode 100644 compiler/build.xml create mode 100644 compiler/codereview.settings create mode 100644 compiler/dart-compiler.gyp create mode 100644 compiler/dartc.mf create mode 100644 compiler/dartc.xml create mode 100644 compiler/dartium.gyp create mode 100644 compiler/eclipse.workspace/README.txt create mode 100644 compiler/eclipse.workspace/dartc/.classpath create mode 100644 compiler/eclipse.workspace/dartc/.project create mode 100644 compiler/eclipse.workspace/dartc/deps/README.txt create mode 100644 compiler/eclipse.workspace/tests/.classpath create mode 100644 compiler/eclipse.workspace/tests/.project create mode 100644 compiler/eclipse.workspace/tests/SharedTests.launch create mode 100644 compiler/eclipse.workspace/tests/TestSharedTests.launch create mode 100644 compiler/eclipse.workspace/tests/dartc_jscomp_suites.launch create mode 100644 compiler/eclipse.workspace/tests/dartc_tests_suites.launch create mode 100644 compiler/generate_source_list.py create mode 100644 compiler/java/com/google/dart/compiler/Backend.java create mode 100644 compiler/java/com/google/dart/compiler/CommandLineOptions.java create mode 100644 compiler/java/com/google/dart/compiler/CompilerConfiguration.java create mode 100644 compiler/java/com/google/dart/compiler/DartArtifactProvider.java create mode 100644 compiler/java/com/google/dart/compiler/DartCompilationError.java create mode 100644 compiler/java/com/google/dart/compiler/DartCompilationPhase.java create mode 100644 compiler/java/com/google/dart/compiler/DartCompiler.java create mode 100644 compiler/java/com/google/dart/compiler/DartCompilerContext.java create mode 100644 compiler/java/com/google/dart/compiler/DartCompilerErrorCode.java create mode 100644 compiler/java/com/google/dart/compiler/DartCompilerListener.java create mode 100644 compiler/java/com/google/dart/compiler/DartCompilerMainContext.java create mode 100644 compiler/java/com/google/dart/compiler/DartIsolateStubGeneratorCompilerConfiguration.java create mode 100644 compiler/java/com/google/dart/compiler/DartSource.java create mode 100644 compiler/java/com/google/dart/compiler/DefaultCompilerConfiguration.java create mode 100644 compiler/java/com/google/dart/compiler/DefaultDartArtifactProvider.java create mode 100644 compiler/java/com/google/dart/compiler/DefaultDartCompilerListener.java create mode 100644 compiler/java/com/google/dart/compiler/DefaultErrorFormatter.java create mode 100644 compiler/java/com/google/dart/compiler/DefaultLibrarySource.java create mode 100644 compiler/java/com/google/dart/compiler/DelegatingCompilerConfiguration.java create mode 100644 compiler/java/com/google/dart/compiler/DeltaAnalyzer.java create mode 100644 compiler/java/com/google/dart/compiler/ErrorCode.java create mode 100644 compiler/java/com/google/dart/compiler/ErrorFormatter.java create mode 100644 compiler/java/com/google/dart/compiler/InternalCompilerException.java create mode 100644 compiler/java/com/google/dart/compiler/LibraryDeps.java create mode 100644 compiler/java/com/google/dart/compiler/LibraryDepsVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/LibrarySource.java create mode 100644 compiler/java/com/google/dart/compiler/PrettyErrorFormatter.java create mode 100644 compiler/java/com/google/dart/compiler/Source.java create mode 100644 compiler/java/com/google/dart/compiler/SourceDelta.java create mode 100644 compiler/java/com/google/dart/compiler/SystemLibrary.java create mode 100644 compiler/java/com/google/dart/compiler/SystemLibraryManager.java create mode 100644 compiler/java/com/google/dart/compiler/UnitTestBatchRunner.java create mode 100644 compiler/java/com/google/dart/compiler/UrlDartSource.java create mode 100644 compiler/java/com/google/dart/compiler/UrlLibrarySource.java create mode 100644 compiler/java/com/google/dart/compiler/UrlSource.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartArrayAccess.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartArrayLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartAssertion.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartBinaryExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartBlock.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartBooleanLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartBreakStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartCase.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartCatchBlock.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartClass.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartClassMember.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartComment.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartConditional.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartContext.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartContinueStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartDeclaration.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartDefault.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartDirective.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartDoWhileStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartDoubleLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartEmptyStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartExprStmt.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartField.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartFieldDefinition.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartForInStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartForStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartFunction.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartFunctionExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartFunctionObjectInvocation.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartFunctionTypeAlias.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartGotoStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartIdentifier.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartIfStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartImportDirective.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartInitializer.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartIntegerLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartInvocation.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartLabel.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartLibraryDirective.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartMapLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartMapLiteralEntry.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartMethodDefinition.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartMethodInvocation.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartModVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartNamedExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartNativeBlock.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartNativeDirective.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartNewExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartNode.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartNodeTraverser.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartNullLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartParameter.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartParameterizedNode.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartParenthesizedExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartPlainVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartPropertyAccess.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartRedirectConstructorInvocation.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartResourceDirective.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartReturnStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartSourceDirective.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartStringInterpolation.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartStringLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartSuperConstructorInvocation.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartSuperExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartSwitchMember.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartSwitchStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartThisExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartThrowStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartToSourceVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartTryStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartTypeExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartTypeNode.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartTypeParameter.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartTypedLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartUnaryExpression.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartUnit.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartUnqualifiedInvocation.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartVariable.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartVariableStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartVisitable.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/ast/DartWhileStatement.java create mode 100644 compiler/java/com/google/dart/compiler/ast/ElementReference.java create mode 100644 compiler/java/com/google/dart/compiler/ast/LibraryNode.java create mode 100644 compiler/java/com/google/dart/compiler/ast/LibraryUnit.java create mode 100644 compiler/java/com/google/dart/compiler/ast/Modifiers.java create mode 100644 compiler/java/com/google/dart/compiler/backend/common/AbstractBackend.java create mode 100644 compiler/java/com/google/dart/compiler/backend/common/TypeHeuristic.java create mode 100644 compiler/java/com/google/dart/compiler/backend/common/TypeHeuristicImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/backend/dart/DartBackend.java create mode 100644 compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationGenerator.java create mode 100644 compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/doc/ElementNameComparator.java create mode 100644 compiler/java/com/google/dart/compiler/backend/doc/LinkInformation.java create mode 100644 compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/BasicOptimizationStrategy.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/Cloner.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ClosureJsCodingConvention.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/DartMangler.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/DollarMangler.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/GenerateNamesAndScopes.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsNameProvider.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsNamer.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsNormalizer.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsParserException.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsPrettyNamer.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/NoOptimizationStrategy.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/NormalizedVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/Normalizer.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/OptimizationStrategy.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ScopeRootInfo.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/TranslationContext.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/TraversalContextProvider.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/UncheckedJsParserException.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/HasArguments.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/HasCondition.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/HasName.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsBlock.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsBooleanLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsBreak.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsCase.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsCatch.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsCatchScope.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsConditional.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsContext.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsContinue.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsDebugger.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsDefault.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsDoWhile.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsEmpty.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsExprStmt.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsExpression.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsFor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsForIn.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsFunction.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsIf.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsInvocation.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsLabel.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsModVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsName.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsNameRef.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsNew.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsNode.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsOperator.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsParameter.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsProgram.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsRegExp.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsReturn.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsRootScope.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsScope.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsStatement.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitch.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsThisRef.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsThrow.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsTry.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsVars.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitable.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/JsWhile.java create mode 100644 compiler/java/com/google/dart/compiler/backend/js/ast/NodeKind.java create mode 100644 compiler/java/com/google/dart/compiler/common/AbstractNode.java create mode 100644 compiler/java/com/google/dart/compiler/common/GenerateSourceMap.java create mode 100644 compiler/java/com/google/dart/compiler/common/HasSourceInfo.java create mode 100644 compiler/java/com/google/dart/compiler/common/HasSymbol.java create mode 100644 compiler/java/com/google/dart/compiler/common/Name.java create mode 100644 compiler/java/com/google/dart/compiler/common/NameFactory.java create mode 100644 compiler/java/com/google/dart/compiler/common/SourceInfo.java create mode 100644 compiler/java/com/google/dart/compiler/common/SourceMapping.java create mode 100644 compiler/java/com/google/dart/compiler/common/Symbol.java create mode 100644 compiler/java/com/google/dart/compiler/metrics/CompilerMetrics.java create mode 100644 compiler/java/com/google/dart/compiler/metrics/DartEventType.java create mode 100644 compiler/java/com/google/dart/compiler/metrics/JvmMetrics.java create mode 100644 compiler/java/com/google/dart/compiler/metrics/SpeedTracerEventType.java create mode 100644 compiler/java/com/google/dart/compiler/metrics/Tracer.java create mode 100644 compiler/java/com/google/dart/compiler/parser/AbstractParser.java create mode 100644 compiler/java/com/google/dart/compiler/parser/CommentPreservingParser.java create mode 100644 compiler/java/com/google/dart/compiler/parser/CompletionHooksParserBase.java create mode 100644 compiler/java/com/google/dart/compiler/parser/DartParser.java create mode 100644 compiler/java/com/google/dart/compiler/parser/DartScanner.java create mode 100644 compiler/java/com/google/dart/compiler/parser/DartScannerParserContext.java create mode 100644 compiler/java/com/google/dart/compiler/parser/ParserContext.java create mode 100644 compiler/java/com/google/dart/compiler/parser/Token.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/AbstractElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ClassElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ClassElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ClassScope.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ConstructorElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ConstructorElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/CoreTypeProvider.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/CoreTypeProviderImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/CyclicDeclarationException.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/DuplicatedInterfaceException.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/DynamicElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/DynamicElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/Element.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ElementKind.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/Elements.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/EnclosingElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/FieldElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/FieldElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/FunctionAliasElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/FunctionAliasElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/LabelElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/LabelElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/LibraryElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/LibraryElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/MemberBuilder.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/MethodElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/MethodElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ResolutionErrorListener.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/ResolveVisitor.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/Resolver.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/Scope.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/SuperElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/SuperElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/SupertypeResolver.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/TypeVariableElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/TypeVariableElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/VariableElement.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/VariableElementImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/resolver/VoidElement.java create mode 100644 compiler/java/com/google/dart/compiler/testing/TestCompilerConfiguration.java create mode 100644 compiler/java/com/google/dart/compiler/testing/TestCompilerContext.java create mode 100644 compiler/java/com/google/dart/compiler/testing/TestDartArtifactProvider.java create mode 100644 compiler/java/com/google/dart/compiler/testing/TestLibrarySource.java create mode 100644 compiler/java/com/google/dart/compiler/type/AbstractType.java create mode 100644 compiler/java/com/google/dart/compiler/type/DynamicType.java create mode 100644 compiler/java/com/google/dart/compiler/type/DynamicTypeImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/type/FunctionAliasType.java create mode 100644 compiler/java/com/google/dart/compiler/type/FunctionAliasTypeImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/type/FunctionType.java create mode 100644 compiler/java/com/google/dart/compiler/type/FunctionTypeImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/type/InterfaceType.java create mode 100644 compiler/java/com/google/dart/compiler/type/InterfaceTypeImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/type/Type.java create mode 100644 compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java create mode 100644 compiler/java/com/google/dart/compiler/type/TypeKind.java create mode 100644 compiler/java/com/google/dart/compiler/type/TypeVariable.java create mode 100644 compiler/java/com/google/dart/compiler/type/TypeVariableImplementation.java create mode 100644 compiler/java/com/google/dart/compiler/type/Types.java create mode 100644 compiler/java/com/google/dart/compiler/type/VoidType.java create mode 100644 compiler/java/com/google/dart/compiler/util/AbstractTextOutput.java create mode 100644 compiler/java/com/google/dart/compiler/util/AstUtil.java create mode 100644 compiler/java/com/google/dart/compiler/util/DartSourceString.java create mode 100644 compiler/java/com/google/dart/compiler/util/DefaultTextOutput.java create mode 100644 compiler/java/com/google/dart/compiler/util/Hack.java create mode 100644 compiler/java/com/google/dart/compiler/util/Lists.java create mode 100644 compiler/java/com/google/dart/compiler/util/Maps.java create mode 100644 compiler/java/com/google/dart/compiler/util/Paths.java create mode 100644 compiler/java/com/google/dart/compiler/util/TextOutput.java create mode 100644 compiler/java/com/google/dart/runner/BundleLibrarySource.java create mode 100644 compiler/java/com/google/dart/runner/DartRunner.java create mode 100644 compiler/java/com/google/dart/runner/JavaScriptLauncher.java create mode 100644 compiler/java/com/google/dart/runner/RhinoLauncher.java create mode 100644 compiler/java/com/google/dart/runner/RunnerError.java create mode 100644 compiler/java/com/google/dart/runner/RunnerFlag.java create mode 100644 compiler/java/com/google/dart/runner/TestRunner.java create mode 100644 compiler/java/com/google/dart/runner/V8Launcher.java create mode 100644 compiler/javatests/com/google/dart/compiler/AbstractSourceFileTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/CodeCompletionParseTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/CompilerTestCase.java create mode 100644 compiler/javatests/com/google/dart/compiler/DartCompilerListenerTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/DartLibrarySourceTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/DartSourceTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/DeltaAnalyzerTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/DeltaBench.java create mode 100644 compiler/javatests/com/google/dart/compiler/IdeTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/IdeTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/MockArtifactProvider.java create mode 100644 compiler/javatests/com/google/dart/compiler/MockBundleLibrarySource.java create mode 100644 compiler/javatests/com/google/dart/compiler/MockLibrarySource.java create mode 100644 compiler/javatests/com/google/dart/compiler/SourceTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/SystemLibraryManagerTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/ast/AstTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/ast/DartToSourceVisitorTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/TypeHeuristicImplementationTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testArrayIncompatibleArrayOperator.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testCombinedExpressions.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleBinaryOp.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleFields.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleMethods.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp1.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithGetter.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithSetter.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleLogicalOp.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleMethods.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/ClosureJsCodingConventionTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/ComparingVisitor.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/ExprOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/FlatteningVisitor.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JavaScriptStringTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsArrayExprOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsBackendTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsBinaryExprOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsClosureExprOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsCompoundBinaryExprOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsConstExprOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsConstructorOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsFieldAccessOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsScopeTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/JsUnaryExprOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/RttTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/SnippetTestCase.java create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testClosureOpt.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testCompoundBinaryExprOpt.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testConstantExprOpt.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testConstructorOptTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testFieldAccessExprOpt.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testListExprOpt.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testListSubTypeExprOpt.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testLiteralExpressions.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testRuntimeTypes.dart create mode 100644 compiler/javatests/com/google/dart/compiler/backend/js/testUnaryDecIncExprOpt.dart create mode 100644 compiler/javatests/com/google/dart/compiler/common/ApplicationSourceFileTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/common/CommonTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/common/GenerateSourceMapTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/common/NameFactoryTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/common/NameTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/common/NameTestCase.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/BasicOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/BasicTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/BasicTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/BasicTest_app.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/End2EndOptTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/End2EndTestCase.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/End2EndTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/MainMethodOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/MainMethodTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/NativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/NativeTest.js create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/NativeTestLib.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/RedirectedConstructorTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/IncrementalCompilationTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/my.app.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/my.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/my.merged.app.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/my.no5ref.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/my.nuke5.app.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.no5ref.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthehole.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthemethodhole.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthenothole.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalfunctionchange.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalvarchange.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.newstaticmethod.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.returntypechange.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.change.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.change.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.newstaticfield.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother34.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.change.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.newstaticfield.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.change.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.change.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.removeclass.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/some.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/some.intfchange.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/some.lib.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/some.newmethod.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.bodychange.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.change.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.dart create mode 100644 compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.lib.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/AbstractParserTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/BadCommentNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/CPParserTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/CatchFinally.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/CommentTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/Comments.dart create mode 100755 compiler/javatests/com/google/dart/compiler/parser/DartASTValidator.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/DartParserRunner.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/DietParserTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/Directives.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/Directives2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/ErrorMessageLocationTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/FactoryInitializersNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/FormalParameters.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/FunctionInterfaces.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/FunctionTypes.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/GenericTypedef.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/GenericTypes.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/LibraryParserTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/ListObjectLiterals.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/MethodSignatures.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/NewWithPrefix.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/ParserEventsTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/ParserRoundTripTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/ParserTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/RedirectedConstructor.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/Shifting.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/StringBuffer.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/Strings.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/StringsErrorsNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/SuperCalls.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/TerminationTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/TerminationTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/TopLevel.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/TryCatch.dart create mode 100644 compiler/javatests/com/google/dart/compiler/parser/TryCatchNegative.dart create mode 100755 compiler/javatests/com/google/dart/compiler/parser/ValidatingSyntaxTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/parser/VoidTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/BadNamedConstructorNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ClassExtendsInterfaceNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsClassNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsUnknownInterfaceNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ConstRedirectedConstructorNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest1.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest3.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest1.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/CyclicRedirectedConstructorNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/Initializer1NegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/Initializer2NegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/Initializer3NegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/Initializer4NegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/Initializer5NegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/Initializer6NegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest1.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest10.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest11.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest4.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest5.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest6.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest7.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest8.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest9.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/NegativeResolverTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest1.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest3.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ResolverTestCase.java create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/ResolverTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/StaticInstanceCallNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/StaticSuperFieldNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/StaticSuperGetterNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/StaticSuperMethodNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest1.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest2.dart create mode 100644 compiler/javatests/com/google/dart/compiler/resolver/UnresolvedSuperFieldNegativeTest.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/FunctionTypeTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerBench.java create mode 100644 compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/type/TypeTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/type/TypeTestCase.java create mode 100644 compiler/javatests/com/google/dart/compiler/type/TypeTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/type/class_with_methods.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/class_with_operators.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/class_with_supertypes.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/class_with_type_parameter.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/classes_with_properties.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/covariant_class.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/generic_class_with_supertypes.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/interfaces.dart create mode 100644 compiler/javatests/com/google/dart/compiler/type/named_function_type_alias.dart create mode 100644 compiler/javatests/com/google/dart/compiler/util/PathsTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/util/UtilTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/DartOptTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/DartTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/DartTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/ImportedDartOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/ImportedLibOptTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/LibOptTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/LibTest.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/LibTests.java create mode 100644 compiler/javatests/com/google/dart/compiler/vm/VmTest.java create mode 100644 compiler/javatests/com/google/dart/corelib/SharedTestCase.java create mode 100644 compiler/javatests/com/google/dart/corelib/SharedTests.java create mode 100644 compiler/javatests/com/google/dart/corelib/TestSharedTests.java create mode 100644 compiler/javatests/com/google/dart/runner/AllTests.java create mode 100644 compiler/javatests/com/google/dart/runner/TestRunnerTest.java create mode 100644 compiler/lib/clock.dart create mode 100644 compiler/lib/corelib.dart create mode 100644 compiler/lib/corelib_impl.dart create mode 100644 compiler/lib/error.dart create mode 100644 compiler/lib/implementation/array.dart create mode 100644 compiler/lib/implementation/array.js create mode 100644 compiler/lib/implementation/arrays.dart create mode 100644 compiler/lib/implementation/bool.dart create mode 100644 compiler/lib/implementation/bool.js create mode 100644 compiler/lib/implementation/collections.dart create mode 100644 compiler/lib/implementation/core.dart create mode 100644 compiler/lib/implementation/core.js create mode 100644 compiler/lib/implementation/date_time_implementation.dart create mode 100644 compiler/lib/implementation/date_time_implementation.js create mode 100644 compiler/lib/implementation/isolate.dart create mode 100644 compiler/lib/implementation/isolate.js create mode 100644 compiler/lib/implementation/isolate_serialization.dart create mode 100644 compiler/lib/implementation/math_natives.dart create mode 100644 compiler/lib/implementation/math_natives.js create mode 100644 compiler/lib/implementation/number.dart create mode 100644 compiler/lib/implementation/number.js create mode 100644 compiler/lib/implementation/object.js create mode 100644 compiler/lib/implementation/print.js create mode 100644 compiler/lib/implementation/regexp.dart create mode 100644 compiler/lib/implementation/regexp.js create mode 100644 compiler/lib/implementation/rtt.js create mode 100644 compiler/lib/implementation/string.dart create mode 100644 compiler/lib/implementation/string.js create mode 100644 compiler/lib/implementation/string_base.dart create mode 100644 compiler/lib/implementation/string_buffer.dart create mode 100644 compiler/lib/implementation/time_zone_implementation.dart create mode 100644 compiler/lib/implementation/type_token.dart create mode 100644 compiler/lib/object.dart create mode 100644 compiler/lib/print.dart create mode 100755 compiler/scripts/build_dartc_for_perf_metrics create mode 100755 compiler/scripts/compiler_compare.sh create mode 100755 compiler/scripts/compiler_metrics.sh create mode 100755 compiler/scripts/compiler_series_test.sh create mode 100755 compiler/scripts/dartc.sh create mode 100644 compiler/scripts/dartc_build_wrapper.py create mode 100755 compiler/scripts/dartc_metrics.sh create mode 100755 compiler/scripts/dartc_run.sh create mode 100755 compiler/scripts/dartc_size.sh create mode 100755 compiler/scripts/dartc_test.sh create mode 100755 compiler/scripts/dartc_wrapper.py create mode 100755 compiler/scripts/generate_my_projects.py create mode 100644 compiler/scripts/metrics_math.sh create mode 100755 compiler/scripts/sample_metrics.sh create mode 100644 compiler/tests/dart/dart.status create mode 100644 compiler/tests/dart/src/TemplateTest.dart create mode 100644 compiler/tests/dart/testcfg.py create mode 100644 compiler/tests/dartc/dartc.status create mode 100644 compiler/tests/dartc/testcfg.py diff --git a/compiler/README b/compiler/README new file mode 100644 index 00000000000..4a2c250ba05 --- /dev/null +++ b/compiler/README @@ -0,0 +1,2 @@ +This directory is a placeholder for the eventual Dart compiler. +The Dart compiler consists of among others a Dart front-end, a Dart->JS backend, a Dart->Dart backend, tree-shaking tools, minification tools. diff --git a/compiler/api.dart b/compiler/api.dart new file mode 100644 index 00000000000..fb8420bd902 --- /dev/null +++ b/compiler/api.dart @@ -0,0 +1,4 @@ +#library("api"); +// dart:core and dart:coreimpl are implicit +#import("dart:html"); // this includes dart:dom +#import("dart:json"); \ No newline at end of file diff --git a/compiler/build.xml b/compiler/build.xml new file mode 100644 index 00000000000..f66b4a89691 --- /dev/null +++ b/compiler/build.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler/codereview.settings b/compiler/codereview.settings new file mode 100644 index 00000000000..ae0f11ec466 --- /dev/null +++ b/compiler/codereview.settings @@ -0,0 +1,4 @@ +# This file is used by gcl to get repository specific information. +CODE_REVIEW_SERVER: https://chromereviews.googleplex.com +VIEW_VC: https://code.google.com/p/dart/source/detail?r= +CC_LIST: diff --git a/compiler/dart-compiler.gyp b/compiler/dart-compiler.gyp new file mode 100644 index 00000000000..ae203cba919 --- /dev/null +++ b/compiler/dart-compiler.gyp @@ -0,0 +1,213 @@ +# 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. + +{ + 'includes': [ + 'sources.gypi', + 'test_sources.gypi', + 'corelib_sources.gypi', + 'compiler_corelib_sources.gypi', + 'closure_compiler_sources.gypi', + ], + 'targets': [ + { + 'target_name': 'dartc', + 'type': 'none', + 'variables': { + # The Dartium build has this layout: + # src/dart/compiler/dart.gyp (this file) + # src/v8/src/d8.gyp + 'v8_location%': '../../v8', + }, + 'dependencies': [ + '<(v8_location)/src/d8.gyp:d8', + 'closure_compiler', + ], + 'actions': [ + { + 'action_name': 'build_dartc', + 'inputs': [ + 'sources.gypi', + 'test_sources.gypi', + 'corelib_sources.gypi', + 'compiler_corelib_sources.gypi', + '<@(java_sources)', + '<@(java_resources)', + '<@(javatests_sources)', + '<@(javatests_resources)', + '<@(corelib_sources)', + '<@(corelib_resources)', + '<@(compiler_corelib_sources)', + '<@(compiler_corelib_resources)', + 'dartc.xml', + 'scripts/dartc.sh', + 'scripts/dartc_test.sh', + 'scripts/dartc_run.sh', + 'scripts/dartc_size.sh', + 'scripts/dartc_metrics.sh', + '../third_party/args4j/2.0.12/args4j-2.0.12.jar', + '<(PRODUCT_DIR)/closure_out/compiler.jar', + '../third_party/guava/r09/guava-r09.jar', + '../third_party/json/r2_20080312/json.jar', + '../third_party/rhino/1_7R3/js.jar', + '../third_party/hamcrest/v1_3/hamcrest-core-1.3.0RC2.jar', + '../third_party/hamcrest/v1_3/hamcrest-generator-1.3.0RC2.jar', + '../third_party/hamcrest/v1_3/hamcrest-integration-1.3.0RC2.jar', + '../third_party/hamcrest/v1_3/hamcrest-library-1.3.0RC2.jar', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/<(_target_name)/tests.jar', + '<(PRODUCT_DIR)/compiler/bin/dartc', + '<(PRODUCT_DIR)/compiler/bin/dartc_test', + '<(PRODUCT_DIR)/compiler/lib/args4j/2.0.12/args4j-2.0.12.jar', + '<(PRODUCT_DIR)/compiler/lib/closure-compiler.jar', + '<(PRODUCT_DIR)/compiler/lib/dartc.jar', + '<(PRODUCT_DIR)/compiler/lib/guava/r09/guava-r09.jar', + '<(PRODUCT_DIR)/compiler/lib/json/r2_20080312/json.jar', + '<(PRODUCT_DIR)/compiler/lib/rhino/1_7R3/js.jar', + ], + 'action' : [ + '../third_party/apache_ant/v1_7_1/bin/ant', + '-f', 'dartc.xml', + '-Dbuild.dir=<(INTERMEDIATE_DIR)/<(_target_name)', + '-Ddist.dir=<(PRODUCT_DIR)/compiler', + '-Dclosure_compiler.jar=<(PRODUCT_DIR)/closure_out/compiler.jar', + 'clean', + 'dist', + 'tests.jar', + ], + 'message': 'Building dartc.', + }, + { + 'action_name': 'strip_d8', + 'inputs': [ + # Add fake dependency on dartc because this action must + # run after ant is invoked + # (which will delete <(PRODUCT_DIR)/compiler). + '<(PRODUCT_DIR)/compiler/bin/dartc', + '<(PRODUCT_DIR)/d8', + ], + 'outputs': [ '<(PRODUCT_DIR)/compiler/bin/d8.<(OS)', ], + 'action': [ 'strip', '-o', '<@(_outputs)', '<(PRODUCT_DIR)/d8', ], + }, + { + 'action_name': 'copy_tests', + 'inputs': [ '<(INTERMEDIATE_DIR)/<(_target_name)/tests.jar' ], + 'outputs': [ '<(PRODUCT_DIR)/compiler-tests.jar' ], + 'action': [ 'cp', '<@(_inputs)', '<@(_outputs)' ] + }, + { + 'action_name': 'copy_dartc_wrapper', + 'inputs': [ + '<(PRODUCT_DIR)/compiler/lib/dartc.jar', + 'scripts/dartc_wrapper.py', + ], + 'outputs': [ '<(PRODUCT_DIR)/dartc' ], + 'action': [ 'cp', 'scripts/dartc_wrapper.py', '<@(_outputs)' ] + }, + { + 'message': 'Compiling dart system libraries', + 'action_name': 'compile_systemlibrary', + 'inputs': [ + '<(PRODUCT_DIR)/dartc', + 'api.dart', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/core/com/google/dart/corelib/corelib.dart.api', + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/dom/dom/dom.dart.api', + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/html/html/html.dart.api', + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/json/json/json.dart.api', + ], + 'action': [ + '<(PRODUCT_DIR)/dartc', 'api.dart', '-out', '<(INTERMEDIATE_DIR)/<(_target_name)/api', + ], + }, + { + 'message': 'Packaging dart:core artifacts', + 'action_name': 'package_corelib_artifacts', + 'inputs': [ + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/core/com/google/dart/corelib/corelib.dart.api', + 'api.dart', + ], + 'outputs': [ + '<(PRODUCT_DIR)/compiler/lib/corelib.jar', + ], + 'action': [ + 'jar', 'u0f', '<(PRODUCT_DIR)/compiler/lib/corelib.jar', '-C', '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/core', 'com', + ], + }, + { + 'message': 'Packaging dart:dom artifacts', + 'action_name': 'package_domlib_artifacts', + 'inputs': [ + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/dom/dom/dom.dart.api', + 'api.dart', + ], + 'outputs': [ + '<(PRODUCT_DIR)/compiler/lib/domlib.jar', + ], + 'action': [ + 'jar', 'u0f', '<(PRODUCT_DIR)/compiler/lib/domlib.jar', '-C', '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/dom', 'dom', + ], + }, + { + 'message': 'Packaging dart:html artifacts', + 'action_name': 'package_htmllib_artifacts', + 'inputs': [ + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/html/html/html.dart.api', + 'api.dart', + ], + 'outputs': [ + '<(PRODUCT_DIR)/compiler/lib/htmllib.jar', + ], + 'action': [ + 'jar', 'u0f', '<(PRODUCT_DIR)/compiler/lib/htmllib.jar', '-C', '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/html', 'html', + ], + }, + { + 'message': 'Packaging dart:json artifacts', + 'action_name': 'package_jsonlib_artifacts', + 'inputs': [ + '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/json/json/json.dart.api', + 'api.dart', + ], + 'outputs': [ + '<(PRODUCT_DIR)/compiler/lib/jsonlib.jar', + ], + 'action': [ + 'jar', 'u0f', '<(PRODUCT_DIR)/compiler/lib/jsonlib.jar', '-C', '<(INTERMEDIATE_DIR)/<(_target_name)/api/dart/json', 'json', + ], + }, + ], + }, + { + 'target_name': 'closure_compiler', + 'type': 'none', + 'dependencies': [], + 'actions': [ + { + 'action_name': 'build_closure_compiler', + 'inputs': [ + 'closure_compiler_sources.gypi', + '../third_party/closure_compiler_src/build.xml', + '<@(closure_compiler_src_sources)', + '<@(closure_compiler_src_resources)', + ], + 'outputs': [ + '<(PRODUCT_DIR)/closure_out/compiler.jar' + ], + 'action': [ + '../third_party/apache_ant/v1_7_1/bin/ant', + '-f', + '../third_party/closure_compiler_src/build.xml', + '-Dclosure.build.dir=<(PRODUCT_DIR)/closure_out', + 'clean', + 'jar', + ], + 'message': 'Building closure compiler' + }, + ] + } + ], +} diff --git a/compiler/dartc.mf b/compiler/dartc.mf new file mode 100644 index 00000000000..6d996e3f658 --- /dev/null +++ b/compiler/dartc.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: com.google.dart.compiler.DartCompiler +Class-Path: rhino.jar deploy_deploy.jar diff --git a/compiler/dartc.xml b/compiler/dartc.xml new file mode 100644 index 00000000000..f6ae26594b3 --- /dev/null +++ b/compiler/dartc.xml @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler/dartium.gyp b/compiler/dartium.gyp new file mode 100644 index 00000000000..c425b2af57b --- /dev/null +++ b/compiler/dartium.gyp @@ -0,0 +1,67 @@ +# 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. + +# TODO(vsm): Remove this file and use dart.gyp once that can be pulled +# into the dartium build. +{ + 'includes': [ + # TODO(iposva): Move shared gyp setup to a shared location. + '../tools/gyp/xcode.gypi', + # TODO(mmendez): Add the appropriate gypi includes here. + 'closure_compiler_sources.gypi', + ], + 'targets': [ + { + 'target_name': 'dartc', + 'type': 'none', + 'dependencies': [ + 'closure_compiler', + ], + 'actions': [ + { + 'action_name': 'Build and test', + 'inputs': [ + ], + 'outputs': [ + 'dummy_target', + ], + 'action' : [ + '../third_party/apache_ant/v1_7_1/bin/ant', + '-Dbuild.dir=<(PRODUCT_DIR)/ant-out', + '-Dclosure_compiler.jar=<(PRODUCT_DIR)/closure_out/compiler.jar', + 'clean', + 'dist', + ], + 'message': 'Building dartc.', + }, + ], + }, + { + 'target_name': 'closure_compiler', + 'type': 'none', + 'dependencies': [], + 'actions': [ + { + 'action_name': 'build_closure_compiler', + 'inputs': [ + '<@(closure_compiler_src_sources)', + '<@(closure_compiler_src_resources)', + ], + 'outputs': [ + '<(PRODUCT_DIR)/closure_out/compiler.jar' + ], + 'action': [ + '../third_party/apache_ant/v1_7_1/bin/ant', + '-f', + '../third_party/closure_compiler_src/build.xml', + '-Dclosure.build.dir=<(PRODUCT_DIR)/closure_out', + 'clean', + 'jar', + ], + 'message': 'Building closure compiler' + }, + ] + } + ], +} diff --git a/compiler/eclipse.workspace/README.txt b/compiler/eclipse.workspace/README.txt new file mode 100644 index 00000000000..cde5ab622ba --- /dev/null +++ b/compiler/eclipse.workspace/README.txt @@ -0,0 +1,60 @@ +This is an Eclipse workspace for Helios Service Release 2. + +HOW TO + +1. When you open Eclipse the first time, it will ask you to create a + workspace or select an existing one. You can use this directory as + workspace, or you can choose a different one. + +2. If you're already using Eclipse, you can either switch to a new + workspace (File > Switch Workspace) or use your current workspace. + +3. Add the folloing "Path Variable" to your workspace: + (Open Preferences... > General > Workspace > Linked Resources) + DART_TRUNK: point to the root of your checkout + D8_EXEC: for example DART_TRUNK/compiler/out/Release_dartc/d8 + +4. Add a "Classpath Variable" to your workspace called DART_TRUNK + that points to the same directory as your DART_TRUNK path variable. + (Open Preferences... > Java > Build Path > Classpath Variables). + +5. Regardless if you're using this directory as a workspace, you have + to import the projects (File > Import... > General > Existing + Projects into Workspace). + +6. Click "Next >" + +7. Select root directory. Browse to: compiler/eclipse.workspace. + +8. It should find and select three projects (dartc and tests). + +9. Click Finish. (At this point Eclipse may get stuck, if so, exit + Eclipse by right-clicking on the dock icon, and restart). + +10. Repeat steps 5-9, to import the project in third_party/closure-compiler-src + +11. Open the "build.xml" file in the "closure-compiler-src" project. + +12. In the Outline view, right-click on the "jar [default]" target, and select + "Run as..." > "Ant Build". (This will build its version of Rhino and JarJar + it). + +13. Open Preferences... > Java > Compiler > Errors/Warnings > Potential + programming problems. Change "Serializable class without + serialVersionUID" to "Ignore". + +14. Open Preferences... > Java > Code Style > Formatter and click + "Import...". Open the file GoogleCodeStyle.xml in this directory. + +15. Import the launch configuration (File > Import... > Run/Debug > + Launch Configurations). + +16. Click "Next >" + +17. In SVN, browse to: compiler/eclipse.workspace/tests. + +18. Select "tests". + +19. Click Finish. + +20. Try running the tests: Run > Run History > dartc test suites. diff --git a/compiler/eclipse.workspace/dartc/.classpath b/compiler/eclipse.workspace/dartc/.classpath new file mode 100644 index 00000000000..f2d159dc153 --- /dev/null +++ b/compiler/eclipse.workspace/dartc/.classpath @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/compiler/eclipse.workspace/dartc/.project b/compiler/eclipse.workspace/dartc/.project new file mode 100644 index 00000000000..93077c873ef --- /dev/null +++ b/compiler/eclipse.workspace/dartc/.project @@ -0,0 +1,58 @@ + + + dartc + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + + + src + 2 + DART_TRUNK/compiler/java + + + dartc.corelib/com/google/dart/corelib + 2 + DART_TRUNK/compiler/lib + + + shared.corelib/com/google/dart/corelib + 2 + DART_TRUNK/corelib + + + + + 1304458098561 + shared.corelib/com/google/dart/corelib + 5 + + org.eclipse.ui.ide.orFilterMatcher + + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*.dart + + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*.app + + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*.lib + + + + + + diff --git a/compiler/eclipse.workspace/dartc/deps/README.txt b/compiler/eclipse.workspace/dartc/deps/README.txt new file mode 100644 index 00000000000..a43be30af2c --- /dev/null +++ b/compiler/eclipse.workspace/dartc/deps/README.txt @@ -0,0 +1,5 @@ +This directory should contain: + +d8 + +Which you can build yourself following the instructions on http://code.google.com/apis/v8/build.html diff --git a/compiler/eclipse.workspace/tests/.classpath b/compiler/eclipse.workspace/tests/.classpath new file mode 100644 index 00000000000..f2f8e656737 --- /dev/null +++ b/compiler/eclipse.workspace/tests/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/compiler/eclipse.workspace/tests/.project b/compiler/eclipse.workspace/tests/.project new file mode 100644 index 00000000000..28eec4d15a4 --- /dev/null +++ b/compiler/eclipse.workspace/tests/.project @@ -0,0 +1,128 @@ + + + tests + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + + + cases + 2 + DART_TRUNK/compiler/javatests + + + d8 + 1 + D8_EXEC + + + jscomp + 2 + DART_TRUNK/compiler/javatests + + + suites + 2 + DART_TRUNK/compiler/javatests + + + test.py + 1 + DART_TRUNK/tools/test.py + + + imported/third_party/java_src/dart/corelib/tests + 2 + DART_TRUNK/corelib/tests + + + imported/third_party/java_src/dart/execution/runtime/tests + 2 + DART_TRUNK/runtime/tests + + + + + 1311331907127 + cases + 22 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*Tests.java + + + + 1311331781529 + imported + 21 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*.dart + + + + 1311331571786 + jscomp + 21 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*OptTests.java + + + + 1311331079908 + suites + 21 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-true-false-*Tests.java + + + + 1311331715338 + cases/com/google/dart/compiler + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-vm + + + + 1311331633045 + jscomp/com/google/dart/compiler + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-vm + + + + 1311331190572 + suites/com/google/dart/compiler + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-vm + + + + 1311331455715 + suites/com/google/dart/compiler/end2end + 6 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*OptTests.java + + + + diff --git a/compiler/eclipse.workspace/tests/SharedTests.launch b/compiler/eclipse.workspace/tests/SharedTests.launch new file mode 100644 index 00000000000..0ebcd7895a3 --- /dev/null +++ b/compiler/eclipse.workspace/tests/SharedTests.launch @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/compiler/eclipse.workspace/tests/TestSharedTests.launch b/compiler/eclipse.workspace/tests/TestSharedTests.launch new file mode 100644 index 00000000000..8a1dbaf028b --- /dev/null +++ b/compiler/eclipse.workspace/tests/TestSharedTests.launch @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/compiler/eclipse.workspace/tests/dartc_jscomp_suites.launch b/compiler/eclipse.workspace/tests/dartc_jscomp_suites.launch new file mode 100644 index 00000000000..4e5aff713a0 --- /dev/null +++ b/compiler/eclipse.workspace/tests/dartc_jscomp_suites.launch @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler/eclipse.workspace/tests/dartc_tests_suites.launch b/compiler/eclipse.workspace/tests/dartc_tests_suites.launch new file mode 100644 index 00000000000..83ddb8b744f --- /dev/null +++ b/compiler/eclipse.workspace/tests/dartc_tests_suites.launch @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler/generate_source_list.py b/compiler/generate_source_list.py new file mode 100644 index 00000000000..02a48bedf5f --- /dev/null +++ b/compiler/generate_source_list.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# 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. + +import StringIO +import os +import sys + +class GenerateError(Exception): + + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) + + +class Generator: + + def __init__(self, base_directory, name, output, path, *excludes): + self.base_directory = base_directory + self.name = name + self.output = output + self.path = path + self.excludes = set() + for x in excludes: + self.excludes.add(x) + self.sources = [] + self.resources = [] + + def _list_files(self): + start_directory = os.path.join(self.base_directory, self.path) + for fullpath, dirs, filenames in os.walk(start_directory): + path = fullpath[len(start_directory) + 1:] + remove_me = [d for d in dirs if d.startswith('.') or + d == 'CVS' or + (d in self.excludes)] + for d in remove_me: + dirs.remove(d) + for filename in filenames: + if (filename.endswith('.java')): + self.sources.append(os.path.join(path, filename)) + elif (filename.endswith('~')): + pass + elif (filename.endswith('.pyc')): + pass + else: + self.resources.append(os.path.join(path, filename)) + self.sources.sort() + self.resources.sort() + + def _print_gypi_files(self, out, name, files): + out.write(" '%s': [\n" % name) + for filename in files: + out.write(" '%s/%s',\n" % (self.path, filename)) + out.write(" ],\n") + + def _print_ant_files(self, out, name, files): + out.write(" \n" % (name, self.path)) + for filename in files: + out.write(" \n" % filename) + out.write(" \n") + out.write(" \n" + % (name, name)) + out.write(" \n" % self.path) + out.write(" \n") + + def _make_output(self, file_name): + if os.path.exists(file_name): + return StringIO.StringIO() + else: + return file(file_name, 'w') + + def _close(self, file_name, output): + if not isinstance(output, StringIO.StringIO): + output.close() + return + new_text = output.getvalue() + output.close() + with open(file_name, 'r') as f: + old_text = f.read() + if old_text == new_text: + return + sys.stderr.write('Updating %s\n' % file_name) + with open(file_name, 'w') as f: + f.write(new_text) + + def generate(self): + self._list_files() + file_name = self.output + '.gypi'; + gypi = self._make_output(file_name) + gypi.write("{\n 'variables': {\n") + self._print_gypi_files(gypi, self.name + '_sources', self.sources) + self._print_gypi_files(gypi, self.name + '_resources', self.resources) + gypi.write(" },\n}\n") + self._close(file_name, gypi) + file_name = self.output + '.xml' + ant = self._make_output(file_name) + ant.write("\n") + self._print_ant_files(ant, self.name + '_sources', self.sources) + self._print_ant_files(ant, self.name + '_resources', self.resources) + ant.write("\n") + self._close(file_name, ant) + + +def Main(script_name = None, name = None, output = None, path = None, + *rest): + if not path: + raise GenerateError("usage: %s NAME OUTPUT PATH EXCLUDE_DIR_NAME ..." + % script_name) + base_directory = os.path.dirname(output) + Generator(base_directory, name, output, path, *rest).generate() + + +if __name__ == '__main__': + sys.exit(Main(*sys.argv)) diff --git a/compiler/java/com/google/dart/compiler/Backend.java b/compiler/java/com/google/dart/compiler/Backend.java new file mode 100644 index 00000000000..e83eaa54696 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/Backend.java @@ -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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.resolver.CoreTypeProvider; + +import java.io.IOException; +import java.util.Collection; + +/** + * Interface for compiler backends. + */ +public interface Backend { + + /** + * Determines whether compilation artifacts are out of date with respect to + * this source. + */ + boolean isOutOfDate(DartSource src, DartCompilerContext context); + + /** + * Compile the given compilation unit. + * @param context The listener through which compilation errors are reported + * (not null) + */ + void compileUnit(DartUnit unit, DartSource src, + DartCompilerContext context, + CoreTypeProvider typeProvider) + throws IOException; + + /** + * Package the given application. + * + * @param app The application library whose entry-point should be called + * @param libraries The transitive set of libraries contained in this + * application + * @param context The listener through which compilation errors are reported + * (not null) + */ + void packageApp(LibrarySource app, + Collection libraries, + DartCompilerContext context, + CoreTypeProvider typeProvider) + throws IOException; + + /** + * The application extension for the backend. + */ + String getAppExtension(); + + /** + * The source map extension for the backend. + */ + String getSourceMapExtension(); +} diff --git a/compiler/java/com/google/dart/compiler/CommandLineOptions.java b/compiler/java/com/google/dart/compiler/CommandLineOptions.java new file mode 100644 index 00000000000..91cc50c05ae --- /dev/null +++ b/compiler/java/com/google/dart/compiler/CommandLineOptions.java @@ -0,0 +1,358 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.dart.runner.DartRunner; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Options that can be specified on the command line. + */ +public class CommandLineOptions { + + /** + * Command line options accepted by the {@link DartCompiler} entry point. + */ + public static class CompilerOptions { + + @Option(name = "--batch", aliases = { "-batch" }, + usage = "run in batch mode, accepting command lines from stdin") + private boolean batch = false; + + @Option(name = "--check-only", aliases = { "-check-only" }, + usage = "do not generate output, only analyze") + private boolean checkOnly = false; + + @Option(name = "-documentation-lib", + usage = "only generate documentation for the given library") + private String documentationLibrary = null; + + @Option(name = "-documentation-out", usage = "directory to receive documentation output") + private String documentationOutputDirectory = null; + + @Option(name = "-generate-documentation", + usage = "generate documentation for the provided source files") + private boolean generateDocumentation = false; + + @Option(name = "-generate-isolate-stubs", + usage = "classes to generate stubs for, comma-separated") + private String generateIsolateStubs = null; + + @Option(name = "--ignore-unrecognized-flags", usage = "ignore unrecognized command line flags") + private boolean ignoreUnrecognizedFlags = false; + + @Option(name = "-isolate-stub-out", usage = "file to receive generated stub output") + private String isolateStubOutputFile = null; + + @Option(name = "-jvm-metrics-detail", usage = "summary or verbose (default is summary)") + private String jvmMetricDetail = "summary"; + + @Option(name = "-jvm-metrics-format", usage = "tabular or pretty (default is tabular)") + private String jvmMetricFormat = "tabular"; + + @Option(name = "-jvm-metrics-type", usage = "comma-separated list, including:\n" + + " all: show all available stat types (default)\n" + + " gc: show garbage collection stats\n" + + " mem: show memory stats\n" + " jit: show jit stats") + private String jvmMetricType = "all"; + + @Option(name = "-noincremental", usage = "disable incremental compilation") + private boolean noincremental = false; + + // see shouldOptimize() below + private boolean optimize = false; + + // TODO(zundel): -out is for backward compatibility until scripts are updated + @Option(name = "--work", aliases = { "-out" }, usage = "directory to receive compiler output") + private File workDirectory = new File("out"); + + @Option(name = "-help", usage = "prints this help message") + private boolean showHelp = false; + + @Option(name = "-jvm-metrics", usage = "print jvm metrics at end of compilation") + private boolean showJvmMetrics = false; + + @Option(name = "-metrics", usage = "print compilation metrics") + private boolean showMetrics = false; + + @Argument + private final List sourceFiles = new ArrayList(); + + @Option(name = "--fatal-type-errors", aliases = { "-fatal-type-errors" }, + usage = "type errors are fatal errors (instead of warnings)") + private boolean typeErrorsAreFatal = false; + + @Option(name = "-Werror", usage = "warnings (excluding type warnings) are fatal errors") + private boolean warningsAreFatal = false; + + /** + * Returns whether the option -check-only is provided. + */ + public boolean checkOnly() { + return checkOnly; + } + + /** + * Returns whether the option -generate-documentation is provided. + */ + public boolean generateDocumentation() { + return generateDocumentation; + } + + /** + * Returns the library to document. If null is returned generate + * documentation for all libraries. + */ + public String getDocumentationLibrary() { + return documentationLibrary; + } + + /** + * Returns the documentation output directory. + */ + public String getDocumentationOutputDirectory() { + return documentationOutputDirectory; + } + + /** + * Returns the names of classes to generate stubs for. + */ + public Set getIsolateStubClasses() { + Set set = new HashSet(); + if (generateIsolateStubs != null) { + set.addAll(Arrays.asList(generateIsolateStubs.split(","))); + } + return set; + } + + public String getIsolateStubOutputFile() { + return isolateStubOutputFile; + } + + public String getJvmMetricOptions() { + if (!showJvmMetrics) { + return null; + } + return jvmMetricDetail + ":" + jvmMetricFormat + ":" + jvmMetricType; + } + + /** + * Returns the list of files passed to the compiler. + */ + public List getSourceFiles() { + return sourceFiles; + } + + /** + * Returns the path to receive compiler intermediate output. + */ + public File getWorkDirectory() { + return workDirectory; + } + + public boolean ignoreUnrecognizedFlags() { + return ignoreUnrecognizedFlags; + } + + /** + * Returns whether the compiler should attempt to incrementally recompile. + */ + public boolean incremental() { + return !noincremental; + } + + public boolean isBatch() { + return batch; + } + + /** + * Enables optimization of the generated JavaScript. + */ + @Option(name = "-optimize", aliases = { "--optimize" }, usage = "produce optimized code") + public void optimize(boolean optimize) { + this.optimize = optimize; + } + + @Option(name = "--out", usage = "write generated JavaScript to the specified file") + private File outputFilename = null; + + /** + * @return the path to receive compiler output. + */ + public File getOutputFilename() { + return outputFilename; + } + + /** + * Returns true if optimization is enabled. + */ + public boolean shouldOptimize() { + return optimize; + } + + /** + * Returns true if the compiler should print it's help message. + */ + public boolean showHelp() { + return showHelp; + } + + public boolean showJvmMetrics() { + return showJvmMetrics; + } + + public boolean showMetrics() { + return showMetrics; + } + + /** + * Returns whether type errors are fatal. + */ + public boolean typeErrorsAreFatal() { + return typeErrorsAreFatal; + } + + /** + * Returns whether warnings (excluding type warnings) are fatal. + */ + public boolean warningsAreFatal() { + return warningsAreFatal; + } + } + + /** + * Command line options accepted by the {@link DartRunner} entry point. + */ + public static class DartRunnerOptions extends CompilerOptions { + + @Option(name = "--compile-only", usage = "compile but do not execute") + private boolean compileOnly = false; + + @Option(name = "--expose_core_impl", usage = "automatic import of dart:coreimpl library") + private boolean exposeCoreImpl = false; + + @Option(name="--prof", usage = "enable profiling") + private boolean prof; + + @Option(name = "--rhino", usage = "use rhino as the JavaScript interpreter") + private boolean rhino = false; + + @Option(name = "--verbose", usage = "extra diagnostic output") + private boolean verbose = false; + + /** + * @return true if the program should compile but not execute. + */ + public boolean shouldCompileOnly() { + return compileOnly; + } + + /** + * @return true to automatically import dart:coreimpl + */ + public boolean shouldExposeCoreImpl() { + return exposeCoreImpl; + } + + /** + * Returns true if profiling is enabled. + */ + public boolean shouldProfile() { + return prof; + } + + /** + * @return true if rhino should be used as the runtime (default is to invoke d8) + */ + public boolean useRhino() { + return rhino; + } + + /** + * @return true to enable diagnostic output + */ + public boolean verbose() { + return verbose; + } + } + + + /** + * Command line options accepted by the {@link TestRunner} entry point. + */ + public static class TestRunnerOptions extends DartRunnerOptions { + } + + /** + * Parses command line options, handling the feature to ignore unrecognized + * flags. + * + * If one of the options is 'ignore-unrecognized-flags', then any exceptions + * for 'not a valid option' are suppressed. + * + * @param args Arguments passed from main() + * @param cmdLineParser An initialized {@link CmdLineParser} for the desired + * argument set. + * @throws CmdLineException Thrown if there is a problem parsing the options. + */ + public static CmdLineParser parse(String[] args, CompilerOptions parsedOptions) + throws CmdLineException { + boolean ignoreUnrecognized = false; + for (String arg : args) { + if (arg.equals("--ignore-unrecognized-flags")) { + ignoreUnrecognized = true; + break; + } + } + + if (!ignoreUnrecognized) { + CmdLineParser cmdLineParser = new CmdLineParser(parsedOptions); + cmdLineParser.parseArgument(args); + return cmdLineParser; + } + CmdLineParser cmdLineParser = new CmdLineParser(parsedOptions); + for (int i = 0, len = args.length; i < len; i++) { + System.out.println("Parsing: " + Joiner.on(" ").join(args)); + try { + cmdLineParser.parseArgument(args); + } catch (CmdLineException e) { + String msg = e.getMessage(); + + if (e.getMessage().endsWith(" is not a valid option")) { + String option = msg.substring(1); + int closeQuote = option.indexOf('\"'); + option = option.substring(0, closeQuote); + List newArgs = Lists.newArrayList(); + for (String arg : args) { + if (arg.equals(option)) { + // TODO(zundel): remove diagnostic output + System.out.println("Ignoring unrecognized flag: " + arg); + continue; + } + newArgs.add(arg); + } + args = newArgs.toArray(new String[newArgs.size()]); + cmdLineParser = new CmdLineParser(parsedOptions); + continue; + } + } + break; + } + return cmdLineParser; + } +} diff --git a/compiler/java/com/google/dart/compiler/CompilerConfiguration.java b/compiler/java/com/google/dart/compiler/CompilerConfiguration.java new file mode 100644 index 00000000000..7d28f2b4ded --- /dev/null +++ b/compiler/java/com/google/dart/compiler/CompilerConfiguration.java @@ -0,0 +1,90 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.metrics.CompilerMetrics; + +import java.io.File; +import java.util.List; + +/** + * A configuration for the Dart compiler specifying which phases + * and backends will be executed. + * + * @author sigmund@google.com (Siggi Cherem) + */ +public interface CompilerConfiguration { + + List getPhases(); + + List getBackends(); + + /** + * Returns true if the compiler's output should be optimized. + */ + boolean shouldOptimize(); + + /** + * Returns the {@link CompilerMetrics} instance or null if metrics should not be + * recorded. + * + * @return the metrics instance, null if metrics should not be recorded + */ + CompilerMetrics getCompilerMetrics(); + + /** + * Returns a comma-separated string list of options for displaying jvm metrics. + * Returns null if jvm metrics are not enabled. + */ + String getJvmMetricOptions(); + + boolean typeErrorsAreFatal(); + + boolean warningsAreFatal(); + + /** + * Returns true if the compiler should try to resolve + * even after having seen parse-errors. + */ + boolean resolveDespiteParseErrors(); + + /** + * Temporary flag to turn on incremental compilation. This will be removed once we're certain + * incremental compilation is correct. + */ + boolean incremental(); + + /** + * The first backend that runs outputs to this filename if set. + */ + File getOutputFilename(); + + /** + * The work directory where incremental build output is stored between invocations. + */ + File getOutputDirectory(); + + /** + * Returns true if the compiler should not produce output. + */ + boolean checkOnly(); + + /** + * Returns true if the compiler should expect an entry point to be defined. + */ + boolean expectEntryPoint(); + + boolean allowNoSuchType(); + + /** + * Returns true if the compiler should collect comments. + */ + boolean collectComments(); + + /** + * Return the system library corresponding to the specified "dart:" spec. + */ + LibrarySource getSystemLibraryFor(String importSpec); +} diff --git a/compiler/java/com/google/dart/compiler/DartArtifactProvider.java b/compiler/java/com/google/dart/compiler/DartArtifactProvider.java new file mode 100644 index 00000000000..a3b165dce57 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartArtifactProvider.java @@ -0,0 +1,67 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; + +/** + * Abstract class that {@link DartCompiler} consumers can use to specify where + * generated files are located. + */ +public abstract class DartArtifactProvider { + + /** + * Gets a reader for an artifact associated with the specified source, which + * must have been written to {@link #getArtifactWriter(Source, String, String)}. The + * caller is responsible for closing the reader. Only one artifact may be + * associated with the given extension. + * + * @param source the source file (not null) + * @param part a component of the source file to get a reader for (may be empty). + * @param extension the file extension for this artifact (not + * null, not empty) + * @return the reader, or null if there is no such artifact + */ + public abstract Reader getArtifactReader(Source source, String part, String extension) + throws IOException; + + /** + * Gets the {@link URI} for an artifact associated with this source. + * + * @param source the source file (not null) + * @param part a component of the source file to get a reader for (may be empty). + * @param extension the file extension for this artifact (not + * null, not empty) + */ + public abstract URI getArtifactUri(Source source, String part, String extension); + + /** + * Gets a writer for an artifact associated with this source. The caller is + * responsible for closing the writer. Only one artifact may be associated + * with the given extension. + * + * @param source the source file (not null) + * @param part a component of the source file to get a reader for (may be empty). + * @param extension the file extension for this artifact (not + * null, not empty) + */ + public abstract Writer getArtifactWriter(Source source, String part, String extension) + throws IOException; + + /** + * Determines whether an artifact for the specified source is out of date + * with respect to some other source. + * + * @param source the source file to check (not null) + * @param base the artifact's base source (not null) + * @param extension the file extension for this artifact (not + * null, not empty) + * @return true if out of date + */ + public abstract boolean isOutOfDate(Source source, Source base, String extension); +} diff --git a/compiler/java/com/google/dart/compiler/DartCompilationError.java b/compiler/java/com/google/dart/compiler/DartCompilationError.java new file mode 100644 index 00000000000..2b4de6e94c1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartCompilationError.java @@ -0,0 +1,295 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.parser.DartScanner.Location; +import com.google.dart.compiler.parser.DartScanner.Position; + +import java.io.IOException; + +/** + * Information about a compilation error. + * + * @see DartCompilerListener + */ +public class DartCompilationError { + + /** + * The character offset from the beginning of the source (zero based) where + * the error occurred. + */ + private int startPosition = 0; + + /** + * The number of characters from the startPosition to the end of the source + * which encompasses the compilation error. + */ + private int length = 0; + + /** + * The line number in the source (one based) where the error occurred or -1 if + * it is undefined. + */ + private int lineNumber = -1; + + /** + * The column number in the source (one based) where the error occurred or -1 + * if it is undefined. + */ + private int columnNumber = -1; + + /** + * The error code associated with the error. + */ + private ErrorCode errorCode; + + /** + * The compilation error message. + */ + private String message; + + /** + * The source in which the error occurred or null if unknown. + */ + private Source source; + + /** + * The exception associated with this compilation error or null + * if none. + */ + private Exception exception; + + /** + * Instantiate a new instance representing an {@link IOException} that + * occurred when reading a source file. + * + * @param source the source file in which the exception occurred + * @param exception the exception that occurred + */ + public DartCompilationError(Source source, Exception exception) { + setSource(source); + setException(exception); + message = exception.getMessage(); + + // TODO (danrubel) Remove once JSON parsing is removed + parseJSONException(); + } + + /** + * This function is only necessary for processing a JSONException and can be + * removed once ApplicationSourceFile and LibrarySourceFile no longer throw + * JSONException + */ + protected void parseJSONException() { + if (message == null) { + return; + } + + // Strip filename off beginning of message + if (message.startsWith("Error reading ")) { + for (int i = 14; i < message.length(); i++) { + if (!Character.isWhitespace(message.charAt(i))) + continue; + i++; + if (i >= message.length() || Character.isWhitespace(message.charAt(i))) + break; + message = message.substring(i); + break; + } + } + + // Strip " at character ###" off the end of the message + for (int i = message.length() - 2; i > 0; i--) { + if (Character.isDigit(message.charAt(i))) + continue; + i++; + if (!message.substring(0, i).endsWith(" at character ")) + break; + try { + startPosition = Integer.valueOf(message.substring(i)); + message = message.substring(0, i - 14); + } catch (NumberFormatException ignored) { + // Fall through without modifying the error message + } + break; + } + + // Strip JSON reference off beginning of error message + if (message.startsWith("JSONObject[\"")) { + int i = message.indexOf('"', 12); + if (i > 12 && i + 2 < message.length()) + message = message.substring(11, i + 1) + message.substring(i + 2); + } + } + + /** + * Instantiate a new instanced representing a compilation error at the + * specified location. + * + * @param location The source location reference.. + * @param message The compilation error message. + * @deprecated use {@link #DartCompilationError(SourceInfo, ErrorCode, Object...)} + */ + @Deprecated + public DartCompilationError(SourceInfo location, String message) { + if (location != null) { + this.lineNumber = location.getSourceLine(); + this.columnNumber = location.getSourceColumn(); + this.startPosition = location.getSourceStart(); + this.length = location.getSourceLength(); + } + this.message = message; + this.source = location.getSource(); + } + + /** + * Instantiate a new instance representing a compilation error at the specified location. + * + * @param location the source range where the error occurred + * @param errorCode the error code to be associated with this error + * @param arguments the arguments used to build the error message + */ + public DartCompilationError(SourceInfo location, ErrorCode errorCode, Object... arguments) { + this.lineNumber = location.getSourceLine(); + this.columnNumber = location.getSourceColumn(); + this.startPosition = location.getSourceStart(); + this.length = location.getSourceLength(); + this.errorCode = errorCode; + this.message = String.format(errorCode.getMessage(), arguments); + this.source = location.getSource(); + } + + /** + * Instantiate a new instance representing a compilation error at the specified location. + * + * @param location the source range where the error occurred + * @param errorCode the error code to be associated with this error + * @param arguments the arguments used to build the error message + */ + public DartCompilationError(Location location, ErrorCode errorCode, Object... arguments) { + this((Source)null, location, errorCode, arguments); + } + + /** + * Instantiate a new instance representing a compilation error at the specified location. + * + * @param source the source reference + * @param location the source range where the error occurred + * @param errorCode the error code to be associated with this error + * @param arguments the arguments used to build the error message + */ + public DartCompilationError(Source source, Location location, ErrorCode errorCode, Object... arguments) { + this.source = source; + this.errorCode = errorCode; + this.message = String.format(errorCode.getMessage(), arguments); + if (location != null) { + Position begin = location.getBegin(); + if (begin != null) { + startPosition = begin.getPos(); + lineNumber = begin.getLine(); + columnNumber = begin.getCol(); + } + Position end = location.getEnd(); + if (end != null) { + length = end.getPos() - startPosition; + if (length < 0) { + length = 0; + } + } + } + } + + /** + * The column number in the source (one based) where the error occurred. + */ + public int getColumnNumber() { + return columnNumber; + } + + /** + * Return the error code associated with the error. + */ + public ErrorCode getErrorCode() { + return errorCode; + } + + /** + * The exception associated with this compilation error or null + * if none. + */ + public Exception getException() { + return exception; + } + + /** + * The line number in the source (one based) where the error occurred. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * The compilation error message. + */ + public String getMessage() { + return message; + } + + /** + * Return the source in which the error occurred or null if + * unknown. + */ + public Source getSource() { + return source; + } + + /** + * The character offset from the beginning of the source (zero based) where + * the error occurred. + */ + public int getStartPosition() { + return startPosition; + } + + /** + * The length of the error location. + */ + public int getLength() { + return length; + } + + @Override + public int hashCode() { + int hashCode = startPosition; + hashCode ^= (message != null) ? message.hashCode() : 0; + hashCode ^= (source != null) ? source.getName().hashCode() : 0; + return hashCode; + } + + /** + * Set the exception associated with this compilation error or + * null if none. + */ + public void setException(Exception exception) { + this.exception = exception; + } + + /** + * Set the source in which the error occurred or null if unknown. + */ + public void setSource(Source source) { + this.source = source; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append((source != null) ? source.getName() : ""); + sb.append("(" + lineNumber + ":" + columnNumber + "): "); + sb.append(message); + return sb.toString(); + } +} diff --git a/compiler/java/com/google/dart/compiler/DartCompilationPhase.java b/compiler/java/com/google/dart/compiler/DartCompilationPhase.java new file mode 100644 index 00000000000..cde657c1fa2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartCompilationPhase.java @@ -0,0 +1,25 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.resolver.CoreTypeProvider; + +/** + * A compiler phase that processes a unit and possibly transforms it or reports + * compilation errors. + * + * @author sigmund@google.com (Siggi Cherem) + */ +public interface DartCompilationPhase { + + /** + * Execute this phase on a unit. + * + * @param unit the program to process + * @param context context where to report error messages + */ + DartUnit exec(DartUnit unit, DartCompilerContext context, CoreTypeProvider typeProvider); +} diff --git a/compiler/java/com/google/dart/compiler/DartCompiler.java b/compiler/java/com/google/dart/compiler/DartCompiler.java new file mode 100644 index 00000000000..31d9df08cc0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartCompiler.java @@ -0,0 +1,1092 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; +import com.google.common.io.Files; +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.dart.compiler.LibraryDeps.Dependency; +import com.google.dart.compiler.UnitTestBatchRunner.Invocation; +import com.google.dart.compiler.ast.DartDirective; +import com.google.dart.compiler.ast.DartLibraryDirective; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.metrics.DartEventType; +import com.google.dart.compiler.metrics.JvmMetrics; +import com.google.dart.compiler.metrics.Tracer; +import com.google.dart.compiler.metrics.Tracer.TraceEvent; +import com.google.dart.compiler.parser.CommentPreservingParser; +import com.google.dart.compiler.parser.DartParser; +import com.google.dart.compiler.parser.DartScanner.Location; +import com.google.dart.compiler.parser.DartScannerParserContext; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.CoreTypeProviderImplementation; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.resolver.MemberBuilder; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.resolver.Resolver; +import com.google.dart.compiler.resolver.SupertypeResolver; +import com.google.dart.compiler.resolver.TopLevelElementBuilder; +import com.google.dart.compiler.type.TypeAnalyzer; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Entry point for the Dart compiler. + */ +public class DartCompiler { + + public static final String EXTENSION_API = "api"; + public static final String EXTENSION_DEPS = "deps"; + public static final String EXTENSION_LOG = "log"; + + public static final String CORELIB_URL_SPEC = "dart:core"; + public static final String MAIN_ENTRY_POINT_NAME = "main"; + + private static class Compiler { + private final LibrarySource app; + private final List embeddedLibraries = new ArrayList(); + private final DartCompilerMainContext context; + private final CompilerConfiguration config; + private final Map libraries = new LinkedHashMap(); + private boolean packageApp = false; + private final boolean checkOnly; + private final boolean collectComments; + private CoreTypeProvider typeProvider; + private final boolean incremental; + private final List phases; + private final List backends; + private final LibrarySource coreLibrarySource; + + private Compiler(LibrarySource app, List embedded, CompilerConfiguration config, + DartCompilerMainContext context) { + this.app = app; + this.config = config; + this.phases = config.getPhases(); + this.backends = config.getBackends(); + this.context = context; + checkOnly = config.checkOnly(); + collectComments = config.collectComments(); + for (LibrarySource library : embedded) { + if (SystemLibraryManager.isDartSpec(library.getName())) { + embeddedLibraries.add(context.getSystemLibraryFor(library.getName())); + } else { + embeddedLibraries.add(library); + } + } + coreLibrarySource = context.getSystemLibraryFor(CORELIB_URL_SPEC); + embeddedLibraries.add(coreLibrarySource); + + if (config.shouldOptimize()) { + // Optimizing turns off incremental compilation. + incremental = false; + } else { + incremental = config.incremental(); + } + } + + private void compile() { + TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.COMPILE) : null; + try { + updateAndResolve(); + if (context.getErrorCount() > 0) { + return; + } + if (!context.getFilesHaveChanged()) { + return; + } + + compileLibraries(); + packageApp(); + } catch (IOException e) { + context.compilationError(new DartCompilationError(app, e)); + } finally { + Tracer.end(logEvent); + } + } + + /** + * Update the current application and any referenced libraries and resolve + * them. + * + * @return a {@link LibraryUnit}, never null + * @throws IOException on IO errors - the caller must log this if it cares + */ + private LibraryUnit updateAndResolve() throws IOException { + TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.UPDATE_RESOLVE) : null; + + CompilerMetrics compilerMetrics = context.getCompilerMetrics(); + if (compilerMetrics != null) { + compilerMetrics.startUpdateAndResolveTime(); + } + + try { + LibraryUnit library = updateLibraries(app); + importEmbeddedLibraries(); + parseOutOfDateFiles(); + if (!context.getFilesHaveChanged()) { + return library; + } + if (incremental) { + addOutOfDateDeps(); + } + if (!config.resolveDespiteParseErrors() && (context.getErrorCount() > 0)) { + return library; + } + buildLibraryScopes(); + LibraryUnit corelibUnit = updateLibraries(coreLibrarySource); + typeProvider = new CoreTypeProviderImplementation(corelibUnit.getElement().getScope(), + context); + resolveLibraries(); + return library; + } finally { + if(compilerMetrics != null) { + compilerMetrics.endUpdateAndResolveTime(); + } + + Tracer.end(logEvent); + } + } + + /** + * This method reads all libraries, updating their apis as necessary. They + * will be populated from some combination of fully-parsed compilation units + * and api files. + */ + private void parseOutOfDateFiles() throws IOException { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.PARSE_OUTOFDATE) : null; + CompilerMetrics compilerMetrics = context.getCompilerMetrics(); + long parseStart = compilerMetrics != null ? CompilerMetrics.getCPUTime() : 0; + + try { + for (LibraryUnit lib : libraries.values()) { + LibrarySource libSrc = lib.getSource(); + LibraryUnit apiLib = new LibraryUnit(libSrc); + LibraryNode selfSourcePath = lib.getSelfSourcePath(); + boolean persist = !incremental || !apiLib.loadApi(context, context); + + // Parse each compilation unit and update the API to reflect its contents. + for (LibraryNode libNode : lib.getSourcePaths()) { + final DartSource dartSrc = libSrc.getSourceFor(libNode.getText()); + if (dartSrc == null || !dartSrc.exists()) { + // Dart Editor needs to have all missing files reported as compilation errors. + // In addition, continue allows lib.populateTopLevelNodes() to be called so that the + // top level elements are populated preventing an NPE later on. + reportMissingSource(context, libSrc, libNode); + continue; + } + + DartUnit apiUnit = apiLib.getUnit(dartSrc.getName()); + if (apiUnit == null || isSourceOutOfDate(dartSrc, libSrc)) { + DartUnit unit = parse(dartSrc, lib.getPrefixes()); + if (unit != null) { + // if we are visiting the tu + if (libNode == selfSourcePath) { + lib.setSelfDartUnit(unit); + } + lib.putUnit(unit); + persist = true; + } + } else { + lib.putUnit(apiUnit); + } + } + + // Persist the api file. + if (persist) { + context.setFilesHaveChanged(); + if (!checkOnly) { + lib.saveApi(context); + } + } + + // Populate the library's class map. This is used later for + // dependency checking. + lib.populateTopLevelNodes(); + } + } finally { + if (compilerMetrics != null) { + compilerMetrics.addParseWallTimeNano(CompilerMetrics.getCPUTime() - parseStart); + } + Tracer.end(logEvent); + } + } + + /** + * This method reads the embedded library sources, making sure they are added + * to the list of libraries to compile. It then adds the libraries as imports + * of all libraries. The import is without prefix. + */ + private void importEmbeddedLibraries() throws IOException { + TraceEvent importEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.IMPORT_EMBEDDED_LIBRARIES) : null; + try { + for (LibrarySource embedded : embeddedLibraries) { + updateLibraries(embedded); + } + + for (LibraryUnit lib : libraries.values()) { + for (LibrarySource embedded : embeddedLibraries) { + LibraryUnit imp = libraries.get(embedded.getUri()); + // Check that the current library is not the embedded library, and + // that the current library does not already import the embedded + // library. + if (lib != imp && !lib.hasImport(imp)) { + lib.addImport(imp, null); + } + } + } + } finally { + Tracer.end(importEvent); + } + } + + /** + * This method reads a library source and sets it up with its imports. When it + * completes, it is guaranteed that {@link Compiler#libraries} will be completely populated. + */ + private LibraryUnit updateLibraries(LibrarySource libSrc) throws IOException { + TraceEvent updateEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.UPDATE_LIBRARIES, "name", + libSrc.getName()) : null; + try { + // Avoid cycles. + LibraryUnit lib = libraries.get(libSrc.getUri()); + if (lib != null) { + return lib; + } + + lib = context.getLibraryUnit(libSrc); + // If we could not find the library, continue. The context will report + // the error at the end. + if (lib == null) { + return null; + } + + libraries.put(libSrc.getUri(), lib); + + // Update dependencies. + for (LibraryNode libNode : lib.getImportPaths()) { + String libSpec = libNode.getText(); + LibrarySource dep; + if (SystemLibraryManager.isDartSpec(libSpec)) { + dep = context.getSystemLibraryFor(libSpec); + } else { + dep = libSrc.getImportFor(libSpec); + } + if (dep == null) { + reportMissingSource(context, libSrc, libNode); + continue; + } + + lib.addImport(updateLibraries(dep), libNode); + } + return lib; + } finally { + Tracer.end(updateEvent); + } + } + + /** + * Determines whether the given source is out-of-date with respect to its artifacts or + * its library's associated api. + */ + private boolean isSourceOutOfDate(DartSource dartSrc, LibrarySource libSrc) { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.IS_SOURCE_OUTOFDATE, "src", + dartSrc.getName()) : null; + try { + // If incremental compilation is disabled, just return true to force all + // units to be recompiled. + if (!incremental) { + return true; + } + + for (Backend backend : backends) { + TraceEvent backendEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.BACKEND_OUTOFDATE, "be", backend + .getClass().getCanonicalName(), "src", dartSrc.getName()) : null; + try { + if (backend.isOutOfDate(dartSrc, context)) { + return true; + } + } finally { + Tracer.end(backendEvent); + } + } + return (context.isOutOfDate(dartSrc, libSrc, EXTENSION_API)); + } finally { + Tracer.end(logEvent); + } + } + + /** + * Build scopes for the given libraries. + */ + private void buildLibraryScopes() { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.BUILD_LIB_SCOPES) : null; + try { + Collection libs = libraries.values(); + + // Build the class elements declared in the sources of a library. + // Loop can be parallelized. + for (LibraryUnit lib : libs) { + new TopLevelElementBuilder().exec(lib, context); + } + + // The library scope can then be constructed, containing types declared + // in the library, and // types declared in the imports. Loop can be + // parallelized. + for (LibraryUnit lib : libs) { + new TopLevelElementBuilder().fillInLibraryScope(lib, context); + } + } finally { + Tracer.end(logEvent); + } + } + + /** + * Parses compilation units that are out-of-date with respect to their dependencies. The + * parsed units will replace api units already in the library. + */ + private void addOutOfDateDeps() throws IOException { + TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.ADD_OUTOFDATE) : null; + try { + for (LibraryUnit lib : libraries.values()) { + // Load the existing DEPS, or create an empty one. + LibraryDeps deps = lib.getDeps(context); + + // Parse units that are out-of-date with respect to their + // dependencies. + for (String sourceName : deps.getSourceNames()) { + LibraryDeps.Source depSource = deps.getSource(sourceName); + if (isSourceOutOfDate(lib, depSource)) { + DartSource dartSrc = lib.getSource().getSourceFor(sourceName); + if ((dartSrc != null) && (dartSrc.exists())) { + DartUnit unit = parse(dartSrc, lib.getPrefixes()); + if (unit != null) { + // Replace the newly-parsed unit within the library. + lib.putUnit(unit); + } + } + } + } + } + } finally { + Tracer.end(logEvent); + } + } + + /** + * Determines whether the given source (as referenced by {@link LibraryDeps.Source}) is + * out-of-date with respect to any of its dependencies. + */ + private boolean isSourceOutOfDate(LibraryUnit lib, LibraryDeps.Source depSource) { + for (String nodeName : depSource.getNodeNames()) { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.IS_CLASS_OUT_OF_DATE, "class", + nodeName) : null; + try { + if (depSource.isHole(nodeName)) { + // The dependency's a "hole", meaning that any new identifier in the + // library scope that shadows it should force a recompile. + if (lib.getTopLevelNode(nodeName) != null) { + // The library defines a top-level node with the same name as the hole, so + // we need to recompile. + return true; + } + } else { + // Normal dependency. + Dependency dep = depSource.getDependency(nodeName); + + // Find the cached API and get its hash. + LibraryUnit depLib = libraries.get(dep.getLibUri()); + if (depLib == null) { + // The library no longer exists, so presume that we need to + // recompile. + return true; + } + + // If there's a hash mismatch, deps are out of date + DartNode depNode = depLib.getTopLevelNode(nodeName); + if (depNode == null) { + // Node was removed. That's about as mismatched as you can get. + return true; + } + String hash = Integer.toString(depNode.computeHash()); + if (!hash.equals(dep.getHash())) { + return true; + } + } + } finally { + Tracer.end(logEvent); + } + } + + // No holes or hash mismatches; in date. + return false; + } + + /** + * Resolve all libraries. Assume that all library scopes are already built. + */ + private void resolveLibraries() { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.RESOLVE_LIBRARIES) : null; + try { + // TODO(jgw): Optimization: Skip work for libraries that have nothing to + // compile. + + // Resolve super class chain, and build the member elements. Both passes + // need the library scope to be setup. Each for loop can be + // parallelized. + for (LibraryUnit lib : libraries.values()) { + for (DartUnit unit : lib.getUnits()) { + // These two method calls can be parallelized. + new SupertypeResolver().exec(unit, context, getTypeProvider()); + new MemberBuilder().exec(unit, context, getTypeProvider()); + } + } + } finally { + Tracer.end(logEvent); + } + } + + private void setEntryPoint() { + LibraryUnit lib = context.getAppLibraryUnit(); + lib.setEntryNode(new LibraryNode(MAIN_ENTRY_POINT_NAME)); + Element element = lib.getElement().lookupLocalElement(MAIN_ENTRY_POINT_NAME); + switch (ElementKind.of(element)) { + case NONE: + // this is ok, it might just be a library + break; + + case METHOD: + MethodElement methodElement = (MethodElement) element; + Modifiers modifiers = methodElement.getModifiers(); + if (!modifiers.isGetter() && !modifiers.isSetter() + && (methodElement.getParameters() == null + || methodElement.getParameters().size() == 0)) { + lib.getElement().setEntryPoint(methodElement); + } else if (modifiers.isGetter()) { + context.compilationError(new DartCompilationError(Location.NONE, + DartCompilerErrorCode.ENTRY_POINT_METHOD_MAY_NOT_BE_GETTER, MAIN_ENTRY_POINT_NAME)); + } else if (modifiers.isSetter()) { + context.compilationError(new DartCompilationError(Location.NONE, + DartCompilerErrorCode.ENTRY_POINT_METHOD_MAY_NOT_BE_SETTER, MAIN_ENTRY_POINT_NAME)); + } else { + context.compilationError(new DartCompilationError(Location.NONE, + DartCompilerErrorCode.ENTRY_POINT_METHOD_CANNOT_HAVE_PARAMETERS, + MAIN_ENTRY_POINT_NAME)); + } + break; + + default: + context.compilationError(new DartCompilationError(Location.NONE, + DartCompilerErrorCode.NOT_A_STATIC_METHOD, MAIN_ENTRY_POINT_NAME)); + break; + } + } + + private boolean checkUnitForLibraryDirective(DartUnit unit) { + List directives = unit.getDirectives(); + if (directives == null || directives.size() == 0) { + return false; + } + for (DartDirective directive : directives) { + if (directive instanceof DartLibraryDirective) { + return true; + } + } + return false; + } + + private void compileLibraries() throws IOException { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.COMPILE_LIBRARIES) : null; + + CompilerMetrics compilerMetrics = context.getCompilerMetrics(); + if (compilerMetrics != null) { + compilerMetrics.startCompileLibrariesTime(); + } + + try { + // Set entry point + setEntryPoint(); + + // The two following for loops can be parallelized. + for (LibraryUnit lib : libraries.values()) { + boolean persist = false; + boolean isAppLibUnit = (lib == context.getAppLibraryUnit()); + DartUnit libSelfUnit = lib.getSelfDartUnit(); + + // Compile all the units in this library. + for (DartUnit unit : lib.getUnits()) { + // Don't compile api-only units. + if (unit.isDiet()) { + continue; + } + + if (!isAppLibUnit) { + // See if this unit was imported from another unit, and if so, + // it's required to have a #library directive + if (libSelfUnit == unit) { + if (!checkUnitForLibraryDirective(unit)) { + context.compilationError(new DartCompilationError(Location.NONE, + DartCompilerErrorCode.MISSING_LIBRARY_DIRECTIVE, unit.getSourceName())); + } + } else { + // Else it's required not to have any directives + if (unit.getDirectives() != null) { + context.compilationError(new DartCompilationError(Location.NONE, + DartCompilerErrorCode.ILLEGAL_DIRECTIVES_IN_SOURCED_UNIT, libSelfUnit + .getSourceName(), unit.getSourceName())); + } + } + } + + // Run all compiler phases including AST simplification and symbol + // resolution. This must run in serial. + for (DartCompilationPhase phase : phases) { + TraceEvent phaseEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.EXEC_PHASE, "phase", phase + .getClass().getCanonicalName(), "lib", lib.getName(), "unit", unit + .getSourceName()) : null; + try { + unit = phase.exec(unit, context, getTypeProvider()); + } finally { + Tracer.end(phaseEvent); + } + if (context.getErrorCount() > 0) { + packageApp = false; + return; + } + } + + if (checkOnly) { + continue; + } + + // Run the unit through all the backends. This loop can also be + // parallelized. + for (Backend be : config.getBackends()) { + TraceEvent backendEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.BACKEND_COMPILE, "be", be + .getClass().getSimpleName(), "lib", lib.getName(), "unit", unit + .getSourceName()) : null; + try { + be.compileUnit(unit, unit.getSource(), context, typeProvider); + } finally { + Tracer.end(backendEvent); + } + } + + // Update deps. + lib.getDeps(context).update(unit, context); + + // We compiled something, so remember that this means we need to + // persist the deps and package the app. + persist = true; + packageApp = true; + } + + // Persist the DEPS file. + if (persist && !checkOnly) { + lib.writeDeps(context); + } + } + } finally { + if (compilerMetrics != null) { + compilerMetrics.endCompileLibrariesTime(); + } + + Tracer.end(logEvent); + } + } + + private void packageApp() throws IOException { + TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.PACKAGE_APP) : null; + + CompilerMetrics compilerMetrics = context.getCompilerMetrics(); + if (compilerMetrics != null) { + compilerMetrics.startPackageAppTime(); + } + + try { + // Package output for each backend. + if (packageApp) { + // When there's no entry-point in the application unit, + // don't attempt to package it. This can happen when + // compileUnit() is called on a library. Always package app + // when generating documentation. + if (context.getApplicationUnit().getEntryNode() == null && !collectComments) { + if (config.expectEntryPoint()) { + context.compilationError(new DartCompilationError(Location.NONE, + DartCompilerErrorCode.NO_ENTRY_POINT)); + } + return; + } + + for (Backend be : backends) { + TraceEvent backendEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.BACKEND_PACKAGE_APP, "be", be + .getClass().getSimpleName()) : null; + try { + be.packageApp(app, libraries.values(), context, typeProvider); + } finally { + Tracer.end(backendEvent); + } + } + } + } finally { + if (compilerMetrics != null) { + compilerMetrics.endPackageAppTime(); + } + Tracer.end(logEvent); + } + } + + DartUnit parse(DartSource dartSrc, Set libraryPrefixes) throws IOException { + TraceEvent parseEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.PARSE, "src", dartSrc.getName()) : null; + CompilerMetrics compilerMetrics = context.getCompilerMetrics(); + long parseStart = compilerMetrics != null ? CompilerMetrics.getThreadTime() : 0; + Reader r = dartSrc.getSourceReader(); + String srcCode; + boolean failed = true; + try { + try { + srcCode = CharStreams.toString(r); + failed = false; + } finally { + Closeables.close(r, failed); + } + + DartParser parser; + if (collectComments) { + DartScannerParserContext parserContext = + CommentPreservingParser.createContext(dartSrc, srcCode, context, + context.getCompilerMetrics()); + parser = new CommentPreservingParser(parserContext, false); + } else { + DartScannerParserContext parserContext = + new DartScannerParserContext(dartSrc, srcCode, context, context.getCompilerMetrics()); + parser = new DartParser(parserContext, libraryPrefixes); + } + DartUnit unit = parser.parseUnit(dartSrc); + if (compilerMetrics != null) { + compilerMetrics.addParseTimeNano(CompilerMetrics.getThreadTime() - parseStart); + } + + if (!config.resolveDespiteParseErrors() && context.getErrorCount() > 0) { + return null; + } + return unit; + } finally { + Tracer.end(parseEvent); + } + } + + private void reportMissingSource(DartCompilerContext context, + LibrarySource libSrc, + LibraryNode libNode) { + DartCompilationError event = new DartCompilationError(libNode, + DartCompilerErrorCode.MISSING_SOURCE, + libNode.getText()); + event.setSource(libSrc); + context.compilationError(event); + } + + CoreTypeProvider getTypeProvider() { + typeProvider.getClass(); // Quick null check. + return typeProvider; + } + } + + /** + * Selectively compile a library. Use supplied ASTs when available. This allows programming + * tools to provide customized ASTs for code that is currently being edited, and may not + * compile correctly. + */ + private static class SelectiveCompiler extends Compiler { + /** Map from source URI to AST representing the source */ + private final Map parsedUnits; + + private SelectiveCompiler(LibrarySource app, Map suppliedUnits, + CompilerConfiguration config, DartCompilerMainContext context) { + super(app, Collections.emptyList(), config, context); + parsedUnits = suppliedUnits; + } + + @Override + DartUnit parse(DartSource dartSrc, Set prefixes) throws IOException { + if (parsedUnits == null) { + return super.parse(dartSrc, prefixes); + } + URI srcUri = dartSrc.getUri(); + DartUnit parsedUnit = parsedUnits.get(srcUri); + return parsedUnit == null ? super.parse(dartSrc, prefixes) : parsedUnit; + } + } + + private static CompilerOptions processCommandLineOptions(String[] args) { + CmdLineParser cmdLineParser = null; + CompilerOptions compilerOptions = null; + try { + compilerOptions = new CompilerOptions(); + cmdLineParser = CommandLineOptions.parse(args, compilerOptions); + if (args.length == 0 || compilerOptions.showHelp()) { + showUsage(cmdLineParser, System.err); + System.exit(1); + } + } catch (CmdLineException e) { + System.err.println(e.getLocalizedMessage()); + showUsage(cmdLineParser, System.err); + System.exit(1); + } + + assert compilerOptions != null; + return compilerOptions; + } + + public static void main(String[] args) { + Tracer.init(); + + CompilerOptions topCompilerOptions = processCommandLineOptions(args); + boolean result = false; + try { + if (topCompilerOptions.isBatch()) { + if (args.length > 1) { + System.err.println("(Extra arguments specified with -batch ignored.)"); + } + UnitTestBatchRunner.runAsBatch(args, new Invocation() { + @Override + public boolean invoke(String[] args) throws Throwable { + CompilerOptions compilerOptions = processCommandLineOptions(args); + if (compilerOptions.isBatch()) { + System.err.println("-batch ignored: Already in batch mode."); + } + return compilerMain(compilerOptions); + } + }); + } else { + result = compilerMain(topCompilerOptions); + } + } catch (Throwable t) { + t.printStackTrace(); + crash(); + } + if (!result) { + System.exit(1); + } + } + + /** + * Invoke the compiler to build single application. + * + * @param compilerOptions parsed command line arguments + * + * @return true on success, false on failure. + */ + public static boolean compilerMain(CompilerOptions compilerOptions) throws IOException { + List sourceFiles = compilerOptions.getSourceFiles(); + if (sourceFiles.size() == 0) { + System.err.println("dartc: no source files were specified."); + showUsage(null, System.err); + return false; + } + + File sourceFile = new File(sourceFiles.get(0)); + if (!sourceFile.exists()) { + System.err.println("dartc: file not found: " + sourceFile); + showUsage(null, System.err); + return false; + } + + CompilerConfiguration config; + if (compilerOptions.getIsolateStubClasses().isEmpty()) { + config = new DefaultCompilerConfiguration(compilerOptions); + } else { + config = new DartIsolateStubGeneratorCompilerConfiguration( + compilerOptions); + } + return compilerMain(sourceFile, config); + } + + /** + * Invoke the compiler to build single application. + * + * @param sourceFile file passed on the command line to build + * @param config compiler configuration built from parsed command line options + * + * @return true on success, false on failure. + */ + public static boolean compilerMain(File sourceFile, CompilerConfiguration config) + throws IOException { + String errorMessage = compileApp(sourceFile, config); + if (errorMessage != null) { + System.err.println(errorMessage); + return false; + } + + TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.WRITE_METRICS) : null; + try { + maybeShowMetrics(config); + } finally { + Tracer.end(logEvent); + } + return true; + } + + public static void crash() { + // Our test scripts look for 253 to signal a "crash". + System.exit(253); + } + + private static void showUsage(CmdLineParser cmdLineParser, PrintStream out) { + out.println("Usage: dartc [] [script-arguments]"); + out.println("Available options:"); + if (cmdLineParser == null) { + cmdLineParser = new CmdLineParser(new CompilerOptions()); + } + cmdLineParser.printUsage(out); + } + + private static void maybeShowMetrics(CompilerConfiguration config) { + CompilerMetrics compilerMetrics = config.getCompilerMetrics(); + if (compilerMetrics != null) { + compilerMetrics.write(System.out); + } + + JvmMetrics.maybeWriteJvmMetrics(System.out, config.getJvmMetricOptions()); + } + + /** + * Compiles the source file which could be a single *.dart source file or a *.app file. If it + * is the former an *.app file is conceptually synthesized. + */ + public static String compileApp(File sourceFile, CompilerConfiguration config) throws IOException { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.COMPILE_APP, "src", sourceFile.toString()) + : null; + File outFile = config.getOutputFilename(); + if (outFile != null && config.getBackends().size() > 1) { + // More than one backend is ambiguous. + throw new IllegalArgumentException("Output filename " + + outFile + " specified. Only valid with a single backend."); + } + try { + File outputDirectory = config.getOutputDirectory(); + DefaultDartArtifactProvider provider = new DefaultDartArtifactProvider(outputDirectory); + DefaultDartCompilerListener listener = new DefaultDartCompilerListener(); + + // Compile the Dart application and its dependencies. + LibrarySource lib = new UrlLibrarySource(sourceFile); + config = new DelegatingCompilerConfiguration(config) { + @Override + public boolean expectEntryPoint() { + return true; + } + }; + + String errorString = compileLib(lib, config, provider, listener); + + // Write out a copy of the generated JS if specified by the user + if (errorString == null && outFile != null && !config.getBackends().isEmpty()) { + File dir = outFile.getParentFile(); + if (dir != null) { + if (dir != null && !dir.exists()) { + throw new IOException("Cannot create: " + outFile.getName() + + ". " + dir + " does not exist"); + } + if (!dir.canWrite()) { + throw new IOException("Cannot write " + outFile.getName() + " to " + + dir + ": Permission denied."); + } + } else { + dir = new File ("."); + if (!dir.canWrite()) { + throw new IOException("Cannot write " + outFile.getName() + " to " + + dir + ": Permission denied."); + } + } + + Reader r = null; + try { + // HACK: there can be more than one backend. Since there isn't + // an obvious way to tell which one the user meant, for now + // just restrict the option to save the output if more than + // one is active. + r = provider.getArtifactReader(lib, "", + config.getBackends().get(0).getAppExtension()); + String js = CharStreams.toString(r); + if (r != null) { + Files.write(js, outFile, Charset.defaultCharset()); + } + } finally { + Closeables.close(r, true); + } + } + return errorString; + } finally { + Tracer.end(logEvent); + } + } + + /** + * Compiles the given library, translating all its source files, and those + * of its imported libraries, transitively. + * + * If the specified library contains an entry-point method, then the application will be packaged + * by each backend. Otherwise, only library artifacts will be generated. + * + * @param lib The library to be compiled (not null) + * @param config The compiler configuration specifying the compilation phases + * and backends + * @param provider A mechanism for specifying where code should be generated + * @param listener An object notified when compilation errors occur + */ + public static String compileLib(LibrarySource lib, CompilerConfiguration config, + DartArtifactProvider provider, DartCompilerListener listener) throws IOException { + return compileLib(lib, Collections.emptyList(), config, provider, listener); + } + + /** + * Same method as above, but also takes a list of libraries that should be + * implicitly imported by all libraries. These libraries are provided by the embedder. + */ + public static String compileLib(LibrarySource lib, + List embeddedLibraries, + CompilerConfiguration config, + DartArtifactProvider provider, + DartCompilerListener listener) throws IOException { + DartCompilerMainContext context = new DartCompilerMainContext(lib, provider, listener, + config); + new Compiler(lib, embeddedLibraries, config, context).compile(); + int errorCount = context.getErrorCount(); + if (config.typeErrorsAreFatal()) { + errorCount += context.getTypeErrorCount(); + } + if (config.warningsAreFatal()) { + errorCount += context.getWarningCount(); + } + if (errorCount > 0) { + return "Compilation failed with " + errorCount + + (errorCount == 1 ? " problem." : " problems."); + } + if (!context.getFilesHaveChanged()) { + return null; + } + if (config.checkOnly()) { + Writer writer = provider.getArtifactWriter(lib, "", EXTENSION_LOG); + boolean threw = true; + try { + writer.write(String.format("Checked %s and found:%n", lib.getName())); + writer.write(String.format(" no load/resolution errors%n")); + writer.write(String.format(" %s type errors%n", context.getTypeErrorCount())); + threw = false; + } finally { + Closeables.close(writer, threw); + } + } + return null; + } + + /** + * Analyzes the given library and all its transitive dependencies. + * + * @param lib The library to be analyzed + * @param parsedUnits A collection of ASTs that should be used + * instead of parsing the associated source from storage. Intended for + * IDE use when modified buffers must be analyzed. AST nodes in the map may be + * ignored if not referenced by {@code lib}. (May be null.) + * @param config The compiler configuration (phases and backends + * will not be used), but resolution and type-analysis will be + * invoked + * @param provider A mechanism for specifying where code should be generated + * @param listener An object notified when compilation errors occur + * @throws NullPointerException if any of the arguments except {@code parsedUnits} + * are {@code null} + * @throws IOException on IO errors, which are not logged + */ + public static LibraryUnit analyzeLibrary(LibrarySource lib, Map parsedUnits, + CompilerConfiguration config, DartArtifactProvider provider, DartCompilerListener listener) + throws IOException { + lib.getClass(); // Quick null check. + provider.getClass(); // Quick null check. + listener.getClass(); // Quick null check. + DartCompilerMainContext context = new DartCompilerMainContext(lib, provider, listener, config); + Compiler compiler = new SelectiveCompiler(lib, parsedUnits, config, context); + LibraryUnit libraryUnit = compiler.updateAndResolve(); + // Ignore errors. Resolver should be able to cope with + // errors. Otherwise, we should fix it. + DartCompilationPhase[] phases = { + new Resolver.Phase(), + new TypeAnalyzer() + }; + for (DartUnit unit : libraryUnit.getUnits()) { + // Don't analyze api-only units. + if (unit.isDiet()) { + continue; + } + + for (DartCompilationPhase phase : phases) { + unit = phase.exec(unit, context, compiler.getTypeProvider()); + // Ignore errors. TypeAnalyzer should be able to cope with + // resolution errors. + } + } + return libraryUnit; + } + + /** + * Re-analyzes source code after a modification. The modification is described by a SourceDelta. + * + * @param delta what has changed + * @param enclosingLibrary the library in which the change occurred + * @param interestStart beginning of interest area (as character offset from the beginning of the + * source file after the change. + * @param interestLength length of interest area + * @return a node which covers the entire interest area. + */ + public static DartNode analyzeDelta(SourceDelta delta, + LibraryElement enclosingLibrary, + LibraryElement coreLibrary, + DartNode interestNode, + int interestStart, + int interestLength, + CompilerConfiguration config, + DartCompilerListener listener) throws IOException { + DeltaAnalyzer analyzer = new DeltaAnalyzer(delta, enclosingLibrary, coreLibrary, + interestNode, interestStart, interestLength, + config, listener); + return analyzer.analyze(); + } +} diff --git a/compiler/java/com/google/dart/compiler/DartCompilerContext.java b/compiler/java/com/google/dart/compiler/DartCompilerContext.java new file mode 100644 index 00000000000..4e7bec4af12 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartCompilerContext.java @@ -0,0 +1,133 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.metrics.CompilerMetrics; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; + +/** + * An interface used internally by the {@link DartCompiler} and implementers of + * {@link Backend} for determining where an artifact should be generated and + * providing feedback during the compilation process. This is an internal + * compiler construct and as such should not be instantiated or implemented by + * those outside the compiler itself. + */ +public interface DartCompilerContext { + + /** + * Parse the application being compiled and return the result. The "application unit" is a + * library that specifies an entry-point. + * + * This method will be removed in favor of {@link #getAppLibraryUnit()}. + * + * @return the parsed result (not null) + */ + LibraryUnit getApplicationUnit(); + + /** + * Parse the application being compiled and return the result. The "app" library unit is a + * library that specifies an entry-point. + * + * @return the parsed result (not null) + */ + LibraryUnit getAppLibraryUnit(); + + /** + * Parse the specified library and return the result. + * + * @param lib the library to parse (not null) + * @return the parsed result (not null) + */ + LibraryUnit getLibraryUnit(LibrarySource lib); + + /** + * Called by the compiler when a compilation error has occurred in a Dart + * file. + * + * @param event the event information (not null) + */ + void compilationError(DartCompilationError event); + + /** + * Called by the compiler when a (non-fatal) type error has been detected. + * + * @param event the event information (not null) + */ + void typeError(DartCompilationError event); + + /** + * Gets a reader for an artifact associated with the specified source, which + * must have been written to {@link #getArtifactWriter(Source, String, String)}. The + * caller is responsible for closing the reader. Only one artifact may be + * associated with the given extension. + * + * @param source the source file (not null) + * @param part a component of the source file to get a reader for (may be empty). + * @param extension the file extension for this artifact (not + * null, not empty) + * @return the reader, or null if no such artifact exists + */ + Reader getArtifactReader(Source source, String part, String extension) throws IOException; + + /** + * Gets the {@link URI} for an artifact associated with this source. + * + * @param source the source file (not null) + * @param part a component of the source file to get a reader for (may be empty). + * @param extension the file extension for this artifact (not + * null, not empty) + */ + URI getArtifactUri(DartSource source, String part, String extension); + + /** + * Gets a writer for an artifact associated with this source. The caller is + * responsible for closing the writer. Only one artifact may be associated + * with the given extension. + * + * @param source the source file (not null) + * @param part a component of the source file to get a reader for (may be empty). + * @param extension the file extension for this artifact (not + * null, not empty) + */ + Writer getArtifactWriter(Source source, String part, String extension) throws IOException; + + /** + * Determines whether an artifact for the specified source is out of date + * with respect to some other source. + * + * @param source the source file to check (not null) + * @param base the artifact's base source (not null) + * @param extension the file extension for this artifact (not + * null, not empty) + * @return true if out of date + */ + boolean isOutOfDate(Source source, Source base, String extension); + + /** + * Returns the {@link CompilerMetrics} instance or null if we should not record + * metrics. + * + * @return the metrics instance, null if metrics should not be recorded + */ + CompilerMetrics getCompilerMetrics(); + + boolean allowNoSuchType(); + + /** + * Returns the {@link CompilerConfiguration} instance. + * @return the compiler configuration instance. + */ + CompilerConfiguration getCompilerConfiguration(); + + /** + * Return the system library corresponding to the specified "dart:" spec. + */ + LibrarySource getSystemLibraryFor(String importSpec); +} diff --git a/compiler/java/com/google/dart/compiler/DartCompilerErrorCode.java b/compiler/java/com/google/dart/compiler/DartCompilerErrorCode.java new file mode 100644 index 00000000000..1d80962bc08 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartCompilerErrorCode.java @@ -0,0 +1,219 @@ +// 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. + +package com.google.dart.compiler; + +/** + * Valid error codes for the errors produced by the Dart compiler. + */ +public enum DartCompilerErrorCode implements ErrorCode { + // TODO(brianwilkerson) Fill in the error messages as error creation sites are converted to use + // these error codes. + ABSTRACT_CLASS("%s is an abstract class because it does not implement the following members:%s"), + ABSTRACT_MEMBER_IN_INTERFACE("SyntaxError: abstract members are not allowed in interfaces"), + CANNOT_ACCESS_OUTER_LABEL("Cannot access label %s declared in an outer function"), + CANNOT_ACCESS_FIELD_IN_INIT("Cannot access an instance field in an initializer expression"), + CANNOT_ASSIGN_TO_FINAL("cannot assign value to final variable \"%s\"."), + CANNOT_BE_RESOLVED("cannot resolve %s"), + CANNOT_BE_RESOLVED_LIBRARY("cannot resolve %s in library %s"), + CANNOT_BE_INITIALIZED("cannot be initialized"), + CANNOT_CALL_LABEL("Labels cannot be called"), + CANNOT_DECLARE_NON_FACTORY_CONSTRUCTOR( + "Cannot declare a non-factory named constructor of another class."), + CANNOT_INIT_FIELD_FROM_SUPERCLASS("Cannot initialize a field from a super class"), + CANNOT_INIT_STATIC_FIELD_IN_INITIALIZER( + "Cannot initialize a static field in an initializer list"), + CANNOT_INSTATIATE_ABSTRACT_CLASS("cannot instantiate abstract class %s"), + CANNOT_OVERRIDE_INSTANCE_MEMBER("static member cannot override instance member %s of %s"), + CANNOT_OVERRIDE_STATIC_MEMBER("cannot override static member %s of %s"), + CANNOT_OVERRIDE_TYPED_MEMBER("cannot override %s of %s because %s is not assignable to %s"), + CANNOT_RESOLVE_CONSTRUCTOR("cannot resolve constructor %s"), + CANNOT_RESOLVE_FIELD("cannot resolve field %s"), + CANNOT_RESOLVE_LABEL("cannot resolve label %s"), + CANNOT_RESOLVE_METHOD("cannot resolve method %s"), + CANNOT_RESOLVE_SUPER_CONSTRUCTOR("cannot resolve method %s"), + CATCH_OR_FINALLY_EXPECTED("catch or finally clause expected."), + CONSTRUCTOR_CANNOT_BE_ABSTRACT("A constructor cannot be asbstract"), + CONSTRUCTOR_CANNOT_BE_STATIC("A constructor cannot be static"), + CONSTRUCTOR_MUST_CALL_SUPER("Constructors must call super constructor"), + CONST_CONSTRUCTOR_CANNOT_HAVE_BODY("A cconst onstructor cannot have a body"), + CONST_CONSTRUCTOR_MUST_CALL_CONST_SUPER("const constructor must call const super constructor"), + CONSTANTS_MUST_BE_INITIALIZED("constants must be initialized"), + CYCLIC_CLASS("%s causes a cycle in the supertype graph"), + DEFAULT_PARAMETER_BEFORE_NORMAL_PARAMETER("SyntaxError: Default parameters must be after normal " + + "parameters"), + DEFAULT_POSITIONAL_PARAMETER("Positional parameters cannot have default values"), + DID_YOU_MEAN_NEW("%1$s is a %2$s. Did you mean (new %1$s)?"), + DISALLOWED_ABSTRACT_KEYWORD("SyntaxError: abstract keyword not allowed here"), + DISALLOWED_FACTORY_KEYWORD("SyntaxError: factory keyword not allowed here"), + DUPLICATE_DEFINITION("duplicate definition of %s"), + DUPLICATED_INTERFACE("%s and %s are duplicated in the supertype graph"), + ENTRY_POINT_IN_LIBRARY("Libraries may not specify an entry point"), + ENTRY_POINT_METHOD_CANNOT_HAVE_PARAMETERS("Main entry point method cannot have parameters"), + ENTRY_POINT_METHOD_MAY_NOT_BE_GETTER("Entry point \"%s\" may not be a getter"), + ENTRY_POINT_METHOD_MAY_NOT_BE_SETTER("Entry point \"%s\" may not be a setter"), + EXPECTED_AN_INSTANCE_FIELD_IN_SUPER_CLASS( + "expected an instance field in the super class, but got %s"), + EXPECTED_ARRAY_OR_MAP_LITERAL("Expected array or map literal"), + EXPECTED_CASE_OR_DEFAULT("Expected 'case' or 'default'"), + EXPECTED_COMMA_OR_RIGHT_BRACE("Expected ',' or '}'"), + EXPECTED_COMMA_OR_RIGHT_PAREN("Expected ',' or ')', but got '%s'"), + EXPECTED_COMPOUND_STATEMENT("SyntaxError: expected if, switch, while, do, or for"), + EXPECTED_CONSTANT_LITERAL("Expected a constant literal"), + EXPECTED_EOS("Unexpected token '%s' (expected end of file)"), + EXPECTED_FIELD_NOT_CLASS("%s is a class, expected a local field"), + EXPECTED_FIELD_NOT_METHOD("%s is a method, expected a local field"), + EXPECTED_FIELD_NOT_PARAMETER("%s is a parameter, expected a local field"), + EXPECTED_FIELD_NOT_TYPE_VAR("%s is a type variable, expected a local field"), + EXPECTED_IDENTIFIER("Expected identifier"), + EXPECTED_ONE_ARGUMENT("Expected one argument"), + EXPECTED_LEFT_BRACKET_OR_LEFT_BRACE("'[' or '{' expected"), + EXPECTED_LEFT_PAREN("'(' expected"), + EXPECTED_LIBRARY("Must begin with 'library' or 'application'"), + EXPECTED_PERIOD_OR_LEFT_BRACKET("SyntaxError: expected '.' or '['"), + EXPECTED_PREFIX_KEYWORD("SyntaxError: expected 'prefix' after comma"), + EXPECTED_SEMICOLON("Expected ';'"), + EXPECTED_STATIC_FIELD("expected a static field, but got %s"), + EXPECTED_STRING_LITERAL("Expected string literal"), + EXPECTED_TYPE("Expected type %s, got %s"), + EXPECTED_TOKEN("Unexpected token '%s' (expected '%s')"), + EXPECTED_VAR_FINAL_OR_TYPE("Expected 'var', 'final' or type"), + EXPORTED_FUNCTIONS_MUST_BE_STATIC("Exported functions must be static"), + EXTENDED_NATIVE_CLASS("Native classes must not extend other classes"), + EXTRA_ARGUMENT("extra argument"), + EXTRA_COMMA("Extra comma"), + EXTRA_QUALIFIER_FOR_TYPE_DECLARATION("SyntaxError: extra qualifier for a class or interface " + + "definition"), + EXTRA_TYPE_ARGUMENT("Type variables may not have type arguments"), + FACTORY_ACCESS_SUPER("Cannot use 'super' in a factory constructor"), + FACTORY_CANNOT_BE_ABSTRACT("SyntaxError: A factory cannot be abstract"), + FACTORY_CANNOT_BE_CONST("SyntaxError: A factory cannot be const"), + FACTORY_CANNOT_BE_STATIC("SyntaxError: A factory cannot be static"), + FACTORY_MEMBER_IN_INTERFACE("SyntaxError: factory members are not allowed in interfaces"), + FIELD_CONFLICTS("%s conflicts with previously defined %s"), + FOR_IN_WITH_COMPLEX_VARIABLE("Only simple variables can be assigned to in a for-in construct"), + FOR_IN_WITH_MULTIPLE_VARIABLES("Too many variable declarations in a for-in construct"), + FOR_IN_WITH_VARIABLE_INITIALIZER("Cannot initialize for-in variables"), + FUNCTION_KEYWORD("'function' keyword is deprecated'"), + FUNCTION_TYPED_PARAMETER_IS_CONST("Formal parameter with a function type cannot be const"), + FUNCTION_TYPED_PARAMETER_IS_FINAL("Formal parameter with a function type cannot be const"), + FUNCTION_TYPED_PARAMETER_IS_VAR("Formal parameter with a function type cannot be var"), + FUNCTION_TYPED_PARAMETER_IS_VARIADIC("Formal parameter with a function type cannot be variadic"), + ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE("SyntaxError: Illegal assignment to non-assignable " + + "expression"), + ILLEGAL_DIRECTIVES_IN_SOURCED_UNIT("A source which was included by another source via a " + + "#source directive cannot itself contain directives: %s -> %s"), + ILLEGAL_FIELD_ACCESS_FROM_STATIC("Illegal access of instance field %s from static scope"), + ILLEGAL_METHOD_ACCESS_FROM_STATIC("Illegal access of instance method %s from static scope"), + ILLEGAL_NUMBER_OF_ARGUMENTS("SyntaxError: Illegal number of arguments"), + INCOMPLETE_STRING_LITERAL("Incomplete string literal"), + INSTANCE_METHOD_FROM_STATIC("Instance methods cannot be referenced from static methods"), + INTERFACE_HAS_NO_METHOD_NAMED("%s has no method named \"%s\""), + INTERNAL_ERROR("internal error: %s"), + INVALID_FIELD_DECLARATION("SyntaxError: wrong syntax for field declaration"), + INVALID_OPERATOR_CHAINING("SyntaxError: cannot chain '%s'"), + INVALID_TYPE_NAME_IN_CONSTRUCTOR("Invalid type in constructor name"), + IS_A_CLASS("%s is a class and cannot be used as an expression"), + IS_A_CONSTRUCTOR("%s.%s is a constructor, expected a method"), + IS_AN_INSTANCE_METHOD("%s.%s is an instance method, not a static method"), + IS_STATIC_FIELD_IN("\"%s\" is a static field in \"%s\""), + IS_STATIC_METHOD_IN("\"%s\" is a static method in \"%s\""), + MALFORMED_FUNCTION_TYPE_ALIAS("SyntaxError: malformed function alias"), + MALFORMED_PARAMETERIZED_TYPE("SyntaxError: malformed parameterized type"), + MEMBER_IS_A_CONSTRUCTOR("%s is a constructor in %s"), + METHOD_MUST_HAVE_BODY("A non-abstract method must have a body"), + MISSING_ARGUMENT("missing argument of type %s"), + MISSING_FUNCTION_NAME("a function name is required for a declaration"), + MISSING_LIBRARY_DIRECTIVE("a library which was imported into another library is missing a " + + "#library directive: %s"), + MISSING_RETURN_VALUE("no return value; expected a value of type %s"), + MISSING_SOURCE("Cannot find referenced source: %s"), + MULTIPLE_ENTRY_POINTS("'entrypoint' may be specified only once"), + MULTIPLE_IMPORT_LISTS("'import' may be specified only once"), + MULTIPLE_NATIVES("'native' may be specified only once"), + MULTIPLE_RESOURCE_LISTS("'resource' may be specified only once"), + MULTIPLE_REST_PARAMETERS("multiple rest parameters"), + MULTIPLE_SOURCE_LISTS("'source' may be specified only once"), + NAMED_AND_VARIADIC_PARAMETERS("Cannot have both named and variadic parameters"), + NAME_CLASSES_EXISTING_MEMBER("name clashes with another previously defined member"), + NEW_EXPRESSION_NOT_CONSTRUCTOR("New expression does not resolve to a constructor"), + NON_CONST_STATIC_MEMBER_IN_INTERFACE("SyntaxError: non-final static members are not allowed in " + + "interfaces"), + NON_FINAL_STATIC_MEMBER_IN_INTERFACE("SyntaxError: non-final static members are not allowed in " + + "interfaces"), + NO_SUCH_TYPE("no such type \"%s\""), + NO_ENTRY_POINT("No entrypoint specified for app"), + NOT_A_CLASS("\"%s\" is not a class"), + NOT_A_CLASS_OR_INTERFACE("\"%s\" is not a class or interface"), + NOT_A_LABEL("\"%s\" is not a label"), + NOT_A_MEMBER_OF("\"%s\" is not a member of %s"), + NOT_A_METHOD_IN("\"%s\" is not a method in %s"), + NOT_AN_INSTANCE_FIELD("%s is not an instance field"), + NOT_AN_INTERFACE("\"%s\" is not an interface"), + NOT_A_FUNCTION("\"%s\" is not a function"), + NOT_A_STATIC_FIELD("\"%s\" is not a static field"), + NOT_A_STATIC_METHOD("\"%s\" is not a static method"), + REDIRECTED_CONSTRUCTOR_CYCLE("Redirected constructor call has a cycle."), + OPERATOR_CANNOT_BE_STATIC("SyntaxError: Operators cannot be static"), + OPERATOR_WRONG_OPERAND_TYPE("operand of \"%s\" must be assignable to \"%s\""), + PARAMETER_INIT_OUTSIDE_CONSTRUCTOR("Parameter initializers can only be used in constructors"), + PARAMETER_INIT_STATIC_FIELD( + "Parameter initializer cannot be use to initialize a static field '%s'"), + PARAMETER_INIT_WITH_REDIR_CONSTRUCTOR( + "Parameter initializers cannot be used with redirected constructors"), + PARAMETER_NOT_MATCH_FIELD("Could not match parameter initializer '%s' with any field"), + STATIC_FINAL_REQUIRES_VALUE("Static final fields must have an initial value"), + STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE( + "static member %s of %s cannot be accessed through an instance"), + STATIC_METHOD_ACCESS_SUPER("Cannot use 'super' in a static method"), + STATIC_METHOD_ACCESS_THIS("Cannot use 'this' in a static method"), + SUPERFLUOUS_FUNCTION_KEYWORD("SyntaxError: superfluous 'function' keyword"), + SUPER_CALL_MUST_BE_FIRST("super call must be first in initializer list"), + SUPER_OUTSIDE_OF_METHOD("Cannot use 'super' outside of a method"), + SUPERTYPE_HAS_FIELD("%s is a field in %s"), + SUPERTYPE_HAS_METHOD("%s is a method in %s"), + TOP_LEVEL_IS_STATIC("Top-level field or method may not be static"), + TOP_LEVEL_METHOD_ACCESS_SUPER("Cannot use 'super' in a top-level method"), + TOP_LEVEL_METHOD_ACCESS_THIS("Cannot use 'this' in a top-level method"), + TYPE_NOT_ASSIGNMENT_COMPATIBLE("%s is not assignable to %s"), + TYPE_VARIABLE_IN_STATIC_CONTEXT("cannot access type variable %s in static context"), + UNEXPECTED_TOKEN("Unexpected token '%s'"), + UNEXPECTED_TOKEN_IN_STRING_INTERPOLATION("Unexpected token in string interpolation: %s"), + UNEXPECTED_TYPE_ARGUMENT("unexpected type argument"), + VARIADIC_PARAMETER_HAS_INITIALIZER("Rest parameter cannot have an initializer"), + UNREFERENCED_LABEL("unreferenced label \"%s\""), + USELESS_LABEL("useless label \"%s\""), + VOID("expression does not yield a value"), + VOID_CANNOT_RETURN_VALUE("cannot return a value from a void function"), + VOID_FIELD("SyntaxError: field cannot be of type void"), + VOID_PARAMETER("SyntaxError: parameter cannot be of type void"), + VOID_VARIABLE("Variable cannot be of type void"), + WRONG_NUMBER_OF_TYPE_ARGUMENTS("%s: wrong number of type arguments"); + + /** + * The message format string used to create the message to be displayed for this error. + */ + private String message; + + private DartCompilerErrorCode() { + // TODO(brianwilkerson) Remove this constructor once all of the error codes have messages + // associated with them. + this("%s"); + } + + /** + * Initialize a newly created error code to have the given message. + * + * @param message the message format string used to create the message to be displayed for this + * error + */ + private DartCompilerErrorCode(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/compiler/java/com/google/dart/compiler/DartCompilerListener.java b/compiler/java/com/google/dart/compiler/DartCompilerListener.java new file mode 100644 index 00000000000..b80806e6a62 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartCompilerListener.java @@ -0,0 +1,25 @@ +// 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. + +package com.google.dart.compiler; + +/** + * Abstract class that {@link DartCompiler} consumers can use to monitor + * compilation progress and report various problems that occur during + * compilation. + */ +public abstract class DartCompilerListener { + + /** + * Called by the compiler when a compilation error has occurred in a Dart + * file. + * + * @param event the event information (not null) + */ + public abstract void compilationError(DartCompilationError event); + + public abstract void compilationWarning(DartCompilationError event); + + public abstract void typeError(DartCompilationError event); +} diff --git a/compiler/java/com/google/dart/compiler/DartCompilerMainContext.java b/compiler/java/com/google/dart/compiler/DartCompilerMainContext.java new file mode 100644 index 00000000000..4079d256e62 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartCompilerMainContext.java @@ -0,0 +1,183 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.parser.DartParser; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * An overall context for the Dart compiler providing an adapter and forwarding + * mechanism for to both {@link DartArtifactProvider} and + * {@link DartCompilerListener}. This is an internal compiler construct and as + * such should not be instantiated or subclassed by those outside the compiler + * itself. + */ +final class DartCompilerMainContext extends DartCompilerListener implements + DartCompilerContext { + + private final LibrarySource lib; + private final DartArtifactProvider provider; + private final DartCompilerListener listener; + private final AtomicInteger errorCount = new AtomicInteger(0); + private final AtomicInteger warningCount = new AtomicInteger(0); + private final AtomicInteger typeErrorCount = new AtomicInteger(0); + private final AtomicBoolean filesHaveChanged = new AtomicBoolean(); + // declared volatile for thread-safety + private volatile LibraryUnit appLibraryUnit = null; + + private final CompilerConfiguration compilerConfiguration; + + DartCompilerMainContext(LibrarySource lib, DartArtifactProvider provider, + DartCompilerListener listener, + CompilerConfiguration compilerConfiguration) { + this.lib = lib; + this.provider = provider; + this.listener = listener; + this.compilerConfiguration = compilerConfiguration; + } + + @Override + public void compilationError(DartCompilationError event) { + incrementErrorCount(); + listener.compilationError(event); + } + + @Override + public void compilationWarning(DartCompilationError event) { + incrementWarningCount(); + listener.compilationWarning(event); + } + + @Override + public void typeError(DartCompilationError event) { + if (!allowNoSuchType() || event.getErrorCode() != DartCompilerErrorCode.CANNOT_BE_RESOLVED) { + incrementTypeErrorCount(); + listener.typeError(event); + } + } + + @Override + public LibraryUnit getApplicationUnit() { + return getAppLibraryUnit(); + } + + @Override + public LibraryUnit getAppLibraryUnit() { + // use double-checked looking pattern with use of volatile + if (appLibraryUnit == null) { + synchronized (this) { + if (appLibraryUnit == null) { + try { + appLibraryUnit = + DartParser.getSourceParser(lib, listener).preProcessLibraryDirectives(lib); + } catch (IOException ex) { + compilationError(new DartCompilationError(lib, ex)); + return null; + } + } + } + } + return appLibraryUnit; + } + + @Override + public Reader getArtifactReader(Source source, String part, String extension) + throws IOException { + return provider.getArtifactReader(source, part, extension); + } + + @Override + public URI getArtifactUri(DartSource source, String part, String extension) { + return provider.getArtifactUri(source, part, extension); + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) + throws IOException { + return provider.getArtifactWriter(source, part, extension); + } + + public int getErrorCount() { + return errorCount.get(); + } + + public int getWarningCount() { + return warningCount.get(); + } + + public int getTypeErrorCount() { + return typeErrorCount.get(); + } + + @Override + public LibraryUnit getLibraryUnit(LibrarySource libSrc) { + if (libSrc == lib) { + return getApplicationUnit(); + } + // TODO (danrubel) cache parsed library results + try { + return DartParser.getSourceParser(libSrc, listener).preProcessLibraryDirectives(libSrc); + } catch (IOException ex) { + compilationError(new DartCompilationError(libSrc, ex)); + return null; + } + } + + @Override + public boolean isOutOfDate(Source source, Source base, String extension) { + return provider.isOutOfDate(source, base, extension); + } + + protected void incrementErrorCount() { + errorCount.incrementAndGet(); + } + + protected void incrementWarningCount() { + warningCount.incrementAndGet(); + } + + protected void incrementTypeErrorCount() { + typeErrorCount.incrementAndGet(); + } + + @Override + public CompilerMetrics getCompilerMetrics() { + return compilerConfiguration.getCompilerMetrics(); + } + + public void setFilesHaveChanged() { + filesHaveChanged.set(true); + } + + public boolean getFilesHaveChanged() { + return filesHaveChanged.get(); + } + + @Override + public boolean allowNoSuchType() { + return compilerConfiguration.allowNoSuchType(); + } + + @Override + public CompilerConfiguration getCompilerConfiguration() { + return compilerConfiguration; + } + + /** + * Return the system library corresponding to the specified "dart:" spec. + */ + @Override + public LibrarySource getSystemLibraryFor(String importSpec) { + return compilerConfiguration.getSystemLibraryFor(importSpec); + } +} diff --git a/compiler/java/com/google/dart/compiler/DartIsolateStubGeneratorCompilerConfiguration.java b/compiler/java/com/google/dart/compiler/DartIsolateStubGeneratorCompilerConfiguration.java new file mode 100644 index 00000000000..ee5469355a4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartIsolateStubGeneratorCompilerConfiguration.java @@ -0,0 +1,22 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; + +import java.io.FileNotFoundException; + +public class DartIsolateStubGeneratorCompilerConfiguration extends DefaultCompilerConfiguration { + + public DartIsolateStubGeneratorCompilerConfiguration(CompilerOptions compilerOptions) + throws FileNotFoundException { + super(compilerOptions); + } + + @Override + public boolean allowNoSuchType() { + return true; + } +} diff --git a/compiler/java/com/google/dart/compiler/DartSource.java b/compiler/java/com/google/dart/compiler/DartSource.java new file mode 100644 index 00000000000..2962e17156a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DartSource.java @@ -0,0 +1,21 @@ +// 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. + +package com.google.dart.compiler; + +/** + * Abstract interface to Dart source. + */ +public interface DartSource extends Source { + + /** + * Gets the library with which this Dart source is associated. + */ + LibrarySource getLibrary(); + + /** + * Gets the path of this source, relative to its library. + */ + String getRelativePath(); +} diff --git a/compiler/java/com/google/dart/compiler/DefaultCompilerConfiguration.java b/compiler/java/com/google/dart/compiler/DefaultCompilerConfiguration.java new file mode 100644 index 00000000000..8ff32c113a4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DefaultCompilerConfiguration.java @@ -0,0 +1,208 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.dart.compiler.backend.doc.DartDocumentationGenerator; +import com.google.dart.compiler.backend.isolate.DartIsolateStubGenerator; +import com.google.dart.compiler.backend.js.ClosureJsBackend; +import com.google.dart.compiler.backend.js.JavascriptBackend; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.resolver.Resolver; +import com.google.dart.compiler.type.TypeAnalyzer; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A configuration for the Dart compiler specifying which phases + * and backends will be executed. + * + * @author sigmund@google.com (Siggi Cherem) + */ +public class DefaultCompilerConfiguration implements CompilerConfiguration { + + private List backends; + + private final CompilerOptions compilerOptions; + + private final CompilerMetrics compilerMetrics; + + private final SystemLibraryManager systemLibraryManager; + + /** + * A default configuration with the {@link JavascriptBackend} + */ + public DefaultCompilerConfiguration() { + this(new JavascriptBackend()); + } + + private static Backend selectBackend(CompilerOptions compilerOptions) + throws FileNotFoundException { + if (compilerOptions.generateDocumentation()) { + return new DartDocumentationGenerator(compilerOptions.getDocumentationOutputDirectory(), + compilerOptions.getDocumentationLibrary()); + } else if (!compilerOptions.getIsolateStubClasses().isEmpty()) { + return new DartIsolateStubGenerator(compilerOptions.getIsolateStubClasses(), + compilerOptions.getIsolateStubOutputFile()); + } else if (compilerOptions.shouldOptimize()) { + return new ClosureJsBackend(); + } else { + return new JavascriptBackend(); + } + } + + /** + * A new instance with the specified {@link CompilerOptions} + * @throws FileNotFoundException + */ + public DefaultCompilerConfiguration(CompilerOptions compilerOptions) + throws FileNotFoundException { + this (selectBackend(compilerOptions), compilerOptions); + } + + /** + * A new instance with the specified {@link Backend} + */ + public DefaultCompilerConfiguration(Backend backend) { + this(new CompilerOptions(), backend); + } + + /** + * A new instance with the specified {@link Backend} and {@link CompilerOptions} + */ + public DefaultCompilerConfiguration(Backend backend, CompilerOptions compilerOptions) { + this(compilerOptions, backend); + } + + /** + * A new instance with the specified list of {@link Backend} + */ + public DefaultCompilerConfiguration(Backend... backends) { + this(new CompilerOptions(), backends); + } + + /** + * A new instance with the specified list of {@link Backend} + */ + public DefaultCompilerConfiguration(CompilerOptions compilerOptions, Backend... backends) { + this(compilerOptions, new SystemLibraryManager(), backends); + } + + /** + * A new instance with the specified options, system library manager, and default {@link Backend + * backends}. + */ + public DefaultCompilerConfiguration(CompilerOptions compilerOptions, + SystemLibraryManager libraryManager) throws FileNotFoundException { + this(compilerOptions, libraryManager, selectBackend(compilerOptions)); + } + + /** + * A new instance with the specified options, system library manager, and list of {@link Backend + * backends}. + */ + public DefaultCompilerConfiguration(CompilerOptions compilerOptions, SystemLibraryManager libraryManager, Backend... backends) { + this.backends = Arrays.asList(backends); + this.compilerOptions = compilerOptions; + this.compilerMetrics = compilerOptions.showMetrics() ? + new CompilerMetrics() : null; + this.systemLibraryManager = libraryManager; + } + + @Override + public List getPhases() { + List phases = new ArrayList(); + phases.add(new Resolver.Phase()); + phases.add(new TypeAnalyzer()); + return phases; + } + + @Override + public List getBackends() { + return backends; + } + + @Override + public boolean shouldOptimize() { + return compilerOptions.shouldOptimize(); + } + + @Override + public CompilerMetrics getCompilerMetrics() { + return compilerMetrics; + } + + @Override + public String getJvmMetricOptions() { + return compilerOptions.getJvmMetricOptions(); + } + + @Override + public boolean typeErrorsAreFatal() { + return compilerOptions.typeErrorsAreFatal(); + } + + @Override + public boolean warningsAreFatal() { + return compilerOptions.warningsAreFatal(); + } + + @Override + public boolean resolveDespiteParseErrors() { + return false; + } + + @Override + public boolean incremental() { + return compilerOptions.incremental(); + } + + @Override + public File getOutputFilename() { + return compilerOptions.getOutputFilename(); + } + + @Override + public File getOutputDirectory() { + return compilerOptions.getWorkDirectory(); + } + + @Override + public boolean checkOnly() { + return compilerOptions.checkOnly(); + } + + @Override + public boolean expectEntryPoint() { + return false; + } + + @Override + public boolean allowNoSuchType() { + return false; + } + + @Override + public boolean collectComments() { + return compilerOptions.generateDocumentation(); + } + + @Override + public LibrarySource getSystemLibraryFor(String importSpec) { + URI systemUri; + try { + systemUri = new URI(importSpec); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + return new UrlLibrarySource(systemUri, this.systemLibraryManager); + } +} \ No newline at end of file diff --git a/compiler/java/com/google/dart/compiler/DefaultDartArtifactProvider.java b/compiler/java/com/google/dart/compiler/DefaultDartArtifactProvider.java new file mode 100644 index 00000000000..48bf311655a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DefaultDartArtifactProvider.java @@ -0,0 +1,232 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * A default implementation of {@link DartArtifactProvider} specifying + * generated files be placed in the same directory as source files. + */ +public class DefaultDartArtifactProvider extends DartArtifactProvider { + + private final File outputDirectory; + + public DefaultDartArtifactProvider() { + this(new File("out")); + } + + public DefaultDartArtifactProvider(File outputDirectory) { + this.outputDirectory = outputDirectory; + } + + @Override + public Reader getArtifactReader(Source source, String part, String extension) + throws IOException { + if (SystemLibraryManager.isDartUri(source.getUri())) { + DartSource bundledSource = getBundledArtifact(source, source, part, extension); + if (bundledSource != null) { + Reader reader = null; + try { + reader = bundledSource.getSourceReader(); + } catch (FileNotFoundException e) { + /* thrown if file doesn't exist, which is fine */ + } + if (reader != null) { + return new BufferedReader(reader); + } + } + } + File file = getArtifactFile(source, part, extension); + if (!file.exists()) { + return null; + } + return new BufferedReader(new FileReader(file)); + } + + @Override + public URI getArtifactUri(Source source, String part, String extension) { + try { + return new URI("file", getArtifactFile(source, part, extension).getPath(), null); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) throws IOException { + return new BufferedWriter(new FileWriter(makeDirectories(getArtifactFile(source, part, + extension)))); + } + + @Override + public boolean isOutOfDate(Source source, Source base, String extension) { + if (SystemLibraryManager.isDartUri(base.getUri())) { + Source bundledSource = getBundledArtifact(source, base, "", extension); + if (bundledSource != null) { + // Note: Artifacts bundled with sources are always up to date + return false; + } + } + File artifactFile = getArtifactFile(base, "", extension); + return artifactFile.lastModified() < source.getLastModified(); + } + + protected DartSource getBundledArtifact(Source source, Source base, String part, String extension) { + LibrarySource library = libraryOf(base); + URI relativeUri = library.getUri().resolve(".").normalize().relativize(base.getUri()); + DartSource bundledSource; + if (!relativeUri.isAbsolute()) { + bundledSource = library.getSourceFor(fullname(relativeUri.getPath(), part, extension)); + } else { + bundledSource = null; + } + return bundledSource; + } + + private LibrarySource libraryOf(Source base) throws AssertionError { + if (base instanceof DartSource) { + return ((DartSource) base).getLibrary(); + } else if (base instanceof LibrarySource){ + return (LibrarySource) base; + } else { + throw new AssertionError(base.getClass().getName()); + } + } + + /** + * Answer the artifact file associated with the specified source. Only one + * artifact may be associated with the given extension. + * + * @param source the source file (not null) + * @param part a component of the source file to get a reader for (may be empty). + * @param extension the file extension for this artifact (not + * null, not empty) + * @return the artifact file (not null) + */ + protected File getArtifactFile(Source source, String part, String extension) { + String name = source.getName(); + name = URI.create(name).normalize().toString(); + name = normalizeArtifactName(name); + File file = new File(outputDirectory, fullname(name, part, extension)); + return file; + } + + /* + * Removes extraneous punctuation and file path syntax. + */ + private String normalizeArtifactName(String name) { + /** + * For efficiency, String operations are replaced with a single pass over + * the character array data. + * + * Note: This is a refactor of a previous version which used repeated calls + * to String.replace(), which turns out to be unnecessarily expensive. This + * particular method has been identified as being called a large number of + * times, thus the need for the micro-optimization here. + * + * This is the original logic being implemented here: + * + * + * name = name.replace("//", File.separator); + * name = name.replace(":", ""); + * name = name.replace("!", ""); + * name = name.replace("..", "_"); + * + * + * Please update the above if the logic being implemented ever changes. + * + * TODO(jbrosenberg): Figure out a better way such that this normalization + * is no longer needed in the first place. Source objects could be built + * from pre-normalized prefixes, etc. Or if it can be called less often, + * then use pre-compiled Patterns and Matchers instead. + */ + boolean lastCharWasSlash = false; + boolean lastCharWasPeriod = false; + boolean madeChanges = false; + int nameLen = name.length(); + char[] newName = new char[nameLen]; + int idx = 0; + for (char ch : name.toCharArray()) { + if (lastCharWasPeriod && ch != '.') { + // didn't get a second period, so append the one we did get + newName[idx++] = '.'; + lastCharWasPeriod = false; + } else if (lastCharWasSlash && ch != '/') { + // didn't get a second slash, so append the one we did get + newName[idx++] = '/'; + lastCharWasSlash = false; + } + + switch (ch) { + case ':': + case '!': + // replace ':'s and '!'s with empty string + madeChanges = true; + break; + case '/': + if (lastCharWasSlash) { + // got a second slash, replace with File.separatorChar + madeChanges = true; + newName[idx++] = File.separatorChar; + lastCharWasSlash = false; + } else { + lastCharWasSlash = true; + } + break; + case '.': + if (lastCharWasPeriod) { + // got a second period, replace with a '_' + madeChanges = true; + newName[idx++] = ('_'); + lastCharWasPeriod = false; + } else { + lastCharWasPeriod = true; + } + break; + default: + newName[idx++] = ch; + lastCharWasSlash = false; + lastCharWasPeriod = false; + } + } + if (lastCharWasPeriod) { + // didn't get a final second period, so append the one we did get + newName[idx++] = '.'; + } else if (lastCharWasSlash) { + // didn't get a final second slash, so append the one we did get + newName[idx++] = '/'; + } + + if (madeChanges) { + name = new String(newName, 0, idx); + } + + return name; + } + + private File makeDirectories(File file) { + file.getParentFile().mkdirs(); + return file; + } + + private String fullname(String name, String part, String extension) { + if (part.isEmpty()) { + return name + "." + extension; + } else { + return name + "$" + part + "." + extension; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/DefaultDartCompilerListener.java b/compiler/java/com/google/dart/compiler/DefaultDartCompilerListener.java new file mode 100644 index 00000000000..ccc2e89460c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DefaultDartCompilerListener.java @@ -0,0 +1,103 @@ +// 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. + +package com.google.dart.compiler; + +/** + * A default implementation of {@link DartCompilerListener} which counts + * compilation errors. + */ +public class DefaultDartCompilerListener extends DartCompilerListener { + + /** + * The number of (fatal) problems that occurred during compilation. + */ + private int problemCount = 0; + + /** + * The number of (non-fatal) problems that occurred during compilation. + */ + private int warningCount = 0; + + /** + * The number of (non-fatal) problems that occurred during compilation. + */ + private int typeErrorCount = 0; + + /** + * Formatter used to report error messages. Marked protected so that + * subclasses can override it (e.g. for a test server using HTML formatting). + */ + protected ErrorFormatter formatter = new PrettyErrorFormatter(useColor()); + + @Override + public void compilationError(DartCompilationError event) { + formatter.format(event); + incrementProblemCount(); + } + + @Override + public void compilationWarning(DartCompilationError event) { + formatter.format(event); + incrementWarningCount(); + } + + @Override + public void typeError(DartCompilationError event) { + formatter.format(event); + incrementTypeErrorCount(); + } + + private boolean useColor() { + return String.valueOf(System.getenv("TERM")).startsWith("xterm"); + } + + /** + * Answer the number of (fatal) problems that occurred during compilation. + * + * @return the number of problems + */ + public int getProblemCount() { + return problemCount; + } + + /** + * Answer the number of (non-fatal) problems that occurred during compilation. + * + * @return the number of problems + */ + public int getWarningCount() { + return warningCount; + } + + /** + * Answer the number of (non-fatal) problems that occurred during compilation. + * + * @return the number of problems + */ + public int getTypeErrorCount() { + return typeErrorCount; + } + + /** + * Increment the {@link #problemCount} by 1 + */ + protected void incrementProblemCount() { + problemCount++; + } + + /** + * Increment the {@link #warningCount} by 1 + */ + protected void incrementWarningCount() { + warningCount++; + } + + /** + * Increment the {@link #typeErrorCount} by 1 + */ + protected void incrementTypeErrorCount() { + typeErrorCount++; + } +} diff --git a/compiler/java/com/google/dart/compiler/DefaultErrorFormatter.java b/compiler/java/com/google/dart/compiler/DefaultErrorFormatter.java new file mode 100644 index 00000000000..df0cfe545d1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DefaultErrorFormatter.java @@ -0,0 +1,29 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.PrintStream; + +/** + * An error formatter that simply prints the file name with the line and column + * location. + */ +public class DefaultErrorFormatter implements ErrorFormatter { + protected PrintStream outputStream = System.err; + + public void setOutputStream(PrintStream outputStream) { + this.outputStream = outputStream; + } + + @Override + public void format(DartCompilationError event) { + outputStream.printf("%s:%d:%d: %s\n", + (event.getSource() != null) + ? event.getSource().getName() : "", + event.getLineNumber(), + event.getColumnNumber(), + event.getMessage()); + } +} diff --git a/compiler/java/com/google/dart/compiler/DefaultLibrarySource.java b/compiler/java/com/google/dart/compiler/DefaultLibrarySource.java new file mode 100644 index 00000000000..e5c940bb155 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DefaultLibrarySource.java @@ -0,0 +1,197 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.util.DartSourceString; +import com.google.dart.compiler.util.Lists; +import com.google.dart.compiler.util.Paths; + +import java.io.File; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Create a default app library specification when compiling a single dart file. + * + * @author johnlenz@google.com (John Lenz) + */ +public class DefaultLibrarySource extends UrlSource implements LibrarySource { + private static final String WRAPPED_NAME_PREFIX = "_DefaultLibrarySource.wrapper."; + private final File sourceFile; + private final Map sources; + private final Map imports; + private String source; + private String wrappedName; + + // TODO: Deprecated + public DefaultLibrarySource(List sources, String entryPoint) { + this(sources.get(0), Paths.toFiles(sources), entryPoint); + } + + // TODO: Deprecated + public DefaultLibrarySource(List sources, List imports, String entryPoint) { + this(sources.get(0), Paths.toFiles(sources), Paths.toFiles(imports), entryPoint); + } + + // TODO: Deprecated + public DefaultLibrarySource(File sourceFile, String entryPoint) { + this(sourceFile.getName(), Lists. create(sourceFile), + Lists. create(), entryPoint); + } + + public DefaultLibrarySource(String appName, List sourceFiles, String entryPoint) { + this(appName, sourceFiles, Lists. create(), entryPoint); + } + + /** + * Answer a new instance representing a {@link LibrarySource} with the + * specified name and that contains the specified imports and source files. + * + * @param appName the application name (not null, not empty) + * @param sourceFiles the source files to be included in the application (not + * null, and must contain at least one file) + * @param importFiles libraries to be imported into the application (e.g. from + * #import directives) + * @param entryPoint The name of the static method to call to invoke the + * library. A synthetic main() method will be generated which wraps a call to + * this method. Pass null to use the default main() method + * lookup. + */ + public DefaultLibrarySource(String appName, List sourceFiles, List importFiles, + String entryPoint) { + this(sourceFiles.get(0)); + + for (File file : sourceFiles) { + String relPath = Paths.relativePathFor(sourceFile, file); + this.sources.put(relPath, new UrlDartSource(file, this)); + } + for (File file : importFiles) { + String relPath = Paths.relativePathFor(sourceFile, file); + this.imports.put(relPath, new UrlLibrarySource(file)); + } + wrappedName = WRAPPED_NAME_PREFIX + appName; + source = generateSource(wrappedName, sourceFile, importFiles, sourceFiles, entryPoint); + this.sources.put(wrappedName, new DartSourceString(wrappedName, source)); + } + + private DefaultLibrarySource(File sourceFile) { + super(sourceFile); + + this.sourceFile = sourceFile; + this.sources = new HashMap(); + this.imports = new HashMap(); + } + + /** + * Generate source declaring a library. An app library will specify a + * non-null entryPoint. + * + * @param name the name of the application or library + * @param imports a collection of relative paths indicating the libraries + * imported by this application or library + * @param sources a collection of relative paths indicating the dart sources + * included in this application or library + * @param entryPoint The name of the static method to call to invoke the + * library. A synthetic main() method will be generated which wraps a call to + * this method. Pass null to use the default main() method + * lookup. + * @return the source (not null) + */ + public static String generateSource(String name, List imports, + List sources, String entryPoint) { + return generateSource(name, new File(name), Paths.toFiles(imports), + Paths.toFiles(sources), entryPoint); + } + + /** + * Generate source declaring a library. If an entryPoint is provided a main() + * method will be synthesized which wraps a call to the provided entryPoint + * method. + * + * @param name the name of the application or library + * @param baseFile the application or library file that will contain this + * source or any file in that same directory (not null, + * but does not need to exist) + * @param importFiles a collection of library files imported by this + * application or library + * @param sourceFiles a collection of dart source files included in this + * application or library + * @param entryPoint The name of the static method to call to invoke the + * library. A synthetic main() method will be generated which wraps a call to + * this method. Pass null to use the default main() method + * lookup. + * @return the source (not null) + */ + public static String generateSource(String name, File baseFile, List importFiles, + List sourceFiles, String entryPoint) { + StringWriter sw = new StringWriter(200); + PrintWriter pw = new PrintWriter(sw); + pw.println("#library(\"" + name + "\");"); + if (importFiles != null) { + for (File file : importFiles) { + String relPath = file.getPath(); + if (!relPath.startsWith("dart:")) { + relPath = Paths.relativePathFor(baseFile, file); + } + if (relPath != null) { + pw.println("#import(\"" + relPath + "\");"); + } + } + } + if (sourceFiles != null) { + for (File file : sourceFiles) { + String relPath = Paths.relativePathFor(baseFile, file); + if (relPath != null) { + pw.println("#source(\"" + relPath + "\");"); + } + } + } + if (entryPoint != null) { + // synthesize a main method, which wraps the entryPoint method call + pw.println(); + pw.println(DartCompiler.MAIN_ENTRY_POINT_NAME + "() {"); + pw.println(" " + entryPoint + "();"); + pw.println("}"); + } + return sw.toString(); + } + + @Override + public Reader getSourceReader() { + return new StringReader(source); + } + + @Override + public URI getUri() { + try { + // A bogus uri (but which ends with our wrappedName) + return new URI("string://" + wrappedName); + } catch (URISyntaxException e) { + throw new AssertionError(e); + } + } + + @Override + public String getName() { + return wrappedName; + } + + @Override + public LibrarySource getImportFor(String relPath) { + return imports.get(relPath); + } + + @Override + public DartSource getSourceFor(String relPath) { + return sources.get(relPath); + } +} diff --git a/compiler/java/com/google/dart/compiler/DelegatingCompilerConfiguration.java b/compiler/java/com/google/dart/compiler/DelegatingCompilerConfiguration.java new file mode 100644 index 00000000000..df528a90485 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DelegatingCompilerConfiguration.java @@ -0,0 +1,103 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.metrics.CompilerMetrics; + +import java.io.File; +import java.util.List; + +/** + * Provides a way to override a specific method of an existing instance of a CompilerConfiguration. + * + * @author zundel@google.com (Eric Ayers) + */ +public class DelegatingCompilerConfiguration implements CompilerConfiguration { + + private CompilerConfiguration delegate; + + public DelegatingCompilerConfiguration(CompilerConfiguration delegate) { + this.delegate = delegate; + } + @Override + public List getPhases() { + return delegate.getPhases(); + } + + @Override + public List getBackends() { + return delegate.getBackends(); + } + + @Override + public boolean shouldOptimize() { + return delegate.shouldOptimize(); + } + + @Override + public CompilerMetrics getCompilerMetrics() { + return delegate.getCompilerMetrics(); + } + + @Override + public String getJvmMetricOptions() { + return delegate.getJvmMetricOptions(); + } + + @Override + public boolean typeErrorsAreFatal() { + return delegate.typeErrorsAreFatal(); + } + + @Override + public boolean warningsAreFatal() { + return delegate.warningsAreFatal(); + } + + @Override + public boolean resolveDespiteParseErrors() { + return delegate.resolveDespiteParseErrors(); + } + + @Override + public boolean incremental() { + return delegate.incremental(); + } + + @Override + public File getOutputFilename() { + return delegate.getOutputFilename(); + } + + @Override + public File getOutputDirectory() { + return delegate.getOutputDirectory(); + } + + @Override + public boolean checkOnly() { + return delegate.checkOnly(); + } + + @Override + public boolean expectEntryPoint() { + return delegate.expectEntryPoint(); + } + + @Override + public boolean allowNoSuchType() { + return delegate.allowNoSuchType(); + } + + @Override + public boolean collectComments() { + return delegate.collectComments(); + } + + @Override + public LibrarySource getSystemLibraryFor(String importSpec) { + return delegate.getSystemLibraryFor(importSpec); + } +} diff --git a/compiler/java/com/google/dart/compiler/DeltaAnalyzer.java b/compiler/java/com/google/dart/compiler/DeltaAnalyzer.java new file mode 100644 index 00000000000..87dbd7fd772 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/DeltaAnalyzer.java @@ -0,0 +1,215 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.parser.DartParser; +import com.google.dart.compiler.parser.DartScannerParserContext; +import com.google.dart.compiler.resolver.TopLevelElementBuilder; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.CoreTypeProviderImplementation; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.resolver.MemberBuilder; +import com.google.dart.compiler.resolver.Resolver; +import com.google.dart.compiler.resolver.Scope; +import com.google.dart.compiler.resolver.SupertypeResolver; +import com.google.dart.compiler.type.TypeAnalyzer; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; + +class DeltaAnalyzer { + private final SourceDelta delta; + private final LibraryElement enclosingLibrary; + private final CompilerConfiguration config; + private final DartCompilerListener listener; + private final CoreTypeProvider typeProvider; + private final DartCompilerContext context; + + public DeltaAnalyzer(SourceDelta delta, + LibraryElement enclosingLibrary, + LibraryElement coreLibrary, + DartNode interestNode, + int interestStart, + int interestLength, + CompilerConfiguration config, + DartCompilerListener listener) { + this.delta = delta; + this.enclosingLibrary = enclosingLibrary; + this.config = config; + this.listener = listener; + typeProvider = new CoreTypeProviderImplementation(coreLibrary.getScope(), listener); + this.context = new Context(); + } + + public DartNode analyze() throws IOException { + Source originalSource = delta.getSourceBefore(); + DartUnit unit = delta.getUnitAfter(); + if (unit == null) { + DartSource source = delta.getSourceAfter(); + unit = getParser(source).parseUnit(source); + } + Scope scope = deltaLibraryScope(originalSource, unit); + // We have to create supertypes and member elements for the entire unit. For example, if you're + // doing code-completion, you are only interested in the current expression, but you may be + // code-completing on a type that is defined outside the current class. + new SupertypeResolver().exec(unit, context, typeProvider); + new MemberBuilder().exec(unit, context, typeProvider); + + // The following two phases can be narrowed down to the interest area. We currently ignore the + // interest area, but long term, we will need to narrow down to the interest area to handle + // very large files. + new Resolver(context, scope, typeProvider).exec(unit); + new TypeAnalyzer().exec(unit, context, typeProvider); + return unit; + } + + private Scope deltaLibraryScope(Source originalSource, DartUnit unit) { + // Create a library unit which holds the new unit. + LibraryUnit libraryUnit = new LibraryUnit(makeLibrarySource("delta")); + libraryUnit.putUnit(unit); + libraryUnit.populateTopLevelNodes(); + + // Create top-level elements for the new unit. + new TopLevelElementBuilder().exec(libraryUnit, context); + new TopLevelElementBuilder().fillInLibraryScope(libraryUnit, listener); + + // Copy all the elements from the old library, except the ones declared in the original source. + Scope scope = libraryUnit.getElement().getScope(); + for (Element member : enclosingLibrary.getMembers()) { + if (member.getNode().getSource() != originalSource) { + scope.declareElement(member.getName(), member); + } + } + return scope; + } + + private LibrarySource makeLibrarySource(final String name) { + final URI uri = URI.create(name); + return new LibrarySource() { + @Override + public URI getUri() { + return uri; + } + + @Override + public Reader getSourceReader() { + throw new AssertionError(); + } + + @Override + public String getName() { + return name; + } + + @Override + public long getLastModified() { + throw new AssertionError(); + } + + @Override + public boolean exists() { + throw new AssertionError(); + } + + @Override + public DartSource getSourceFor(String relPath) { + throw new AssertionError(); + } + + @Override + public LibrarySource getImportFor(String relPath) { + throw new AssertionError(); + } + }; + } + + private DartParser getParser(Source source) throws IOException { + Reader r = source.getSourceReader(); + String sourceString = CharStreams.toString(r); + Closeables.close(r, false); + return new DartParser(new DartScannerParserContext(source, sourceString, listener), false); + } + + private class Context extends DartCompilerListener implements DartCompilerContext { + @Override + public LibraryUnit getApplicationUnit() { + throw new AssertionError(); + } + + @Override + public LibraryUnit getAppLibraryUnit() { + throw new AssertionError(); + } + + @Override + public LibraryUnit getLibraryUnit(LibrarySource lib) { + throw new AssertionError(); + } + + @Override + public Reader getArtifactReader(Source source, String part, String extension) { + throw new AssertionError(); + } + + @Override + public URI getArtifactUri(DartSource source, String part, String extension) { + throw new AssertionError(); + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) { + throw new AssertionError(); + } + + @Override + public boolean isOutOfDate(Source source, Source base, String extension) { + throw new AssertionError(); + } + + @Override + public CompilerMetrics getCompilerMetrics() { + return null; + } + + @Override + public boolean allowNoSuchType() { + return false; + } + + @Override + public CompilerConfiguration getCompilerConfiguration() { + return config; + } + + @Override + public LibrarySource getSystemLibraryFor(String importSpec) { + throw new AssertionError(); + } + + @Override + public void compilationError(DartCompilationError event) { + listener.compilationError(event); + } + + @Override + public void compilationWarning(DartCompilationError event) { + listener.compilationWarning(event); + } + + @Override + public void typeError(DartCompilationError event) { + listener.typeError(event); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/ErrorCode.java b/compiler/java/com/google/dart/compiler/ErrorCode.java new file mode 100644 index 00000000000..0b17865ec75 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ErrorCode.java @@ -0,0 +1,16 @@ +// 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. + +package com.google.dart.compiler; + +/** + * The behavior common to objects representing error codes associated with + * {@link DartCompilationError Dart compilation errors}. + */ +public interface ErrorCode { + /** + * Return the message template used to create the message to be displayed for this error. + */ + String getMessage(); +} diff --git a/compiler/java/com/google/dart/compiler/ErrorFormatter.java b/compiler/java/com/google/dart/compiler/ErrorFormatter.java new file mode 100644 index 00000000000..b04ed130391 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ErrorFormatter.java @@ -0,0 +1,16 @@ +// 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. + +package com.google.dart.compiler; + +/** + * A class that that helps presenting error messages in the command line. + * + * @see DefaultErrorFormatter + * @see PrettyErrorFormatter + */ +public interface ErrorFormatter { + + public void format(DartCompilationError event); +} diff --git a/compiler/java/com/google/dart/compiler/InternalCompilerException.java b/compiler/java/com/google/dart/compiler/InternalCompilerException.java new file mode 100644 index 00000000000..5db265e920a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/InternalCompilerException.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler; + +/** + * Exception thrown when the compiler encounters an unexpected internal error. + */ +public class InternalCompilerException extends RuntimeException { + + public InternalCompilerException(String message) { + super(message); + } +} diff --git a/compiler/java/com/google/dart/compiler/LibraryDeps.java b/compiler/java/com/google/dart/compiler/LibraryDeps.java new file mode 100644 index 00000000000..a1ec4a0100a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/LibraryDeps.java @@ -0,0 +1,189 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartUnit; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Represents a library's dependencies artifact. + */ +public class LibraryDeps { + + /** + * Each dependency record contains the library in which it was found, along with a hash of its + * structure. Any change in the hash of the target dependency will force a recompile of the + * associated compilation unit. + */ + public static class Dependency { + private final URI libUri; + private final String hash; + + public Dependency(URI libUri, String hash) { + this.libUri = libUri; + this.hash = hash; + } + + public String getHash() { + return hash; + } + + public URI getLibUri() { + return libUri; + } + } + + /** + * Each source is a map from class names to its associated {@link Dependency}. + * + * A special dependency entry, called a 'hole', represents a name that, if + * newly-defined in the library scope, will force a recompile of the unit. + * This is represented by the static constant {@link Source#HOLE}. + */ + public static class Source { + private final Map deps = new ConcurrentHashMap(); + private final static Dependency HOLE = new Dependency(null, null); + + /** + * Gets the node names of all dependencies for this source. + */ + public Iterable getNodeNames() { + return deps.keySet(); + } + + public void putDependency(String nodeName, Dependency dep) { + deps.put(nodeName, dep); + } + + public Dependency getDependency(String nodeName) { + return deps.get(nodeName); + } + + public void putHole(String nodeName) { + deps.put(nodeName, HOLE); + } + + public boolean isHole(String nodeName) { + return deps.containsKey(nodeName) && (deps.get(nodeName) == HOLE); + } + } + + public static LibraryDeps fromReader(Reader reader) throws IOException { + LibraryDeps deps = new LibraryDeps(); + BufferedReader buf = new BufferedReader(reader); + String srcName; + while (null != (srcName = buf.readLine())) { + Source src = new Source(); + + String line; + while (null != (line = buf.readLine())) { + // Blank line: next source. + if (line.length() == 0) { + break; + } + + String[] parts = line.split(" "); + switch (parts.length) { + case 3: + // Full dependency. + try { + src.deps.put(parts[0], new Dependency(new URI(parts[1]), parts[2])); + } catch (URISyntaxException e) { + return null; + } + break; + case 1: + // Name only: hole. + src.deps.put(parts[0], Source.HOLE); + break; + default: + return null; + } + } + + deps.sources.put(srcName, src); + } + + return deps; + } + + private final Map sources = new ConcurrentHashMap(); + + public LibraryDeps() { + } + + public Source getSource(String sourceName) { + return sources.get(sourceName); + } + + public Iterable getSourceNames() { + return sources.keySet(); + } + + public void setSource(String sourceName, Source source) { + sources.put(sourceName, source); + } + + @Override + public String toString() { + try { + StringWriter writer = new StringWriter(); + write(writer); + return writer.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public void update(DartUnit unit, DartCompilerContext context) { + // Update the library deps to reflect this unit's classes. + LibraryDepsVisitor.exec(unit, this); + } + + public void write(Writer writer) throws IOException { + // For stability from run to run, this output needs to be sorted + ArrayList sortedSourceNames = new ArrayList(sources.size()); + sortedSourceNames.addAll(sources.keySet()); + Collections.sort(sortedSourceNames); + + for (String srcName : sortedSourceNames) { + writer.write(srcName); + writer.write('\n'); + Source src = sources.get(srcName); + + // sort the types per source name + ArrayList sortedTypes = new ArrayList(src.deps.size()); + sortedTypes.addAll(src.deps.keySet()); + Collections.sort(sortedTypes); + + for (String type : sortedTypes) { + writer.write(type); + + Dependency dep = src.getDependency(type); + if (dep != Source.HOLE) { + writer.write(' '); + writer.write(dep.libUri.toString()); + writer.write(' '); + writer.write(dep.hash); + } + + writer.write('\n'); + } + + writer.write('\n'); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/LibraryDepsVisitor.java b/compiler/java/com/google/dart/compiler/LibraryDepsVisitor.java new file mode 100644 index 00000000000..47c980ef693 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/LibraryDepsVisitor.java @@ -0,0 +1,160 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.EnclosingElement; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; + +import java.net.URI; + +/** + * A visitor that fills in {@link LibraryDeps} for a compilation unit. + */ +public class LibraryDepsVisitor extends DartNodeTraverser { + + /** + * Fill in library dependencies from a compilation unit. + * + * @param unit the unit whose dependencies are to be filled in + * @param deps the target library deps + */ + static void exec(DartUnit unit, LibraryDeps deps) { + LibraryDepsVisitor v = new LibraryDepsVisitor(); + unit.accept(v); + + String relPath = unit.getSource().getRelativePath(); + deps.setSource(relPath, v.source); + } + + private final LibraryDeps.Source source = new LibraryDeps.Source(); + private DartClass currentClass; + + private LibraryDepsVisitor() { + } + + @Override + public Void visitIdentifier(DartIdentifier node) { + Element target = node.getTargetSymbol(); + ElementKind kind = ElementKind.of(target); + + // Deal with field and method references: + // - Add explicit dependencies on top-level fields and method. + // - Add "holes" for fields and methods found in a superclass (see LibraryDeps for further + // explanation). + switch (kind) { + case FIELD: + case METHOD: + EnclosingElement enclosing = target.getEnclosingElement(); + addHoleIfSuper(node, enclosing); + if (enclosing.getKind().equals(ElementKind.LIBRARY)) { + addElementDependency(target); + } + break; + } + + // Add dependency on the computed type of identifiers. + switch (kind) { + case NONE: + case DYNAMIC: + break; + + default: { + Type type = target.getType(); + if (type != null) { + Element element = type.getElement(); + if (ElementKind.of(element).equals(ElementKind.CLASS)) { + addElementDependency(element); + } + } + break; + } + } + + return null; + } + + @Override + public Void visitPropertyAccess(DartPropertyAccess node) { + // Skip rhs of property accesses, so that all identifiers we visit will be unqualified. + return node.getQualifier().accept(this); + } + + @Override + public Void visitClass(DartClass node) { + currentClass = node; + node.visitChildren(this); + currentClass = null; + return null; + } + + @Override + public Void visitTypeNode(DartTypeNode node) { + if (TypeKind.of(node.getType()).equals(TypeKind.INTERFACE)) { + addElementDependency(((InterfaceType) node.getType()).getElement()); + } + node.visitChildren(this); + return null; + } + + /** + * Add a 'hole' for the given identifier, if its declaring class is a superclass of the current + * class. A 'hole' dependency specifies a name that, if filled by something in the library scope, + * would require this unit to be recompiled. + * + * This situation occurs because names in the library scope bind more strongly than unqualified + * superclass members. + */ + private void addHoleIfSuper(DartIdentifier node, Element holder) { + if (ElementKind.of(holder).equals(ElementKind.CLASS) + && holder != currentClass.getSymbol()) { + source.putHole(node.getTargetName()); + } + } + + /** + * Adds a direct dependency on the given class. + */ + private void addElementDependency(Element elem) { + DartNode node = elem.getNode(); + if (node != null) { + Source nodeSource = node.getSource(); + URI libUri = ((DartSource) nodeSource).getLibrary().getUri(); + LibraryDeps.Dependency dep = new LibraryDeps.Dependency(libUri, + Integer.toString(node.computeHash())); + + String name; + switch (elem.getKind()) { + case CLASS: + name = ((DartClass) node).getClassName(); + break; + case FIELD: + name = ((DartField) node).getName().getTargetName(); + break; + case METHOD: + DartMethodDefinition method = (DartMethodDefinition) node; + DartIdentifier ident = (DartIdentifier) method.getName(); + name = ident.getTargetName(); + break; + default: + throw new AssertionError("Unexpected top-level node type"); + } + + source.putDependency(name, dep); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/LibrarySource.java b/compiler/java/com/google/dart/compiler/LibrarySource.java new file mode 100644 index 00000000000..395f7168f13 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/LibrarySource.java @@ -0,0 +1,37 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.IOException; + +/** + * Abstract interface to library source. + * + * TODO(jgw): Consider requiring implementors to intern LibrarySource instances + * so that users can depend upon reference equality to avoid cycles (or require + * them to use .equals()). As it is, there are two pieces of code in + * DartCompiler and one in JavascriptBackend that do this manually, and it's a + * little tricky. + */ +public interface LibrarySource extends Source { + + /** + * Answer the {@link LibrarySource} for the path specified in the receiver's + * imports declaration + * + * @param relPath path to the {@link LibrarySource} relative to the receiver + * @return the dart source or null if could not be found + */ + LibrarySource getImportFor(String relPath) throws IOException; + + /** + * Answer the {@link DartSource} for the path specified in the receiver's + * sources declaration + * + * @param relPath the path to the {@link DartSource} relative to the receiver + * @return the dart source or null if could not be found + */ + DartSource getSourceFor(String relPath); +} diff --git a/compiler/java/com/google/dart/compiler/PrettyErrorFormatter.java b/compiler/java/com/google/dart/compiler/PrettyErrorFormatter.java new file mode 100644 index 00000000000..f6aa86641a7 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/PrettyErrorFormatter.java @@ -0,0 +1,143 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.common.io.Closeables; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +/** + * An error formatter that scans the source file and prints the error line and + * some context around it. This formatter has two modes: with or without color. + * When using colors, it prints the error message in red, and it highlights the + * portion of the line containing the error in red. Wihout colors, it prints an + * extra line underlying the portion of the line containing the error. + */ +public class PrettyErrorFormatter extends DefaultErrorFormatter { + private static String RED_BOLD_COLOR = "\033[31;1m"; + private static String RED_COLOR = "\033[31m"; + private static String NO_COLOR = "\033[0m"; + + private final boolean useColor; + + public PrettyErrorFormatter(boolean useColor) { + this.useColor = useColor; + } + + @Override + public void format(DartCompilationError event) { + Source sourceFile = event.getSource(); + + // if no source file is available, default to the basic error formatter + if (!(sourceFile instanceof DartSource)) { + super.format(event); + return; + } + + BufferedReader reader = null; + try { + Reader sourceReader = sourceFile.getSourceReader(); + if (sourceReader != null) { + reader = new BufferedReader(sourceReader); + } + + // get the error line and the line above it (note: line starts at 1) + int line = event.getLineNumber(); + String lineBefore = null; + String lineText = null; + + if (reader != null) { + lineBefore = getLineAt(reader, line - 1); + lineText = getLineAt(reader, 1); + } + + // if there is no line to highlight, default to the basic error formatter + if (lineText == null) { + super.format(event); + return; + } + + // get column/length and ensure they are within the line limits. + int col = event.getColumnNumber() - 1; + // TODO(ngeoffray): if length is 0, we may want to expand it in order + // to highlight something. + int length = event.getLength(); + col = between(col, 0, lineText.length()); + length = between(length, 0, lineText.length() - col); + + // print the error message + StringBuilder buf = new StringBuilder(); + buf.append(String.format("%s%s:%d: %s%s\n", + useColor ? RED_BOLD_COLOR : "", + sourceFile.getName(), + event.getLineNumber(), + event.getMessage(), + useColor ? NO_COLOR : "")); + + // show the previous line for context + if (lineBefore != null) { + buf.append(String.format("%6d: %s\n", line - 1, lineBefore)); + } + + if (useColor) { + // highlight error in red + buf.append(String.format("%6d: %s%s%s%s%s\n", + line, + lineText.substring(0, col), + RED_COLOR, + lineText.substring(col, col + length), + NO_COLOR, + lineText.substring(col + length))); + } else { + // print the error line without formatting + buf.append(String.format("%6d: %s\n", line, lineText)); + + // underline error portion + buf.append(" "); + for (int i = 0; i < col; ++i) { + buf.append(' '); + } + buf.append('~'); + if (length > 1) { + for (int i = 0; i < length - 2; ++i) { + buf.append('~'); + } + buf.append('~'); + } + buf.append('\n'); + } + + outputStream.println(buf.toString()); + } catch (IOException ex) { + super.format(event); + } finally { + if (reader != null) { + Closeables.closeQuietly(reader); + } + } + } + + private String getLineAt(BufferedReader reader, int line) throws IOException { + if (line <= 0) { + return null; + } + String currentLine = null; + // TODO(sigmund): do something more efficient - we currently do a linear + // scan of the file every time an error is reported. This will not scale + // when many errors are reported on the same file. + while ((currentLine = reader.readLine()) != null && line-- > 1); + return currentLine; + } + + /** + * Returns the closest value in {@code [start,end]} to the given value. If + * the given range is entirely empty, then {@code start} is returned. + */ + private static int between(int val, int start, int end) { + return Math.max(start, Math.min(val, end)); + } +} diff --git a/compiler/java/com/google/dart/compiler/Source.java b/compiler/java/com/google/dart/compiler/Source.java new file mode 100644 index 00000000000..f0ff5797f73 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/Source.java @@ -0,0 +1,45 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.IOException; +import java.io.Reader; +import java.net.URI; +import java.util.Date; + +/** + * Abstract interface to a source file. + */ +public interface Source { + + /** + * Determines whether the given source exists. + */ + boolean exists(); + + /** + * Returns the last-modified timestamp for this source, using the same units as + * {@link Date#getTime()}. + */ + long getLastModified(); + + /** + * Gets the name of this source. + */ + String getName(); + + /** + * Gets a reader for the dart file's source code. The caller is responsible for closing the + * returned reader. + */ + Reader getSourceReader() throws IOException; + + /** + * Gets the identifier for this source. This is used to uniquely identify the + * source, but should not be used to obtain the source content. Use + * {@link #getSourceReader()} to obtain the source content. + */ + URI getUri(); +} diff --git a/compiler/java/com/google/dart/compiler/SourceDelta.java b/compiler/java/com/google/dart/compiler/SourceDelta.java new file mode 100644 index 00000000000..e048c4f9a45 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/SourceDelta.java @@ -0,0 +1,57 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartUnit; + +/** + * Representation of changes to source. + */ +public abstract class SourceDelta { + public abstract Source getSourceBefore(); + + public abstract DartSource getSourceAfter(); + + public abstract DartUnit getUnitAfter(); + + public final SourceDelta after(DartSource sourceAfter) { + return new BeforeAfter(getSourceBefore(), sourceAfter, null); + } + + public final SourceDelta after(DartUnit nodeAfter) { + return new BeforeAfter(getSourceBefore(), null, nodeAfter); + } + + public static SourceDelta before(final DartSource sourceBefore) { + return new BeforeAfter(sourceBefore, sourceBefore, null); + } + + private static class BeforeAfter extends SourceDelta { + private final Source sourceBefore; + private final DartSource sourceAfter; + private final DartUnit nodeAfter; + + BeforeAfter(Source sourceBefore, DartSource sourceAfter, DartUnit nodeAfter) { + this.sourceBefore = sourceBefore; + this.sourceAfter = sourceAfter; + this.nodeAfter = nodeAfter; + } + + @Override + public Source getSourceBefore() { + return sourceBefore; + } + + @Override + public DartSource getSourceAfter() { + return sourceAfter; + } + + @Override + public DartUnit getUnitAfter() { + return nodeAfter; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/SystemLibrary.java b/compiler/java/com/google/dart/compiler/SystemLibrary.java new file mode 100644 index 00000000000..2f05761afff --- /dev/null +++ b/compiler/java/com/google/dart/compiler/SystemLibrary.java @@ -0,0 +1,70 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * A library accessible via the "dart:.lib" protocol. + */ +public class SystemLibrary { + + private final String shortName; + private final String host; + private final String pathToLib; + private final File dirOrZip; + + /** + * Define a new system library such that dart:[shortLibName] will automatically be expanded to + * dart://[host]/[pathToLib]. For example this call + * + *
+   *    new SystemLibrary("dom.lib", "dom", "dart_dom.lib");
+   * 
+ * + * will define a new system library such that "dart:dom.lib" to automatically be expanded to + * "dart://dom/dart_dom.lib". The dirOrZip argument is either the root directory or a zip file + * containing all files for this library. + */ + public SystemLibrary(String shortName, String host, String pathToLib, File dirOrZip) { + this.shortName = shortName; + this.host = host; + this.pathToLib = pathToLib; + this.dirOrZip = dirOrZip; + } + + public String getHost() { + return host; + } + + public String getPathToLib() { + return pathToLib; + } + + public String getShortName() { + return shortName; + } + + public URI translateUri(URI dartUri) { + if (!dirOrZip.exists()) { + throw new RuntimeException("System library for " + dartUri + " does not exist: " + dirOrZip.getPath()); + } + String spec = "file:" + dirOrZip.getPath(); + if (dirOrZip.isFile()) { + spec = "jar:" + spec + "!"; + } + try { + return new URI(spec + dartUri.getPath()); + } catch (URISyntaxException e) { + throw new AssertionError(); + } + } + + public File getFile() { + return this.dirOrZip; + } +} diff --git a/compiler/java/com/google/dart/compiler/SystemLibraryManager.java b/compiler/java/com/google/dart/compiler/SystemLibraryManager.java new file mode 100644 index 00000000000..5b53048df31 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/SystemLibraryManager.java @@ -0,0 +1,314 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarFile; + +/** + * Manages the collection of {@link SystemLibrary}s. + */ +public class SystemLibraryManager { + private enum SystemLibraryPath { + CORE("core", "core", "com/google/dart/corelib/", "corelib.dart", "corelib.jar", true), + COREIMPL("core", "coreimpl", "com/google/dart/corelib/", "corelib_impl.dart", "corelib.jar", + CORE, true), + DOM("dom", "dom", "dom/", "dom.dart", "domlib.jar"), + HTML("html", "html", "html/", "html.dart", "htmllib.jar"), + JSON("json", "json", "json/", "json.dart", "jsonlib.jar"); + + final String hostName; + final SystemLibraryPath base; + final String shortName; + final String jar; + final String lib; + final boolean failIfMissing; + + SystemLibraryPath(String hostName, String shortName, String path, String file, String jar, + boolean failIfMissing) { + this(hostName, shortName, path, file, jar, null, failIfMissing); + } + + SystemLibraryPath(String hostName, String shortName, String path, String file, String jar) { + this(hostName, shortName, path, file, jar, null, false); + } + + SystemLibraryPath(String hostName, String shortName, String path, String file, String jar, + SystemLibraryPath base, boolean failIfMissing) { + this.hostName = hostName; + this.shortName = shortName; + this.jar = jar; + this.lib = path + file; + this.base = base; + this.failIfMissing = failIfMissing; + } + } + + private static final String DART_SCHEME = "dart"; + private static final String DART_SCHEME_SPEC = "dart:"; + + // executionFile is used to search for loose files on disk when the system libraries + // are not on the classpath (e.g. Eclipse) + private static final File executionFile = new File(SystemLibraryManager.class + .getProtectionDomain().getCodeSource().getLocation().getPath()); + + private HashMap expansionMap; + private Map hostMap; + + private SystemLibrary[] libraries; + + public SystemLibraryManager() { + setLibraries(getDefaultLibraries()); + } + + /** + * Expand a relative or short URI (e.g. "dart:dom") which is implementation independent to its + * full URI (e.g. "dart://dom/com/google/dart/domlib/dom.dart") and then translate that URI to + * either a "file:" or "jar:" URI (e.g. + * "jar:file:/some/install/director/dom.jar!/com/google/dart/domlib/dom.dart"). + * + * @param uri the original URI + * @return the expanded and translated URI, which may be null and may not exist + * @exception RuntimeException if the URI is a "dart" scheme, but does not map to a defined system + * library + */ + public URI resolveDartUri(URI uri) { + return translateDartUri(expandRelativeDartUri(uri)); + } + + /** + * Translate the URI from dart://[host]/[pathToLib] (e.g. dart://dom/dom.dart) + * to either a "file:" or "jar:" URI (e.g. "jar:file:/some/install/director/dom.jar!/dom.dart") + * + * @param uri the original URI + * @return the translated URI, which may be null and may not exist + * @exception RuntimeException if the URI is a "dart" scheme, + * but does not map to a defined system library + */ + public URI translateDartUri(URI uri) { + if (isDartUri(uri)) { + String host = uri.getHost(); + SystemLibrary library = hostMap.get(host); + if (library == null) { + throw new RuntimeException("No system library defined for " + uri); + } + return library.translateUri(uri); + } + + return uri; + } + + /** + * Expand a relative or short URI (e.g. "dart:dom") which is implementation independent + * to its full URI (e.g. "dart://dom/com/google/dart/domlib/dom.dart") + * + * @param uri the relative URI + * @return the expanded URI or the original URI if it could not be expanded + * @exception RuntimeException if the short URI is of the form "dart:" + * but does not correspond to a system library + */ + public URI expandRelativeDartUri(URI uri) throws AssertionError { + if (isDartUri(uri)) { + String host = uri.getHost(); + if (host == null) { + String spec = uri.getSchemeSpecificPart(); + String replacement = expansionMap.get(spec); + if (replacement != null) { + try { + uri = new URI(DART_SCHEME + ":" + replacement); + } catch (URISyntaxException e) { + throw new AssertionError(); + } + } else { + throw new RuntimeException("Don't know how to expand dart URI: " + uri); + } + } + } + return uri; + } + + /** + * Answer true if the specified URI has a "dart" scheme + */ + public static boolean isDartUri(URI uri) { + return uri != null && DART_SCHEME.equals(uri.getScheme()); + } + + /** + * Answer true if the string is a dart spec + */ + public static boolean isDartSpec(String spec) { + return spec != null && spec.startsWith(DART_SCHEME_SPEC); + } + + /** + * Register system libraries for the "dart:" protocol such that dart:[shortLibName] (e.g. + * "dart:dom") will automatically be expanded to dart://[host]/[pathToLib] (e.g. + * dart://dom/dom.dart) + */ + private void setLibraries(SystemLibrary[] newLibraries) { + libraries = newLibraries; + hostMap = new HashMap(); + expansionMap = new HashMap(); + for (SystemLibrary library : libraries) { + hostMap.put(library.getHost(), library); + expansionMap.put(library.getShortName(), + "//" + library.getHost() + "/" + library.getPathToLib()); + } + } + + private File getResource(String name, boolean failOnMissing) { + URL baseUrl = SystemLibraryManager.class.getClassLoader().getResource(name); + if (baseUrl == null) { + if (!failOnMissing) { + return null; + } + throw new RuntimeException("Failed to find the system library: " + name); + } + return resolveResource(baseUrl, name); + } + + static private File resolveResource(URL baseUrl, String name) { + if (baseUrl == null) { + return null; + } + File coreDirOrZip = null; + String protocol = baseUrl.getProtocol(); + String path = baseUrl.getPath(); + if ("file".equals(protocol)) { + coreDirOrZip = new File(path.substring(0, path.lastIndexOf(name))); + } else if ("jar".equals(protocol)) { + // jar:file://www.foo.com/bar/baz.jar!/com/google/some.class + if (path.startsWith("file:")) { + int index = path.indexOf('!'); + coreDirOrZip = new File(path.substring(5, index > 0 ? index : path.length())); + } + } + if (coreDirOrZip == null) { + throw new RuntimeException("Failed to find system library in " + baseUrl); + } + if (!coreDirOrZip.exists()) { + throw new RuntimeException("System library container does not exist " + coreDirOrZip + + "\n from " + baseUrl); + } + return coreDirOrZip; + } + + private File searchForResource(String searchPath, String libraryName, boolean failOnMissing) { + URL urlPath = null; + File sourcePath = new File(searchPath); + + /* The source can be a directory or a jar file. Search both for our library. */ + if (sourcePath.isDirectory()) { + File foundLibrary = new File(sourcePath.getPath() + File.separator + libraryName); + if (!foundLibrary.exists()) { + if (failOnMissing) { + throw new RuntimeException("Failed to find system library " + libraryName + " with " + + sourcePath.toString()); + } + return null; + } + try { + urlPath = foundLibrary.toURI().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } else if (sourcePath.isFile() && sourcePath.toString().endsWith(".jar")) { + // Support for jar only right now... + JarFile jarFile = null; + try { + jarFile = new JarFile(sourcePath); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (null != jarFile.getJarEntry(libraryName)) { + String path = "jar:file:" + sourcePath.getPath() + "!/" + libraryName; + try { + urlPath = new URL(path); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } else { + if (failOnMissing) { + throw new RuntimeException("Failed to find system library " + libraryName + " with " + + sourcePath.getPath()); + } + return null; + } + } + + File foundLibrary = resolveResource(urlPath, libraryName); + if (foundLibrary == null && failOnMissing) { + throw new RuntimeException("Failed to find system library " + libraryName + " with " + + sourcePath.getPath()); + } + return foundLibrary; + } + + protected SystemLibrary locateSystemLibrary(SystemLibraryPath path) { + // First, check for jars on the class path + File libraryDirOrZip = getResource(path.lib, path.failIfMissing); + + // TODO(codefu): This is a hack. To keep Eclipse happy and to find the + // sources, we hard code this path. In the future, when the libraries are + // all gathered into a common "lib/" path, we can search from there. + if (libraryDirOrZip == null) { + // Eclipse's executionPath should be a directory, unless a jar file was included. + String executionPath; + if (executionFile.isDirectory()) { + // Universal location of eclipse workspace to the dart source tree is + // 'dart/compiler/eclipse.workspace/dartc/output' + // and we need 'dart/client' + executionPath = + executionFile.getParent() + File.separator + ".." + File.separator + ".." + + File.separator + ".." + File.separator + "client"; + } else { + executionPath = executionFile.getParent() + File.separator + path.jar; + } + libraryDirOrZip = searchForResource(executionPath, path.lib, false); + if (libraryDirOrZip == null && executionFile.isFile()) { + // Last ditch; are the artifacts just in a flat file... + libraryDirOrZip = searchForResource(executionFile.getParent(), path.lib, false); + } + } + if (libraryDirOrZip != null) { + return new SystemLibrary(path.shortName, path.hostName, path.lib, + libraryDirOrZip); + } + return null; + } + + /** + * Answer the libraries that are built into the compiler jar + */ + protected SystemLibrary[] getDefaultLibraries() { + ArrayList defaultLibraries = new ArrayList(); + File[] baseFiles = new File[SystemLibraryPath.values().length]; + + for (SystemLibraryPath path : SystemLibraryPath.values()) { + if (path.base != null) { + defaultLibraries.add(new SystemLibrary(path.shortName, path.hostName, path.lib, + baseFiles[path.base.ordinal()])); + baseFiles[path.ordinal()] = baseFiles[path.base.ordinal()]; + } else { + SystemLibrary library = locateSystemLibrary(path); + if (library != null) { + defaultLibraries.add(library); + baseFiles[path.ordinal()] = library.getFile(); + } + } + } + + return defaultLibraries.toArray(new SystemLibrary[defaultLibraries.size()]); + } +} diff --git a/compiler/java/com/google/dart/compiler/UnitTestBatchRunner.java b/compiler/java/com/google/dart/compiler/UnitTestBatchRunner.java new file mode 100644 index 00000000000..e8225f41715 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/UnitTestBatchRunner.java @@ -0,0 +1,61 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * Provides a framework to read command line options from stdin and feed them to + * either the DartCompiler or TestRunner. + * + */ +public class UnitTestBatchRunner { + + public interface Invocation { + public boolean invoke (String[] args) throws Throwable; + } + + /** + * Run the tool in 'batch' mode, receiving command lines through stdin and returning + * pass/fail status through stdout. This feature is intended for use in unit testing. + * + * @param batchArgs command line arguments forwarded from main(). + */ + public static void runAsBatch(String[] batchArgs, Invocation toolInvocation) throws Throwable { + System.out.println(">>> BATCH START"); + + // Read command lines in from stdin and create a new compiler for each one. + BufferedReader cmdlineReader = new BufferedReader(new InputStreamReader( + System.in)); + long startTime = System.currentTimeMillis(); + int testsFailed = 0; + int totalTests = 0; + try { + String line; + for (; (line = cmdlineReader.readLine()) != null; totalTests++) { + long testStart = System.currentTimeMillis(); + // TODO(zundel): These are shell script cmdlines: be smarter about + // quoted strings. + String[] args = line.trim().split("\\s+"); + boolean result = toolInvocation.invoke(args); + if (!result) { + testsFailed++; + } + System.out.println(">>> TEST " + (result ? "PASS" : "FAIL") + " " + + (System.currentTimeMillis() - testStart) + "ms"); + System.out.flush(); + } + } catch (Throwable e) { + System.out.println(">>> TEST CRASH"); + System.out.flush(); + throw e; + } + long elapsed = System.currentTimeMillis() - startTime; + System.out.println(">>> BATCH END (" + (totalTests - testsFailed) + "/" + + totalTests + ") " + elapsed + "ms"); + System.out.flush(); + } +} diff --git a/compiler/java/com/google/dart/compiler/UrlDartSource.java b/compiler/java/com/google/dart/compiler/UrlDartSource.java new file mode 100644 index 00000000000..8d0913b22b9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/UrlDartSource.java @@ -0,0 +1,54 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * A {@link DartSource} backed by a URL. + */ +public class UrlDartSource extends UrlSource implements DartSource { + + private final LibrarySource lib; + private final String relPath; + + protected UrlDartSource(URI uri, String relPath, LibrarySource lib, SystemLibraryManager slm) { + super(uri,slm); + this.relPath = relPath; + this.lib = lib; + } + + protected UrlDartSource(URI uri, String relPath, LibrarySource lib) { + this(uri, relPath, lib, null); + } + + public UrlDartSource(File file, LibrarySource lib) { + super(file); + this.relPath = file.getPath(); + this.lib = lib; + } + + @Override + public LibrarySource getLibrary() { + return lib; + } + + @Override + public String getName() { + try { + String uriSafeName = new URI(null, null, relPath, null).toString(); + return lib.getName() + File.separatorChar + uriSafeName; + } catch (URISyntaxException e) { + throw new AssertionError(e); + } + } + + @Override + public String getRelativePath() { + return relPath; + } +} diff --git a/compiler/java/com/google/dart/compiler/UrlLibrarySource.java b/compiler/java/com/google/dart/compiler/UrlLibrarySource.java new file mode 100644 index 00000000000..ed973ea92d1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/UrlLibrarySource.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * A {@link LibrarySource} backed by a URL. + */ +public class UrlLibrarySource extends UrlSource implements LibrarySource { + + public UrlLibrarySource(URI uri, SystemLibraryManager slm) { + super(uri, slm); + } + + public UrlLibrarySource(URI uri) { + this(uri, null); + } + + public UrlLibrarySource(File file) { + super(file); + } + + @Override + public String getName() { + return getUri().toString(); + } + + @Override + public DartSource getSourceFor(final String relPath) { + try { + // Force the creation of an escaped relative URI to deal with spaces, etc. + URI uri = getAbsoluteUri().resolve(new URI(null, null, relPath, null)).normalize(); + return new UrlDartSource(uri, relPath, this, systemLibraryManager); + } catch (URISyntaxException e) { + throw new AssertionError(e); + } + } + + @Override + public LibrarySource getImportFor(String relPath) { + return new UrlLibrarySource(getAbsoluteUri().resolve(relPath).normalize(), systemLibraryManager); + } +} diff --git a/compiler/java/com/google/dart/compiler/UrlSource.java b/compiler/java/com/google/dart/compiler/UrlSource.java new file mode 100644 index 00000000000..a59866f2837 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/UrlSource.java @@ -0,0 +1,194 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.jar.JarEntry; + +/** + * A {@link Source} backed by a URL (or optionally by a file). + */ +public abstract class UrlSource implements Source { + + private final static String FILE_PROTOCOL = "file"; + private final static String JAR_PROTOCOL = "jar"; + private final static URI CURRENT_DIR = new File(".").toURI().normalize(); + private final static Charset UTF8 = Charset.forName("UTF8"); + private final static URI BASE_URI = CURRENT_DIR; + + private final URI uri; + private final URI absoluteUri; + private final URI translatedUri; + private final boolean shouldCareAboutLastModified; + private volatile boolean exists = false; + private volatile long lastModified = -1; + private volatile boolean propertiesInitialized = false; + + // generally, one or the other of these will be non-null after properties are initialized + private volatile File sourceFile = null; + private volatile JarURLConnection jarConn = null; + + protected final SystemLibraryManager systemLibraryManager; + + protected UrlSource(URI uri) { + this(uri,null); + } + + protected UrlSource(URI uri, SystemLibraryManager slm) { + URI expanded = slm != null ? slm.expandRelativeDartUri(uri) : uri; + this.uri = BASE_URI.relativize(expanded.normalize()); + this.absoluteUri = BASE_URI.resolve(expanded); + this.systemLibraryManager = slm; + if (SystemLibraryManager.isDartUri(this.uri)) { + assert slm != null; + this.shouldCareAboutLastModified = false; + this.translatedUri = slm.resolveDartUri(this.absoluteUri); + } else { + this.shouldCareAboutLastModified = true; + this.translatedUri = this.absoluteUri; + } + } + + protected UrlSource(File file) { + URI uri = file.toURI().normalize(); + if (!file.exists()) { + // TODO(jgw): This is a bit ugly, but some of the test infrastructure depends upon + // non-existant relative files being looked up as classpath resources. This was + // previously embedded in DartSourceFile.getSourceReader(). + URL url = getClass().getClassLoader().getResource(file.getPath()); + if (url != null) { + uri = URI.create(url.toString()); + } + } + + this.uri = BASE_URI.relativize(uri); + this.translatedUri = this.absoluteUri = BASE_URI.resolve(uri); + this.systemLibraryManager = null; + this.shouldCareAboutLastModified = true; + } + + @Override + public boolean exists() { + initProperties(); + return exists; + } + + @Override + public long getLastModified() { + if (!shouldCareAboutLastModified) { + return 0; + } + initProperties(); + return lastModified; + } + + @Override + public Reader getSourceReader() throws IOException { + initProperties(); + if (sourceFile != null) { + return new FileReader(sourceFile); + } else if (jarConn != null) { + return new InputStreamReader(jarConn.getInputStream()); + } + // fall back case + if (translatedUri != null) { + InputStream stream = translatedUri.toURL().openStream(); + if (stream != null) { + return new InputStreamReader(stream, UTF8); + } + } + throw new FileNotFoundException(getName()); + } + + @Override + public URI getUri() { + return uri; + } + + protected URI getAbsoluteUri() { + return absoluteUri; + } + + private void initProperties() { + if (!propertiesInitialized) { + synchronized(this) { + if (!propertiesInitialized) { + try { + URI resolvedUri = BASE_URI.resolve(uri); + String scheme = resolvedUri.getScheme(); + if (scheme == null || FILE_PROTOCOL.equals(scheme)) { + // Faster than using URLConnection + File file = new File(resolvedUri); + lastModified = file.lastModified(); + exists = file.exists(); + sourceFile = file; + } else { + try { + URL url = translatedUri.toURL(); + if (JAR_PROTOCOL.equals(url.getProtocol())) { + getJarEntryProperties(url); + } else { + /* + * TODO(jbrosenberg): Flesh out the support for other + * protocols, like http, etc. Note, calling + * URLConnection.getLastModified() can be dangerous, some + * URLConnection sub-classes don't have a way to close a + * connection opened by this call. Return 0 for now. + */ + lastModified = 0; + // Default this to true for now. + exists = true; + } + } catch (MalformedURLException e) { + return; + } + } + } finally { + propertiesInitialized = true; + } + } + } + } + } + + private void getJarEntryProperties(URL url) { + try { + jarConn = (JarURLConnection) url.openConnection(); + // useCaches is usually set to true by default, but make sure here + jarConn.setUseCaches(true); + // See if our entry exists + JarEntry jarEntry = jarConn.getJarEntry(); + if (jarEntry != null) { + exists = true; + if (!shouldCareAboutLastModified) { + lastModified = 0; + return; + } + // TODO(jbrosenberg): Note the time field for a jarEntry can be + // unreliable, and is not always required in a jar file. Consider using + // the timestamp on the jar file itself. + lastModified = jarEntry.getTime(); + } + if (!exists) { + lastModified = -1; + return; + } + } catch (IOException e) { + exists = false; + lastModified = -1; + } + } +} \ No newline at end of file diff --git a/compiler/java/com/google/dart/compiler/ast/DartArrayAccess.java b/compiler/java/com/google/dart/compiler/ast/DartArrayAccess.java new file mode 100644 index 00000000000..75136f03410 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartArrayAccess.java @@ -0,0 +1,65 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.resolver.Element; + +/** + * Represents a Dart array access expression (a[b]). + */ +public class DartArrayAccess extends DartExpression implements ElementReference { + + private DartExpression target; + private DartExpression key; + private Element referencedElement; + + public DartArrayAccess(DartExpression target, DartExpression key) { + this.target = becomeParentOf(target); + this.key = becomeParentOf(key); + } + + @Override + public boolean isAssignable() { + return true; + } + + public DartExpression getKey() { + return key; + } + + public DartExpression getTarget() { + return target; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + target = becomeParentOf(v.accept(target)); + key = becomeParentOf(v.accept(key)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + target.accept(visitor); + key.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitArrayAccess(this); + } + + @Override + public Element getReferencedElement() { + return referencedElement; + } + + @Override + public void setReferencedElement(Element element) { + referencedElement = element; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartArrayLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartArrayLiteral.java new file mode 100644 index 00000000000..376e8ce40fa --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartArrayLiteral.java @@ -0,0 +1,45 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart array literal value. + */ +public class DartArrayLiteral extends DartTypedLiteral { + + private final List expressions; + + public DartArrayLiteral(boolean isConst, List typeArguments, + List expressions) { + super(isConst, typeArguments); + this.expressions = becomeParentOf(expressions); + } + + public List getExpressions() { + return expressions; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + super.traverse(v, ctx); + v.acceptWithInsertRemove(this, expressions); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + super.visitChildren(visitor); + visitor.visit(expressions); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitArrayLiteral(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartAssertion.java b/compiler/java/com/google/dart/compiler/ast/DartAssertion.java new file mode 100644 index 00000000000..6b8622adb64 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartAssertion.java @@ -0,0 +1,58 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Implements the "assert" statement. + */ +public class DartAssertion extends DartStatement { + private DartExpression expression; + private DartExpression message; + + public DartAssertion(DartExpression expression, DartExpression message) { + this.expression = becomeParentOf(expression); + this.message = becomeParentOf(message); + } + + public void setExpression(DartExpression expression) { + this.expression = expression; + } + + public DartExpression getExpression() { + return expression; + } + + public void setMessage(DartExpression message) { + this.message = message; + } + + public DartExpression getMessage() { + return message; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + expression = becomeParentOf(v.accept(expression)); + if (message != null) { + message = becomeParentOf(v.accept(message)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + expression.accept(visitor); + if (message != null) { + message.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitAssertion(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartBinaryExpression.java b/compiler/java/com/google/dart/compiler/ast/DartBinaryExpression.java new file mode 100644 index 00000000000..592ca63d051 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartBinaryExpression.java @@ -0,0 +1,84 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.Element; + +/** + * Represents a Dart binary expression. + */ +public class DartBinaryExpression extends DartExpression implements ElementReference { + + private final Token op; + private DartExpression arg1; + private DartExpression arg2; + private DartExpression normalizedNode = this; + private Element referencedElement; + + public DartBinaryExpression(Token op, DartExpression arg1, DartExpression arg2) { + assert op.isBinaryOperator() : op; + + this.op = op; + this.arg1 = becomeParentOf(arg1); + this.arg2 = becomeParentOf(arg2); + } + + public DartExpression getArg1() { + return arg1; + } + + public DartExpression getArg2() { + return arg2; + } + + public Token getOperator() { + return op; + } + + public void setNormalizedNode(DartExpression normalizedNode) { + normalizedNode.setSourceInfo(this); + this.normalizedNode = normalizedNode; + } + + @Override + public DartExpression getNormalizedNode() { + return normalizedNode; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (op.isAssignmentOperator()) { + arg1 = becomeParentOf(v.acceptLvalue(arg1)); + } else { + arg1 = becomeParentOf(v.accept(arg1)); + } + arg2 = becomeParentOf(v.accept(arg2)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + arg1.accept(visitor); + arg2.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitBinaryExpression(this); + } + + @Override + public Element getReferencedElement() { + return referencedElement; + } + + @Override + public void setReferencedElement(Element referencedElement) { + this.referencedElement = referencedElement; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartBlock.java b/compiler/java/com/google/dart/compiler/ast/DartBlock.java new file mode 100644 index 00000000000..151c6d7ac2c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartBlock.java @@ -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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart statement block. + */ +public class DartBlock extends DartStatement { + + private final List stmts; + + public DartBlock(List statements) { + this.stmts = becomeParentOf(statements); + } + + public List getStatements() { + return stmts; + } + + @Override + public boolean isAbruptCompletingStatement() { + for (DartStatement stmt : stmts) { + if (stmt.isAbruptCompletingStatement()) { + return true; + } + } + return false; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(this, stmts); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + visitor.visit(stmts); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitBlock(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartBooleanLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartBooleanLiteral.java new file mode 100644 index 00000000000..f1da6cb7af6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartBooleanLiteral.java @@ -0,0 +1,40 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart boolean literal value. + */ +public class DartBooleanLiteral extends DartLiteral { + + public static DartBooleanLiteral get(boolean value) { + return new DartBooleanLiteral(value); + } + + private final boolean value; + + private DartBooleanLiteral(boolean value) { + this.value = value; + } + + public boolean getValue() { + return value; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitBooleanLiteral(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartBreakStatement.java b/compiler/java/com/google/dart/compiler/ast/DartBreakStatement.java new file mode 100644 index 00000000000..4f25c14be31 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartBreakStatement.java @@ -0,0 +1,34 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'break' statement. + */ +public class DartBreakStatement extends DartGotoStatement { + + public DartBreakStatement(DartIdentifier label) { + super(label); + } + + @Override + public boolean isAbruptCompletingStatement() { + return true; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + DartIdentifier label = getLabel(); + if (v.visit(this, ctx) && label != null) { + label = becomeParentOf(v.accept(label)); + } + v.endVisit(this, ctx); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitBreakStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartCase.java b/compiler/java/com/google/dart/compiler/ast/DartCase.java new file mode 100644 index 00000000000..4e62e4b0e3d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartCase.java @@ -0,0 +1,55 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart 'case' switch member. + */ +public class DartCase extends DartSwitchMember { + + private DartExpression expr; + private DartCase normalizedNode = this; + + public DartCase(DartExpression expr, DartLabel label, List statements) { + super(label, statements); + this.expr = becomeParentOf(expr); + } + + public DartExpression getExpr() { + return expr; + } + + public void setNormalizedNode(DartCase normalizedNode) { + normalizedNode.setSourceInfo(this); + this.normalizedNode = normalizedNode; + } + + @Override + public DartCase getNormalizedNode() { + return normalizedNode; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + expr = becomeParentOf(v.accept(expr)); + v.acceptWithInsertRemove(this, getStatements()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + expr.accept(visitor); + super.visitChildren(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitCase(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartCatchBlock.java b/compiler/java/com/google/dart/compiler/ast/DartCatchBlock.java new file mode 100644 index 00000000000..78820d654a8 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartCatchBlock.java @@ -0,0 +1,60 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'catch' block. + */ +public class DartCatchBlock extends DartStatement { + private DartParameter exception; + private DartParameter stackTrace; + private DartBlock block; + + public DartCatchBlock(DartBlock block, + DartParameter exception, + DartParameter stackTrace) { + this.block = becomeParentOf(block); + this.exception = becomeParentOf(exception); + this.stackTrace = becomeParentOf(stackTrace); + } + + public DartParameter getException() { + return exception; + } + + public DartParameter getStackTrace() { + return stackTrace; + } + + public DartBlock getBlock() { + return block; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + exception = becomeParentOf(v.accept(exception)); + if (stackTrace != null) { + stackTrace = becomeParentOf(v.accept(stackTrace)); + } + block = becomeParentOf(v.accept(block)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + exception.accept(visitor); + if (stackTrace != null) { + stackTrace.accept(visitor); + } + block.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitCatchBlock(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartClass.java b/compiler/java/com/google/dart/compiler/ast/DartClass.java new file mode 100644 index 00000000000..31394935301 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartClass.java @@ -0,0 +1,188 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.ClassElement; + +import java.util.List; + +/** + * Represents a Dart class. + */ +public class DartClass extends DartDeclaration implements HasSymbol { + + private ClassElement element; + + private DartTypeNode superclass; + + private final List members; + private final List typeParameters; + private final List interfaces; + + private boolean isInterface; + private DartTypeNode defaultClass; + + private int hash = -1; + + // If the Dart class is implemented by a native JS class the nativeName + // points to the JS class. Otherwise it is null. + private final DartStringLiteral nativeName; + + public DartClass(DartIdentifier name, DartStringLiteral nativeName, + DartTypeNode superclass, List interfaces, + List members, + List typeParameters) { + this(name, nativeName, superclass, interfaces, members, typeParameters, null, false); + } + + public DartClass(DartIdentifier name, DartTypeNode superclass, List interfaces, + List members, + List typeParameters, DartTypeNode defaultClass) { + this(name, null, superclass, interfaces, members, typeParameters, defaultClass, true); + } + + /** + * Set the diet-string hash code for the class + * @param hash the hash code to set + */ + void setHash(int hash) { + this.hash = hash; + } + + public DartClass(DartIdentifier name, DartStringLiteral nativeName, + DartTypeNode superclass, List interfaces, + List members, + List typeParameters, DartTypeNode defaultClass, + boolean isInterface) { + super(name); + this.nativeName = nativeName; + this.superclass = becomeParentOf(superclass); + this.members = becomeParentOf(members); + this.typeParameters = becomeParentOf(typeParameters); + this.interfaces = becomeParentOf(interfaces); + this.defaultClass = becomeParentOf(defaultClass); + this.isInterface = isInterface; + } + + public boolean isInterface() { + return isInterface; + } + + public List getMembers() { + return members; + } + + public List getTypeParameters() { + return typeParameters; + } + + public List getInterfaces() { + return interfaces; + } + + public String getClassName() { + if (getName() == null) { + return null; + } + return getName().getTargetName(); + } + + public DartTypeNode getSuperclass() { + return superclass; + } + + public DartTypeNode getDefaultClass() { + return defaultClass; + } + + public Symbol getDefaultSymbol() { + if (defaultClass != null) { + return defaultClass.getType().getElement(); + } else { + return null; + } + } + + public Symbol getSuperSymbol() { + if (superclass != null) { + return superclass.getType().getElement(); + } else { + return null; + } + } + + @Override + public ClassElement getSymbol() { + return element; + } + + public void setDefaultClass(DartTypeNode newName) { + defaultClass = becomeParentOf(newName); + } + + public void setSuperclass(DartTypeNode newName) { + superclass = becomeParentOf(newName); + } + + @Override + public void setSymbol(Symbol symbol) { + this.element = (ClassElement) symbol; + } + + public DartStringLiteral getNativeName() { + return nativeName; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (superclass != null) { + superclass = becomeParentOf(v.accept(superclass)); + } + v.acceptWithInsertRemove(this, getMembers()); + if (getTypeParameters() != null) { + v.acceptWithInsertRemove(this, getTypeParameters()); + } + if (getInterfaces() != null) { + v.acceptWithInsertRemove(this, getInterfaces()); + } + if (defaultClass != null) { + defaultClass = becomeParentOf(v.accept(defaultClass)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + visitor.visit(typeParameters); + if (superclass != null) { + superclass.accept(visitor); + } + visitor.visit(interfaces); + if (defaultClass != null) { + defaultClass.accept(visitor); + } + visitor.visit(members); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitClass(this); + } + + @Override + public int computeHash() { + // TODO(jgw): Remove this altogether in fixing b/5324113. + + // Cache hashes for DartClass, because they're always needed. + if (this.hash == -1) { + this.hash = super.computeHash(); + } + return this.hash; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartClassMember.java b/compiler/java/com/google/dart/compiler/ast/DartClassMember.java new file mode 100644 index 00000000000..4669a460bd8 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartClassMember.java @@ -0,0 +1,33 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.resolver.Element; + +/** + * Base class for class members (fields and methods). + */ +public abstract class DartClassMember extends DartDeclaration + implements HasSymbol { + + private final Modifiers modifiers; + + protected DartClassMember(N name, Modifiers modifiers) { + super(name); + this.modifiers = modifiers; + } + + public Modifiers getModifiers() { + return modifiers; + } + + @Override + public abstract Element getSymbol(); + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartComment.java b/compiler/java/com/google/dart/compiler/ast/DartComment.java new file mode 100644 index 00000000000..2f8cbae1253 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartComment.java @@ -0,0 +1,64 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.Source; + +public class DartComment extends DartNode { + + private static final long serialVersionUID = 6066713446767517627L; + + public static enum Style { + END_OF_LINE, BLOCK, DART_DOC; + } + + private Style style; + + public DartComment(Source source, int start, int length, int line, int col, Style style) { + setSourceLocation(source, line, col, start, length); + this.style = style; + } + + /** + * Return true if this comment is a block comment. + * + * @return true if this comment is a block comment + */ + public boolean isBlock() { + return style == Style.BLOCK; + } + + /** + * Return true if this comment is a DartDoc comment. + * + * @return true if this comment is a DartDoc comment + */ + public boolean isDartDoc() { + return style == Style.DART_DOC; + } + + /** + * Return true if this comment is an end-of-line comment. + * + * @return true if this comment is an end-of-line comment + */ + public boolean isEndOfLine() { + return style == Style.END_OF_LINE; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return null; + } + +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartConditional.java b/compiler/java/com/google/dart/compiler/ast/DartConditional.java new file mode 100644 index 00000000000..23989789cf9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartConditional.java @@ -0,0 +1,55 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart conditional expression. + */ +public class DartConditional extends DartExpression { + + private DartExpression condition; + private DartExpression elseExpr; + private DartExpression thenExpr; + + public DartConditional(DartExpression condition, DartExpression thenExpr, + DartExpression elseExpr) { + this.condition = becomeParentOf(condition); + this.thenExpr = becomeParentOf(thenExpr); + this.elseExpr = becomeParentOf(elseExpr); + } + + public DartExpression getCondition() { + return condition; + } + + public DartExpression getElseExpression() { + return elseExpr; + } + + public DartExpression getThenExpression() { + return thenExpr; + } + + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + condition = becomeParentOf(v.accept(condition)); + thenExpr = becomeParentOf(v.accept(thenExpr)); + elseExpr = becomeParentOf(v.accept(elseExpr)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + condition.accept(visitor); + thenExpr.accept(visitor); + elseExpr.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitConditional(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartContext.java b/compiler/java/com/google/dart/compiler/ast/DartContext.java new file mode 100644 index 00000000000..07d6a0f28c5 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartContext.java @@ -0,0 +1,27 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * The context in which a DartNode visitation occurs. This represents the set of + * possible operations a DartVisitor subclass can perform on the currently + * visited node. + */ +public interface DartContext { + + boolean canInsert(); + + boolean canRemove(); + + void insertAfter(DartVisitable node); + + void insertBefore(DartVisitable node); + + boolean isLvalue(); + + void removeMe(); + + void replaceMe(DartVisitable node); +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartContinueStatement.java b/compiler/java/com/google/dart/compiler/ast/DartContinueStatement.java new file mode 100644 index 00000000000..fbd6cccbaff --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartContinueStatement.java @@ -0,0 +1,34 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'continue' statement. + */ +public class DartContinueStatement extends DartGotoStatement { + + public DartContinueStatement(DartIdentifier label) { + super(label); + } + + @Override + public boolean isAbruptCompletingStatement() { + return true; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + DartIdentifier label = getLabel(); + if (v.visit(this, ctx) && label != null) { + label = becomeParentOf(v.accept(label)); + } + v.endVisit(this, ctx); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitContinueStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartDeclaration.java b/compiler/java/com/google/dart/compiler/ast/DartDeclaration.java new file mode 100644 index 00000000000..c2b3eafcc69 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartDeclaration.java @@ -0,0 +1,33 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.resolver.Element; + +/** + * Common supertype for most declarations. A declaration introduces a new name + * in a scope. Certain tools, such as the IDE, need to know the location of this + * name, but the name should otherwise be considered a part of the declaration, + * not an independent node. So the name is not visited when traversing the AST. + */ +public abstract class DartDeclaration extends DartNode { + + private N name; // Not visited. + + protected DartDeclaration(N name) { + this.name = becomeParentOf(name); + } + + public final N getName() { + return name; + } + + public final void setName(N newName) { + name = becomeParentOf(newName); + } + + @Override + public abstract Element getSymbol(); +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartDefault.java b/compiler/java/com/google/dart/compiler/ast/DartDefault.java new file mode 100644 index 00000000000..c495f5213ca --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartDefault.java @@ -0,0 +1,30 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart 'default' switch member. + */ +public class DartDefault extends DartSwitchMember { + + public DartDefault(DartLabel label, List statements) { + super(label, statements); + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(this, getStatements()); + } + v.endVisit(this, ctx); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitDefault(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartDirective.java b/compiler/java/com/google/dart/compiler/ast/DartDirective.java new file mode 100644 index 00000000000..3a1fe7e0122 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartDirective.java @@ -0,0 +1,11 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Base class for directives. + */ +public abstract class DartDirective extends DartNode { +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartDoWhileStatement.java b/compiler/java/com/google/dart/compiler/ast/DartDoWhileStatement.java new file mode 100644 index 00000000000..6c5d7eab916 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartDoWhileStatement.java @@ -0,0 +1,47 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'do/while' statement. + */ +public class DartDoWhileStatement extends DartStatement { + + private DartExpression condition; + private DartStatement body; + + public DartDoWhileStatement(DartExpression condition, DartStatement body) { + this.condition = becomeParentOf(condition); + this.body = becomeParentOf(body); + } + + public DartStatement getBody() { + return body; + } + + public DartExpression getCondition() { + return condition; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + condition = becomeParentOf(v.accept(condition)); + body = becomeParentOf(v.accept(body)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + condition.accept(visitor); + body.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitDoWhileStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartDoubleLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartDoubleLiteral.java new file mode 100644 index 00000000000..9c35c942c5c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartDoubleLiteral.java @@ -0,0 +1,40 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart double literal value. + */ +public class DartDoubleLiteral extends DartLiteral { + + public static DartDoubleLiteral get(double x) { + return new DartDoubleLiteral(x); + } + + private final double value; + + private DartDoubleLiteral(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitDoubleLiteral(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartEmptyStatement.java b/compiler/java/com/google/dart/compiler/ast/DartEmptyStatement.java new file mode 100644 index 00000000000..1c59e328ecb --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartEmptyStatement.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents an empty Dart statement. + */ +public class DartEmptyStatement extends DartStatement { + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitEmptyStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartExprStmt.java b/compiler/java/com/google/dart/compiler/ast/DartExprStmt.java new file mode 100644 index 00000000000..40c7a9fcedf --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartExprStmt.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart expression-as-statement. + */ +public class DartExprStmt extends DartStatement { + + private DartExpression expr; + + public DartExprStmt(DartExpression expr) { + this.expr = becomeParentOf(expr); + } + + public DartExpression getExpression() { + return expr; + } + + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + expr = becomeParentOf(v.accept(expr)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + expr.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitExprStmt(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartExpression.java b/compiler/java/com/google/dart/compiler/ast/DartExpression.java new file mode 100644 index 00000000000..9b0bb15a81c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartExpression.java @@ -0,0 +1,21 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Abstract base class for Dart expressions. + */ +public abstract class DartExpression extends DartNode { + + public boolean isAssignable() { + // By default you cannot assign to expressions. + return false; + } + + @Override + public DartExpression getNormalizedNode() { + return (DartExpression) super.getNormalizedNode(); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartField.java b/compiler/java/com/google/dart/compiler/ast/DartField.java new file mode 100644 index 00000000000..4a2c587fab4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartField.java @@ -0,0 +1,95 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.FieldElement; + +/** + * Represents a single field within a field definition. + */ +public class DartField extends DartClassMember { + + private DartExpression value; + private FieldElement element; + private DartMethodDefinition accessor; + + public DartField(DartIdentifier name, Modifiers modifiers, DartMethodDefinition accessor, + DartExpression value) { + super(name, modifiers); + this.accessor = becomeParentOf(accessor); + this.value = becomeParentOf(value); + } + + public void setValue(DartExpression value) { + this.value = becomeParentOf(value); + } + + public DartExpression getValue() { + return value; + } + + public void setAccessor(DartMethodDefinition accessor) { + this.accessor = becomeParentOf(accessor); + } + + public DartMethodDefinition getAccessor() { + return accessor; + } + + @Override + public FieldElement getSymbol() { + return element; + } + + @Override + public void setSymbol(Symbol symbol) { + this.element = (FieldElement) symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (getValue() != null) { + setValue(v.accept(getValue())); + } + if (getAccessor() != null) { + setAccessor(v.accept(getAccessor())); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + super.visitChildren(visitor); + if (getAccessor() != null) { + getAccessor().accept(visitor); + } + if (getValue() != null) { + getValue().accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitField(this); + } + + @Override + public int computeHash() { + // TODO(jgw): Remove this altogether in fixing b/5324113. + + // DartField doesn't include the type-node, so we directly return the hash of its type, which + // is all that matters for the purposes of dependency-tracking. + DartFieldDefinition def = (DartFieldDefinition) getParent(); + DartTypeNode typeNode = def.getTypeNode(); + if (typeNode == null) { + // Use 0 to represent an untyped field. + return 0; + } + return typeNode.computeHash(); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartFieldDefinition.java b/compiler/java/com/google/dart/compiler/ast/DartFieldDefinition.java new file mode 100644 index 00000000000..8e90cc0ef00 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartFieldDefinition.java @@ -0,0 +1,57 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart field definition. + */ +public class DartFieldDefinition extends DartNode { + + private DartTypeNode typeNode; + private final List fields; + + public DartFieldDefinition(DartTypeNode typeNode, List fields) { + this.setTypeNode(typeNode); + this.fields = becomeParentOf(fields); + } + + public DartTypeNode getTypeNode() { + return typeNode; + } + + public void setTypeNode(DartTypeNode typeNode) { + this.typeNode = becomeParentOf(typeNode); + } + + public List getFields() { + return fields; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (getTypeNode() != null) { + setTypeNode(v.accept(getTypeNode())); + } + v.acceptWithInsertRemove(this, getFields()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (getTypeNode() != null) { + getTypeNode().accept(visitor); + } + visitor.visit(getFields()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitFieldDefinition(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartForInStatement.java b/compiler/java/com/google/dart/compiler/ast/DartForInStatement.java new file mode 100644 index 00000000000..31f096c399e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartForInStatement.java @@ -0,0 +1,78 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'for (.. in ..)' statement. + */ +public class DartForInStatement extends DartStatement { + + private DartStatement setup; + private DartExpression iterable; + private DartStatement body; + + private DartStatement normalizedNode = this; + + public DartForInStatement(DartStatement setup, + DartExpression iterable, + DartStatement body) { + this.setup = becomeParentOf(setup); + this.iterable = becomeParentOf(iterable); + this.body = becomeParentOf(body); + } + + public DartStatement getBody() { + return body; + } + + public DartExpression getIterable() { + return iterable; + } + + public void setNormalizedNode(DartStatement statement) { + normalizedNode = statement; + } + + @Override + public DartStatement getNormalizedNode() { + return normalizedNode; + } + + public boolean introducesVariable() { + return setup instanceof DartVariableStatement; + } + + public DartIdentifier getIdentifier() { + assert !introducesVariable(); + return (DartIdentifier) ((DartExprStmt) setup).getExpression(); + } + + public DartVariableStatement getVariableStatement() { + assert introducesVariable(); + return (DartVariableStatement) setup; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + setup = becomeParentOf(v.accept(setup)); + iterable = becomeParentOf(v.accept(iterable)); + body = becomeParentOf(v.accept(body)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + setup.accept(visitor); + iterable.accept(visitor); + body.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitForInStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartForStatement.java b/compiler/java/com/google/dart/compiler/ast/DartForStatement.java new file mode 100644 index 00000000000..b5f635f052c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartForStatement.java @@ -0,0 +1,76 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'for' statement. + */ +public class DartForStatement extends DartStatement { + + private DartStatement init; + private DartExpression condition; + private DartExpression increment; + private DartStatement body; + + public DartForStatement(DartStatement init, DartExpression condition, DartExpression increment, + DartStatement body) { + this.init = becomeParentOf(init); + this.condition = becomeParentOf(condition); + this.increment = becomeParentOf(increment); + this.body = becomeParentOf(body); + } + + public DartStatement getBody() { + return body; + } + + public DartExpression getCondition() { + return condition; + } + + public DartExpression getIncrement() { + return increment; + } + + public DartStatement getInit() { + return init; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (init != null) { + init = becomeParentOf(v.accept(init)); + } + if (condition != null) { + condition = becomeParentOf(v.accept(condition)); + } + if (increment != null) { + increment = becomeParentOf(v.accept(increment)); + } + body = becomeParentOf(v.accept(body)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (init != null) { + init.accept(visitor); + } + if (condition != null) { + condition.accept(visitor); + } + if (increment != null) { + increment.accept(visitor); + } + body.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitForStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartFunction.java b/compiler/java/com/google/dart/compiler/ast/DartFunction.java new file mode 100644 index 00000000000..76e161c6f7d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartFunction.java @@ -0,0 +1,68 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart function. + */ +public class DartFunction extends DartNode { + + private final List params; + private DartBlock body; + private DartTypeNode returnTypeNode; + + public DartFunction(List arguments, DartBlock body, DartTypeNode returnTypeNode) { + this.params = becomeParentOf(arguments); + this.body = becomeParentOf(body); + this.returnTypeNode = becomeParentOf(returnTypeNode); + } + + public void addParam(DartParameter param) { + params.add(param); + } + + public DartBlock getBody() { + return body; + } + + public List getParams() { + return params; + } + + public DartTypeNode getReturnTypeNode() { + return returnTypeNode; + } + + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(this, params); + if (body != null) { + body = becomeParentOf(v.accept(body)); + } + if (returnTypeNode != null) { + returnTypeNode = becomeParentOf(v.accept(returnTypeNode)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + visitor.visit(params); + if (body != null) { + body.accept(visitor); + } + if (returnTypeNode != null) { + returnTypeNode.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitFunction(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartFunctionExpression.java b/compiler/java/com/google/dart/compiler/ast/DartFunctionExpression.java new file mode 100644 index 00000000000..06245acf23c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartFunctionExpression.java @@ -0,0 +1,80 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.MethodElement; + +/** + * Represents a Dart 'function' expression. + */ +public class DartFunctionExpression extends DartExpression implements HasSymbol { + + // Not visited. Similar to DartDeclaration, but DartDeclaration shouldn't be + // a statement or an expression. + private DartIdentifier name; + + private final boolean isStmt; + private MethodElement symbol; + private DartFunction function; + + public DartFunctionExpression(DartIdentifier name, DartFunction function, boolean isStmt) { + this.name = becomeParentOf(name); + this.function = becomeParentOf(function); + this.isStmt = isStmt; + } + + public DartFunction getFunction() { + return function; + } + + public String getFunctionName() { + if (name == null) { + return null; + } + return name.getTargetName(); + } + + public DartIdentifier getName() { + return name; + } + + @Override + public MethodElement getSymbol() { + return symbol; + } + + public boolean isStatement() { + return isStmt; + } + + public void setName(DartIdentifier newName) { + name = becomeParentOf(newName); + } + + @Override + public void setSymbol(Symbol symbol) { + this.symbol = (MethodElement) symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + function = becomeParentOf(v.accept(function)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + function.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitFunctionExpression(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartFunctionObjectInvocation.java b/compiler/java/com/google/dart/compiler/ast/DartFunctionObjectInvocation.java new file mode 100644 index 00000000000..400f349259e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartFunctionObjectInvocation.java @@ -0,0 +1,46 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Function-object invocation AST node. + */ +public class DartFunctionObjectInvocation extends DartInvocation { + + private DartExpression target; + + public DartFunctionObjectInvocation(DartExpression target, + List args) { + super(args); + this.target = becomeParentOf(target); + } + + @Override + public DartExpression getTarget() { + return target; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + target = becomeParentOf(v.accept(target)); + v.acceptWithInsertRemove(this, getArgs()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + target.accept(visitor); + visitor.visit(getArgs()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitFunctionObjectInvocation(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartFunctionTypeAlias.java b/compiler/java/com/google/dart/compiler/ast/DartFunctionTypeAlias.java new file mode 100644 index 00000000000..22dac3c032c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartFunctionTypeAlias.java @@ -0,0 +1,83 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.FunctionAliasElement; + +import java.util.List; + +/** + * Named function-type alias AST node. + */ +public class DartFunctionTypeAlias extends DartDeclaration implements HasSymbol { + + private DartTypeNode returnTypeNode; + private final List parameters; + private FunctionAliasElement element; + private final List typeParameters; + + public DartFunctionTypeAlias(DartIdentifier name, DartTypeNode returnTypeNode, + List parameters, + List typeParameters) { + super(name); + this.returnTypeNode = becomeParentOf(returnTypeNode); + this.parameters = becomeParentOf(parameters); + this.typeParameters = becomeParentOf(typeParameters); + } + + public List getParameters() { + return parameters; + } + + public DartTypeNode getReturnTypeNode() { + return returnTypeNode; + } + + public List getTypeParameters() { + return typeParameters; + } + + @Override + public FunctionAliasElement getSymbol() { + return element; + } + + public void setReturnTypeNode(DartTypeNode newReturnType) { + returnTypeNode = becomeParentOf(newReturnType); + } + + @Override + public void setSymbol(Symbol symbol) { + element = (FunctionAliasElement) symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (returnTypeNode != null) { + returnTypeNode = becomeParentOf(v.accept(returnTypeNode)); + } + v.acceptWithInsertRemove(this, parameters); + v.acceptWithInsertRemove(this, typeParameters); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (returnTypeNode != null) { + returnTypeNode.accept(visitor); + } + visitor.visit(parameters); + visitor.visit(typeParameters); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitFunctionTypeAlias(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartGotoStatement.java b/compiler/java/com/google/dart/compiler/ast/DartGotoStatement.java new file mode 100644 index 00000000000..5ad227f54b3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartGotoStatement.java @@ -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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.Symbol; + +/** + * Base class of {@link DartBreakStatement} and {@link DartContinueStatement}. + */ +public abstract class DartGotoStatement extends DartStatement { + + private DartIdentifier label; + private Symbol targetSymbol; + + public DartGotoStatement(DartIdentifier label) { + this.label = becomeParentOf(label); + } + + public DartIdentifier getLabel() { + return label; + } + + public String getTargetName() { + if (label == null) { + return null; + } + return label.getTargetName(); + } + + public Symbol getTargetSymbol() { + return targetSymbol; + } + + public void setLabel(DartIdentifier newLabel) { + label = newLabel; + } + + @Override + public void setSymbol(Symbol symbol) { + this.targetSymbol = symbol; + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (label != null) { + label.accept(visitor); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartIdentifier.java b/compiler/java/com/google/dart/compiler/ast/DartIdentifier.java new file mode 100644 index 00000000000..fcd2a515fa4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartIdentifier.java @@ -0,0 +1,86 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.Element; + +/** + * Represents a Dart identifier expression. + */ +public class DartIdentifier extends DartExpression implements ElementReference { + + private final String targetName; + private Element targetSymbol; + private DartExpression normalizedNode = this; + private Element referencedElement; + + public DartIdentifier(String targetName) { + assert targetName != null; + this.targetName = targetName; + } + + public DartIdentifier(DartIdentifier original) { + this.targetName = original.targetName; + } + + public void setNormalizedNode(DartExpression normalizedNode) { + normalizedNode.setSourceInfo(this); + this.normalizedNode = normalizedNode; + } + + @Override + public DartExpression getNormalizedNode() { + return normalizedNode; + } + + @Override + public Element getSymbol() { + return targetSymbol; + } + + @Override + public boolean isAssignable() { + return true; + } + + public String getTargetName() { + return targetName; + } + + public Element getTargetSymbol() { + return targetSymbol; + } + + @Override + public void setSymbol(Symbol symbol) { + this.targetSymbol = (Element) symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitIdentifier(this); + } + + @Override + public void setReferencedElement(Element element) { + referencedElement = element; + } + + @Override + public Element getReferencedElement() { + return referencedElement; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartIfStatement.java b/compiler/java/com/google/dart/compiler/ast/DartIfStatement.java new file mode 100644 index 00000000000..7df42b89437 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartIfStatement.java @@ -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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'if' statement. + */ +public class DartIfStatement extends DartStatement { + + private DartExpression condition; + private DartStatement thenStmt; + private DartStatement elseStmt; + + public DartIfStatement(DartExpression condition, DartStatement thenStmt, DartStatement elseStmt) { + this.condition = becomeParentOf(condition); + this.thenStmt = becomeParentOf(thenStmt); + this.elseStmt = becomeParentOf(elseStmt); + } + + public DartExpression getCondition() { + return condition; + } + + public DartStatement getElseStatement() { + return elseStmt; + } + + public DartStatement getThenStatement() { + return thenStmt; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + condition = becomeParentOf(v.accept(condition)); + thenStmt = becomeParentOf(v.accept(thenStmt)); + if (elseStmt != null) { + elseStmt = becomeParentOf(v.accept(elseStmt)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + condition.accept(visitor); + thenStmt.accept(visitor); + if (elseStmt != null) { + elseStmt.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitIfStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartImportDirective.java b/compiler/java/com/google/dart/compiler/ast/DartImportDirective.java new file mode 100644 index 00000000000..c5c65be8acb --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartImportDirective.java @@ -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. + +package com.google.dart.compiler.ast; + +/** + * Implements the #import directive. + */ +public class DartImportDirective extends DartDirective { + private DartStringLiteral libraryUri; + + private DartStringLiteral prefix; + + public DartImportDirective(DartStringLiteral libraryUri, DartStringLiteral prefix) { + this.libraryUri = becomeParentOf(libraryUri); + this.prefix = becomeParentOf(prefix); + } + + public DartStringLiteral getLibraryUri() { + return libraryUri; + } + + public DartStringLiteral getPrefix() { + return prefix; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + libraryUri = becomeParentOf(v.accept(libraryUri)); + if (prefix != null) { + prefix = becomeParentOf(v.accept(prefix)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + libraryUri.accept(visitor); + if (prefix != null) { + prefix.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitImportDirective(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartInitializer.java b/compiler/java/com/google/dart/compiler/ast/DartInitializer.java new file mode 100644 index 00000000000..18a06730619 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartInitializer.java @@ -0,0 +1,74 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a constructor initializer expression. + */ +public class DartInitializer extends DartNode { + + private DartIdentifier name; + private DartExpression value; + + public DartInitializer(DartIdentifier name, DartExpression value) { + this.name = becomeParentOf(name); + this.value = becomeParentOf(value); + } + + public String getInitializerName() { + if (name == null) { + return null; + } + return name.getTargetName(); + } + + public DartIdentifier getName() { + return name; + } + + public DartExpression getValue() { + return value; + } + + /** + * Determines if initializer is an invocation. + * @return true if initializer is either super or redirected constructor invocation. + */ + public boolean isInvocation() { + return name == null; + } + + public void setName(DartIdentifier newName) { + name = becomeParentOf(newName); + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (name != null) { + name = becomeParentOf(v.accept(name)); + } + if (value != null) { + value = becomeParentOf(v.accept(value)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (name != null) { + name.accept(visitor); + } + if (value != null) { + value.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitInitializer(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartIntegerLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartIntegerLiteral.java new file mode 100644 index 00000000000..e00ff421107 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartIntegerLiteral.java @@ -0,0 +1,46 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.math.BigInteger; + +/** + * Represents a Dart integer literal value. + */ +public class DartIntegerLiteral extends DartLiteral { + + public static DartIntegerLiteral get(BigInteger x) { + return new DartIntegerLiteral(x); + } + + public static DartIntegerLiteral one() { + return new DartIntegerLiteral(BigInteger.ONE); + } + + private final BigInteger value; + + private DartIntegerLiteral(BigInteger value) { + this.value = value; + } + + public BigInteger getValue() { + return value; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitIntegerLiteral(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartInvocation.java b/compiler/java/com/google/dart/compiler/ast/DartInvocation.java new file mode 100644 index 00000000000..4993e2d6077 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartInvocation.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Common superclass for all invocation expressions. In + * a Dart program, there are different kinds of invocation: + *
    + *
  • expression.identifier() is a method invocation, where the + * receiver is 'expression' and the method name 'identifier'. + * This invocation is represented as a DartMethodInvocation. + * Examples: A.foo(), this.foo(), super.foo(), bar().foo(). + *
  • + * + *
  • identifier() is an unqualified invocation. After the resolver has + * resolved 'identifier', the normalizer will transform the node to + * either a DartFunctionObjectInvocation or a DartMethodInvocation. + * This invocation is represented as a DartUnqualifiedInvocation. + * Examples: foo(). + *
  • + * + *
  • expression() is a function object invocation. + * This invocation is represented as a DartFunctionObjectInvocation. + * Examples: bar()(), (A.bar)(), bar[0](), (bar)(). + *
  • + *
+ * + */ +public abstract class DartInvocation extends DartExpression { + + private List args; + + public DartInvocation(List args) { + this.args = becomeParentOf(args); + } + + public DartExpression getTarget() { + return null; + } + + public List getArgs() { + return args; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartLabel.java b/compiler/java/com/google/dart/compiler/ast/DartLabel.java new file mode 100644 index 00000000000..33fdaedf16c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartLabel.java @@ -0,0 +1,73 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; + +/** + * Represents a Dart statement label. + */ +public class DartLabel extends DartStatement implements HasSymbol { + + // Not visited. Similar to DartDeclaration, but DartDeclaration shouldn't be + // a statement or an expression. + private DartIdentifier label; + + private Symbol symbol; + + private DartStatement statement; + + public DartLabel(DartIdentifier label, DartStatement statement) { + this.label = becomeParentOf(label); + this.statement = becomeParentOf(statement); + } + + public DartIdentifier getLabel() { + return label; + } + + public String getName() { + return label.getTargetName(); + } + + public DartStatement getStatement() { + return statement; + } + + @Override + public Symbol getSymbol() { + return symbol; + } + + public void setLabel(DartIdentifier newLabel) { + label = newLabel; + } + + @Override + public void setSymbol(Symbol symbol) { + this.symbol = symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + statement = becomeParentOf(v.accept(statement)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (statement != null) { + statement.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitLabel(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartLibraryDirective.java b/compiler/java/com/google/dart/compiler/ast/DartLibraryDirective.java new file mode 100644 index 00000000000..18433d7b924 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartLibraryDirective.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Implements the #library directive. + */ +public class DartLibraryDirective extends DartDirective { + private DartStringLiteral name; + + public DartLibraryDirective(DartStringLiteral name) { + this.name = becomeParentOf(name); + } + + public DartStringLiteral getName() { + return name; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + name = becomeParentOf(v.accept(name)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + name.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitLibraryDirective(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartLiteral.java new file mode 100644 index 00000000000..289dcb05a91 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartLiteral.java @@ -0,0 +1,24 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.type.Type; + +/** + * Abstract base class for Dart literal values. + */ +public abstract class DartLiteral extends DartExpression { + private Type type; + + @Override + public void setType(Type type) { + this.type = type; + } + + @Override + public Type getType() { + return type; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartMapLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartMapLiteral.java new file mode 100644 index 00000000000..ad0bcdb9ae9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartMapLiteral.java @@ -0,0 +1,45 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart map literal value. + */ +public class DartMapLiteral extends DartTypedLiteral { + + private final List entries; + + public DartMapLiteral(boolean isConst, List typeArguments, + List entries) { + super(isConst, typeArguments); + this.entries = becomeParentOf(entries); + } + + public List getEntries() { + return entries; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + super.traverse(v, ctx); + v.acceptWithInsertRemove(this, entries); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + super.visitChildren(visitor); + visitor.visit(entries); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitMapLiteral(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartMapLiteralEntry.java b/compiler/java/com/google/dart/compiler/ast/DartMapLiteralEntry.java new file mode 100644 index 00000000000..e913bad381b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartMapLiteralEntry.java @@ -0,0 +1,47 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents an entry in a Dart map literal value. + */ +public class DartMapLiteralEntry extends DartNode { + + private DartExpression key; + private DartExpression value; + + public DartMapLiteralEntry(DartExpression key, DartExpression value) { + this.key = becomeParentOf(key); + this.value = becomeParentOf(value); + } + + public DartExpression getKey() { + return key; + } + + public DartExpression getValue() { + return value; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + key = becomeParentOf(v.accept(key)); + value = becomeParentOf(v.accept(value)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + key.accept(visitor); + value.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitMapLiteralEntry(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartMethodDefinition.java b/compiler/java/com/google/dart/compiler/ast/DartMethodDefinition.java new file mode 100644 index 00000000000..2836c446429 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartMethodDefinition.java @@ -0,0 +1,128 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.MethodElement; + +import java.util.Collections; +import java.util.List; + +/** + * Represents a Dart method definition. + */ +public class DartMethodDefinition extends DartClassMember { + + protected DartFunction function; + private MethodElement element; + private DartMethodDefinition normalizedNode = this; + private final List typeParameters; + + public static DartMethodDefinition create(DartExpression name, + DartFunction function, + Modifiers modifiers, + List initializers, + List typeParameters) { + if (initializers == null) { + return new DartMethodDefinition(name, function, modifiers, typeParameters); + } else { + return new DartMethodWithInitializersDefinition(name, function, modifiers, initializers); + } + } + + private DartMethodDefinition(DartExpression name, DartFunction function, Modifiers modifiers, + List typeParameters) { + super(name, modifiers); + this.function = becomeParentOf(function); + this.typeParameters = typeParameters; + } + + public DartFunction getFunction() { + return function; + } + + @Override + public MethodElement getSymbol() { + return element; + } + + @Override + public void setSymbol(Symbol symbol) { + element = (MethodElement) symbol; + } + + public void setNormalizedNode(DartMethodDefinition normalizedNode) { + normalizedNode.setSourceInfo(this); + this.normalizedNode = normalizedNode; + } + + @Override + public DartMethodDefinition getNormalizedNode() { + return normalizedNode; + } + + public List getInitializers() { + return Collections.emptyList(); + } + + public List getTypeParameters() { + return typeParameters; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + function = becomeParentOf(v.accept(function)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + super.visitChildren(visitor); + function.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitMethodDefinition(this); + } + + private static class DartMethodWithInitializersDefinition extends DartMethodDefinition { + + private final List initializers; + + DartMethodWithInitializersDefinition(DartExpression name, + DartFunction function, + Modifiers modifiers, + List initializers) { + super(name, function, modifiers, null); + this.initializers = becomeParentOf(initializers); + } + + @Override + public List getInitializers() { + return initializers; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + function = becomeParentOf(v.accept(function)); + v.acceptWithInsertRemove(this, initializers); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + super.visitChildren(visitor); + visitor.visit(initializers); + if (getTypeParameters() != null) { + visitor.visit(getTypeParameters()); + } + } + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartMethodInvocation.java b/compiler/java/com/google/dart/compiler/ast/DartMethodInvocation.java new file mode 100644 index 00000000000..3d9d377cdb1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartMethodInvocation.java @@ -0,0 +1,79 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.Symbol; + +import java.util.List; + +/** + * Method invocation AST node. The name of the method must not be + * null. The receiver is an expression, super, or a classname. + */ +public class DartMethodInvocation extends DartInvocation { + + private DartExpression target; + private DartIdentifier functionName; + private Symbol targetSymbol; + + public DartMethodInvocation(DartExpression target, + DartIdentifier functionName, + List args) { + super(args); + functionName.getClass(); // Quick null-check. + this.target = becomeParentOf(target); + this.functionName = becomeParentOf(functionName); + } + + @Override + public DartExpression getTarget() { + return target; + } + + public String getFunctionNameString() { + return functionName.getTargetName(); + } + + public DartIdentifier getFunctionName() { + return functionName; + } + + public void setFunctionName(DartIdentifier newName) { + newName.getClass(); // Quick null-check. + functionName = becomeParentOf(newName); + } + + @Override + public void setSymbol(Symbol symbol) { + this.targetSymbol = symbol; + } + + public Symbol getTargetSymbol() { + return targetSymbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + target = becomeParentOf(v.accept(target)); + functionName = becomeParentOf(v.accept(functionName)); + functionName.getClass(); // Quick null-check. + v.acceptWithInsertRemove(this, getArgs()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + target.accept(visitor); + functionName.accept(visitor); + visitor.visit(getArgs()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitMethodInvocation(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartModVisitor.java b/compiler/java/com/google/dart/compiler/ast/DartModVisitor.java new file mode 100644 index 00000000000..7e9db90ad3f --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartModVisitor.java @@ -0,0 +1,190 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.util.Hack; + +import java.util.List; + +/** + * A visitor for iterating through and modifying an AST. + */ +public class DartModVisitor extends DartVisitor { + + private class ListContext implements DartContext { + + private DartNode parent; + private List collection; + private int index; + private boolean removed; + private boolean replaced; + + public ListContext(DartNode parent) { + this.parent = parent; + } + + public boolean canInsert() { + return true; + } + + public boolean canRemove() { + return true; + } + + public void insertAfter(DartVisitable node) { + checkRemoved(); + parent.becomeParentOf((DartNode) node); + collection.add(index + 1, Hack.cast(node)); + didChange = true; + } + + public void insertBefore(DartVisitable node) { + checkRemoved(); + parent.becomeParentOf((DartNode) node); + collection.add(index++, Hack.cast(node)); + didChange = true; + } + + public boolean isLvalue() { + return false; + } + + public void removeMe() { + checkState(); + collection.remove(index--); + didChange = removed = true; + } + + public void replaceMe(DartVisitable node) { + checkState(); + checkReplacement(collection.get(index), node); + parent.becomeParentOf((DartNode) node); + collection.set(index, Hack.cast(node)); + didChange = replaced = true; + } + + protected void traverse(List collection) { + this.collection = collection; + for (index = 0; index < collection.size(); ++index) { + removed = replaced = false; + doTraverse(collection.get(index), this); + } + } + + private void checkRemoved() { + if (removed) { + throw new RuntimeException("Node was already removed"); + } + } + + private void checkState() { + checkRemoved(); + if (replaced) { + throw new RuntimeException("Node was already replaced"); + } + } + } + + private class LvalueContext extends NodeContext { + @Override + public boolean isLvalue() { + return true; + } + } + + private class NodeContext implements DartContext { + private T node; + private boolean replaced; + + public boolean canInsert() { + return false; + } + + public boolean canRemove() { + return false; + } + + public void insertAfter(DartVisitable node) { + throw new UnsupportedOperationException(); + } + + public void insertBefore(DartVisitable node) { + throw new UnsupportedOperationException(); + } + + public boolean isLvalue() { + return false; + } + + public void removeMe() { + throw new UnsupportedOperationException(); + } + + public void replaceMe(DartVisitable node) { + if (replaced) { + throw new RuntimeException("Node was already replaced"); + } + checkReplacement(this.node, node); + this.node = Hack.cast(node); + didChange = replaced = true; + } + + protected T traverse(T node) { + this.node = node; + replaced = false; + doTraverse(node, this); + return this.node; + } + } + + protected static void checkReplacement(T origNode, T newNode) { + if (newNode == null) { + throw new RuntimeException("Cannot replace with null"); + } + if (newNode == origNode) { + throw new RuntimeException("The replacement is the same as the original"); + } + } + + protected boolean didChange = false; + + @Override + public boolean didChange() { + return didChange; + } + + @Override + protected T doAccept(T node) { + return new NodeContext().traverse(node); + } + + @Override + protected void doAcceptList(List collection) { + doAcceptListImpl(collection); + } + + private void doAcceptListImpl(List collection) { + NodeContext ctx = new NodeContext(); + for (int i = 0, c = collection.size(); i < c; ++i) { + ctx.traverse(collection.get(i)); + if (ctx.replaced) { + collection.set(i, ctx.node); + } + } + } + + @Override + protected DartExpression doAcceptLvalue(DartExpression expr) { + return new LvalueContext().traverse(expr); + } + + @Override + protected List doAcceptWithInsertRemove( + DartNode parent, List collection) { + ListContext ctx = new ListContext(parent); + ctx.traverse(collection); + return ctx.collection; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartNamedExpression.java b/compiler/java/com/google/dart/compiler/ast/DartNamedExpression.java new file mode 100644 index 00000000000..a49c1cc9d70 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartNamedExpression.java @@ -0,0 +1,56 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a labeled expression (used in named method arguments). + */ +public class DartNamedExpression extends DartExpression { + + private DartIdentifier name; + private DartExpression expression; + + public DartNamedExpression(DartIdentifier ident, DartExpression expression) { + this.name = ident; + this.expression = becomeParentOf(expression); + } + + public DartIdentifier getName() { + return name; + } + + public DartExpression getExpression() { + return expression; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (name != null) { + name = becomeParentOf(v.accept(name)); + } + if (expression != null) { + expression = becomeParentOf(v.accept(expression)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (name != null) { + name.accept(visitor); + } + if (expression != null) { + expression.accept(visitor); + } + expression.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitNamedExpression(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartNativeBlock.java b/compiler/java/com/google/dart/compiler/ast/DartNativeBlock.java new file mode 100644 index 00000000000..7030feef4c1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartNativeBlock.java @@ -0,0 +1,31 @@ +// 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. + +package com.google.dart.compiler.ast; + + +/** + * Unofficial Dart native block for built in native invocations. + */ +public class DartNativeBlock extends DartBlock { + + public DartNativeBlock() { + super(null); + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitNativeBlock(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartNativeDirective.java b/compiler/java/com/google/dart/compiler/ast/DartNativeDirective.java new file mode 100644 index 00000000000..2780a984fb4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartNativeDirective.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Implements the #native directive. + */ +public class DartNativeDirective extends DartDirective { + private DartStringLiteral nativeUri; + + public DartNativeDirective(DartStringLiteral nativeUri) { + this.nativeUri = becomeParentOf(nativeUri); + } + + public DartStringLiteral getNativeUri() { + return nativeUri; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + nativeUri = becomeParentOf(v.accept(nativeUri)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + nativeUri.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitNativeDirective(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartNewExpression.java b/compiler/java/com/google/dart/compiler/ast/DartNewExpression.java new file mode 100644 index 00000000000..7d9012c604e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartNewExpression.java @@ -0,0 +1,69 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.ConstructorElement; + +import java.util.List; + +/** + * Represents a Dart 'new' expression. + */ +public class DartNewExpression extends DartInvocation implements HasSymbol { + + private DartNode constructor; + private ConstructorElement typeSymbol; + private final boolean isConst; + + public DartNewExpression(DartNode constructor, List args, boolean isConst) { + super(args); + this.constructor = becomeParentOf(constructor); + this.isConst = isConst; + } + + public DartNode getConstructor() { + return constructor; + } + + public boolean isConst() { + return isConst; + } + + @Override + public ConstructorElement getSymbol() { + return typeSymbol; + } + + public void setConstructor(DartExpression newConstructor) { + constructor = becomeParentOf(newConstructor); + } + + @Override + public void setSymbol(Symbol symbol) { + this.typeSymbol = (ConstructorElement) symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + constructor = becomeParentOf(v.accept(constructor)); + v.acceptWithInsertRemove(this, getArgs()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + constructor.accept(visitor); + visitor.visit(getArgs()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitNewExpression(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartNode.java b/compiler/java/com/google/dart/compiler/ast/DartNode.java new file mode 100644 index 00000000000..2f7f18952ee --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartNode.java @@ -0,0 +1,167 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.AbstractNode; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.Types; +import com.google.dart.compiler.util.DefaultTextOutput; + +import java.util.List; + +/** + * Base class for all Dart AST nodes. + */ +public abstract class DartNode extends AbstractNode implements DartVisitable { + + private DartNode parent; + + public final String toSource() { + DefaultTextOutput out = new DefaultTextOutput(false); + new DartToSourceVisitor(out).accept(this); + return out.toString(); + } + + public Symbol getSymbol() { + return null; + } + + public void setSymbol(Symbol symbol) { + throw new UnsupportedOperationException(getClass().getSimpleName()); + } + + public void setType(Type type) { + throw new UnsupportedOperationException(getClass().getSimpleName()); + } + + public DartNode getNormalizedNode() { + return this; + } + + public Type getType() { + return Types.newDynamicType(); + } + + @Override + public final String toString() { + return this.toSource(); + } + + /** + * Returns this node's parent node, or null if this is the + * root node. + *

+ * Note that the relationship between an AST node and its parent node + * may change over the lifetime of a node. + * + * @return the parent of this node, or null if none + */ + public final DartNode getParent() { + return parent; + } + + /** + * Return the node at the root of this node's AST structure. Note that this + * method's performance is linear with respect to the depth of the node in + * the AST structure (O(depth)). + * + * @return the node at the root of this node's AST structure + */ + public final DartNode getRoot() { + DartNode root = this; + DartNode parent = getParent(); + while (parent != null) { + root = parent; + parent = root.getParent(); + } + return root; + } + + /** + * Returns the length in characters of the original source file indicating + * where the source fragment corresponding to this node ends. + *

+ * The parser supplies useful well-defined source ranges to the nodes it + * creates. + * + * @return a (possibly 0) length, or 0 if no source startPosition + * information is recorded for this node + * @see #getStartPosition() + * @see #setSourceRange(int, int) + * @deprecated + */ + @Deprecated + public int getLength() { + return getSourceLength(); + } + + /** + * Returns the character index into the original source file indicating where + * the source fragment corresponding to this node begins. + *

+ * The parser supplies useful well-defined source ranges to the nodes it + * creates. See {@link ASTParser#setKind(int)} for details on precisely where + * source ranges begin and end. + * + * @return the 0-based character index, or -1 if no source + * startPosition information is recorded for this node + * @see #getLength() + * @see #setSourceRange(int, int) + * @deprecated + */ + @Deprecated + public int getStartPosition() { + return getSourceStart(); + } + + protected T becomeParentOf(T child) { + if (child != null) { + child.setParent(this); + } + return child; + } + + protected > L becomeParentOf(L children) { + if (children != null) { + for (DartNode child : children) { + child.setParent(this); + } + } + return children; + } + + private void setParent(DartNode newParent) { + parent = newParent; + } + + public abstract void visitChildren(DartPlainVisitor visitor); + + public abstract R accept(DartPlainVisitor visitor); + + public int computeHash() { + // TODO(jgw): Remove this altogether in fixing b/5324113. + // + // This computes a "hash" of the class' interface by simply serializing it to diet source and + // computing a hash of the string. This will work for now, but encodes too much information in + // the hash, and is slower than it should be. It should also cache the result and invalidate it + // if anything substantive changes. + // + // Examples of changes incorrectly captured by this hash, which would cause unnecessary + // recompiled include: + // - any change in method/field order would trigger an unnecessary recompile. + // - purely lexical changes such as {int x; int y;} => {int x, y;} + // + DefaultTextOutput out = new DefaultTextOutput(false); + new DartToSourceVisitor(out, true).accept(this); + return out.toString().trim().hashCode(); + } + + @Override + public DartNode clone() { + // TODO (fabiomfv) - Implement proper cloning when strictly needed. + return this; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartNodeTraverser.java b/compiler/java/com/google/dart/compiler/ast/DartNodeTraverser.java new file mode 100644 index 00000000000..941e6982dc0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartNodeTraverser.java @@ -0,0 +1,441 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * An alternative visitor implementation to {@link DartVisitor}. + * + *

With DartVisitor, you would write: + * + *

+ * @Override
+ * public boolean visit(DartArrayAccess node, DartContext ctx) {
+ *   // Actions before visiting subnodes.
+ *   return true;
+ * }
+ *
+ * @Override
+ * public R endVisit(DartArrayAccess x, DartContext ctx) {
+ *   // Actions after visiting subnodes.
+ * }
+ * 
+ * + *

With DartNodeTraverser, the pre- and post-actions are combined + * in the same method: + * + *

+ * @Override
+ * public R visitArrayAccess(DartArrayAccess node) {
+ *   // Actions before visiting subnodes.
+ *   this.visitChildren(node);
+ *   // Actions after visiting subnodes.
+ * }
+ * 
+ * + *

In addition, this visitor takes advantage of the AST-node class + * hierarchy and makes it easy to perform an action for, for example, + * all statements: + * + *

+ * @Override
+ * public R visitStatement(DartStatement node) {
+ *   // Action that must be performed for all statements.
+ * }
+ * 
+ */ +public class DartNodeTraverser implements DartPlainVisitor { + + public R visitNode(DartNode node) { + node.visitChildren(this); + return null; + } + + public R visitDirective(DartDirective node) { + return visitNode(node); + } + + public R visitInvocation(DartInvocation node) { + return visitExpression(node); + } + + public R visitExpression(DartExpression node) { + return visitNode(node); + } + + public R visitStatement(DartStatement node) { + return visitNode(node); + } + + public R visitLiteral(DartLiteral node) { + return visitExpression(node); + } + + public R visitGotoStatement(DartGotoStatement node) { + return visitStatement(node); + } + + public R visitSwitchMember(DartSwitchMember node) { + return visitNode(node); + } + + public R visitDeclaration(DartDeclaration node) { + return visitNode(node); + } + + public R visitClassMember(DartClassMember node) { + return visitDeclaration(node); + } + + @Override + public R visitArrayAccess(DartArrayAccess node) { + return visitExpression(node); + } + + @Override + public R visitArrayLiteral(DartArrayLiteral node) { + return visitExpression(node); + } + + @Override + public R visitAssertion(DartAssertion node) { + return visitStatement(node); + } + + @Override + public R visitBinaryExpression(DartBinaryExpression node) { + return visitExpression(node); + } + + @Override + public R visitBlock(DartBlock node) { + return visitStatement(node); + } + + @Override + public R visitBooleanLiteral(DartBooleanLiteral node) { + return visitLiteral(node); + } + + @Override + public R visitBreakStatement(DartBreakStatement node) { + return visitGotoStatement(node); + } + + @Override + public R visitFunctionObjectInvocation(DartFunctionObjectInvocation node) { + return visitInvocation(node); + } + + @Override + public R visitMethodInvocation(DartMethodInvocation node) { + return visitInvocation(node); + } + + @Override + public R visitUnqualifiedInvocation(DartUnqualifiedInvocation node) { + return visitInvocation(node); + } + + @Override + public R visitSuperConstructorInvocation(DartSuperConstructorInvocation node) { + return visitInvocation(node); + } + + @Override + public R visitCase(DartCase node) { + return visitSwitchMember(node); + } + + @Override + public R visitClass(DartClass node) { + return visitDeclaration(node); + } + + @Override + public R visitConditional(DartConditional node) { + return visitExpression(node); + } + + @Override + public R visitContinueStatement(DartContinueStatement node) { + return visitGotoStatement(node); + } + + @Override + public R visitDefault(DartDefault node) { + return visitSwitchMember(node); + } + + @Override + public R visitDoubleLiteral(DartDoubleLiteral node) { + return visitLiteral(node); + } + + @Override + public R visitDoWhileStatement(DartDoWhileStatement node) { + return visitStatement(node); + } + + @Override + public R visitEmptyStatement(DartEmptyStatement node) { + return visitStatement(node); + } + + @Override + public R visitExprStmt(DartExprStmt node) { + return visitStatement(node); + } + + @Override + public R visitField(DartField node) { + return visitClassMember(node); + } + + @Override + public R visitFieldDefinition(DartFieldDefinition node) { + return visitNode(node); + } + + @Override + public R visitForInStatement(DartForInStatement node) { + return visitStatement(node); + } + + @Override + public R visitForStatement(DartForStatement node) { + return visitStatement(node); + } + + @Override + public R visitFunction(DartFunction node) { + return visitNode(node); + } + + @Override + public R visitFunctionExpression(DartFunctionExpression node) { + return visitExpression(node); + } + + @Override + public R visitFunctionTypeAlias(DartFunctionTypeAlias node) { + return visitDeclaration(node); + } + + @Override + public R visitIdentifier(DartIdentifier node) { + return visitExpression(node); + } + + @Override + public R visitIfStatement(DartIfStatement node) { + return visitStatement(node); + } + + @Override + public R visitImportDirective(DartImportDirective node) { + return visitDirective(node); + } + + @Override + public R visitInitializer(DartInitializer node) { + return visitNode(node); + } + + @Override + public R visitIntegerLiteral(DartIntegerLiteral node) { + return visitLiteral(node); + } + + @Override + public R visitLabel(DartLabel node) { + return visitStatement(node); + } + + @Override + public R visitLibraryDirective(DartLibraryDirective node) { + return visitDirective(node); + } + + @Override + public R visitMapLiteral(DartMapLiteral node) { + return visitExpression(node); + } + + @Override + public R visitMapLiteralEntry(DartMapLiteralEntry node) { + return visitNode(node); + } + + @Override + public R visitMethodDefinition(DartMethodDefinition node) { + return visitClassMember(node); + } + + @Override + public R visitNativeDirective(DartNativeDirective node) { + return visitDirective(node); + } + + @Override + public R visitNewExpression(DartNewExpression node) { + return visitInvocation(node); + } + + @Override + public R visitNullLiteral(DartNullLiteral node) { + return visitLiteral(node); + } + + @Override + public R visitParameter(DartParameter node) { + return visitDeclaration(node); + } + + @Override + public R visitParameterizedNode(DartParameterizedNode node) { + return visitExpression(node); + } + + @Override + public R visitParenthesizedExpression(DartParenthesizedExpression node) { + return visitExpression(node); + } + + @Override + public R visitPropertyAccess(DartPropertyAccess node) { + return visitExpression(node); + } + + @Override + public R visitTypeNode(DartTypeNode node) { + return visitNode(node); + } + + @Override + public R visitResourceDirective(DartResourceDirective node) { + return visitDirective(node); + } + + @Override + public R visitReturnStatement(DartReturnStatement node) { + return visitStatement(node); + } + + @Override + public R visitSourceDirective(DartSourceDirective node) { + return visitDirective(node); + } + + @Override + public R visitStringLiteral(DartStringLiteral node) { + return visitLiteral(node); + } + + @Override + public R visitStringInterpolation(DartStringInterpolation node) { + return visitLiteral(node); + } + + @Override + public R visitSuperExpression(DartSuperExpression node) { + return visitExpression(node); + } + + @Override + public R visitSwitchStatement(DartSwitchStatement node) { + return visitStatement(node); + } + + @Override + public R visitSyntheticErrorExpression(DartSyntheticErrorExpression node) { + return visitExpression(node); + } + + @Override + public R visitSyntheticErrorStatement(DartSyntheticErrorStatement node) { + return visitStatement(node); + } + + @Override + public R visitThisExpression(DartThisExpression node) { + return visitExpression(node); + } + + @Override + public R visitThrowStatement(DartThrowStatement node) { + return visitStatement(node); + } + + @Override + public R visitCatchBlock(DartCatchBlock node) { + return visitStatement(node); + } + + @Override + public R visitTryStatement(DartTryStatement node) { + return visitStatement(node); + } + + @Override + public R visitUnaryExpression(DartUnaryExpression node) { + return visitExpression(node); + } + + @Override + public R visitUnit(DartUnit node) { + return visitNode(node); + } + + @Override + public R visitVariable(DartVariable node) { + return visitDeclaration(node); + } + + @Override + public R visitVariableStatement(DartVariableStatement node) { + return visitStatement(node); + } + + @Override + public R visitWhileStatement(DartWhileStatement node) { + return visitStatement(node); + } + + @Override + public void visit(List nodes) { + if (nodes != null) { + for (DartNode node : nodes) { + node.accept(this); + } + } + } + + @Override + public R visitNamedExpression(DartNamedExpression node) { + return visitExpression(node); + } + + @Override + public R visitTypeExpression(DartTypeExpression node) { + return visitExpression(node); + } + + @Override + public R visitTypeParameter(DartTypeParameter node) { + return visitDeclaration(node); + } + + @Override + public R visitNativeBlock(DartNativeBlock node) { + return visitBlock(node); + } + + @Override + public R visitRedirectConstructorInvocation(DartRedirectConstructorInvocation node) { + return visitInvocation(node); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartNullLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartNullLiteral.java new file mode 100644 index 00000000000..bc6873f2d38 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartNullLiteral.java @@ -0,0 +1,33 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'null' literal value. + */ +public class DartNullLiteral extends DartLiteral { + + public static DartNullLiteral get() { + return new DartNullLiteral(); + } + + private DartNullLiteral() { + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitNullLiteral(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartParameter.java b/compiler/java/com/google/dart/compiler/ast/DartParameter.java new file mode 100644 index 00000000000..9c82f8c06fb --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartParameter.java @@ -0,0 +1,126 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.common.base.Preconditions; +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.VariableElement; + +import java.util.List; + +/** + * Represents a Dart function parameter. + */ +public class DartParameter extends DartDeclaration implements HasSymbol { + + private VariableElement symbol; + private DartTypeNode typeNode; + private List functionParameters; + private DartExpression defaultExpr; + private DartParameter normalizedNode = this; + private final Modifiers modifiers; + + public DartParameter(DartExpression name, + DartTypeNode typeNode, + List functionParameters, + DartExpression defaultExpr, + Modifiers modifiers) { + super(name); + Preconditions.checkArgument((name instanceof DartIdentifier) + || (name instanceof DartPropertyAccess), "name"); + this.typeNode = becomeParentOf(typeNode); + this.functionParameters = becomeParentOf(functionParameters); + this.defaultExpr = becomeParentOf(defaultExpr); + this.modifiers = modifiers; + } + + public DartExpression getDefaultExpr() { + return defaultExpr; + } + + public String getParameterName() { + // TODO(fabiomfv) remove instanceof (http://b/issue?id=4729144) + if (getName() instanceof DartIdentifier) { + return ((DartIdentifier)getName()).getTargetName(); + } + return ((DartPropertyAccess)getName()).getPropertyName(); + } + + @Override + public VariableElement getSymbol() { + return symbol; + } + + public List getFunctionParameters() { + return functionParameters; + } + + public DartTypeNode getTypeNode() { + return typeNode; + } + + public Modifiers getModifiers() { + return modifiers; + } + + public DartNode getQualifier() { + if (getName() instanceof DartPropertyAccess) { + return ((DartPropertyAccess)getName()).getQualifier(); + } + return null; + } + + public void setNormalizedNode(DartParameter normalizedNode) { + normalizedNode.setSourceInfo(this); + this.normalizedNode = normalizedNode; + } + + @Override + public DartParameter getNormalizedNode() { + return normalizedNode; + } + + @Override + public void setSymbol(Symbol symbol) { + this.symbol = (VariableElement) symbol; + } + + public void setTypeNode(DartTypeNode typeNode) { + this.typeNode = becomeParentOf(typeNode); + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (typeNode != null) { + typeNode = becomeParentOf(v.accept(typeNode)); + } + if (defaultExpr != null) { + defaultExpr = becomeParentOf(v.accept(defaultExpr)); + } + if (functionParameters != null) { + v.acceptWithInsertRemove(this, functionParameters); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (typeNode != null) { + typeNode.accept(visitor); + } + if (defaultExpr != null) { + defaultExpr.accept(visitor); + } + visitor.visit(functionParameters); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitParameter(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartParameterizedNode.java b/compiler/java/com/google/dart/compiler/ast/DartParameterizedNode.java new file mode 100644 index 00000000000..016f15f950f --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartParameterizedNode.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +public class DartParameterizedNode extends DartExpression { + private DartExpression expression; + private List typeParameters; + + public DartParameterizedNode(DartExpression expression, List typeParameters) { + this.setExpression(becomeParentOf(expression)); + this.setTypeParameters(becomeParentOf(typeParameters)); + } + + public DartExpression getExpression() { + return expression; + } + + public void setExpression(DartExpression expression) { + this.expression = becomeParentOf(expression); + } + + public List getTypeParameters() { + return typeParameters; + } + + public void setTypeParameters(List typeParameters) { + this.typeParameters = becomeParentOf(typeParameters); + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + setExpression(v.accept(getExpression())); + setTypeParameters(v.acceptWithInsertRemove(this, getTypeParameters())); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + getExpression().accept(visitor); + visitor.visit(getTypeParameters()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitParameterizedNode(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartParenthesizedExpression.java b/compiler/java/com/google/dart/compiler/ast/DartParenthesizedExpression.java new file mode 100644 index 00000000000..b649157c069 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartParenthesizedExpression.java @@ -0,0 +1,43 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart parenthesized expression. + */ +public class DartParenthesizedExpression extends DartExpression { + + private DartExpression expression; + + public DartParenthesizedExpression(DartExpression expression) { + this.expression = becomeParentOf(expression); + } + + public DartExpression getExpression() { + return expression; + } + + public void setExpression(DartExpression newExpression) { + expression = newExpression; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + expression = becomeParentOf(v.accept(expression)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + expression.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitParenthesizedExpression(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartPlainVisitor.java b/compiler/java/com/google/dart/compiler/ast/DartPlainVisitor.java new file mode 100644 index 00000000000..710abfad872 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartPlainVisitor.java @@ -0,0 +1,148 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +public interface DartPlainVisitor { + + void visit(List nodes); + + R visitArrayAccess(DartArrayAccess node); + + R visitArrayLiteral(DartArrayLiteral node); + + R visitAssertion(DartAssertion node); + + R visitBinaryExpression(DartBinaryExpression node); + + R visitBlock(DartBlock node); + + R visitBooleanLiteral(DartBooleanLiteral node); + + R visitBreakStatement(DartBreakStatement node); + + R visitFunctionObjectInvocation(DartFunctionObjectInvocation node); + + R visitMethodInvocation(DartMethodInvocation node); + + R visitSuperConstructorInvocation(DartSuperConstructorInvocation node); + + R visitCase(DartCase node); + + R visitClass(DartClass node); + + R visitConditional(DartConditional node); + + R visitContinueStatement(DartContinueStatement node); + + R visitDefault(DartDefault node); + + R visitDoubleLiteral(DartDoubleLiteral node); + + R visitDoWhileStatement(DartDoWhileStatement node); + + R visitEmptyStatement(DartEmptyStatement node); + + R visitExprStmt(DartExprStmt node); + + R visitField(DartField node); + + R visitFieldDefinition(DartFieldDefinition node); + + R visitForInStatement(DartForInStatement node); + + R visitForStatement(DartForStatement node); + + R visitFunction(DartFunction node); + + R visitFunctionExpression(DartFunctionExpression node); + + R visitFunctionTypeAlias(DartFunctionTypeAlias node); + + R visitIdentifier(DartIdentifier node); + + R visitIfStatement(DartIfStatement node); + + R visitImportDirective(DartImportDirective node); + + R visitInitializer(DartInitializer node); + + R visitIntegerLiteral(DartIntegerLiteral node); + + R visitLabel(DartLabel node); + + R visitLibraryDirective(DartLibraryDirective node); + + R visitMapLiteral(DartMapLiteral node); + + R visitMapLiteralEntry(DartMapLiteralEntry node); + + R visitMethodDefinition(DartMethodDefinition node); + + R visitNativeDirective(DartNativeDirective node); + + R visitNewExpression(DartNewExpression node); + + R visitNullLiteral(DartNullLiteral node); + + R visitParameter(DartParameter node); + + R visitParameterizedNode(DartParameterizedNode node); + + R visitParenthesizedExpression(DartParenthesizedExpression node); + + R visitPropertyAccess(DartPropertyAccess node); + + R visitTypeNode(DartTypeNode node); + + R visitResourceDirective(DartResourceDirective node); + + R visitReturnStatement(DartReturnStatement node); + + R visitSourceDirective(DartSourceDirective node); + + R visitStringLiteral(DartStringLiteral node); + + R visitStringInterpolation(DartStringInterpolation node); + + R visitSuperExpression(DartSuperExpression node); + + R visitSwitchStatement(DartSwitchStatement node); + + R visitSyntheticErrorExpression(DartSyntheticErrorExpression node); + + R visitSyntheticErrorStatement(DartSyntheticErrorStatement node); + + R visitThisExpression(DartThisExpression node); + + R visitThrowStatement(DartThrowStatement node); + + R visitCatchBlock(DartCatchBlock node); + + R visitTryStatement(DartTryStatement node); + + R visitUnaryExpression(DartUnaryExpression node); + + R visitUnit(DartUnit node); + + R visitUnqualifiedInvocation(DartUnqualifiedInvocation node); + + R visitVariable(DartVariable node); + + R visitVariableStatement(DartVariableStatement node); + + R visitWhileStatement(DartWhileStatement node); + + R visitNamedExpression(DartNamedExpression node); + + R visitTypeExpression(DartTypeExpression node); + + R visitTypeParameter(DartTypeParameter node); + + R visitNativeBlock(DartNativeBlock node); + + R visitRedirectConstructorInvocation(DartRedirectConstructorInvocation node); +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartPropertyAccess.java b/compiler/java/com/google/dart/compiler/ast/DartPropertyAccess.java new file mode 100644 index 00000000000..26a6c7a0e2a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartPropertyAccess.java @@ -0,0 +1,83 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.Element; + +/** + * Represents a Dart property access expression (a.b). + */ +public class DartPropertyAccess extends DartExpression { + + private DartNode qualifier; + private DartIdentifier name; + private DartExpression normalizedNode = this; + + public DartPropertyAccess(DartNode qualifier, DartIdentifier name) { + this.qualifier = becomeParentOf(qualifier); + this.name = becomeParentOf(name); + } + + @Override + public boolean isAssignable() { + return true; + } + + public String getPropertyName() { + return name.getTargetName(); + } + + public DartIdentifier getName() { + return name; + } + + public DartNode getQualifier() { + return qualifier; + } + + public void setName(DartIdentifier newName) { + name = becomeParentOf(newName); + } + + @Override + public void setSymbol(Symbol symbol) { + name.setSymbol(symbol); + } + + public Element getTargetSymbol() { + return name.getTargetSymbol(); + } + + public void setNormalizedNode(DartExpression normalizedNode) { + normalizedNode.setSourceInfo(this); + this.normalizedNode = normalizedNode; + } + + @Override + public DartExpression getNormalizedNode() { + return normalizedNode; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + qualifier = becomeParentOf(v.accept(qualifier)); + name = becomeParentOf(v.accept(name)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + qualifier.accept(visitor); + name.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitPropertyAccess(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartRedirectConstructorInvocation.java b/compiler/java/com/google/dart/compiler/ast/DartRedirectConstructorInvocation.java new file mode 100644 index 00000000000..42d6f823904 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartRedirectConstructorInvocation.java @@ -0,0 +1,63 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.ConstructorElement; + +import java.util.List; + +/** + * Redirected constructor invocation AST node. + */ +public class DartRedirectConstructorInvocation extends DartInvocation implements HasSymbol { + + private DartIdentifier name; + private ConstructorElement symbol; + + public DartRedirectConstructorInvocation(DartIdentifier name, List args) { + super(args); + this.name = becomeParentOf(name); + } + + public DartIdentifier getName() { + return name; + } + + @Override + public void setSymbol(Symbol symbol) { + this.symbol = (ConstructorElement) symbol; + } + + @Override + public ConstructorElement getSymbol() { + return symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (name != null) { + name = becomeParentOf(v.accept(name)); + } + v.acceptWithInsertRemove(this, getArgs()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (name != null) { + name.accept(visitor); + } + visitor.visit(getArgs()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitRedirectConstructorInvocation(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartResourceDirective.java b/compiler/java/com/google/dart/compiler/ast/DartResourceDirective.java new file mode 100644 index 00000000000..0db5bffbfca --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartResourceDirective.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Implements the #resource directive. + */ +public class DartResourceDirective extends DartDirective { + private DartStringLiteral resourceUri; + + public DartResourceDirective(DartStringLiteral resourceUri) { + this.resourceUri = becomeParentOf(resourceUri); + } + + public DartStringLiteral getResourceUri() { + return resourceUri; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + resourceUri = becomeParentOf(v.accept(resourceUri)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + resourceUri.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitResourceDirective(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartReturnStatement.java b/compiler/java/com/google/dart/compiler/ast/DartReturnStatement.java new file mode 100644 index 00000000000..ee2cb9ee00e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartReturnStatement.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'return' statement. + */ +public class DartReturnStatement extends DartStatement { + + private DartExpression value; + + public DartReturnStatement(DartExpression value) { + this.value = becomeParentOf(value); + } + + public DartExpression getValue() { + return value; + } + + @Override + public boolean isAbruptCompletingStatement() { + return true; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (value != null) { + value = becomeParentOf(v.accept(value)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (value != null) { + value.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitReturnStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartSourceDirective.java b/compiler/java/com/google/dart/compiler/ast/DartSourceDirective.java new file mode 100644 index 00000000000..a33c40930fe --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartSourceDirective.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Implements the #source directive. + */ +public class DartSourceDirective extends DartDirective { + private DartStringLiteral sourceUri; + + public DartSourceDirective(DartStringLiteral sourceUri) { + this.sourceUri = becomeParentOf(sourceUri); + } + + public DartStringLiteral getSourceUri() { + return sourceUri; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + sourceUri = becomeParentOf(v.accept(sourceUri)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + sourceUri.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitSourceDirective(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartStatement.java b/compiler/java/com/google/dart/compiler/ast/DartStatement.java new file mode 100644 index 00000000000..e0694153266 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartStatement.java @@ -0,0 +1,14 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Abstract base class for Dart statement objects. + */ +public abstract class DartStatement extends DartNode { + public boolean isAbruptCompletingStatement() { + return false; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartStringInterpolation.java b/compiler/java/com/google/dart/compiler/ast/DartStringInterpolation.java new file mode 100644 index 00000000000..0049193fa14 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartStringInterpolation.java @@ -0,0 +1,64 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.common.base.Preconditions; + +import java.util.List; + +/** + * Represents a Dart string interpolation of the form: "1 ${a} 2 ${b} 3". + */ +public class DartStringInterpolation extends DartLiteral { + + /** + * Literal string portions. The interpolation alternates between strings and + * expressions. We preserve the invariant that {@code string.size() = + * expressions.size() + 1}. Empty string constants are used to represent + * adjacent expressions (e.g. $"${a} ${b}${c}" is represented by 4 strings + * ("", " ", "", "") and 3 expressions (#(){a}, #(){b}, #(){c}). + */ + private List strings; + + /** Embedded expressions (see {@link strings} for details). */ + private List expressions; + + public DartStringInterpolation(List strings, + List expressions) { + Preconditions.checkNotNull(strings); + Preconditions.checkNotNull(expressions); + Preconditions.checkArgument(strings.size() == expressions.size() + 1); + this.strings = becomeParentOf(strings); + this.expressions = becomeParentOf(expressions); + } + + public List getStrings() { + return strings; + } + + public List getExpressions() { + return expressions; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(this, strings); + v.acceptWithInsertRemove(this, expressions); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + visitor.visit(strings); + visitor.visit(expressions); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitStringInterpolation(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartStringLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartStringLiteral.java new file mode 100644 index 00000000000..5f5891973d7 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartStringLiteral.java @@ -0,0 +1,40 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart string literal value. + */ +public class DartStringLiteral extends DartLiteral { + + public static DartStringLiteral get(String x) { + return new DartStringLiteral(x); + } + + private final String value; + + private DartStringLiteral(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitStringLiteral(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartSuperConstructorInvocation.java b/compiler/java/com/google/dart/compiler/ast/DartSuperConstructorInvocation.java new file mode 100644 index 00000000000..dff69178cba --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartSuperConstructorInvocation.java @@ -0,0 +1,74 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.ConstructorElement; + +import java.util.List; + +/** + * Super constructor invocation AST node. + */ +public class DartSuperConstructorInvocation extends DartInvocation implements HasSymbol { + + private DartIdentifier name; + private ConstructorElement symbol; + + public DartSuperConstructorInvocation(DartIdentifier name, List args) { + super(args); + this.name = becomeParentOf(name); + } + + public String getConstructorName() { + if (name == null) { + return null; + } + return name.getTargetName(); + } + + public DartIdentifier getName() { + return name; + } + + public void setName(DartIdentifier newName) { + name = becomeParentOf(newName); + } + + @Override + public void setSymbol(Symbol symbol) { + this.symbol = (ConstructorElement) symbol; + } + + @Override + public ConstructorElement getSymbol() { + return symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (name != null) { + name = becomeParentOf(v.accept(name)); + } + v.acceptWithInsertRemove(this, getArgs()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (name != null) { + name.accept(visitor); + } + visitor.visit(getArgs()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitSuperConstructorInvocation(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartSuperExpression.java b/compiler/java/com/google/dart/compiler/ast/DartSuperExpression.java new file mode 100644 index 00000000000..97a6309f991 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartSuperExpression.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.SuperElement; + +/** + * Represents a Dart 'super' expression. + */ +public class DartSuperExpression extends DartExpression { + + private SuperElement targetSymbol; + + public static DartSuperExpression get() { + return new DartSuperExpression(); + } + + private DartSuperExpression() { + } + + @Override + public void setSymbol(Symbol symbol) { + this.targetSymbol = (SuperElement) symbol; + } + + @Override + public SuperElement getSymbol() { + return targetSymbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitSuperExpression(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartSwitchMember.java b/compiler/java/com/google/dart/compiler/ast/DartSwitchMember.java new file mode 100644 index 00000000000..8bf0a0daff6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartSwitchMember.java @@ -0,0 +1,41 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart 'switch' member ('case' or 'default'). + */ +public abstract class DartSwitchMember extends DartNode { + + private final List statements; + private final DartLabel label; + + public DartSwitchMember(DartLabel label, List statements) { + this.label = becomeParentOf(label); + this.statements = becomeParentOf(statements); + } + + public void addStatement(DartStatement statement) { + statements.add(statement); + } + + public List getStatements() { + return statements; + } + + public DartLabel getLabel() { + return label; + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (label != null) { + label.accept(visitor); + } + visitor.visit(statements); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartSwitchStatement.java b/compiler/java/com/google/dart/compiler/ast/DartSwitchStatement.java new file mode 100644 index 00000000000..f2fd75bc2d0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartSwitchStatement.java @@ -0,0 +1,49 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart 'switch' statement. + */ +public class DartSwitchStatement extends DartStatement { + + private DartExpression expression; + private final List members; + + public DartSwitchStatement(DartExpression expression, List members) { + this.expression = becomeParentOf(expression); + this.members = becomeParentOf(members); + } + + public DartExpression getExpression() { + return expression; + } + + public List getMembers() { + return members; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + expression = becomeParentOf(v.accept(expression)); + v.acceptWithInsertRemove(this, members); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + expression.accept(visitor); + visitor.visit(members); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitSwitchStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorExpression.java b/compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorExpression.java new file mode 100644 index 00000000000..9e6603e36e2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorExpression.java @@ -0,0 +1,39 @@ +// 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. +package com.google.dart.compiler.ast; + +/** + * An expression node representing an unparseable expression. + */ +public class DartSyntheticErrorExpression extends DartExpression { + + private final String tokenString; + + public DartSyntheticErrorExpression() { + this(null); + } + + public DartSyntheticErrorExpression(String tokenString) { + this.tokenString = tokenString; + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitSyntheticErrorExpression(this); + } + + public String getTokenString() { + return tokenString; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorStatement.java b/compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorStatement.java new file mode 100644 index 00000000000..8f534360035 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartSyntheticErrorStatement.java @@ -0,0 +1,39 @@ +// 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. +package com.google.dart.compiler.ast; + +/** + * A statement node representing an unparseable statement. + */ +public class DartSyntheticErrorStatement extends DartStatement { + + private final String tokenString; + + public DartSyntheticErrorStatement() { + this(null); + } + + public DartSyntheticErrorStatement(String tokenString) { + this.tokenString = tokenString; + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitSyntheticErrorStatement(this); + } + + public String getTokenString() { + return tokenString; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartThisExpression.java b/compiler/java/com/google/dart/compiler/ast/DartThisExpression.java new file mode 100644 index 00000000000..37d6c78d9c5 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartThisExpression.java @@ -0,0 +1,33 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'this' expression. + */ +public class DartThisExpression extends DartExpression { + + public static DartThisExpression get() { + return new DartThisExpression(); + } + + private DartThisExpression() { + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitThisExpression(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartThrowStatement.java b/compiler/java/com/google/dart/compiler/ast/DartThrowStatement.java new file mode 100644 index 00000000000..61691ba4458 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartThrowStatement.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'throw' statement. + */ +public class DartThrowStatement extends DartStatement { + + private DartExpression exception; + + public DartThrowStatement(DartExpression exception) { + this.exception = becomeParentOf(exception); + } + + public DartExpression getException() { + return exception; + } + + @Override + public boolean isAbruptCompletingStatement() { + return true; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (exception != null) { + exception = becomeParentOf(v.accept(exception)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (exception != null) { + exception.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitThrowStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartToSourceVisitor.java b/compiler/java/com/google/dart/compiler/ast/DartToSourceVisitor.java new file mode 100644 index 00000000000..5b170280966 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartToSourceVisitor.java @@ -0,0 +1,995 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.common.GenerateSourceMap; +import com.google.dart.compiler.common.HasSourceInfo; +import com.google.dart.compiler.common.SourceMapping; +import com.google.dart.compiler.util.TextOutput; +import com.google.debugging.sourcemap.FilePosition; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +/** + * Used by {@link DartNode} to generate Dart source from an AST subtree. + */ +public class DartToSourceVisitor extends DartVisitor { + + private final TextOutput out; + private boolean buildMappings; + private List mappings = Lists.newArrayList(); + private final boolean isDiet; + + public DartToSourceVisitor(TextOutput out) { + this(out, false); + } + + public DartToSourceVisitor(TextOutput out, boolean isDiet) { + this.out = out; + this.isDiet = isDiet; + } + + public void generateSourceMap(boolean generate) { + this.buildMappings = generate; + } + + public void writeSourceMap(Appendable out, String name) throws IOException { + GenerateSourceMap generator = new GenerateSourceMap(); + for (SourceMapping m : mappings) { + generator.addMapping(m.getNode(), m.getStart(), m.getEnd()); + } + generator.appendTo(out, name); + } + + @Override + public void doTraverse(DartVisitable x, DartContext ctx) { + SourceMapping m = null; + + boolean mapThis = shouldMap(x); + if (mapThis) { + m = new SourceMapping((HasSourceInfo) x, new FilePosition(out.getLine(), out.getColumn())); + mappings.add(m); + } + + super.doTraverse(x, ctx); + + if (mapThis) { + m.setEnd(new FilePosition(out.getLine(), out.getColumn())); + } + } + + /** + * Filter uninteresting AST nodes out of the source map + */ + private boolean shouldMap(DartVisitable x) { + return buildMappings && !(x instanceof DartExprStmt); + } + + @Override + public boolean visit(DartUnit x, DartContext ctx) { + p("// unit " + x.getSourceName()); + nl(); + acceptList(x.getTopLevelNodes()); + return false; + } + + @Override + public boolean visit(DartNativeBlock x, DartContext ctx) { + p("native;"); + return false; + } + + private void pTypeParameters(List typeParameters) { + if (typeParameters != null && !typeParameters.isEmpty()) { + p("<"); + boolean first = true; + for (DartNode node : typeParameters) { + if (!first) { + p(", "); + } + accept(node); + first = false; + } + p(">"); + } + } + + @Override + public boolean visit(DartFunctionTypeAlias x, DartContext ctx) { + p("typedef "); + + if (x.getReturnTypeNode() != null) { + accept(x.getReturnTypeNode()); + } else { + p("function "); + } + + p(" "); + accept(x.getName()); + pTypeParameters(x.getTypeParameters()); + + p("("); + printSeparatedByComma(x.getParameters()); + p(")"); + + p(";"); + nl(); + nl(); + return false; + } + + @Override + public boolean visit(DartClass x, DartContext ctx) { + if (x.isInterface()) { + p("interface "); + } else { + p("class "); + } + accept(x.getName()); + pTypeParameters(x.getTypeParameters()); + + if (x.getSuperclass() != null) { + p(" extends "); + accept(x.getSuperclass()); + } + + List interfaces = x.getInterfaces(); + if (interfaces != null && !interfaces.isEmpty()) { + if (x.isInterface()) { + p(" extends "); + } else { + p(" implements "); + } + boolean first = true; + for (DartTypeNode cls : interfaces) { + if (!first) { + p(", "); + } + accept(cls); + first = false; + } + } + + if (x.getNativeName() != null) { + p(" native "); + accept(x.getNativeName()); + } + + if (x.getDefaultClass() != null) { + p(" factory "); + accept(x.getDefaultClass()); + } + + p(" {"); + nl(); + indent(); + + acceptList(x.getMembers()); + + outdent(); + p("}"); + nl(); + nl(); + return false; + } + + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + List arguments = x.getTypeArguments(); + if (arguments != null && !arguments.isEmpty()) { + p("<"); + printSeparatedByComma(arguments); + p(">"); + } + return false; + } + + @Override + public boolean visit(DartTypeParameter x, DartContext ctx) { + accept(x.getName()); + DartTypeNode bound = x.getBound(); + if (bound != null) { + p(" extends "); + accept(bound); + } + return false; + } + + @Override + public boolean visit(DartFieldDefinition x, DartContext ctx) { + Modifiers modifiers = x.getFields().get(0).getModifiers(); + if (modifiers.isAbstractField()) { + pAbstractField(x, ctx); + } else { + pFieldModifiers(x); + if (x.getTypeNode() != null) { + accept(x.getTypeNode()); + p(" "); + } else { + if (!modifiers.isFinal()) { + p("var "); + } + } + printSeparatedByComma(x.getFields()); + p(";"); + } + + nl(); + + return false; + } + + @Override + public boolean visit(DartField x, DartContext ctx) { + accept(x.getName()); + if (x.getValue() != null) { + p(" = "); + accept(x.getValue()); + } + return false; + } + + @Override + public boolean visit(DartParameter x, DartContext ctx) { + if (x.getModifiers().isFinal()) { + p("final "); + } + if (x.getTypeNode() != null) { + accept(x.getTypeNode()); + p(" "); + } + if (x.getModifiers().isVariadic()) { + p("..."); + } + accept(x.getName()); + if (x.getFunctionParameters() != null) { + p("("); + printSeparatedByComma(x.getFunctionParameters()); + p(")"); + } + if (x.getDefaultExpr() != null) { + p(" = "); + accept(x.getDefaultExpr()); + } + return false; + } + + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + nl(); + pMethodModifiers(x); + DartFunction func = x.getFunction(); + if (func.getReturnTypeNode() != null) { + accept(func.getReturnTypeNode()); + p(" "); + } + if (x.getModifiers().isOperator()) { + p("operator "); + } else if (x.getModifiers().isGetter()) { + p("get "); + } else if (x.getModifiers().isSetter()) { + p("set "); + } + pFunctionDeclaration(x.getName(), func); + p(" "); + if (!isDiet) { + List inits = x.getInitializers(); + if (!inits.isEmpty()) { + p(": "); + for (int i = 0; i < inits.size(); ++i) { + accept(inits.get(i)); + if (i < inits.size() - 1) { + p(", "); + } + } + } + } + if (x.getFunction().getBody() != null) { + accept(x.getFunction().getBody()); + } else { + if (isDiet && x.getModifiers().isRedirectedConstructor() && !x.getModifiers().isConstant()) { + p("{ }"); + } else { + p(";"); + } + nl(); + } + return false; + } + + @Override + public boolean visit(DartInitializer x, DartContext ctx) { + if (!x.isInvocation()) { + p("this."); + p(x.getInitializerName()); + p(" = "); + } + accept(x.getValue()); + return false; + } + + private void pBlock(DartBlock x, boolean newline) { + p("{"); + nl(); + + indent(); + acceptList(x.getStatements()); + outdent(); + + p("}"); + if (newline) { + nl(); + } + } + + private void pFunctionDeclaration(DartNode name, DartFunction x) { + if (name != null) { + accept(name); + } + p("("); + pFormalParameters(x.getParams()); + p(")"); + } + + private void pFormalParameters(List params) { + boolean first = true, hasNamed = false; + for (DartParameter param : params) { + if (!first) { + p(", "); + } + if (!hasNamed && param.getModifiers().isNamed()) { + hasNamed = true; + p("["); + } + accept(param); + first = false; + } + if (hasNamed) { + p("]"); + } + } + + @Override + public boolean visit(DartBlock x, DartContext ctx) { + if (isDiet) { + p("{ }"); + nl(); + return false; + } + + pBlock(x, true); + return false; + } + + @Override + public boolean visit(DartAssertion x, DartContext ctx) { + p("assert("); + accept(x.getExpression()); + p(");"); + nl(); + return false; + } + + @Override + public boolean visit(DartIfStatement x, DartContext ctx) { + p("if ("); + accept(x.getCondition()); + p(") "); + pIfBlock(x.getThenStatement(), x.getElseStatement() == null); + if (x.getElseStatement() != null) { + p(" else "); + pIfBlock(x.getElseStatement(), true); + } + return false; + } + + @Override + public boolean visit(DartSwitchStatement x, DartContext ctx) { + p("switch ("); + accept(x.getExpression()); + p(") {"); + nl(); + + indent(); + acceptList(x.getMembers()); + outdent(); + + p("}"); + nl(); + return false; + } + + @Override + public boolean visit(DartCase x, DartContext ctx) { + p("case "); + accept(x.getExpr()); + p(":"); + nl(); + indent(); + acceptList(x.getStatements()); + outdent(); + return false; + } + + @Override + public boolean visit(DartDefault x, DartContext ctx) { + p("default:"); + nl(); + indent(); + acceptList(x.getStatements()); + outdent(); + return false; + } + + @Override + public boolean visit(DartWhileStatement x, DartContext ctx) { + p("while ("); + accept(x.getCondition()); + p(") "); + pIfBlock(x.getBody(), true); + return false; + } + + @Override + public boolean visit(DartDoWhileStatement x, DartContext ctx) { + p("do "); + pIfBlock(x.getBody(), false); + p(" while ("); + accept(x.getCondition()); + p(");"); + nl(); + return false; + } + + @Override + public boolean visit(DartForStatement x, DartContext ctx) { + p("for ("); + + // Setup + DartStatement setup = x.getInit(); + if (setup != null) { + if (setup instanceof DartVariableStatement) { + // Special case to avoid an extra semicolon & newline after the var + // statement. + p("var "); + printSeparatedByComma(((DartVariableStatement) setup).getVariables()); + } else { + // Plain old expression. + assert setup instanceof DartExprStmt; + accept(((DartExprStmt) setup).getExpression()); + } + } + p("; "); + + // Condition + if (x.getCondition() != null) { + accept(x.getCondition()); + } + p("; "); + + // Next + if (x.getIncrement() != null) { + accept(x.getIncrement()); + } + p(") "); + + // Body + accept(x.getBody()); + nl(); + return false; + } + + @Override + public boolean visit(DartForInStatement x, DartContext ctx) { + p("for ("); + if (x.introducesVariable()) { + DartTypeNode type = x.getVariableStatement().getTypeNode(); + if (type != null) { + accept(type); + p(" "); + } else { + p("var "); + } + printSeparatedByComma(x.getVariableStatement().getVariables()); + } else { + accept(x.getIdentifier()); + } + + p(" in "); + + // iterable + accept(x.getIterable()); + p(") "); + + // Body + accept(x.getBody()); + nl(); + return false; + } + + @Override + public boolean visit(DartContinueStatement x, DartContext ctx) { + p("continue"); + if (x.getTargetName() != null) { + p(" " + x.getTargetName()); + } + p(";"); + nl(); + return false; + } + + @Override + public boolean visit(DartBreakStatement x, DartContext ctx) { + p("break"); + if (x.getTargetName() != null) { + p(" " + x.getTargetName()); + } + p(";"); + nl(); + return false; + } + + @Override + public boolean visit(DartReturnStatement x, DartContext ctx) { + p("return"); + if (x.getValue() != null) { + p(" "); + accept(x.getValue()); + } + p(";"); + nl(); + return false; + } + + @Override + public boolean visit(DartTryStatement x, DartContext ctx) { + p("try "); + accept(x.getTryBlock()); + acceptList(x.getCatchBlocks()); + if (x.getFinallyBlock() != null) { + p("finally "); + accept(x.getFinallyBlock()); + } + return false; + } + + private void visitCatchParameter(DartParameter x) { + if (!x.getModifiers().isFinal() && x.getTypeNode() == null) { + p("var "); + } + accept(x); + } + + @Override + public boolean visit(DartCatchBlock x, DartContext ctx) { + p("catch ("); + visitCatchParameter(x.getException()); + if (x.getStackTrace() != null) { + p(", "); + visitCatchParameter(x.getStackTrace()); + } + p(") "); + accept(x.getBlock()); + return false; + } + + @Override + public boolean visit(DartThrowStatement x, DartContext ctx) { + p("throw"); + if (x.getException() != null) { + p(" "); + accept(x.getException()); + } + p(";"); + nl(); + return false; + } + + @Override + public boolean visit(DartVariableStatement x, DartContext ctx) { + if (x.getTypeNode() != null) { + accept(x.getTypeNode()); + p(" "); + } else { + p("var "); + } + printSeparatedByComma(x.getVariables()); + p(";"); + nl(); + return false; + } + + @Override + public boolean visit(DartVariable x, DartContext ctx) { + accept(x.getName()); + if (x.getValue() != null) { + p(" = "); + accept(x.getValue()); + } + return false; + } + + @Override + public boolean visit(DartEmptyStatement x, DartContext ctx) { + p(";"); + nl(); + return false; + } + + @Override + public boolean visit(DartLabel x, DartContext ctx) { + p(x.getName()); + p(": "); + accept(x.getStatement()); + return false; + } + + @Override + public boolean visit(DartExprStmt x, DartContext ctx) { + accept(x.getExpression()); + p(";"); + nl(); + return false; + } + + @Override + public boolean visit(DartBinaryExpression x, DartContext ctx) { + accept(x.getArg1()); + p(" "); + p(x.getOperator().getSyntax()); + p(" "); + accept(x.getArg2()); + return false; + } + + @Override + public boolean visit(DartConditional x, DartContext ctx) { + accept(x.getCondition()); + p(" ? "); + accept(x.getThenExpression()); + p(" : "); + accept(x.getElseExpression()); + return false; + } + + @Override + public boolean visit(DartUnaryExpression x, DartContext ctx) { + if (x.isPrefix()) { + p(x.getOperator().getSyntax()); + } + accept(x.getArg()); + if (!x.isPrefix()) { + p(x.getOperator().getSyntax()); + } + return false; + } + + @Override + public boolean visit(DartPropertyAccess x, DartContext ctx) { + accept(x.getQualifier()); + p("."); + p(x.getPropertyName()); + return false; + } + + @Override + public boolean visit(DartArrayAccess x, DartContext ctx) { + accept(x.getTarget()); + p("["); + accept(x.getKey()); + p("]"); + return false; + } + + private void pArgs(List args) { + p("("); + printSeparatedByComma(args); + p(")"); + } + + @Override + public boolean visit(DartUnqualifiedInvocation x, DartContext ctx) { + accept(x.getTarget()); + pArgs(x.getArgs()); + return false; + } + + @Override + public boolean visit(DartFunctionObjectInvocation x, DartContext ctx) { + accept(x.getTarget()); + pArgs(x.getArgs()); + return false; + } + + @Override + public boolean visit(DartMethodInvocation x, DartContext ctx) { + accept(x.getTarget()); + p("."); + accept(x.getFunctionName()); + pArgs(x.getArgs()); + return false; + } + + @Override + public boolean visit(DartSyntheticErrorExpression node, DartContext ctx) { + p("[error: " + node.getTokenString() + "]"); + return false; + } + + @Override + public boolean visit(DartSyntheticErrorStatement node, DartContext ctx) { + p("[error: " + node.getTokenString() + "]"); + return false; + } + + @Override + public boolean visit(DartThisExpression x, DartContext ctx) { + p("this"); + return false; + } + + @Override + public boolean visit(DartSuperExpression x, DartContext ctx) { + p("super"); + return false; + } + + @Override + public boolean visit(DartSuperConstructorInvocation x, DartContext ctx) { + p("super"); + if (x.getName() != null) { + p("."); + accept(x.getName()); + } + pArgs(x.getArgs()); + return false; + } + + @Override + public boolean visit(DartNewExpression x, DartContext ctx) { + p("new "); + accept(x.getConstructor()); + pArgs(x.getArgs()); + return false; + } + + @Override + public boolean visit(DartFunctionExpression x, DartContext ctx) { + DartFunction func = x.getFunction(); + if (func.getReturnTypeNode() != null) { + accept(func.getReturnTypeNode()); + p(" "); + } + DartIdentifier name = x.getName(); + if (name != null) { + if (func.getReturnTypeNode() == null) { + p("function "); + } + } else { + p("function"); + } + pFunctionDeclaration(name, x.getFunction()); + p(" "); + if (x.getFunction().getBody() != null) { + pBlock(x.getFunction().getBody(), false); + } + return false; + } + + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + + @Override + public boolean visit(DartNullLiteral x, DartContext ctx) { + p("null"); + return false; + } + + @Override + public boolean visit(DartRedirectConstructorInvocation x, DartContext ctx) { + p("this"); + if (x.getName() != null) { + p("."); + accept(x.getName()); + } + pArgs(x.getArgs()); + return false; + } + + @Override + public boolean visit(DartStringLiteral x, DartContext ctx) { + p("\""); + // 'replaceAll' takes regular expressions as first argument and parses the second argument + // for captured groups. We must escape backslashes twice: once to escape them in the source + // code and once for the regular expression parser. + String escaped = x.getValue().replaceAll("\\\\", "\\\\\\\\"); + escaped = escaped.replaceAll("\"", "\\\\\""); + escaped = escaped.replaceAll("'", "\\\\'"); + escaped = escaped.replaceAll("\\n", "\\\\n"); + // In the replacement string '$' is used to refer to captured groups. We have to escape the + // dollar. + escaped = escaped.replaceAll("\\$", "\\\\\\$"); + p(escaped); + p("\""); + return false; + } + + @Override + public boolean visit(DartStringInterpolation x, DartContext ctx) { + p("\""); + // do not use the default visitor recursion, instead alternate strings and + // expressions: + Iterator eIter = x.getExpressions().iterator(); + boolean first = true; + for (DartStringLiteral lit : x.getStrings()) { + if (first) { + first = false; + } else { + p("${"); + assert eIter.hasNext() : "DartStringInterpolation invariant broken."; + accept(eIter.next()); + p("}"); + } + p(lit.getValue().replaceAll("\"", "\\\"")); + } + p("\""); + return false; + } + + @Override + public boolean visit(DartBooleanLiteral x, DartContext ctx) { + p(Boolean.toString(x.getValue())); + return false; + } + + @Override + public boolean visit(DartIntegerLiteral x, DartContext ctx) { + p(x.getValue().toString()); + return false; + } + + @Override + public boolean visit(DartDoubleLiteral x, DartContext ctx) { + p(Double.toString(x.getValue())); + return false; + } + + @Override + public boolean visit(DartArrayLiteral x, DartContext ctx) { + p("["); + printSeparatedByComma(x.getExpressions()); + p("]"); + return false; + } + + @Override + public boolean visit(DartMapLiteral x, DartContext ctx) { + p("{"); + List entries = x.getEntries(); + for (int i = 0; i < entries.size(); ++i) { + DartMapLiteralEntry entry = entries.get(i); + accept(entry); + if (i < entries.size() - 1) { + p(", "); + } + } + p("}"); + return false; + } + + @Override + public boolean visit(DartMapLiteralEntry x, DartContext ctx) { + // Always quote keys just to be safe. This could be optimized to only quote + // unsafe identifiers. + accept(x.getKey()); + p(" : "); + accept(x.getValue()); + return false; + } + + @Override + public boolean visit(DartParameterizedNode x, DartContext ctx) { + accept(x.getExpression()); + p("<"); + printSeparatedByComma(x.getTypeParameters()); + p(">"); + return false; + } + + @Override + public boolean visit(DartParenthesizedExpression x, DartContext ctx) { + p("("); + accept(x.getExpression()); + p(")"); + return false; + } + + @Override + public boolean visit(DartNamedExpression x, DartContext ctx) { + accept(x.getName()); + p(":"); + accept(x.getExpression()); + return false; + } + + private void pAbstractField(DartFieldDefinition x, DartContext ctx) { + accept(x.getFields().get(0).getAccessor()); + } + + private void pIfBlock(DartStatement stmt, boolean newline) { + if (stmt instanceof DartBlock) { + pBlock((DartBlock) stmt, newline); + } else { + p("{"); + nl(); + indent(); + accept(stmt); + outdent(); + p("}"); + if (newline) { + nl(); + } + } + } + + private void printSeparatedByComma(List nodes) { + boolean first = true; + for (DartNode node : nodes) { + if (!first) { + p(", "); + } + accept(node); + first = false; + } + } + + private void pFieldModifiers(DartFieldDefinition field) { + Modifiers modifiers = field.getFields().get(0).getModifiers(); + if (modifiers.isStatic()) { + p("static "); + } + if (modifiers.isFinal()) { + p("final "); + } + } + + private void pMethodModifiers(DartMethodDefinition method) { + if (method.getModifiers().isConstant()) { + p("const "); + } + if (method.getModifiers().isStatic()) { + p("static "); + } + if (method.getModifiers().isAbstract()) { + p("abstract "); + } + if (method.getModifiers().isFactory()) { + p("factory "); + } + } + + private void p(String x) { + out.print(x); + } + + private void nl() { + out.newline(); + } + + private void indent() { + out.indentIn(); + } + + private void outdent() { + out.indentOut(); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartTryStatement.java b/compiler/java/com/google/dart/compiler/ast/DartTryStatement.java new file mode 100644 index 00000000000..2c0758d3675 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartTryStatement.java @@ -0,0 +1,62 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart 'try/catch' statement. + */ +public class DartTryStatement extends DartStatement { + + private DartBlock tryBlock; + private List catchBlocks; + private DartBlock finallyBlock; + + public DartTryStatement(DartBlock tryBlock, List catchBlocks, + DartBlock finallyBlock) { + this.tryBlock = becomeParentOf(tryBlock); + this.catchBlocks = becomeParentOf(catchBlocks); + this.finallyBlock = becomeParentOf(finallyBlock); + } + + public List getCatchBlocks() { + return catchBlocks; + } + + public DartBlock getFinallyBlock() { + return finallyBlock; + } + + public DartBlock getTryBlock() { + return tryBlock; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + tryBlock = becomeParentOf(v.accept(tryBlock)); + v.acceptWithInsertRemove(this, catchBlocks); + if (finallyBlock != null) { + finallyBlock = becomeParentOf(v.accept(finallyBlock)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + tryBlock.accept(visitor); + visitor.visit(catchBlocks); + if (finallyBlock != null) { + finallyBlock.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitTryStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartTypeExpression.java b/compiler/java/com/google/dart/compiler/ast/DartTypeExpression.java new file mode 100644 index 00000000000..4c788672b03 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartTypeExpression.java @@ -0,0 +1,39 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a type expression at the right hand side of an 'is'. + */ +public class DartTypeExpression extends DartExpression { + + private DartTypeNode typeNode; + + public DartTypeExpression(DartTypeNode type) { + this.typeNode = becomeParentOf(type); + } + + public DartTypeNode getTypeNode() { + return typeNode; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + typeNode = becomeParentOf(v.accept(typeNode)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + typeNode.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitTypeExpression(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartTypeNode.java b/compiler/java/com/google/dart/compiler/ast/DartTypeNode.java new file mode 100644 index 00000000000..58cf8b0f464 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartTypeNode.java @@ -0,0 +1,67 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.type.Type; + +import java.util.ArrayList; +import java.util.List; + +/** + * Representation of a Dart type name. + */ +public class DartTypeNode extends DartNode { + + private DartNode identifier; + private List typeArguments = new ArrayList(); + private Type type; + + public DartTypeNode(DartNode identifier) { + this(identifier, new ArrayList()); + } + + public DartTypeNode(DartNode identifier, List typeArguments) { + this.identifier = becomeParentOf(identifier); + this.typeArguments = becomeParentOf(typeArguments); + } + + public DartNode getIdentifier() { + return identifier; + } + + public List getTypeArguments() { + return typeArguments; + } + + @Override + public void setType(Type type) { + this.type = type; + } + + @Override + public Type getType() { + return type; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + identifier = becomeParentOf(v.accept(identifier)); + v.acceptWithInsertRemove(this, typeArguments); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + identifier.accept(visitor); + visitor.visit(typeArguments); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitTypeNode(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartTypeParameter.java b/compiler/java/com/google/dart/compiler/ast/DartTypeParameter.java new file mode 100644 index 00000000000..16a8d822270 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartTypeParameter.java @@ -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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.resolver.Element; + +/** + * Represents a type parameter in a class or interface declaration. + */ +public class DartTypeParameter extends DartDeclaration { + + private DartTypeNode bound; + + public DartTypeParameter(DartIdentifier name, DartTypeNode bound) { + super(name); + this.bound = becomeParentOf(bound); + } + + public DartTypeNode getBound() { + return bound; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (bound != null) { + bound = becomeParentOf(v.accept(bound)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (bound != null) { + bound.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitTypeParameter(this); + } + + @Override + public Element getSymbol() { + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartTypedLiteral.java b/compiler/java/com/google/dart/compiler/ast/DartTypedLiteral.java new file mode 100644 index 00000000000..dfc19d436e8 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartTypedLiteral.java @@ -0,0 +1,64 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; + +import java.util.List; + +public abstract class DartTypedLiteral extends DartExpression { + private List typeArguments; + private final boolean isConst; + private InterfaceType type; + + DartTypedLiteral(boolean isConst, List typeArguments) { + this.isConst = isConst; + setTypeArguments(typeArguments); + } + + public boolean isConst() { + return isConst; + } + + public void setTypeArguments(List typeArguments) { + if (typeArguments == null) { + typeArguments = Lists.newArrayList(); + } + this.typeArguments = becomeParentOf(typeArguments); + } + + /** + * @return a non-null list + */ + public List getTypeArguments() { + return typeArguments; + } + + @Override + public void setType(Type type) { + this.type = (InterfaceType) type; + } + + @Override + public InterfaceType getType() { + return type; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (typeArguments.size() > 0) { + v.acceptWithInsertRemove(this, typeArguments); + } + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (typeArguments.size() > 0) { + visitor.visit(getTypeArguments()); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartUnaryExpression.java b/compiler/java/com/google/dart/compiler/ast/DartUnaryExpression.java new file mode 100644 index 00000000000..570ed48cb10 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartUnaryExpression.java @@ -0,0 +1,82 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.Element; + +/** + * Represents a Dart unary expression. + */ +public class DartUnaryExpression extends DartExpression implements ElementReference { + + private final Token operator; + private DartExpression arg; + private final boolean isPrefix; + private DartExpression normalizedNode = this; + private Element referencedElement; + + public DartUnaryExpression(Token operator, DartExpression arg, boolean isPrefix) { + assert operator.isUnaryOperator() || operator == Token.SUB; + + this.isPrefix = isPrefix; + this.operator = operator; + this.arg = becomeParentOf(arg); + } + + public DartExpression getArg() { + return arg; + } + + public Token getOperator() { + return operator; + } + + public boolean isPrefix() { + return isPrefix; + } + + public void setNormalizedNode(DartExpression normalizedNode) { + normalizedNode.setSourceInfo(this); + this.normalizedNode = normalizedNode; + } + + @Override + public DartExpression getNormalizedNode() { + return normalizedNode; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (operator.isCountOperator()) { + arg = becomeParentOf(v.acceptLvalue(getArg())); + } else { + arg = becomeParentOf(v.accept(getArg())); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + arg.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitUnaryExpression(this); + } + + @Override + public Element getReferencedElement() { + return referencedElement; + } + + @Override + public void setReferencedElement(Element referencedElement) { + this.referencedElement = referencedElement; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartUnit.java b/compiler/java/com/google/dart/compiler/ast/DartUnit.java new file mode 100644 index 00000000000..f621a8918bf --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartUnit.java @@ -0,0 +1,152 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.util.DefaultTextOutput; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents a Dart compilation unit. + */ +public class DartUnit extends DartNode { + + private static final long serialVersionUID = -3407637869012712127L; + + private LibraryUnit library; + private List directives; + private final List topLevelNodes; + private final DartSource source; + /** A list of comments. May be null. */ + private List comments; + private boolean isDiet; + private String dietParse; + + public DartUnit(DartSource sourceName) { + this(sourceName, new ArrayList()); + } + + public DartUnit(DartSource source, + List nodes) { + this.source = source; + this.topLevelNodes = becomeParentOf(nodes); + } + + public void addTopLevelNode(DartNode node) { + topLevelNodes.add(becomeParentOf(node)); + } + + public String getSourceName() { + return source.getName(); + } + + @Override + public DartSource getSource() { + return source; + } + + public void addComment(DartComment comment) { + if (comments == null) { + comments = new ArrayList(); + } + comments.add(becomeParentOf(comment)); + } + + public List getComments() { + return comments == null ? null : Collections.unmodifiableList(comments); + } + + public boolean removeComment(DartComment comment) { + return comments == null ? false : comments.remove(comment); + } + + public void setLibrary(LibraryUnit library) { + this.library = library; + } + + public LibraryUnit getLibrary() { + return library; + } + + public List getTopLevelNodes() { + return topLevelNodes; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (directives != null) { + v.acceptWithInsertRemove(this, directives); + } + v.acceptWithInsertRemove(this, topLevelNodes); + if (comments != null) { + v.acceptWithInsertRemove(this, comments); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (directives != null) { + visitor.visit(directives); + } + visitor.visit(topLevelNodes); + if (comments != null) { + visitor.visit(comments); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitUnit(this); + } + + /** + * Sets this unit to be a diet unit, meaning it contains no method bodies. + */ + public void setDiet(boolean isDiet) { + this.isDiet = isDiet; + } + + /** + * Whether this is an diet unit, meaning it contains no method bodies. + */ + public boolean isDiet() { + return isDiet; + } + + /** + * Generates a diet version of this unit, which contains no method bodies. + */ + public final String toDietSource() { + if (dietParse == null) { + DefaultTextOutput out = new DefaultTextOutput(false); + new DartToSourceVisitor(out, true).accept(this); + dietParse = out.toString(); + } + return dietParse; + } + + /** + * Add the specified directive to the receiver's list of directives + */ + public void addDirective(DartDirective directive) { + if (directives == null) { + directives = new ArrayList(); + } + directives.add(becomeParentOf(directive)); + } + + /** + * Answer the receiver's directives or null if none + */ + public List getDirectives() { + return directives; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartUnqualifiedInvocation.java b/compiler/java/com/google/dart/compiler/ast/DartUnqualifiedInvocation.java new file mode 100644 index 00000000000..681052efef3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartUnqualifiedInvocation.java @@ -0,0 +1,46 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Unqualified function invocation AST node. + */ +public class DartUnqualifiedInvocation extends DartInvocation { + + private DartIdentifier target; + + public DartUnqualifiedInvocation(DartIdentifier target, + List args) { + super(args); + this.target = becomeParentOf(target); + } + + @Override + public DartIdentifier getTarget() { + return target; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + target = becomeParentOf(v.accept(target)); + v.acceptWithInsertRemove(this, getArgs()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + target.accept(visitor); + visitor.visit(getArgs()); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitUnqualifiedInvocation(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartVariable.java b/compiler/java/com/google/dart/compiler/ast/DartVariable.java new file mode 100644 index 00000000000..5ed960ec9ed --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartVariable.java @@ -0,0 +1,64 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.Element; + +/** + * Represents a single variable declaration in a {@link DartVariableStatement}. + */ +public class DartVariable extends DartDeclaration implements HasSymbol { + + private Element symbol; + + private DartExpression value; + + public DartVariable(DartIdentifier name, DartExpression value) { + super(name); + this.value = becomeParentOf(value); + } + + public String getVariableName() { + return getName().getTargetName(); + } + + @Override + public Element getSymbol() { + return symbol; + } + + public DartExpression getValue() { + return value; + } + + @Override + public void setSymbol(Symbol symbol) { + this.symbol = (Element) symbol; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (value != null) { + value = becomeParentOf(v.accept(value)); + } + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (value != null) { + value.accept(visitor); + } + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitVariable(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartVariableStatement.java b/compiler/java/com/google/dart/compiler/ast/DartVariableStatement.java new file mode 100644 index 00000000000..7d28b8eec6f --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartVariableStatement.java @@ -0,0 +1,63 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Represents a Dart 'var' statement. + */ +public class DartVariableStatement extends DartStatement { + + private final List vars; + private DartTypeNode typeNode; + private final Modifiers modifiers; + + public DartVariableStatement(List vars, DartTypeNode type) { + this(vars, type, Modifiers.NONE); + } + + public DartVariableStatement(List vars, DartTypeNode type, Modifiers modifiers) { + this.vars = becomeParentOf(vars); + this.typeNode = becomeParentOf(type); + this.modifiers = modifiers; + } + + public List getVariables() { + return vars; + } + + public DartTypeNode getTypeNode() { + return typeNode; + } + + public Modifiers getModifiers() { + return modifiers; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + if (typeNode != null) { + typeNode = becomeParentOf(v.accept(typeNode)); + } + v.acceptWithInsertRemove(this, getVariables()); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + if (typeNode != null) { + typeNode.accept(visitor); + } + visitor.visit(vars); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitVariableStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartVisitable.java b/compiler/java/com/google/dart/compiler/ast/DartVisitable.java new file mode 100644 index 00000000000..56e0ddafe9a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartVisitable.java @@ -0,0 +1,19 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Interface to be implemented by a class that can be visited by {@link DartVisitor}. + */ +public interface DartVisitable { + + /** + * Causes this object to have the visitor visit itself and its children. + * + * @param visitor the visitor that should traverse this node + * @param ctx the context of an existing traversal + */ + void traverse(DartVisitor v, DartContext ctx); +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartVisitor.java b/compiler/java/com/google/dart/compiler/ast/DartVisitor.java new file mode 100644 index 00000000000..97b7157b8f2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartVisitor.java @@ -0,0 +1,622 @@ +// 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. + +package com.google.dart.compiler.ast; + +import java.util.List; + +/** + * Base class that can be extended to visit all child nodes of a given root + * node. + */ +public class DartVisitor { + + protected static final DartContext LVALUE_CONTEXT = new DartContext() { + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(DartVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(DartVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return true; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(DartVisitable node) { + throw new UnsupportedOperationException(); + } + }; + + protected static final DartContext UNMODIFIABLE_CONTEXT = new DartContext() { + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(DartVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(DartVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return false; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(DartVisitable node) { + throw new UnsupportedOperationException(); + } + }; + + public final T accept(T node) { + return this.doAccept(node); + } + + public final void acceptList(List collection) { + doAcceptList(collection); + } + + public DartExpression acceptLvalue(DartExpression expr) { + return doAcceptLvalue(expr); + } + + public final List acceptWithInsertRemove( + DartNode parent, List collection) { + return doAcceptWithInsertRemove(parent, collection); + } + + public boolean didChange() { + throw new UnsupportedOperationException(); + } + + public void endVisit(DartArrayAccess x, DartContext ctx) { + } + + public void endVisit(DartArrayLiteral x, DartContext ctx) { + } + + public void endVisit(DartAssertion x, DartContext ctx) { + } + + public void endVisit(DartBinaryExpression x, DartContext ctx) { + } + + public void endVisit(DartBlock x, DartContext ctx) { + } + + public void endVisit(DartBooleanLiteral x, DartContext ctx) { + } + + public void endVisit(DartBreakStatement x, DartContext ctx) { + } + + public void endVisit(DartInvocation x, DartContext ctx) { + } + + public void endVisit(DartFunctionObjectInvocation x, DartContext ctx) { + } + + public void endVisit(DartMethodInvocation x, DartContext ctx) { + } + + public void endVisit(DartUnqualifiedInvocation x, DartContext ctx) { + } + + public void endVisit(DartSuperConstructorInvocation x, DartContext ctx) { + } + + public void endVisit(DartRedirectConstructorInvocation x, DartContext ctx) { + } + + public void endVisit(DartCase x, DartContext ctx) { + } + + public void endVisit(DartClass x, DartContext ctx) { + } + + public void endVisit(DartConditional x, DartContext ctx) { + } + + public void endVisit(DartContinueStatement x, DartContext ctx) { + } + + public void endVisit(DartDefault x, DartContext ctx) { + } + + public void endVisit(DartDoubleLiteral x, DartContext ctx) { + } + + public void endVisit(DartDoWhileStatement x, DartContext ctx) { + } + + public void endVisit(DartEmptyStatement x, DartContext ctx) { + } + + public void endVisit(DartExprStmt x, DartContext ctx) { + } + + public void endVisit(DartField x, DartContext ctx) { + } + + public void endVisit(DartFieldDefinition x, DartContext ctx) { + } + + public void endVisit(DartForInStatement x, DartContext ctx) { + } + + public void endVisit(DartForStatement x, DartContext ctx) { + } + + public void endVisit(DartFunction x, DartContext ctx) { + } + + public void endVisit(DartFunctionExpression x, DartContext ctx) { + } + + public void endVisit(DartFunctionTypeAlias node, DartContext ctx) { + } + + public void endVisit(DartIdentifier x, DartContext ctx) { + } + + public void endVisit(DartIfStatement x, DartContext ctx) { + } + + public void endVisit(DartImportDirective x, DartContext ctx) { + } + + public void endVisit(DartInitializer dartInitializer, DartContext ctx) { + } + + public void endVisit(DartIntegerLiteral x, DartContext ctx) { + } + + public void endVisit(DartLabel x, DartContext ctx) { + } + + public void endVisit(DartLibraryDirective x, DartContext ctx) { + } + + public void endVisit(DartMapLiteral x, DartContext ctx) { + } + + public void endVisit(DartMapLiteralEntry x, DartContext ctx) { + } + + public void endVisit(DartMethodDefinition x, DartContext ctx) { + } + + public void endVisit(DartNativeBlock x, DartContext ctx) { + } + + public void endVisit(DartNativeDirective x, DartContext ctx) { + } + + public void endVisit(DartNewExpression x, DartContext ctx) { + } + + public void endVisit(DartNullLiteral x, DartContext ctx) { + } + + public void endVisit(DartParameter x, DartContext ctx) { + } + + public void endVisit(DartParameterizedNode x, DartContext ctx) { + } + + public void endVisit(DartParenthesizedExpression x, DartContext ctx) { + } + + public void endVisit(DartPropertyAccess x, DartContext ctx) { + } + + public void endVisit(DartResourceDirective x, DartContext ctx) { + } + + public void endVisit(DartReturnStatement x, DartContext ctx) { + } + + public void endVisit(DartSourceDirective x, DartContext ctx) { + } + + public void endVisit(DartStringLiteral x, DartContext ctx) { + } + + public void endVisit(DartStringInterpolation x, DartContext ctx) { + } + + public void endVisit(DartSuperExpression x, DartContext ctx) { + } + + public void endVisit(DartSwitchStatement x, DartContext ctx) { + } + + public void endVisit(DartSyntheticErrorExpression node, DartContext ctx) { + } + + public void endVisit(DartSyntheticErrorStatement x, DartContext ctx) { + } + + public void endVisit(DartThisExpression x, DartContext ctx) { + } + + public void endVisit(DartThrowStatement x, DartContext ctx) { + } + + public void endVisit(DartCatchBlock x, DartContext ctx) { + } + + public void endVisit(DartTryStatement x, DartContext ctx) { + } + + public void endVisit(DartTypeNode x, DartContext ctx) { + } + + public void endVisit(DartTypeParameter x, DartContext ctx) { + } + + public void endVisit(DartUnaryExpression x, DartContext ctx) { + } + + public void endVisit(DartUnit x, DartContext ctx) { + } + + public void endVisit(DartVariable x, DartContext ctx) { + } + + public void endVisit(DartVariableStatement x, DartContext ctx) { + } + + public void endVisit(DartWhileStatement x, DartContext ctx) { + } + + public void endVisit(DartNamedExpression x, DartContext ctx) { + } + + public void endVisit(DartTypeExpression x, DartContext ctx) { + } + + public boolean visit(DartArrayAccess x, DartContext ctx) { + return true; + } + + public boolean visit(DartArrayLiteral x, DartContext ctx) { + return true; + } + + public boolean visit(DartAssertion x, DartContext ctx) { + return true; + } + + public boolean visit(DartBinaryExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartBlock x, DartContext ctx) { + return true; + } + + public boolean visit(DartBooleanLiteral x, DartContext ctx) { + return true; + } + + public boolean visit(DartBreakStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartInvocation x, DartContext ctx) { + return true; + } + + public boolean visit(DartFunctionObjectInvocation x, DartContext ctx) { + return true; + } + + public boolean visit(DartMethodInvocation x, DartContext ctx) { + return true; + } + + public boolean visit(DartUnqualifiedInvocation x, DartContext ctx) { + return true; + } + + public boolean visit(DartSuperConstructorInvocation x, DartContext ctx) { + return true; + } + + public boolean visit(DartRedirectConstructorInvocation x, DartContext ctx) { + return true; + } + + public boolean visit(DartCase x, DartContext ctx) { + return true; + } + + public boolean visit(DartClass x, DartContext ctx) { + return true; + } + + public boolean visit(DartConditional x, DartContext ctx) { + return true; + } + + public boolean visit(DartContinueStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartDefault x, DartContext ctx) { + return true; + } + + public boolean visit(DartDoubleLiteral x, DartContext ctx) { + return true; + } + + public boolean visit(DartDoWhileStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartEmptyStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartExprStmt x, DartContext ctx) { + return true; + } + + public boolean visit(DartField x, DartContext ctx) { + return true; + } + + public boolean visit(DartFieldDefinition x, DartContext ctx) { + return true; + } + + public boolean visit(DartForInStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartForStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartFunction x, DartContext ctx) { + return true; + } + + public boolean visit(DartFunctionExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartFunctionTypeAlias node, DartContext ctx) { + return true; + } + + public boolean visit(DartIdentifier x, DartContext ctx) { + return true; + } + + public boolean visit(DartIfStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartImportDirective x, DartContext ctx) { + return true; + } + + public boolean visit(DartInitializer x, DartContext ctx) { + return true; + } + + public boolean visit(DartIntegerLiteral x, DartContext ctx) { + return true; + } + + public boolean visit(DartLabel x, DartContext ctx) { + return true; + } + + public boolean visit(DartLibraryDirective x, DartContext ctx) { + return true; + } + + public boolean visit(DartMapLiteral x, DartContext ctx) { + return true; + } + + public boolean visit(DartMapLiteralEntry x, DartContext ctx) { + return true; + } + + public boolean visit(DartMethodDefinition x, DartContext ctx) { + return true; + } + + public boolean visit(DartNativeBlock x, DartContext ctx) { + return true; + } + + public boolean visit(DartNativeDirective x, DartContext ctx) { + return true; + } + + public boolean visit(DartNewExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartNullLiteral x, DartContext ctx) { + return true; + } + + public boolean visit(DartParameter x, DartContext ctx) { + return true; + } + + public boolean visit(DartParameterizedNode x, DartContext ctx) { + return true; + } + + public boolean visit(DartParenthesizedExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartPropertyAccess x, DartContext ctx) { + return true; + } + + public boolean visit(DartResourceDirective x, DartContext ctx) { + return true; + } + + public boolean visit(DartReturnStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartSourceDirective x, DartContext ctx) { + return true; + } + + public boolean visit(DartStringLiteral x, DartContext ctx) { + return true; + } + + public boolean visit(DartStringInterpolation x, DartContext ctx) { + return true; + } + + public boolean visit(DartSuperExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartSwitchStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartSyntheticErrorExpression node, DartContext ctx) { + return true; + } + + public boolean visit(DartSyntheticErrorStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartThisExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartThrowStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartCatchBlock x, DartContext ctx) { + return true; + } + + public boolean visit(DartTryStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartTypeNode x, DartContext ctx) { + return true; + } + + public boolean visit(DartTypeParameter x, DartContext ctx) { + return true; + } + + public boolean visit(DartUnaryExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartUnit x, DartContext ctx) { + return true; + } + + public boolean visit(DartVariable x, DartContext ctx) { + return true; + } + + public boolean visit(DartVariableStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartWhileStatement x, DartContext ctx) { + return true; + } + + public boolean visit(DartNamedExpression x, DartContext ctx) { + return true; + } + + public boolean visit(DartTypeExpression x, DartContext ctx) { + return true; + } + + protected T doAccept(T node) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + return node; + } + + protected void doAcceptList(List collection) { + for (DartVisitable node : collection) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + } + } + + protected DartExpression doAcceptLvalue(DartExpression expr) { + doTraverse(expr, LVALUE_CONTEXT); + return expr; + } + + protected List doAcceptWithInsertRemove( + DartNode parent, List collection) { + for (T node : collection) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + } + return collection; + } + + protected void doTraverse(DartVisitable node, DartContext ctx) { + node.traverse(this, ctx); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/DartWhileStatement.java b/compiler/java/com/google/dart/compiler/ast/DartWhileStatement.java new file mode 100644 index 00000000000..136fd637955 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/DartWhileStatement.java @@ -0,0 +1,47 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Represents a Dart 'while' statement. + */ +public class DartWhileStatement extends DartStatement { + + private DartExpression condition; + private DartStatement body; + + public DartWhileStatement(DartExpression condition, DartStatement body) { + this.condition = becomeParentOf(condition); + this.body = becomeParentOf(body); + } + + public DartStatement getBody() { + return body; + } + + public DartExpression getCondition() { + return condition; + } + + @Override + public void traverse(DartVisitor v, DartContext ctx) { + if (v.visit(this, ctx)) { + condition = becomeParentOf(v.accept(condition)); + body = becomeParentOf(v.accept(body)); + } + v.endVisit(this, ctx); + } + + @Override + public void visitChildren(DartPlainVisitor visitor) { + condition.accept(visitor); + body.accept(visitor); + } + + @Override + public R accept(DartPlainVisitor visitor) { + return visitor.visitWhileStatement(this); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/ElementReference.java b/compiler/java/com/google/dart/compiler/ast/ElementReference.java new file mode 100644 index 00000000000..393ebc989bb --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/ElementReference.java @@ -0,0 +1,19 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.resolver.Element; + +/** + * An {@link ElementReference} is an AST node that references an element, which is + * used to calculate its type. + * For example, a method invocation references the {@link MethodElement} that the call + * resolves to. + */ +public interface ElementReference { + public Element getReferencedElement(); + + public void setReferencedElement(Element element); +} diff --git a/compiler/java/com/google/dart/compiler/ast/LibraryNode.java b/compiler/java/com/google/dart/compiler/ast/LibraryNode.java new file mode 100644 index 00000000000..b9f7c9a2133 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/LibraryNode.java @@ -0,0 +1,42 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.common.AbstractNode; + +/** + * An element in a library or application manifest + * + * TODO(jgw): This class works with both JSON and the new library syntax. It can be greatly + * simplified once support for the JSON syntax is removed. + */ +public class LibraryNode extends AbstractNode { + + private final String text; + private final String prefix; + + /** + * Construct a new library node instance + * + * @param manifest the library manifest declaration (not null) + * @param text the text comprising the node (not null) + */ + public LibraryNode(String text) { + this(text, null); + } + + public LibraryNode(String text, String prefix) { + this.text = text; + this.prefix = prefix; + } + + public String getText() { + return text; + } + + public String getPrefix() { + return prefix; + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/LibraryUnit.java b/compiler/java/com/google/dart/compiler/ast/LibraryUnit.java new file mode 100644 index 00000000000..a5f4e33fb21 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/LibraryUnit.java @@ -0,0 +1,451 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.common.io.CharStreams; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibraryDeps; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.metrics.DartEventType; +import com.google.dart.compiler.metrics.Tracer; +import com.google.dart.compiler.metrics.Tracer.TraceEvent; +import com.google.dart.compiler.parser.DartParser; +import com.google.dart.compiler.parser.DartScannerParserContext; +import com.google.dart.compiler.resolver.Elements; +import com.google.dart.compiler.resolver.LibraryElement; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListMap; + +/** + * Represents the parsed source from a {@link LibrarySource}. + */ +public class LibraryUnit { + + // This is intentionally unparseable as Dart. + private static final String UNIT_SEPARATOR = "--- unit: "; + + private final LibrarySource libSource; + private final LibraryNode selfSourcePath; + private final Collection importPaths = new ArrayList(); + private final Collection sourcePaths = new ArrayList(); + private final Collection resourcePaths = new ArrayList(); + private final Collection nativePaths = new ArrayList(); + + private final Map units = new ConcurrentSkipListMap(); + private final Collection imports = new ArrayList(); + private final Map prefixes = new HashMap(); + + private final LibraryElement element; + + private Map topLevelNodes; + private LibraryDeps deps; + + private LibraryNode entryNode; + private DartUnit selfDartUnit; + + private String name; + + private DartExpression entryPoint; + + private int sourceCount; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public LibraryUnit(LibrarySource libSource) { + assert libSource != null; + this.libSource = libSource; + element = Elements.libraryElement(this); + + // get the name part of the path, since it needs to be relative + // TODO(jbrosenberg): change this to use lazy init + // Note: We don't want an encoded relative path. + String self = libSource.getUri().getSchemeSpecificPart(); + int lastSlash; + if ((lastSlash = self.lastIndexOf('/')) > -1) { + self = self.substring(lastSlash + 1); + } + selfSourcePath = new LibraryNode(self); + } + + public void addImportPath(LibraryNode path) { + assert path != null; + importPaths.add(path); + } + + public void addSourcePath(LibraryNode path) { + assert path != null; + sourcePaths.add(path); + sourceCount++; + } + + public void addResourcePath(LibraryNode path) { + assert path != null; + resourcePaths.add(path); + } + + public int getSourceCount() { + return sourceCount; + } + + public void addNativePath(LibraryNode path) { + assert path != null; + nativePaths.add(path); + } + + public void putUnit(DartUnit unit) { + unit.setLibrary(this); + units.put(unit.getSourceName(), unit); + } + + public DartUnit getUnit(String sourceName) { + return units.get(sourceName); + } + + public void addImport(LibraryUnit unit, LibraryNode node) { + imports.add(unit); + if (node != null && node.getPrefix() != null) { + prefixes.put(unit, node.getPrefix()); + } + } + + public String getPrefixOf(LibraryUnit library) { + return prefixes.get(library); + } + + public LibraryElement getElement() { + return element; + } + + public Iterable getUnits() { + return units.values(); + } + + public Iterable getImports() { + return imports; + } + + public boolean hasImport(LibraryUnit unit) { + return imports.contains(unit); + } + + public DartExpression getEntryPoint() { + return entryPoint; + } + + public void setEntryPoint(DartExpression entry) { + this.entryPoint = entry; + } + + public DartUnit getSelfDartUnit() { + return this.selfDartUnit; + } + + public void setSelfDartUnit(DartUnit unit) { + this.selfDartUnit = unit; + } + + /** + * Return a collection of paths to {@link LibrarySource}s upon which this + * library or application depends. + * + * @return the paths (not null, contains no null) + */ + public Iterable getImportPaths() { + return importPaths; + } + + /** + * Return all prefixes used by this library. + */ + public Set getPrefixes() { + return new HashSet(prefixes.values()); + } + + /** + * Return the path for dart source that corresponds to the same dart file as + * this library unit. This is added to the set of sourcePaths for this unit. + * + * @return the self source path for this unit. + */ + public LibraryNode getSelfSourcePath() { + return selfSourcePath; + } + + /** + * Answer the source associated with this unit + * + * @return the library source (not null) + */ + public LibrarySource getSource() { + return libSource; + } + + /** + * Return a collection of paths to {@link DartSource}s that are included in + * this library or application. + * + * @return the paths (not null, contains no null) + */ + public Iterable getSourcePaths() { + return sourcePaths; + } + + /** + * Return a collection of paths to resources that are included in + * this library or application. + * + * @return the paths (not null, contains no null) + */ + public Iterable getResourcePaths() { + return resourcePaths; + } + + /** + * Returns a collection of paths to native {@link DartSource}s that are included in this library. + * + * @return the paths (not null, contains no null) + */ + public Iterable getNativePaths() { + return nativePaths; + } + + /** + * Loads this library's associated api. If the api file exists, this will result in the library + * being populated with "diet" units (i.e., {@link DartUnit#isDiet()} will return + * true). + * + * @return true if the library was loaded from its api + */ + public boolean loadApi(DartCompilerContext context, DartCompilerListener listener) + throws IOException { + TraceEvent parseEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.PARSE_API, "src", getSource().getUri() + .toString()) : null; + try { + final Reader r = context.getArtifactReader(libSource, "", DartCompiler.EXTENSION_API); + if (r == null) { + return false; + } + + // Read the API file. + String srcCode = CharStreams.toString(r); + r.close(); + + // Split it up by unit. + int idx = srcCode.indexOf(UNIT_SEPARATOR); + while (idx != -1) { + idx += UNIT_SEPARATOR.length(); + int endIdx = srcCode.indexOf('\n', idx); + String unitName = srcCode.substring(idx, endIdx); + idx = endIdx; + + endIdx = srcCode.indexOf(UNIT_SEPARATOR, idx); + if (endIdx != -1) { + parseApiUnit(unitName, srcCode.substring(idx, endIdx), libSource, listener); + } else { + parseApiUnit(unitName, srcCode.substring(idx, srcCode.length()), libSource, listener); + } + idx = endIdx; + } + + return true; + } finally { + Tracer.end(parseEvent); + } + } + + /** + * Saves this library's contents to its associated api file. + */ + public void saveApi(DartCompilerContext context) throws IOException { + Writer w = context.getArtifactWriter(libSource, "", DartCompiler.EXTENSION_API); + for (String unitName : units.keySet()) { + w.write(UNIT_SEPARATOR + unitName + "\n"); + w.write(units.get(unitName).toDietSource()); + } + w.close(); + } + + /** + * Populates this unit's class map. This can be called only once per unit, and must be called + * before {@link #getTopLevelNode(String)} and {@link #getTopLevelNodes()}. + */ + public void populateTopLevelNodes() { + assert topLevelNodes == null; + topLevelNodes = new HashMap(); + + DartNodeTraverser visitor = new DartNodeTraverser() { + @Override + public Void visitClass(DartClass node) { + topLevelNodes.put(node.getClassName(), node); + return null; + } + + @Override + public Void visitMethodDefinition(DartMethodDefinition node) { + // Method names are always identifiers, except for factories, which cannot appear + // in this context. + DartIdentifier name = (DartIdentifier) node.getName(); + topLevelNodes.put(name.getTargetName(), node); + return null; + } + + @Override + public Void visitField(DartField node) { + topLevelNodes.put(node.getName().getTargetName(), node); + return null; + } + }; + + for (DartUnit unit : units.values()) { + visitor.visitUnit(unit); + } + } + + /** + * Get an unmodifiable collection of the classes in this library. You must call + * {@link #populateTopLevelNodes()} before this method will work. + */ + public Collection getTopLevelNodes() { + return Collections.unmodifiableCollection(topLevelNodes.values()); + } + + /** + * Gets the {@link DartClass} associated with the given name. You must call + * {@link #populateTopLevelNodes()} before this method will work. + */ + public DartNode getTopLevelNode(String name) { + assert topLevelNodes != null; + return topLevelNodes.get(name); + } + + /** + * Return the declared entry method, if any + * + * @return the entry method or null if not defined + */ + public LibraryNode getEntryNode() { + return entryNode; + } + + /** + * Set the declared entry method. + * + * @param libraryNode the entry method or null if none + */ + public void setEntryNode(LibraryNode libraryNode) { + this.entryNode = libraryNode; + } + + /** + * Gets the dependencies associated with this library. If no dependencies artifact exists, + * or the file is invalid, it will return an empty deps object. + */ + public LibraryDeps getDeps(DartCompilerContext context) throws IOException { + if (deps != null) { + return deps; + } + + Reader reader = context.getArtifactReader(libSource, "", DartCompiler.EXTENSION_DEPS); + if (reader != null) { + deps = LibraryDeps.fromReader(reader); + reader.close(); + } + + if (deps == null) { + deps = new LibraryDeps(); + } + return deps; + } + + /** + * Writes this library's associated dependencies. + */ + public void writeDeps(DartCompilerContext context) throws IOException { + Writer writer = context.getArtifactWriter(libSource, "", DartCompiler.EXTENSION_DEPS); + deps.write(writer); + writer.close(); + } + + private void parseApiUnit(final String unitName, String srcCode, final LibrarySource libSrc, + DartCompilerListener listener) { + // Dummy source for the api unit. + DartSource src = new DartSource() { + @Override + public LibrarySource getLibrary() { + return libSrc; + } + + @Override + public String getName() { + return unitName; + } + + @Override + public Reader getSourceReader() { + return null; + } + + @Override + public URI getUri() { + return URI.create(unitName); + } + + @Override + public boolean exists() { + return true; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public String getRelativePath() { + return unitName; + } + }; + + DartScannerParserContext parserContext = new DartScannerParserContext(src, srcCode, listener); + DartParser parser = new DartParser(parserContext); + DartUnit unit = parser.parseUnit(src); + + // When parsing from an API file, generate and store the hash for top level + // classes while we have the string available. Reduces the time needed to + // recompute this later with a visitor. + for (DartNode node : unit.getTopLevelNodes()) { + if (node instanceof DartClass) { + SourceInfo nodeInfo = node.getSourceInfo(); + String nodeString = srcCode.substring(nodeInfo.getSourceStart(), + nodeInfo.getSourceStart()+nodeInfo.getSourceLength()); + ((DartClass)node).setHash(nodeString.hashCode()); + } + } + unit.setDiet(true); + putUnit(unit); + } +} diff --git a/compiler/java/com/google/dart/compiler/ast/Modifiers.java b/compiler/java/com/google/dart/compiler/ast/Modifiers.java new file mode 100644 index 00000000000..8306b48cf11 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/ast/Modifiers.java @@ -0,0 +1,102 @@ +// 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. + +package com.google.dart.compiler.ast; + +/** + * Methods for working with modifier bits on various nodes. + */ +public class Modifiers { + + public static final Modifiers NONE = new Modifiers(); + + // Sorting this list would be confusing when adding new modifiers, + // as each one depends on the previously declared one. + // TODO(ngeoffray): Make this an enum. + private static final int FLAG_STATIC = 1; + private static final int FLAG_CONSTANT = FLAG_STATIC << 1; + private static final int FLAG_FACTORY = FLAG_CONSTANT << 1; + private static final int FLAG_ABSTRACT = FLAG_FACTORY << 1; + private static final int FLAG_GETTER = FLAG_ABSTRACT << 1; + private static final int FLAG_SETTER = FLAG_GETTER << 1; + private static final int FLAG_OPERATOR = FLAG_SETTER << 1; + private static final int FLAG_NATIVE = FLAG_OPERATOR << 1; + private static final int FLAG_INLINABLE = FLAG_NATIVE << 1; + private static final int FLAG_VARIADIC = FLAG_INLINABLE << 1; + private static final int FLAG_ABSTRACTFIELD = FLAG_VARIADIC << 1; + private static final int FLAG_REDIRECTEDCONSTRUCTOR = FLAG_ABSTRACTFIELD << 1; + private static final int FLAG_FINAL = FLAG_REDIRECTEDCONSTRUCTOR << 1; + private static final int FLAG_NAMED = FLAG_FINAL << 1; + + private final int value; + + public boolean isStatic() { return is(FLAG_STATIC); } + public boolean isConstant() { return is(FLAG_CONSTANT); } + public boolean isFactory() { return is(FLAG_FACTORY); } + public boolean isAbstract() { return is(FLAG_ABSTRACT); } + public boolean isGetter() { return is(FLAG_GETTER); } + public boolean isSetter() { return is(FLAG_SETTER); } + public boolean isOperator() { return is(FLAG_OPERATOR); } + public boolean isNative() { return is(FLAG_NATIVE); } + public boolean isInlinable() { return is(FLAG_INLINABLE); } + public boolean isVariadic() { return is(FLAG_VARIADIC); } + public boolean isAbstractField() { return is(FLAG_ABSTRACTFIELD); } + public boolean isRedirectedConstructor() { return is(FLAG_REDIRECTEDCONSTRUCTOR); } + public boolean isFinal() { return is(FLAG_FINAL); } + public boolean isNamed() { return is(FLAG_NAMED); } + + public Modifiers makeStatic() { return make(FLAG_STATIC); } + public Modifiers makeConstant() { return make(FLAG_CONSTANT); } + public Modifiers makeFactory() { return make(FLAG_FACTORY); } + public Modifiers makeAbstract() { return make(FLAG_ABSTRACT); } + public Modifiers makeGetter() { return make(FLAG_GETTER); } + public Modifiers makeSetter() { return make(FLAG_SETTER); } + public Modifiers makeOperator() { return make(FLAG_OPERATOR); } + public Modifiers makeNative() { return make(FLAG_NATIVE); } + public Modifiers makeInlinable() { return make(FLAG_INLINABLE); } + public Modifiers makeVariadic() { return make(FLAG_VARIADIC); } + public Modifiers makeAbstractField() { return make(FLAG_ABSTRACTFIELD); } + public Modifiers makeRedirectedConstructor() { return make(FLAG_REDIRECTEDCONSTRUCTOR); } + public Modifiers makeFinal() { return make(FLAG_FINAL); } + public Modifiers makeNamed() { return make(FLAG_NAMED); } + + public Modifiers removeStatic() { return remove(FLAG_STATIC); } + public Modifiers removeConstant() { return remove(FLAG_CONSTANT); } + public Modifiers removeFactory() { return remove(FLAG_FACTORY); } + public Modifiers removeAbstract() { return remove(FLAG_ABSTRACT); } + public Modifiers removeGetter() { return remove(FLAG_GETTER); } + public Modifiers removeSetter() { return remove(FLAG_SETTER); } + public Modifiers removeOperator() { return remove(FLAG_OPERATOR); } + public Modifiers removeNative() { return remove(FLAG_NATIVE); } + public Modifiers removeInlinable() { return remove(FLAG_INLINABLE); } + public Modifiers removeVariadic() { return remove(FLAG_VARIADIC); } + public Modifiers removeAbstractField() { return remove(FLAG_ABSTRACTFIELD); } + public Modifiers removeRedirectedConstructor() { return remove(FLAG_REDIRECTEDCONSTRUCTOR); } + public Modifiers removeFinal() { return remove(FLAG_FINAL); } + public Modifiers removeNamed() { return remove(FLAG_NAMED); } + + public boolean is(int flag) { + return (value & flag) != 0; + } + + public boolean is(Modifiers modifier) { + return is(modifier.value); + } + + public Modifiers make(int flag) { + return new Modifiers(value | flag); + } + + public Modifiers remove(int flag) { + return new Modifiers(value & ~flag); + } + + private Modifiers() { + this.value = 0; + } + + private Modifiers(int value) { + this.value = value; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/common/AbstractBackend.java b/compiler/java/com/google/dart/compiler/backend/common/AbstractBackend.java new file mode 100644 index 00000000000..9779abab71a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/common/AbstractBackend.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler.backend.common; + +import com.google.dart.compiler.Backend; + +/** + * Implementations of methods common to all the backends. + * + * @author johnlenz@google.com (John Lenz) + */ +public abstract class AbstractBackend implements Backend { +} diff --git a/compiler/java/com/google/dart/compiler/backend/common/TypeHeuristic.java b/compiler/java/com/google/dart/compiler/backend/common/TypeHeuristic.java new file mode 100644 index 00000000000..de304193ea7 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/common/TypeHeuristic.java @@ -0,0 +1,47 @@ +// 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. + +package com.google.dart.compiler.backend.common; + +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.type.DynamicType; +import com.google.dart.compiler.type.Type; + +import java.util.Set; + +/** + * Interface for providing information about the types of expressions to backends. + */ +public interface TypeHeuristic { + + public enum FieldKind { + GETTER, SETTER + } + + /** + * Provides the list of all possible types of the given expression. This list may be used for + * optimization, and must therefore include all possible types of the given expression. If the + * type is unknown, it must return a list containing a single {@link DynamicType}. + */ + public abstract Set getTypesOf(DartExpression expr); + + /** + * Returns true if types is <'dynamic'> or the set contains more than one type. false otherwise. + */ + public abstract boolean isDynamic(Set types); + + /** + * Returns the set of method implementations for a given expression. + */ + public abstract Set getImplementationsOf(DartExpression expr); + + /** + * Returns the set of field implementations for a given expression. + */ + public abstract Set getFieldImplementationsOf(DartExpression expr, + FieldKind asGetter); + +} diff --git a/compiler/java/com/google/dart/compiler/backend/common/TypeHeuristicImplementation.java b/compiler/java/com/google/dart/compiler/backend/common/TypeHeuristicImplementation.java new file mode 100644 index 00000000000..c0cd8e4c4f5 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/common/TypeHeuristicImplementation.java @@ -0,0 +1,1165 @@ +// 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. + +package com.google.dart.compiler.backend.common; + +import com.google.common.collect.Sets; +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartAssertion; +import com.google.dart.compiler.ast.DartBinaryExpression; +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.DartConditional; +import com.google.dart.compiler.ast.DartContinueStatement; +import com.google.dart.compiler.ast.DartDefault; +import com.google.dart.compiler.ast.DartDoWhileStatement; +import com.google.dart.compiler.ast.DartDoubleLiteral; +import com.google.dart.compiler.ast.DartEmptyStatement; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIfStatement; +import com.google.dart.compiler.ast.DartImportDirective; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartLibraryDirective; +import com.google.dart.compiler.ast.DartLiteral; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMapLiteralEntry; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNamedExpression; +import com.google.dart.compiler.ast.DartNativeBlock; +import com.google.dart.compiler.ast.DartNativeDirective; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNullLiteral; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartParameterizedNode; +import com.google.dart.compiler.ast.DartParenthesizedExpression; +import com.google.dart.compiler.ast.DartPlainVisitor; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; +import com.google.dart.compiler.ast.DartResourceDirective; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartSourceDirective; +import com.google.dart.compiler.ast.DartStringInterpolation; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartSuperConstructorInvocation; +import com.google.dart.compiler.ast.DartSuperExpression; +import com.google.dart.compiler.ast.DartSwitchStatement; +import com.google.dart.compiler.ast.DartSyntheticErrorExpression; +import com.google.dart.compiler.ast.DartSyntheticErrorStatement; +import com.google.dart.compiler.ast.DartThisExpression; +import com.google.dart.compiler.ast.DartThrowStatement; +import com.google.dart.compiler.ast.DartTryStatement; +import com.google.dart.compiler.ast.DartTypeExpression; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartUnqualifiedInvocation; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.DartVariableStatement; +import com.google.dart.compiler.ast.DartWhileStatement; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.resolver.VariableElement; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.InterfaceType.Member; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; +import com.google.dart.compiler.type.Types; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class TypeHeuristicImplementation implements TypeHeuristic { + + private ExpressionTypeInfo typeInfo; + private final Set dynTypes; + + public TypeHeuristicImplementation(DartUnit unit, CoreTypeProvider typeProvider) { + typeInfo = TypeInfoVisitor.computeTypeInfo(unit, typeProvider); + dynTypes = Sets. newHashSet(typeProvider.getDynamicType()); + } + + @Override + public Set getTypesOf(DartExpression expr) { + Set types = typeInfo.getTypeSets().get(expr.getNormalizedNode()); + if (types != null) { + return types; + } + return dynTypes; + } + + @Override + public boolean isDynamic(Set types) { + return (types == dynTypes || (types.size() > 1) || TypeKind.of(types.iterator().next()) + .equals(TypeKind.DYNAMIC)); + } + + @Override + public Set getImplementationsOf(DartExpression expr) { + return typeInfo.getMethodImpl().get(expr.getNormalizedNode()); + } + + @Override + public Set getFieldImplementationsOf(DartExpression expr, FieldKind fieldKind) { + Set fields = null; + if (fieldKind == FieldKind.GETTER) { + fields = typeInfo.getGettersImpl().get(expr.getNormalizedNode()); + } else { + fields = typeInfo.getSettersImpl().get(expr.getNormalizedNode()); + } + assert assertFieldsMatch(fields, fieldKind); + return fields; + } + + public static Element maybeGetTargetElement(DartExpression expr) { + return maybeGetTargetElement(expr, null); + } + + private static Element maybeGetTargetElement(DartNode dartNode, Set elements) { + Element element = null; + String propName = null; + elements = Sets.newHashSet(); + if (dartNode instanceof DartPropertyAccess) { + DartPropertyAccess propAccess = (DartPropertyAccess) dartNode; + propName = propAccess.getPropertyName(); + element = maybeGetTargetElement(propAccess.getQualifier(), elements); + } else if (dartNode instanceof DartIdentifier) { + element = ((DartIdentifier) dartNode).getTargetSymbol(); + if (ElementKind.of(element).equals(ElementKind.FIELD)) { + propName = element.getName(); + element = element.getEnclosingElement(); + } else { + return element; + } + } else if (dartNode instanceof DartArrayAccess) { + return maybeGetTargetElement(((DartArrayAccess) dartNode).getTarget()); + } + if (element != null) { + if (TypeKind.of(element.getType()).equals(TypeKind.INTERFACE)) { + InterfaceType iType = (InterfaceType) element.getType(); + Member member = iType.lookupMember(propName); + if (member != null) { + element = member.getElement(); + elements.add(element); + } + ClassElement classElement = iType.getElement(); + for (InterfaceType subType : classElement.getSubtypes()) { + member = subType.lookupMember(propName); + if (member != null) { + elements.add(member.getElement()); + } + } + if (elements.size() != 1) { + return null; + } + } else { + // TypeKind (DYNAMIC, NONE, FUNCTION, ...) + return null; + } + } + return element; + } + + private static boolean assertFieldsMatch(Set fields, FieldKind fieldKind) { + if (fields == null) { + return true; + } + boolean allMatch = true; + for (FieldElement fieldElement : fields) { + boolean singleMatch; + Modifiers modifiers = fieldElement.getModifiers(); + if (modifiers.isAbstractField()) { + singleMatch = fieldKind == FieldKind.GETTER ? fieldElement.getGetter() != null + : fieldElement.getSetter() != null; + } else { + singleMatch = true; + } + allMatch &= singleMatch; + } + return allMatch; + } + + private static class TypeInfoVisitor implements DartPlainVisitor { + + private final ExpressionTypeInfo typeInfo; + private final CoreTypeProvider typeProvider; + private final Types typeUtils; + private InterfaceType currentClass; + Set visitedConstants; + + public static ExpressionTypeInfo computeTypeInfo(DartUnit unit, CoreTypeProvider typeProvider) { + TypeInfoVisitor typeInfoVisitor = new TypeInfoVisitor(typeProvider); + typeInfoVisitor.visitUnit(unit); + return typeInfoVisitor.typeInfo; + } + + private TypeInfoVisitor(CoreTypeProvider typeProvider) { + this.typeProvider = typeProvider; + this.typeUtils = Types.getInstance(typeProvider); + this.typeInfo = ExpressionTypeInfo.create(); + } + + @Override + public Type visitUnit(DartUnit node) { + visitedConstants = Sets.newHashSet(); + Type type = visitChildrenAndReturnVoid(node); + visitedConstants = null; + return type; + } + + @Override + public Type visitClass(DartClass node) { + beginClassContext(node); + visitChildren(node); + endClassContext(); + return dynamicType(); + } + + private void beginClassContext(DartClass node) { + currentClass = node.getSymbol().getType(); + } + + private void endClassContext() { + currentClass = null; + } + + @Override + public Type visitThisExpression(DartThisExpression node) { + return recordTypeInfo(node, currentClass.getElement().getType()); + } + + @Override + public Type visitArrayLiteral(DartArrayLiteral node) { + visit(node.getExpressions()); + return getType(node); + } + + @Override + public Type visitMapLiteral(DartMapLiteral node) { + return recordTypeInfo(node, getType(node)); + } + + @Override + public Type visitMapLiteralEntry(DartMapLiteralEntry node) { + return computeType(node); + } + + @Override + public Type visitBooleanLiteral(DartBooleanLiteral node) { + return recordTypeInfo(node, getType(node)); + } + + @Override + public Type visitDoubleLiteral(DartDoubleLiteral node) { + return recordTypeInfo(node, getType(node)); + } + + @Override + public Type visitIntegerLiteral(DartIntegerLiteral node) { + return recordTypeInfo(node, getType(node)); + } + + @Override + public Type visitStringLiteral(DartStringLiteral node) { + return recordTypeInfo(node, getType(node)); + } + + @Override + public Type visitStringInterpolation(DartStringInterpolation node) { + return recordTypeInfo(node, getType(node)); + } + + @Override + public Type visitNullLiteral(DartNullLiteral node) { + return recordTypeInfo(node, nullType()); + } + + @Override + public Type visitParenthesizedExpression(DartParenthesizedExpression node) { + return recordTypeInfo(node, computeType(node.getExpression())); + } + + @Override + public Type visitTypeNode(DartTypeNode node) { + return node.getType() == null ? dynamicType() : node.getType(); + } + + @Override + public Type visitBlock(DartBlock node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitBreakStatement(DartBreakStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitContinueStatement(DartContinueStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitDefault(DartDefault node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitEmptyStatement(DartEmptyStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitExprStmt(DartExprStmt node) { + return visit(node.getExpression()); + } + + @Override + public Type visitParameter(DartParameter node) { + visit(node.getDefaultExpr()); + return getType(node); + } + + @Override + public Type visitMethodDefinition(DartMethodDefinition node) { + visitChildren(node); + return getType(node); + } + + @Override + public Type visitNewExpression(DartNewExpression node) { + visit(node.getArgs()); + return getType(node); + } + + @Override + public Type visitMethodInvocation(DartMethodInvocation node) { + visit(node.getArgs()); + Type type = computeType(node.getTarget()); + String selectorName = node.getFunctionNameString(); + type = computeAndRecordSelectorTypes(node, type, selectorName); + return type; + } + + @Override + public Type visitFunction(DartFunction node) { + visit(node.getParams()); + computeType(node.getBody()); + return getType(node.getReturnTypeNode()); + } + + @Override + public Type visitAssertion(DartAssertion node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitImportDirective(DartImportDirective node) { + return voidType(); + } + + @Override + public Type visitLibraryDirective(DartLibraryDirective node) { + return voidType(); + } + + @Override + public Type visitNativeDirective(DartNativeDirective node) { + return voidType(); + } + + @Override + public Type visitResourceDirective(DartResourceDirective node) { + return voidType(); + } + + @Override + public Type visitSourceDirective(DartSourceDirective node) { + return voidType(); + } + + @Override + public void visit(List nodes) { + if (nodes != null) { + for (DartNode node : nodes) { + node.getNormalizedNode().accept(this); + } + } + } + + @Override + public Type visitArrayAccess(DartArrayAccess node) { + Type type = computeType(node.getTarget()); + type = computeAndRecordSelectorTypes(node, type, getOperatorSelectorName(Token.INDEX)); + visit(node.getKey()); + return type; + } + + @Override + public Type visitPropertyAccess(DartPropertyAccess node) { + String selectorName = node.getPropertyName(); + Type receiver = computeType(node.getQualifier()); + Type selectorType = computeAndRecordSelectorTypes(node, receiver, selectorName); + return selectorType; + } + + private boolean canBindConstantValue(DartExpression expr) { + if (expr instanceof DartLiteral) { + return true; + } else if (expr instanceof DartBinaryExpression) { + DartBinaryExpression binExpr = (DartBinaryExpression) expr; + return canBindConstantValue(binExpr.getArg1()) && canBindConstantValue(binExpr.getArg2()); + } else if (expr instanceof DartUnaryExpression) { + return canBindConstantValue(((DartUnaryExpression) expr).getArg()); + } else if (expr instanceof DartParenthesizedExpression) { + return canBindConstantValue(((DartParenthesizedExpression) expr).getExpression()); + } else if (expr instanceof DartIdentifier || expr instanceof DartPropertyAccess) { + Element e = maybeGetTargetElement(expr); + switch (ElementKind.of(e)) { + case FIELD: + if (visitedConstants.contains(e)) { + return false; + } + visitedConstants.add(e); + FieldElement field = (FieldElement) e; + DartField fieldNode = (DartField) field.getNode(); + boolean result = field.getModifiers().isFinal() + && canBindConstantValue(fieldNode.getValue()); + visitedConstants.remove(e); + return result; + case VARIABLE: + VariableElement var = (VariableElement) e; + DartVariable varNode = (DartVariable) var.getNode(); + return var.getModifiers().isFinal() && canBindConstantValue(varNode.getValue()); + } + } + return false; + } + + private void maybeBindConstantValues(DartExpression expr, boolean isAssignee) { + if (canBindConstantValue(expr) && (expr == expr.getNormalizedNode())) { + DartExpression foldedExpr = null; + Element target = maybeGetTargetElement(expr); + switch (ElementKind.of(target)) { + case VARIABLE: + if (!isAssignee && target.getModifiers().isFinal()) { + DartVariable var = (DartVariable) target.getNode(); + foldedExpr = (DartExpression) var.getValue().clone(); + } + break; + case FIELD: + if (target.getModifiers().isFinal()) { + DartField field = (DartField) target.getNode(); + foldedExpr = (DartExpression) field.getValue().clone(); + } + break; + default: + return; + } + // TODO (fabiomfv) : consider adding normalized field to DartExpression. + if (foldedExpr != null) { + if (expr instanceof DartIdentifier) { + ((DartIdentifier) expr).setNormalizedNode(foldedExpr); + } else if (expr instanceof DartPropertyAccess) { + ((DartPropertyAccess) expr).setNormalizedNode(foldedExpr); + } + } + } + } + + @Override + public Type visitBinaryExpression(DartBinaryExpression node) { + Token opToken = node.getOperator(); + maybeBindConstantValues(node.getArg1(), opToken.isAssignmentOperator()); + Type receiver = computeType(node.getArg1()); + maybeBindConstantValues(node.getArg2(), false); + computeType(node.getArg2()); + switch (opToken) { + case ADD: + case SUB: + case MUL: + case DIV: + case MOD: + case BIT_AND: + case BIT_OR: + case BIT_XOR: + case SAR: + case SHL: + case SHR: + case ASSIGN_ADD: + case ASSIGN_SUB: + case ASSIGN_MUL: + case ASSIGN_DIV: + case ASSIGN_MOD: + case ASSIGN_BIT_AND: + case ASSIGN_BIT_OR: + case ASSIGN_BIT_XOR: + case ASSIGN_SAR: + case ASSIGN_SHL: + case ASSIGN_SHR: { + computeAndRecordSelectorTypes(node, receiver, getOperatorSelectorName(opToken)); + return receiver; + } + + case NE: + // There is no NE operator implementation. NE is conceptually implemented as !(e1 == e2). + assert !opToken.isUserDefinableOperator() : "Transformation at the line below is not valid anymore"; + opToken = Token.EQ; + // $FALL-THROUGH$ + case AND: + case OR: + case NOT: + case EQ: + case EQ_STRICT: + case NE_STRICT: + case LT: + case GT: + case LTE: + case GTE: { + Type opType = boolType(); + computeAndRecordSelectorTypes(node, receiver, getOperatorSelectorName(opToken)); + recordTypeInfo(node, Sets.newHashSet(opType)); + return opType; + } + + case ASSIGN: { + return receiver; + } + + case COMMA: + return computeType(node.getArg2()); + } + return dynamicType(); + } + + // Object is implicit and when looking for == operator we will never find Object== if a child + // class overrides it. Since Object is also an exception when calling classElement.getSubTypes() + // we need to handle this as a special case. + private void maybeAddObjectSelectors(DartExpression node, Type receiver, String selectorName) { + Member iMember = typeProvider.getObjectType().lookupMember(selectorName); + if ((iMember != null) && ElementKind.of(iMember.getElement()).equals(ElementKind.METHOD)) { + recordMethodImpl(node, (MethodElement) iMember.getElement()); + } + } + + private Type computeAndRecordSelectorTypes(DartExpression expression, Type receiver, + String selectorName) { + Type type = dynamicType(); + if (receiver != null) { + if (TypeKind.of(receiver).equals(TypeKind.INTERFACE)) { + InterfaceType baseType = (InterfaceType) receiver; + Set types = Sets.newHashSet(); + for (InterfaceType subType : baseType.getElement().getSubtypes()) { + InterfaceType sType = subType; + if (isParameterizedType(sType)) { + sType = substSubType(sType, baseType); + } + Type computedType = computeAndRecordSelectorType(expression, sType, selectorName); + // void is returned as dynamic. return null to make sure void is not a valid expression + // return type as in setters or void methods. + if (computedType != null) { + type = computedType; + types.addAll(getConcreteSubTypes(type)); + } + } + if (types.size() > 1) { + type = getCommonSuperType(types); + types = Sets.newHashSet(dynamicType()); + } + recordTypeInfo(expression, types); + } + } + return type; + } + + private Type computeAndRecordSelectorType(DartExpression expression, InterfaceType type, + String selectorName) { + Member iMember = type.lookupMember(selectorName); + // TODO (fabiomfv): refactor this. + if (iMember != null) { + Element element = iMember.getElement(); + switch (ElementKind.of(element)) { + case METHOD: + if (!type.getElement().isInterface()) { + recordMethodImpl(expression, (MethodElement) element); + maybeAddObjectSelectors(expression, type, selectorName); + } + if (canInstantiateParametrizedType(iMember)) { + FunctionType ftype = (FunctionType) iMember.getType(); + return ftype.getReturnType(); + } + return dynamicType(); + case FIELD: + FieldElement fieldElement = (FieldElement) element; + recordFieldImpl(expression, fieldElement); + Modifiers modifiers = fieldElement.getModifiers(); + if (modifiers.isAbstractField() && modifiers.isSetter()) { + // void is currently dynamic which make the computation incorrect. + return null; + } + return iMember.getType(); + default: + } + } + return dynamicType(); + } + + private Set getConcreteSubTypes(Type type) { + Set concreteTypes = Sets. newHashSet(); + if (TypeKind.of(type).equals(TypeKind.INTERFACE)) { + ClassElement cls = (ClassElement) type.getElement(); + for (InterfaceType subType : cls.getSubtypes()) { + if (!subType.getElement().isInterface()) { + concreteTypes.add(substSubType(subType, (InterfaceType) type)); + } + } + } + return concreteTypes; + } + + private InterfaceType substSubType(InterfaceType subType, InterfaceType baseType) { + List typeArgs = baseType.getArguments(); + List typeParams = asInstanceOf(subType, baseType.getElement()).getArguments(); + if (typeArgs != null && !typeArgs.isEmpty()) { + return subType.subst(typeArgs, typeParams); + } + return subType; + } + + private boolean isParameterizedType(InterfaceType type) { + return type.getArguments() != null && !type.getArguments().isEmpty(); + } + + private boolean canInstantiateParametrizedType(Member member) { + InterfaceType iface = member.getHolder(); + List typeArgs = iface.getArguments(); + List typeParams = iface.getElement().getTypeParameters(); + return typeArgs.size() == typeParams.size(); + } + + @Override + public Type visitIdentifier(DartIdentifier node) { + Element element = node.getTargetSymbol(); + switch (ElementKind.of(element)) { + case CLASS: + recordTypeInfo(node, element.getType()); + return element.getType(); + case VARIABLE: + case PARAMETER: + Type type = element.getType(); + recordTypeInfo(node, Sets. newHashSet(type)); + return type; + case FIELD: { + Element enclosing = element.getEnclosingElement(); + switch (ElementKind.of(enclosing)) { + case CLASS: + ClassElement cls = (ClassElement) enclosing; + computeAndRecordSelectorTypes(node, cls.getType(), element.getName()); + break; + case LIBRARY: + // TODO (fabiomfv). + } + return element.getType(); + } + default: + return dynamicType(); + } + } + + @Override + public Type visitUnaryExpression(DartUnaryExpression node) { + maybeBindConstantValues(node.getArg(), true); + Type receiver = computeType(node.getArg()); + Token op = node.getOperator(); + switch (node.getOperator()) { + case NOT: + assert !op.isUserDefinableOperator(); + receiver = boolType(); + break; + case INC: + case DEC: + assert !op.isUserDefinableOperator(); + receiver = intType(); + break; + case SUB: + case BIT_NOT: + computeAndRecordSelectorTypes(node, receiver, getOperatorSelectorName(op)); + break; + } + recordTypeInfo(node, receiver); + return receiver; + } + + @Override + public Type visitUnqualifiedInvocation(DartUnqualifiedInvocation node) { + visit(node.getArgs()); + Type type = getType(node); + if (node.getTarget() != null) { + String selectorName = node.getTarget().getTargetName(); + Element element = node.getTarget().getTargetSymbol(); + if (element == null) { + return type; + } + Element enclosing = element.getEnclosingElement(); + switch (ElementKind.of(enclosing)) { + case CLASS: { + ClassElement cls = (ClassElement) element.getEnclosingElement(); + switch (ElementKind.of(element)) { + case FIELD: + computeAndRecordSelectorTypes(node, cls.getType(), selectorName); + type = element.getType(); + break; + case METHOD: + computeAndRecordSelectorTypes(node, cls.getType(), selectorName); + FunctionType fType = (FunctionType) element.getType(); + type = fType.getReturnType(); + break; + default: + type = element.getType(); + break; + } + break; + } + case LIBRARY: + // TODO (fabiomfv). + break; + case NONE: + if (TypeKind.of(element.getType()).equals(TypeKind.FUNCTION)) { + type = ((FunctionType) element.getType()).getReturnType(); + recordTypeInfo(node, type); + } + break; + } + } + return type; + } + + @Override + public Type visitField(DartField node) { + visitChildren(node); + return getType(node); + } + + @Override + public Type visitFieldDefinition(DartFieldDefinition node) { + visitChildrenAndReturnVoid(node); + Type type = getType(node); + return type; + } + + @Override + public Type visitFunctionExpression(DartFunctionExpression node) { + visitChildren(node); + return dynamicType(); + } + + @Override + public Type visitFunctionTypeAlias(DartFunctionTypeAlias node) { + return dynamicType(); + } + + @Override + public Type visitFunctionObjectInvocation(DartFunctionObjectInvocation node) { + visit(node.getArgs()); + Type type = computeType(node.getTarget()); + recordTypeInfo(node, type); + return type; + } + + @Override + public Type visitCase(DartCase node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitConditional(DartConditional node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitDoWhileStatement(DartDoWhileStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitForInStatement(DartForInStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitForStatement(DartForStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitIfStatement(DartIfStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitInitializer(DartInitializer node) { + visit(node.getValue()); + return voidType(); + } + + @Override + public Type visitLabel(DartLabel node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitReturnStatement(DartReturnStatement node) { + return (node.getValue() == null) ? voidType() : computeType(node.getValue()); + } + + @Override + public Type visitSuperExpression(DartSuperExpression node) { + visitChildren(node); + return getType(node); + } + + @Override + public Type visitSwitchStatement(DartSwitchStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitSyntheticErrorExpression(DartSyntheticErrorExpression node) { + visitChildren(node); + return dynamicType(); + } + + @Override + public Type visitSyntheticErrorStatement(DartSyntheticErrorStatement node) { + visitChildren(node); + return dynamicType(); + } + + @Override + public Type visitThrowStatement(DartThrowStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitCatchBlock(DartCatchBlock node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitTryStatement(DartTryStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitVariable(DartVariable node) { + maybeBindConstantValues(node.getValue(), false); + visit(node.getValue()); + return dynamicType(); + } + + @Override + public Type visitVariableStatement(DartVariableStatement node) { + Type type = computeType(node.getTypeNode()); + visitChildren(node); + return type; + } + + @Override + public Type visitWhileStatement(DartWhileStatement node) { + return visitChildrenAndReturnVoid(node); + } + + @Override + public Type visitNamedExpression(DartNamedExpression node) { + visit(node.getExpression()); + return getType(node.getExpression()); + } + + @Override + public Type visitTypeExpression(DartTypeExpression node) { + return getType(node); + } + + @Override + public Type visitTypeParameter(DartTypeParameter node) { + return getType(node); + } + + @Override + public Type visitNativeBlock(DartNativeBlock node) { + return dynamicType(); + } + + @Override + public Type visitRedirectConstructorInvocation(DartRedirectConstructorInvocation node) { + return getType(node); + } + + @Override + public Type visitSuperConstructorInvocation(DartSuperConstructorInvocation node) { + return getType(node); + } + + @Override + public Type visitParameterizedNode(DartParameterizedNode node) { + return node.getExpression().accept(this); + } + + private Type recordTypeInfo(DartExpression node, Type type) { + if (type == null) { + return type; + } + DartExpression targetNode = node.getNormalizedNode(); + Map> typeSets = typeInfo.getTypeSets(); + Set types = typeSets.get(targetNode); + if (types == null) { + types = new HashSet(); + } + switch (TypeKind.of(type)) { + case INTERFACE: { + ClassElement cls = (ClassElement) type.getElement(); + for (InterfaceType subType : cls.getSubtypes()) { + InterfaceType rSubType = substSubType(subType, (InterfaceType) type); + if (!rSubType.getElement().isInterface()) { + types.add(rSubType); + } + } + } + break; + case FUNCTION: + case FUNCTION_ALIAS: + case VARIABLE: + // We dont handle these yet. + case DYNAMIC: + case NONE: + types.add(dynamicType()); + break; + } + // We need to be resilient. in case interface is defined with no concrete + // types, we should + // not add any partial/incomplete type info we may have gathered so far. + // for instance DOM does not have concrete types yet. + if (!types.isEmpty()) { + typeSets.put(targetNode, types); + } + return type; + } + + private void recordMethodImpl(DartExpression expression, MethodElement method) { + assert method != null; + Set methodImpls = typeInfo.getMethodImpl().get(expression); + if (methodImpls == null) { + methodImpls = Sets.newHashSet(); + typeInfo.getMethodImpl().put(expression, methodImpls); + } + methodImpls.add(method); + } + + private void recordFieldImpl(DartExpression expression, FieldElement field) { + assert field != null; + Set getters = typeInfo.getGettersImpl().get(expression); + if (getters == null) { + getters = Sets.newHashSet(); + typeInfo.getGettersImpl().put(expression, getters); + } + Set setters = typeInfo.getSettersImpl().get(expression); + if (setters == null) { + setters = Sets.newHashSet(); + typeInfo.getSettersImpl().put(expression, setters); + } + Modifiers modifiers = field.getModifiers(); + if (modifiers.isAbstractField()) { + if (field.getSetter() != null) { + setters.add(field); + } + if (field.getGetter() != null) { + getters.add(field); + } + } else { + getters.add(field); + setters.add(field); + } + } + + private void recordTypeInfo(DartExpression node, Set types) { + for (Type type : types) { + recordTypeInfo(node, type); + } + } + + void visitChildren(DartNode node) { + node.getNormalizedNode().visitChildren(this); + } + + private Type visitChildrenAndReturnVoid(DartNode node) { + visitChildren(node); + return voidType(); + } + + private Type visit(DartExpression expression) { + if (expression != null) { + return expression.getNormalizedNode().accept(this); + } + return voidType(); + } + + Type getType(DartNode node) { + return (node == null) ? dynamicType() : node.getNormalizedNode().getType(); + } + + Type computeType(DartNode node) { + return (node == null) ? dynamicType() : node.getNormalizedNode().accept(this); + } + + private Type dynamicType() { + return typeProvider.getDynamicType(); + } + + private Type nullType() { + return typeProvider.getNullType(); + } + + private Type boolType() { + return typeProvider.getBoolType(); + } + + private Type intType() { + return typeProvider.getIntType(); + } + + private Type voidType() { + return typeProvider.getVoidType(); + } + + private String getOperatorSelectorName(Token op) { + switch (op) { + case SUB: + return "operator negate"; + + case ASSIGN_ADD: + return getOperatorSelectorName(Token.ADD); + case ASSIGN_SUB: + return getOperatorSelectorName(Token.SUB); + case ASSIGN_MUL: + return getOperatorSelectorName(Token.MUL); + case ASSIGN_DIV: + return getOperatorSelectorName(Token.DIV); + + case ASSIGN_BIT_OR: + return getOperatorSelectorName(Token.BIT_OR); + case ASSIGN_BIT_XOR: + return getOperatorSelectorName(Token.BIT_XOR); + case ASSIGN_BIT_AND: + return getOperatorSelectorName(Token.BIT_AND); + + case ASSIGN_SHL: + return getOperatorSelectorName(Token.SHL); + case ASSIGN_SAR: + return getOperatorSelectorName(Token.SAR); + case ASSIGN_SHR: + return getOperatorSelectorName(Token.SHR); + } + return ("operator " + op.getSyntax()); + } + + private InterfaceType asInstanceOf(Type t, ClassElement element) { + return typeUtils.asInstanceOf(t, element); + } + + // TODO (fabiomfv) : revisit this. + // returns the 'root' type of the set of types or dynamic if can't find a common root type. + private Type getCommonSuperType(Set ts) { + if (ts.size() == 1) { + return ts.iterator().next(); + } + for (Type t : ts) { + if (TypeKind.of(t).equals(TypeKind.INTERFACE) && isSuperTypeOf(t, ts)) { + if (((ClassElement) t.getElement()).isObject()) { + continue; + } + return t; + } + } + return dynamicType(); + } + + private boolean isSuperTypeOf(Type type, Set sts) { + for (Type st : sts) { + st.getClass(); + type.getClass(); + if (!typeUtils.isSubtype(st, type)) { + return false; + } + } + return true; + } + } + + /** + * Stores expressions type and implementation information. + */ + static class ExpressionTypeInfo { + + private final Map> typeSets; + private final Map> getterImpl; + private final Map> setterImpl; + private final Map> methodImpl; + + static ExpressionTypeInfo create() { + return new ExpressionTypeInfo(); + } + + private ExpressionTypeInfo() { + this.typeSets = new HashMap>(); + this.getterImpl = new HashMap>(); + this.setterImpl = new HashMap>(); + this.methodImpl = new HashMap>(); + } + + Map> getTypeSets() { + return typeSets; + } + + Map> getGettersImpl() { + return getterImpl; + } + + Map> getSettersImpl() { + return setterImpl; + } + + Map> getMethodImpl() { + return methodImpl; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/dart/DartBackend.java b/compiler/java/com/google/dart/compiler/backend/dart/DartBackend.java new file mode 100644 index 00000000000..661d53cf74e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/dart/DartBackend.java @@ -0,0 +1,119 @@ +// 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. + +package com.google.dart.compiler.backend.dart; + +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.ast.DartToSourceVisitor; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.backend.common.AbstractBackend; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.util.DefaultTextOutput; +import com.google.dart.compiler.util.TextOutput; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; + +/** + * A compiler backend that produces optimized Dart. + */ +public class DartBackend extends AbstractBackend { + + public static final String EXTENSION_DART = "opt.dart"; + public static final String EXTENSION_DART_SRC_MAP = "opt.dart.map"; + + + + private static void packageLibs(Collection libraries, + Writer w, + DartCompilerContext context) + throws IOException { + for (LibraryUnit libUnit : libraries) { + for (DartUnit unit : libUnit.getUnits()) { + DartSource src = unit.getSource(); + if (src != null) { + Reader r = context.getArtifactReader(src, "", EXTENSION_DART); + boolean failed = true; + try { + CharStreams.copy(r, w); + failed = false; + } finally { + Closeables.close(r, failed); + } + } + } + } + } + + @Override + public boolean isOutOfDate(DartSource src, DartCompilerContext context) { + return context.isOutOfDate(src, src, EXTENSION_DART); + } + + @Override + public void compileUnit(DartUnit unit, DartSource src, + DartCompilerContext context, CoreTypeProvider typeProvider) throws IOException { + // Generate Javascript output. + TextOutput out = new DefaultTextOutput(false); + DartToSourceVisitor srcGenerator = new DartToSourceVisitor(out); + // TODO(johnlenz): Determine if we want to make source maps + // optional. + srcGenerator.generateSourceMap(true); + srcGenerator.accept(unit); + Writer w = context.getArtifactWriter(src, "", EXTENSION_DART); + boolean failed = true; + try { + w.write(out.toString()); + failed = false; + } finally { + Closeables.close(w, failed); + } + // Write out the source map. + w = context.getArtifactWriter(src, "", EXTENSION_DART_SRC_MAP); + failed = true; + try { + srcGenerator.writeSourceMap(w, src.getName()); + failed = false; + } finally { + Closeables.close(w, failed); + } + } + + @Override + public void packageApp(LibrarySource app, + Collection libraries, + DartCompilerContext context, + CoreTypeProvider typeProvider) throws IOException { + Writer out = context.getArtifactWriter(app, "", EXTENSION_DART); + boolean failed = true; + try { + // Emit the concatenated Javascript sources in dependency order. + packageLibs(libraries, out, context); + + // Emit entry point call. + // TODO: How does a dart app start? + // out.write(app.getEntryMethod() + "();"); + failed = false; + } finally { + Closeables.close(out, failed); + } + } + + @Override + public String getAppExtension() { + return EXTENSION_DART; + } + + @Override + public String getSourceMapExtension() { + return EXTENSION_DART_SRC_MAP; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationGenerator.java b/compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationGenerator.java new file mode 100644 index 00000000000..68ec59a578b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationGenerator.java @@ -0,0 +1,246 @@ +// 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. + +package com.google.dart.compiler.backend.doc; + +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.backend.common.AbstractBackend; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.type.Type; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Generate documentation based on DartDoc comments. + */ +public class DartDocumentationGenerator extends AbstractBackend { + + private String outputDirectory; + private String library; + private PrintStream stream; + private Set anchors; + private Set links; + + public DartDocumentationGenerator(String out, String lib) { + library = lib; + outputDirectory = out; + anchors = new HashSet(); + links = new HashSet(); + } + + private void generate(DartUnit unit) { + LibraryUnit lib = unit.getLibrary(); + if (library == null || library.equals(lib.getName())) { + DartDocumentationVisitor visitor = + new DartDocumentationVisitor(outputDirectory, + library, + unit.getComments(), + anchors, + links); + visitor.initialize(unit); + visitor.visitNode(unit); + } + } + + private boolean isPrivateName(String name) { + return name.charAt(0) == '_'; + } + + private void setPrintStream(String name) { + String fileName = outputDirectory + File.separator + name + ".html"; + try { + stream = new PrintStream(fileName); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + private void printLibraryMembers(LibraryUnit lib) { + List classes = new ArrayList(10); + List exceptions = new ArrayList(10); + List fields = new ArrayList(10); + List methods = new ArrayList(10); + for (DartNode dartNode : lib.getTopLevelNodes()) { + if (dartNode instanceof DartClass) { + DartClass dartClass = (DartClass) dartNode; + ClassElement classElement = dartClass.getSymbol(); + String name = classElement.getName(); + if (isPrivateName(name)) { + continue; + } + if (name.contains("Exception")) { + exceptions.add(classElement); + } else { + classes.add(classElement); + } + } else if (dartNode instanceof DartField) { + DartField dartField = (DartField) dartNode; + FieldElement fieldElement = dartField.getSymbol(); + String name = fieldElement.getName(); + if (!isPrivateName(name)) { + fields.add(fieldElement); + } + } else if (dartNode instanceof DartMethodDefinition) { + DartMethodDefinition dartMethod = (DartMethodDefinition) dartNode; + MethodElement methodElement = dartMethod.getSymbol(); + String name = methodElement.getName(); + if (!isPrivateName(name)) { + methods.add(methodElement); + } + } + } + if (classes.size() > 0 || exceptions.size() > 0) { + Collections.sort(classes, new ElementNameComparator()); + Collections.sort(exceptions, new ElementNameComparator()); + stream.println("\n
"); + stream.println("

Classes and interfaces

"); + stream.print("
    "); + printClassLibraryMembers(classes); + printClassLibraryMembers(exceptions); + stream.println("
"); + stream.println("
"); + } + if (methods.size() > 0) { + Collections.sort(methods, new ElementNameComparator()); + stream.println("\n
"); + stream.println("

Top-level methods

"); + stream.print("
    "); + for (MethodElement methodElement : methods) { + String name = methodElement.getName(); + // TODO(ager): Generate link to the right place once global methods are documented. + stream.print("
  • "); + stream.print(name); + stream.print("
  • "); + } + stream.println("
"); + stream.println("
"); + } + if (fields.size() > 0) { + Collections.sort(fields, new ElementNameComparator()); + stream.println("\n
"); + stream.println("

Top-level fields

"); + stream.print("
    "); + for (FieldElement fieldElement : fields) { + String name = fieldElement.getName(); + // TODO(ager): Generate link to the right place once global fields are documented. + stream.print("
  • "); + stream.print(name); + stream.print("
  • "); + } + stream.println("
"); + stream.println("
"); + } + } + + private void printClassLibraryMembers(List classes) { + for (ClassElement classElement : classes) { + String name = classElement.getName(); + stream.print("
  • "); + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + name, + name + "::" + name); + links.add(linkInfo); + stream.print(linkInfo.anchorReferenceStartTag()); + stream.print(name); + List typeParameters = classElement.getTypeParameters(); + if (typeParameters.size() > 0) { + stream.print("<"); + boolean first = true; + for (Type type : typeParameters) { + if (!first) { + stream.print(","); + } else { + first = false; + } + stream.print(type.getElement().getName()); + } + stream.print(">"); + } + stream.print(""); + stream.print("
  • "); + } + } + + @Override + public boolean isOutOfDate(DartSource src, DartCompilerContext context) { + return true; + } + + @Override + public void compileUnit(DartUnit unit, DartSource src, DartCompilerContext context, + CoreTypeProvider typeProvider) { + generate(unit); + } + + @Override + public void packageApp(LibrarySource app, Collection libraries, + DartCompilerContext context, CoreTypeProvider typeProvider) { + // Generate index.html containing a list of libraries and their classes. + setPrintStream("index"); + stream.println(""); + stream.println(""); + stream.println(""); + stream.println(""); + stream.print(""); + stream.print("Dart : Libraries"); + stream.println(""); + stream.print(""); + + stream.println(""); + stream.println("\n
    \n"); + + stream.println("

    Libraries

    "); + + stream.println("
    "); + for (LibraryUnit lib : libraries) { + if (library == null || library.equals(lib.getName())) { + if (lib.getTopLevelNodes().size() > 0) { + stream.print("

    "); + stream.print(lib.getName()); + stream.println("

    "); + printLibraryMembers(lib); + } + } + } + stream.println("
    "); + stream.println("\n
    \n"); + stream.println(""); + // Validate the generated links. + for (LinkInformation linkInfo : links) { + if (!anchors.contains(linkInfo)) { + System.out.print("Warning: link to element without anchor: "); + System.out.println(linkInfo.className + "::" + linkInfo.elementName); + } + } + } + + @Override + public String getAppExtension() { + throw new UnsupportedOperationException(); + } + + @Override + public String getSourceMapExtension() { + throw new UnsupportedOperationException(); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationVisitor.java b/compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationVisitor.java new file mode 100644 index 00000000000..ba650f98ee0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/doc/DartDocumentationVisitor.java @@ -0,0 +1,805 @@ +// 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. + +package com.google.dart.compiler.backend.doc; + +import com.google.common.io.CharStreams; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartComment; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.EnclosingElement; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.resolver.VariableElement; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; + +import java.io.CharArrayWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +class DartDocumentationVisitor extends DartNodeTraverser { + private String outputDirectory; + private String library; + private PrintStream stream; + private List dartDocComments; + private Set anchors; + private Set links; + private char[] unitSource; + + DartDocumentationVisitor(String out, + String lib, + List comments, + Set anchors, + Set links) { + outputDirectory = out; + library = lib; + dartDocComments = (comments == null) + ? Collections.emptyList() + : new ArrayList(comments); + this.anchors = anchors; + this.links = links; + stream = null; + unitSource = null; + } + + public void initialize(DartUnit unit) { + readSource(unit.getSource()); + } + + private boolean isPrivateName(String name) { + return !name.isEmpty() && name.charAt(0) == '_'; + } + + private void readSource(Source source) { + try { + Reader reader = source.getSourceReader(); + CharArrayWriter writer = new CharArrayWriter(); + CharStreams.copy(reader, writer); + unitSource = writer.toCharArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + // TODO(ager): This is inefficient but fast enough for now. Optimize it. + private DartComment getDocComment(int position) { + // Find closest comment before the position. + DartComment closest = null; + for (DartComment comment : dartDocComments) { + if (position > comment.getSourceStart()) { + if (closest == null) { + closest = comment; + } else { + int bestDistance = position - closest.getSourceStart(); + int currentDistance = position - comment.getSourceStart(); + if (currentDistance < bestDistance) { + closest = comment; + } + } + } + } + if (closest == null) { + return null; + } + // Check that there are only white space characters between the + // end of the comment and the position. + int commentEnd = closest.getSourceStart() + closest.getSourceLength(); + for (int i = commentEnd; i < position; i++) { + if (!Character.isWhitespace(unitSource[i])) { + return null; + } + } + return closest; + } + + // Names of anchor elements can only contain letters and + // digits. Method names can contain other characters. If they do, a + // simple mangling is used by just printing the code point value + // instead. + // + // TODO(ager): Come up with something better for the mangling? + private String anchorEscape(String string) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < string.length(); i++) { + char c = string.charAt(i); + if (Character.isLetterOrDigit(c)) { + buffer.append(c); + } else { + buffer.append(string.codePointAt(i)); + } + } + return buffer.toString(); + } + + // TODO(ager): This is inefficient. Use something else. + private void escapeAndPrint(PrintStream stream, String string) { + for (int i = 0; i < string.length(); i++) { + char c = string.charAt(i); + switch (c) { + case '<': + stream.print("<"); + break; + case '>': + stream.print(">"); + break; + case '&': + stream.print("&"); + break; + case '"': + stream.print("""); + break; + default: + stream.print(c); + break; + } + } + } + + // TODO(ager): This is inefficient. Use something else. + private void escapeAndAppendChar(StringBuffer buffer, char c) { + switch (c) { + case '<': + buffer.append("<"); + break; + case '>': + buffer.append(">"); + break; + case '&': + buffer.append("&"); + break; + case '"': + buffer.append("""); + break; + default: + buffer.append(c); + break; + } + } + + private String commentToString(DartComment comment) { + if (comment.isDartDoc()) { + // Get rid of all the '*' and '/' in the beginning. + StringBuffer buffer = new StringBuffer(); + int index = comment.getSourceStart(); + while (unitSource[index] != '/') { + index++; + } + index += 3; // '/**' + while (index < unitSource.length) { + while (unitSource[index] != '\n' && unitSource[index] != '*') { + escapeAndAppendChar(buffer, unitSource[index++]); + } + // Check if a '*' is the end of comment. + if (unitSource[index] == '*') { + if (unitSource[index + 1] == '/') { + return buffer.toString(); + } else { + buffer.append(unitSource[index++]); + continue; + } + } + // Preserve newline chars. + buffer.append('\n'); + while (unitSource[index] != '*') { + index++; + } + index++; // '*' + if (unitSource[index] == '/') { + assert((index + 1 - comment.getSourceStart()) == comment.getSourceLength()); + return buffer.toString(); + } + } + } + return ""; + } + + private void printClassTypeParameters(ClassElement classElement) { + List typeParameters = classElement.getTypeParameters(); + if (typeParameters.size() > 0) { + stream.print("<"); + boolean first = true; + for (Type type : typeParameters) { + if (!first) { + stream.print(","); + } else { + first = false; + } + escapeAndPrint(stream, type.getElement().getName()); + } + stream.print(">"); + } + } + + private void printClassReference(ClassElement classElement) { + String name = classElement.getName(); + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + name, + name + "::" + name); + links.add(linkInfo); + stream.print(linkInfo.anchorReferenceStartTag()); + stream.print(name); + printClassTypeParameters(classElement); + stream.print(""); + } + + /** + * @param elm the element from which to start the resolution of the name. + * @param name the name of the referred element. + * @return true iff the element reference can be resolved and is printed. + */ + private boolean printElementReference(Element elm, String name) { + // For methods search the parameters. + if (elm.getKind() == ElementKind.METHOD || + elm.getKind() == ElementKind.CONSTRUCTOR) { + MethodElement method = (MethodElement) elm; + for (VariableElement param : method.getParameters()) { + if (param.getName().equals(name)) { + assert(elm.getEnclosingElement().getKind() == ElementKind.CLASS); + ClassElement enclosingClass = (ClassElement) elm.getEnclosingElement(); + String libraryName = enclosingClass.getLibrary().getName(); + String className = enclosingClass.getName(); + String elementReference = + enclosingClass.getName() + "::" + anchorEscape(method.getName()) + "::" + name; + LinkInformation linkInfo = new LinkInformation(libraryName, className, elementReference); + links.add(linkInfo); + stream.print(""); + stream.print(linkInfo.anchorReferenceStartTag()); + stream.print(name); + stream.print(""); + return true; + } + } + } + + // Search enclosing elements. For classes start with the element + // itself to allow class comments to refer to members of the + // class. + EnclosingElement enclosing = elm.getEnclosingElement(); + if (elm.getKind() == ElementKind.CLASS) { + enclosing = (ClassElement) elm; + } + while (enclosing != null) { + if (printElementReferenceFromEnclosingElement(name, enclosing)) { + return true; + } + enclosing = enclosing.getEnclosingElement(); + } + return false; + } + + + /** + * @param name the name of the referred element. + * @param enclosing the enclosing element to start the resolution from. + * @return true iff the element reference can be resolved and is printed. + */ + private boolean printElementReferenceFromEnclosingElement(String name, + EnclosingElement enclosing) { + Element localElement = enclosing.lookupLocalElement(name); + if (localElement != null) { + switch (localElement.getKind()) { + case METHOD: + case FIELD: + assert(localElement.getEnclosingElement().getKind() == ElementKind.CLASS); + ClassElement enclosingClass = (ClassElement) localElement.getEnclosingElement(); + if (enclosingClass != null) { + String className = enclosingClass.getName(); + String elementReference = className + "::" + name; + LinkInformation linkInfo = new LinkInformation(enclosingClass.getLibrary().getName(), + className, + elementReference); + links.add(linkInfo); + stream.print(""); + stream.print(linkInfo.anchorReferenceStartTag()); + stream.print(name); + stream.print(""); + return true; + } else { + return false; + } + case CLASS: + stream.print(""); + printClassReference((ClassElement) localElement); + stream.print(""); + return true; + default: + return false; + } + } + // For classes search type parameters, superclass and interfaces. + if (enclosing.getKind() == ElementKind.CLASS) { + ClassElement classElement = (ClassElement) enclosing; + List typeParameters = classElement.getTypeParameters(); + for (Type type : typeParameters) { + if (type.getElement().getName().equals(name)) { + String className = classElement.getName(); + String elementReference = className + "::" + className; + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + className, + elementReference); + links.add(linkInfo); + stream.print(""); + stream.print(linkInfo.anchorReferenceStartTag()); + escapeAndPrint(stream, type.getElement().getName()); + stream.print(""); + return true; + } + } + InterfaceType supertype = classElement.getSupertype(); + if (supertype != null) { + ClassElement superElement = supertype.getElement(); + if (printElementReferenceFromEnclosingElement(name, superElement)) { + return true; + } + } + List interfaces = classElement.getInterfaces(); + for (InterfaceType interfaceType : interfaces) { + if (printElementReferenceFromEnclosingElement(name, interfaceType.getElement())) { + return true; + } + } + } + return false; + } + + private void printElementComment(Element elm) { + DartComment comment = getDocComment(elm.getNode().getSourceStart()); + if (comment != null) { + String rawComment = commentToString(comment); + int length = rawComment.length(); + int index = 0; + while (index < length) { + int escapeIndex = rawComment.indexOf('[', index); + if (escapeIndex == -1) { + stream.print(rawComment.substring(index)); + break; + } else { + stream.print(rawComment.substring(index, escapeIndex)); + int escapeEndIndex = rawComment.indexOf(']', escapeIndex); + if (escapeEndIndex == -1) { + stream.print("["); + index = escapeIndex + 1; + } else { + if (rawComment.charAt(escapeIndex + 1) == ':' && + rawComment.charAt(escapeEndIndex - 1) == ':') { + // Pre-formatted code block [: code :]. + String code = rawComment.substring(escapeIndex + 2, escapeEndIndex - 1); + stream.print(""); + if (code.contains("\n")) { + stream.println("
    "); + } + stream.print(code.replaceAll("\n", "
    ").replaceAll(" ", " ")); + if (code.contains("\n")) { + stream.println("
    "); + } + stream.println("
    \n"); + } else { + // Element reference. + String name = rawComment.substring(escapeIndex + 1, escapeEndIndex); + if (!printElementReference(elm, name)) { + stream.print("["); + stream.print(name); + stream.print("]"); + } + } + index = escapeEndIndex + 1; + } + } + } + } + } + + @Override + public Void visitMethodDefinition(DartMethodDefinition node) { + documentToplevelMethod(node.getSymbol()); + return null; + } + + @Override + public Void visitField(DartField node) { + documentToplevelField(node.getSymbol()); + return null; + } + + @Override + public Void visitClass(DartClass node) { + documentClass(node.getSymbol()); + return null; + } + + private void printFunctionTypeParameterList(FunctionType type, Element element) { + List paramTypes = type.getParameterTypes(); + stream.print("("); + boolean first = true; + for (Type paramType : paramTypes) { + if (first) { + first = false; + } else { + stream.print(", "); + } + if (!printElementReference(element, paramType.getElement().getName())) { + escapeAndPrint(stream, paramType.getElement().getName()); + } + } + stream.print(")"); + } + + private void printMethodParameterList(MethodElement method, ClassElement classElement) { + String className = classElement.getName(); + stream.print("("); + boolean first = true; + boolean foundOptionalParam = false; + for (VariableElement param : method.getParameters()) { + if (first) { + first = false; + } else { + stream.print(", "); + } + if (!foundOptionalParam && param.isNamed()) { + stream.print("["); + foundOptionalParam = true; + } + // Print type. Only print return type for function types. The + // argument types are left for after the parameter name. + Type paramType = param.getType(); + boolean isFunctionType = paramType.getKind() == TypeKind.FUNCTION; + Element paramTypeElement = paramType.getElement(); + if (isFunctionType) { + FunctionType functionType = (FunctionType) paramType; + Element returnTypeElement = functionType.getReturnType().getElement(); + if (!printElementReference(method, returnTypeElement.getName())) { + escapeAndPrint(stream, returnTypeElement.getName()); + } + } else { + if (!printElementReference(method, paramTypeElement.getName())) { + escapeAndPrint(stream, paramTypeElement.getName()); + } + } + stream.print(" "); + String elementReference = + className + "::" + anchorEscape(method.getName()) + "::" + param.getName(); + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + className, + elementReference); + anchors.add(linkInfo); + stream.print(linkInfo.anchorStartTag()); + stream.print(param.getName()); + stream.print(""); + if (isFunctionType) { + FunctionType functionType = (FunctionType) paramType; + printFunctionTypeParameterList(functionType, method); + } + if (foundOptionalParam && param.getDefaultValue() != null) { + stream.print(" = "); + stream.print(param.getDefaultValue().toString()); + } + } + if (foundOptionalParam) { + stream.print("]"); + } + stream.print(")"); + } + + private void printSupertype(ClassElement classElement) { + InterfaceType supertype = classElement.getSupertype(); + if (supertype != null && !isPrivateName(supertype.getElement().getName())) { + stream.println("\n
    "); + stream.println("

    Supertype:

    "); + stream.print("
    • "); + // If the supertype is defined in a library for which we are not generating + // documentation do not attempt to link to the non-existent documentation. + LibraryUnit supertypeLibraryUnit = supertype.getElement().getLibrary().getLibraryUnit(); + if (library == null || library.equals(supertypeLibraryUnit.getName())) { + printClassReference(supertype.getElement()); + } else { + stream.print(supertype.getElement().getName()); + } + stream.println("
    "); + stream.println("
    "); + } + } + + private void printInterfaces(ClassElement classElement) { + List interfaces = classElement.getInterfaces(); + List nonPrivateInterfaces = new LinkedList(); + for (InterfaceType type : interfaces) { + ClassElement element = type.getElement(); + if (!isPrivateName(element.getName())) { + nonPrivateInterfaces.add(element); + } + } + if (nonPrivateInterfaces.size() > 0) { + Collections.sort(nonPrivateInterfaces, new ElementNameComparator()); + stream.println("\n
    "); + stream.println("

    Implemented interfaces:

    "); + stream.println("
      "); + boolean first = true; + for (ClassElement element : nonPrivateInterfaces) { + stream.print("
    • "); + printClassReference(element); + stream.println("
    • "); + } + stream.println("
    "); + stream.println("
    "); + } + } + + private void printSubTypes(ClassElement classElement) { + Set subtypes = classElement.getSubtypes(); + List relevantSubtypes = new LinkedList(); + // Filter out private subtypes. In addition, filter out subtypes not in the + // library for which we are generating documentation. + for (InterfaceType type : subtypes) { + ClassElement element = type.getElement(); + if (!isPrivateName(element.getName())) { + LibraryUnit subtypeLibraryUnit = element.getLibrary().getLibraryUnit(); + if (library == null || library.equals(subtypeLibraryUnit.getName())) { + relevantSubtypes.add(element); + } + } + } + // Subtypes include the type itself. + if (relevantSubtypes.size() > 1) { + Collections.sort(relevantSubtypes, new ElementNameComparator()); + stream.println("\n
    "); + stream.println("

    Subtypes:

    "); + stream.println("
      "); + for (ClassElement subtype : relevantSubtypes) { + // Don't list the class itself as a subtype. + if (subtype == classElement) { + continue; + } + stream.print("
    • "); + printClassReference(subtype); + stream.println("
    • "); + } + stream.println("
    "); + stream.println("
    "); + } + } + + private void documentFields(ClassElement classElement, List fields) { + if (fields.size() == 0) { + return; + } + Collections.sort(fields, new ElementNameComparator()); + stream.println("

    Fields

    "); + stream.println("
    "); + for (FieldElement field : fields) { + documentMemberField(field, classElement); + } + stream.println("
    "); + } + + private void documentConstructors(ClassElement classElement, + List constructors) { + if (constructors.size() == 0) { + return; + } + stream.println("

    Constructors

    "); + stream.println("
    "); + for (ConstructorElement constr : constructors) { + documentConstructor(constr, classElement); + } + stream.println("
    "); + } + + private void documentMethods(ClassElement classElement, List methods) { + if (methods.size() == 0) { + return; + } + Collections.sort(methods, new ElementNameComparator()); + stream.println("

    Methods

    "); + stream.println("
    "); + for (MethodElement method : methods) { + documentMemberField(method, classElement); + } + stream.println("
    "); + } + + private void documentClass(ClassElement classElement) { + String name = classElement.getName(); + String fileName = outputDirectory + File.separator + name + ".html"; + try { + stream = new PrintStream(fileName); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + stream.println(""); + stream.println(""); + + // Head. + stream.println("\n"); + stream.println(""); + stream.print(""); + stream.print("Dart : Libraries : "); + stream.print(library + " : "); + stream.print(name); + stream.println(""); + stream.println(""); + + // Body. + stream.println("\n"); + + stream.println("\n
    \n"); + + stream.print("

    "); + if (classElement.isInterface()) { + stream.print("interface "); + } else { + stream.print("class "); + } + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + name, + name + "::" + name); + anchors.add(linkInfo); + stream.print(linkInfo.anchorStartTag()); + stream.print(name); + printClassTypeParameters(classElement); + stream.println("

    "); + + stream.println("\n
    "); + printSupertype(classElement); + printInterfaces(classElement); + printSubTypes(classElement); + stream.println("\n
    "); + + stream.println("\n
    "); + printElementComment(classElement); + stream.println("
    "); + + List fields = new ArrayList(10); + List methods = new ArrayList(10); + List constructors = new ArrayList(10); + + getNonPrivateMembers(classElement, fields, methods, constructors); + + stream.println("\n
    "); + documentFields(classElement, fields); + stream.println("
    "); + + stream.println("\n
    "); + documentConstructors(classElement, constructors); + stream.println("
    "); + + stream.println("\n
    "); + documentMethods(classElement, methods); + stream.println("
    "); + + stream.println("\n
    \n"); + stream.println(""); + } + + private void getNonPrivateMembers(ClassElement classElement, + List fields, + List methods, + List constructors) { + for (Element member : classElement.getMembers()) { + String elementName = member.getName(); + if (isPrivateName(elementName)) { + continue; + } + switch (member.getKind()) { + case METHOD: + methods.add((MethodElement) member); + break; + case FIELD: + fields.add((FieldElement) member); + break; + } + } + + List constructorList = classElement.getConstructors(); + for (ConstructorElement c : constructorList) { + if (isPrivateName(c.getName())) { + continue; + } + constructors.add(c); + } + } + + private void documentMemberField(MethodElement method, ClassElement classElement) { + String className = classElement.getName(); + stream.println("
    "); + stream.print(""); + if (method.isStatic()) { + stream.print("static "); + } + Type returnType = ((FunctionType) method.getType()).getReturnType(); + String returnTypeName = returnType.getElement().getName(); + if (!printElementReference(method, returnTypeName)) { + escapeAndPrint(stream, returnTypeName); + } + stream.print(" "); + + String elementReference = className + "::" + anchorEscape(method.getName()); + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + className, + elementReference); + anchors.add(linkInfo); + stream.print(linkInfo.anchorStartTag()); + escapeAndPrint(stream, method.getName()); + stream.print(""); + printMethodParameterList(method, classElement); + stream.print(""); + stream.println("
    "); + stream.println("
    "); + printElementComment(method); + stream.println("
    "); + } + + private void documentMemberField(FieldElement field, ClassElement classElement) { + String className = classElement.getName(); + stream.println("
    "); + stream.print(""); + String typeName = field.getType().getElement().getName(); + if (!printElementReference(field, typeName)) { + escapeAndPrint(stream, typeName); + } + stream.println(""); + stream.print(""); + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + className, + className + "::" + field.getName()); + anchors.add(linkInfo); + stream.print(linkInfo.anchorStartTag()); + stream.println(field.getName()); + stream.println(""); + stream.println("
    "); + stream.println("
    "); + printElementComment(field); + stream.println("
    "); + + } + + private void documentConstructor(ConstructorElement constr, ClassElement classElement) { + String className = classElement.getName(); + stream.println("
    "); + stream.print(""); + printClassReference(constr.getConstructorType()); + if (!constr.getName().isEmpty()) { + stream.print("."); + String elementReference = className + "::" + constr.getName(); + LinkInformation linkInfo = new LinkInformation(classElement.getLibrary().getName(), + className, + elementReference); + anchors.add(linkInfo); + stream.print(linkInfo.anchorStartTag()); + stream.print(constr.getName()); + stream.print(""); + } + printMethodParameterList(constr, classElement); + stream.println(""); + stream.println("
    "); + stream.println("
    "); + printElementComment(constr); + stream.println("
    "); + } + + private void documentToplevelMethod(MethodElement method) { + } + + private void documentToplevelField(FieldElement field) { + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/doc/ElementNameComparator.java b/compiler/java/com/google/dart/compiler/backend/doc/ElementNameComparator.java new file mode 100644 index 00000000000..09ca2e68e2f --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/doc/ElementNameComparator.java @@ -0,0 +1,18 @@ +// 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. + +package com.google.dart.compiler.backend.doc; + +import com.google.dart.compiler.resolver.Element; + +import java.util.Comparator; + +class ElementNameComparator implements Comparator { + @Override + public int compare(Element e1, Element e2) { + String name1 = e1.getName(); + String name2 = e2.getName(); + return name1.compareTo(name2); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/doc/LinkInformation.java b/compiler/java/com/google/dart/compiler/backend/doc/LinkInformation.java new file mode 100644 index 00000000000..9113a76007b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/doc/LinkInformation.java @@ -0,0 +1,42 @@ +// 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. + +package com.google.dart.compiler.backend.doc; + +public class LinkInformation { + public String libName; + public String className; + public String elementName; + + public LinkInformation(String libName, String className, String elementName) { + this.libName = libName; + this.className = className; + this.elementName = elementName; + } + + @Override + public boolean equals(Object other) { + if (other instanceof LinkInformation) { + LinkInformation otherLink = (LinkInformation) other; + return libName.equals(otherLink.libName) && + className.equals(otherLink.className) && + elementName.equals(otherLink.elementName); + } else { + return false; + } + } + + @Override + public int hashCode() { + return elementName.hashCode(); + } + + public String anchorReferenceStartTag() { + return ""; + } + + public String anchorStartTag() { + return ""; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java b/compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java new file mode 100644 index 00000000000..41511a0435a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/isolate/DartIsolateStubGenerator.java @@ -0,0 +1,754 @@ +// 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. + +package com.google.dart.compiler.backend.isolate; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartContext; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartVisitor; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.backend.common.AbstractBackend; +import com.google.dart.compiler.resolver.CoreTypeProvider; + +/** + * Generate code for proxies and dispatchers for cross-isolate calls. + */ +public class DartIsolateStubGenerator extends AbstractBackend { + private final Set stubInterfaces; + private PrintStream outStream; + + public DartIsolateStubGenerator(final Set classes, String out) + throws FileNotFoundException { + outStream = new PrintStream(out); + stubInterfaces = classes; + } + + private void autoGenerate(DartUnit unit) { + if (stubInterfaces.isEmpty()) { + return; + } + + DartVisitor visitor = new DartVisitor() { + private boolean first = true; + + @Override + public boolean visit(DartClass clazz, DartContext ctx) { + if (clazz.isInterface() && stubInterfaces.contains(clazz.getClassName())) { + if (!first) + nl(); + first = false; + p("/* class = " + clazz.getClassName() + " (" + + clazz.getSource().getName() + ": " + clazz.getSourceLine() + ") */"); + nl(); + nl(); + generateProxyClass(clazz); + nl(); + generateDispatchClass(clazz); + nl(); + generateIsolateClass(clazz); + } + return false; + } + + }; + visitor.accept(unit); + outStream.flush(); + } + + private static boolean isConstructor(DartMethodDefinition x) { + return x.getSymbol().isConstructor(); + } + + private static boolean isSimpleType(DartTypeNode x) { + if (!x.getTypeArguments().isEmpty()) + return false; + if (!(x.getIdentifier() instanceof DartIdentifier)) + return false; + String name = ((DartIdentifier)x.getIdentifier()).getTargetName(); + if (name.equals("int") || name.equals("void")) + return true; + return false; + } + + private static boolean isVoid(DartTypeNode x) { + if (!isSimpleType(x)) + return false; + return ((DartIdentifier)x.getIdentifier()).getTargetName().equals("void"); + } + + private static boolean isProxyType(DartTypeNode x) { + if (!x.getTypeArguments().isEmpty()) + return false; + if (!(x.getIdentifier() instanceof DartIdentifier)) + return false; + return ((DartIdentifier)x.getIdentifier()).getTargetName().endsWith("$Proxy"); + } + + private void p(String str) { + outStream.print(str); + } + + private void nl() { + outStream.println(); + } + + class ProxifyingVisitor extends DartVisitor { + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + printTypeArguments(x); + if (!isSimpleType(x)) + p("$Proxy"); + return false; + } + + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + } + + private void printTypeArguments(DartTypeNode x) { + List arguments = x.getTypeArguments(); + if (arguments != null && !arguments.isEmpty()) { + p("<"); + printParams(arguments); + p(">"); + } + } + + private void printParams(List nodes) { + boolean first = true; + for (DartNode node : nodes) { + if (!first) { + p(", "); + } + DartVisitor param = new DartVisitor() { + @Override + public boolean visit(DartParameter x, DartContext ctx) { + if (x.getModifiers().isFinal()) { + p("final "); + } + if (x.getTypeNode() != null) { + accept(x.getTypeNode()); + p(" "); + } + if (x.getModifiers().isVariadic()) { + p("..."); + } + accept(x.getName()); + if (x.getFunctionParameters() != null) { + p("("); + printParams(x.getFunctionParameters()); + p(")"); + } + if (x.getDefaultExpr() != null) { + p(" = "); + accept(x.getDefaultExpr()); + } + return false; + } + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + printTypeArguments(x); + return false; + } + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + }; + param.accept(node); + first = false; + } + } + + private void printProxyInterfaceFunctions(DartClass clazz) { + DartVisitor visitor = new DartVisitor() { + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + if (isConstructor(x)) { + return false; + } + nl(); + final DartFunction func = x.getFunction(); + final DartTypeNode returnTypeNode = func.getReturnTypeNode(); + final boolean isVoid = isVoid(returnTypeNode); + final boolean isSimple = isSimpleType(returnTypeNode); + final boolean isProxy = isProxyType(returnTypeNode); + p(" "); + if (!isVoid && isSimple) { + p("Promise<"); + } + accept(returnTypeNode); + if (!isVoid) { + if (isSimple) { + p(">"); + } else if (!isProxy) { + p("$Proxy"); + } + } + p(" "); + accept(x.getName()); + p("("); + printParams(func.getParams()); + p(");"); + nl(); + + return false; + } + + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + printTypeArguments(x); + return false; + } + + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + + }; + visitor.acceptList(clazz.getMembers()); + } + + /** + * Produce something looking like: + * + * + * interface Purse$Proxy { + * void init(Mint$Proxy mint, int balance); + * + * Promise queryBalance(); + * + * Purse$Proxy sproutPurse(); + * + * void deposit(int amount, Purse$Proxy source); + * } + * + * class Purse$ProxyImpl extends Proxy implements Purse$Proxy { + * Purse$ProxyImpl(Promise port) : super.forReply(port) { } + * Purse$ProxyImpl.forIsolate(Proxy isolate) : super.forReply(isolate.call([null])) { } + * factory Purse$ProxyImpl.createIsolate() { + * Proxy isolate = new Proxy.forIsolate(new Purse$Dispatcher$Isolate()); + * return new Purse$ProxyImpl.forIsolate(isolate); + * } + * factory Purse$ProxyImpl.localProxy(Purse obj) { + * return new Purse$ProxyImpl(new Promise.fromValue(Dispatcher.serve( + * new Purse$Dispatcher(obj)))); + * } + * + * void init(Mint$Proxy mint, int balance) { + * this.send(["init", mint, balance]); + * } + * + * Promise queryBalance() { + * return this.call(["queryBalance"]); + * } + * + * Purse$Proxy sproutPurse() { + * return new Purse$ProxyImpl(this.call(["sproutPurse"])); + * } + * + * void deposit(int amount, Purse$Proxy source) { + * this.send(["deposit", amount, source]); + * } + * } + */ + private void generateProxyClass(DartClass clazz) { + String name = clazz.getClassName(); + p("interface " + name + "$Proxy {"); + printProxyInterfaceFunctions(clazz); + p("}"); + nl(); + nl(); + + p("class " + name + "$ProxyImpl extends Proxy implements " + name + "$Proxy {"); + nl(); + p(" " + name + "$ProxyImpl(Promise port) : super.forReply(port) { }"); + nl(); + p(" " + name + + "$ProxyImpl.forIsolate(Proxy isolate) : super.forReply(isolate.call([null])) { }"); + nl(); + + p(" factory " + name + "$ProxyImpl.createIsolate() {"); + nl(); + p(" Proxy isolate = new Proxy.forIsolate(new " + name + "$Dispatcher$Isolate());"); + nl(); + p(" return new " + name + "$ProxyImpl.forIsolate(isolate);"); + nl(); + p(" }"); + nl(); + + // FIXME(benl, kasperl): We should be able to get hold of our existing dispatcher, not have to + // create a new one... + p(" factory " + name + "$ProxyImpl.localProxy(" + name + " obj) {"); + nl(); + p(" return new " + name + "$ProxyImpl(new Promise.fromValue(Dispatcher.serve(new " + + name + "$Dispatcher(obj))));"); + nl(); + p(" }"); + nl(); + + DartVisitor visitor = new DartVisitor() { + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + if (isConstructor(x)) { + return false; + } + nl(); + final DartFunction func = x.getFunction(); + final DartTypeNode returnTypeNode = func.getReturnTypeNode(); + final boolean isVoid = isVoid(returnTypeNode); + final boolean isSimple = isSimpleType(returnTypeNode); + final boolean isProxy = isProxyType(returnTypeNode); + p(" "); + if (!isVoid && isSimple) { + p("Promise<"); + } + accept(returnTypeNode); + if (!isVoid) { + if (isSimple) { + p(">"); + } else if (!isProxy) { + p("$Proxy"); + } + } + p(" "); + accept(x.getName()); + p("("); + printParams(func.getParams()); + p(") {"); + nl(); + p(" "); + if (!isVoid) { + p("return "); + if (!isSimple) { + p("new "); + accept(returnTypeNode); + if (!isProxy) { + p("$Proxy"); + } + p("Impl("); + } + } + p("this."); + if (isVoid) { + p("send"); + } else { + p("call"); + } + p("([\""); + accept(x.getName()); + p("\""); + DartVisitor params = new DartVisitor() { + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(", "); + p(x.getTargetName()); + return false; + } + + @Override + public boolean visit(DartParameter x, DartContext ctx) { + accept(x.getName()); + return false; + } + }; + params.acceptList(func.getParams()); + p("])"); + if (!isSimple) { + p(")"); + } + p(";"); + nl(); + + p(" }"); + nl(); + + return false; + } + + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + printTypeArguments(x); + return false; + } + + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + + }; + visitor.acceptList(clazz.getMembers()); + p("}"); + nl(); + } + + private void printSelector(List members) { + boolean first = true; + for (DartNode member : members) { + if (first) { + p(" "); + } else { + p(" else "); + } + p("if (command == \""); + printFunctionName(member); + p("\") {"); + nl(); + unpackParams(member); + callTarget((DartMethodDefinition)member); + p(" }"); + first = false; + } + p(" else {"); + nl(); + p(" // TODO(kasperl,benl): Somehow throw an exception instead."); + nl(); + p(" reply(\"Exception: command not understood.\");"); + nl(); + p(" }"); + nl(); + } + + private void callTarget(DartMethodDefinition member) { + if (isConstructor(member)) { + return; + } + p(" "); + boolean isVoid = isVoid(member.getFunction().getReturnTypeNode()); + if (!isVoid) { + printReturnType(member); + p(" "); + printFunctionName(member); + p(" = "); + } + p("target."); + printFunctionName(member); + p("("); + printParamNames(member); + p(");"); + nl(); + String returnType = stringReturnType(member); + if (stubInterfaces.contains(returnType)) { + p(" SendPort port = Dispatcher.serve(new " + returnType + "$Dispatcher("); + printFunctionName(member); + p("));"); + nl(); + p(" reply(port);"); + nl(); + } else if (!isVoid) { + p(" reply("); + printFunctionName(member); + p(");"); + nl(); + } + } + + private static String stringReturnType(DartMethodDefinition member) { + return stringType(member.getFunction().getReturnTypeNode()); + } + + private void printParamNames(DartMethodDefinition member) { + boolean first = true; + for(DartParameter param : member.getFunction().getParams()) { + if (!first) { + p(", "); + } + printName(param.getName()); + first = false; + } + } + + private void printName(DartExpression name) { + DartVisitor visitor = new DartVisitor() { + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + }; + visitor.accept(name); + } + + private void printReturnType(DartMethodDefinition member) { + printType(member.getFunction().getReturnTypeNode()); + } + + private void printType(DartTypeNode type) { + DartVisitor visitor = new DartVisitor() { + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + printTypeArguments(x); + return false; + } + }; + visitor.accept(type); + } + + private static String stringType(DartTypeNode type) { + final StringBuilder strType = new StringBuilder(); + DartVisitor visitor = new DartVisitor() { + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + strType.append(x.getTargetName()); + return false; + } + + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + List arguments = x.getTypeArguments(); + if (arguments != null && !arguments.isEmpty()) { + strType.append("<"); + // Really we should do + // strType.append(stringParams(arguments)); + // but for now, this will suffice + strType.append("..."); + strType.append(">"); + } + return false; + } + }; + visitor.accept(type); + return strType.toString(); + } + + private void unpackParams(DartNode member) { + DartVisitor visitor = new DartVisitor() { + private int pos; + + @Override + public boolean visit(DartTypeNode x, DartContext ctx) { + accept(x.getIdentifier()); + printTypeArguments(x); + return false; + } + + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + pos = 1; + for (DartParameter param : x.getFunction().getParams()) { + p(" "); + accept(param); + nl(); + ++pos; + } + return false; + } + + @Override + public boolean visit(DartParameter x, DartContext ctx) { + boolean isSimpleType = isSimpleType(x.getTypeNode()); + + accept(x.getTypeNode()); + p(" "); + accept(x.getName()); + p(" = "); + if (!isSimpleType) { + p("new "); + accept(x.getTypeNode()); + p("Impl(new Promise.fromValue("); + } + p("message[" + pos + "]"); + if (!isSimpleType) { + p("))"); + } + p(";"); + return false; + } + }; + visitor.accept(member); + } + + private void printFunctionName(DartNode member) { + DartVisitor functionName = new DartVisitor() { + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + accept(x.getName()); + return false; + } + + @Override + public boolean visit(DartIdentifier x, DartContext ctx) { + p(x.getTargetName()); + return false; + } + }; + functionName.accept(member); + } + + /** + * Generate a dispatcher, looking like: + * + * class Purse$Dispatcher extends Dispatcher { + * Purse$Dispatcher(Purse thing) : super(thing) { } + * + * void process(var message, void reply(var response)) { + * String command = message[0]; + * if (command == "queryBalance") { + * int queryBalance = target.queryBalance(); + * reply(queryBalance); + * } else if (command == "sproutPurse") { + * Purse sproutPurse = target.sproutPurse(); + * SendPort port = Dispatcher.serve(new Purse$Dispatcher(sproutPurse)); + * reply(port); + * } else if (command == "deposit") { + * int amount = message[1]; + * Proxy source = new Proxy.forPort(message[2]); + * target.deposit(amount, source); + * } else { + * // TODO(kasperl,benl): Somehow throw an exception instead. + * reply("Exception: command not understood."); + * } + * } + * } + */ + private void generateDispatchClass(DartClass clazz) { + String name = clazz.getClassName(); + p("class " + name + "$Dispatcher extends Dispatcher<" + name + "> {"); + nl(); + p(" " + name + "$Dispatcher(" + name + " thing) : super(thing) { }"); + nl(); + nl(); + p(" void process(var message, void reply(var response)) {"); + nl(); + p(" String command = message[0];"); + nl(); + printSelector(clazz.getMembers()); + p(" }"); + nl(); + p("}"); + nl(); + } + + /** + * Generate a dispatcher isolate, looking like: + * + * class Purse$Dispatcher extends Dispatcher { + * Purse$Dispatcher(Purse thing) : super(thing) { } + * + * void process(var message, void reply(var response)) { + * String command = message[0]; + * if (command == "Purse") { + * } else if (command == "init") { + * Mint$Proxy mint = new Mint$ProxyImpl(new Promise.fromValue(message[1])); + * int balance = message[2]; + * target.init(mint, balance); + * } else if (command == "queryBalance") { + * int queryBalance = target.queryBalance(); + * reply(queryBalance); + * } else if (command == "sproutPurse") { + * Purse sproutPurse = target.sproutPurse(); + * SendPort port = Dispatcher.serve(new Purse$Dispatcher(sproutPurse)); + * reply(port); + * } else if (command == "deposit") { + * int amount = message[1]; + * Purse$Proxy source = new Purse$ProxyImpl(new Promise.fromValue(message[2])); + * target.deposit(amount, source); + * } else { + * // TODO(kasperl,benl): Somehow throw an exception instead. + * reply("Exception: command not understood."); + * } + * } + * } + */ + private void generateIsolateClass(DartClass clazz) { + String name = clazz.getClassName(); + p("class " + name + "$Dispatcher$Isolate extends Isolate {"); + nl(); + p(" " + name + "$Dispatcher$Isolate() : super() { }"); + nl(); + nl(); + p(" void main() {"); + nl(); + p(" this.port.receive(void _(var message, SendPort replyTo) {"); + nl(); + p(" " + name + " thing = new " + name + "();"); + nl(); + p(" SendPort port = Dispatcher.serve(new " + name + "$Dispatcher(thing));"); + nl(); + p(" Proxy proxy = new Proxy.forPort(replyTo);"); + nl(); + p(" proxy.send([port]);"); + nl(); + p(" });"); + nl(); + p(" }"); + nl(); + p("}"); + nl(); + } + + @Override + public boolean isOutOfDate(DartSource src, DartCompilerContext context) { + return true; + } + + @Override + public void compileUnit(DartUnit unit, DartSource src, DartCompilerContext context, + CoreTypeProvider typeProvider) throws IOException { + autoGenerate(unit); + } + + @Override + public void packageApp(LibrarySource app, Collection libraries, + DartCompilerContext context, CoreTypeProvider typeProvider) + throws IOException { + // TODO Auto-generated method stub + } + + @Override + public String getAppExtension() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getSourceMapExtension() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java b/compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java new file mode 100644 index 00000000000..af213a9dc2e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/AbstractJsBackend.java @@ -0,0 +1,467 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.Multiset; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.backend.common.AbstractBackend; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.metrics.DartEventType; +import com.google.dart.compiler.metrics.Tracer; +import com.google.dart.compiler.metrics.Tracer.TraceEvent; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.MethodElement; + +import java.io.IOException; +import java.io.Writer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Set; + +/** + * Methods common to the ClosureJsBackend and JavascriptBackend. + * @author johnlenz@google.com (John Lenz) + */ +public abstract class AbstractJsBackend extends AbstractBackend { + + private static final String ROOT_PART_NAME = ""; + private static final String STATICS_PART_NAME = "$statics$"; + private static final String SEPARATOR_PART_NAME = "$seperator$"; + + protected static class Part { + final LibraryUnit lib; + final DartUnit unit; + final String part; + final ClassElement element; + final ClassElement superElement; + + public Part(LibraryUnit lib, DartUnit unit, String part, + ClassElement element, ClassElement superElement) { + this.lib = lib; + this.unit = unit; + this.element = element; + this.part = part; + this.superElement = superElement; + } + + @Override + public int hashCode() { + if (element != null) { + return element.hashCode(); + } + final int prime = 31; + int result = 1; + result = prime * result + ((part == null) ? 0 : part.hashCode()); + result = prime * result + ((unit == null) ? 0 : unit.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Part other = (Part) obj; + if (element != null) { + return element.equals(other.element); + } + return unit.equals(other.unit) && part.equals(other.part); + } + } + + protected static interface DepsCallback { + void visitNative(LibraryUnit libUnit, LibraryNode node) throws IOException; + void visitPart(Part part) throws IOException; + } + + protected static class DependencyBuilder { + private final List parts; + private final Part staticSeparator = new Part(null, null, SEPARATOR_PART_NAME, null, null); + + static void build(LibraryUnit libUnit, DepsCallback callback) throws IOException { + DependencyBuilder builder = new DependencyBuilder(); + builder.gatherParts(libUnit); + builder.sortParts(); + builder.writeParts(callback); + } + + private DependencyBuilder() { + this.parts = new ArrayList(); + } + + private void gatherParts(LibraryUnit libUnit) { + gatherParts(libUnit, new HashSet()); + } + + private void gatherParts(LibraryUnit libUnit, Set seenLibs) { + // Avoid cycles. + if (seenLibs.contains(libUnit)) { + return; + } + seenLibs.add(libUnit); + + // Visit dependencies first. + for (LibraryUnit importUnit : libUnit.getImports()) { + gatherParts(importUnit, seenLibs); + } + + for (DartUnit unit : libUnit.getUnits()) { + DartSource src = unit.getSource(); + if (src != null) { + // get the list of source source parts + gatherUnitParts(libUnit, unit); + } + } + } + + private void gatherUnitParts(LibraryUnit libUnit, DartUnit unit) { + List nodes = ((DartUnit)unit.getNormalizedNode()).getTopLevelNodes(); + for (DartNode node : nodes) { + DartNode norm = node.getNormalizedNode(); + if (norm instanceof DartClass) { + DartClass clasz = (DartClass)norm; + ClassElement superElement = null; + DartTypeNode superType = clasz.getSuperclass(); + if (superType != null) { + superElement = (ClassElement) clasz.getSuperclass().getSymbol(); + } + + parts.add(new Part(libUnit, unit, + clasz.getClassName(), clasz.getSymbol(), superElement)); + } + } + + parts.add(new Part(libUnit, unit, ROOT_PART_NAME, null, null)); // top-level bits + parts.add(new Part(libUnit, unit, STATICS_PART_NAME, null, null)); // static initializer bits + } + + /** + * @param items The list of items to sort. + * @param deps A map of dependencies between items. + * @return A list of items in dependency order. + */ + private static List topologicalStableSort( + List items, Multimap deps) { + final Map originalIndex = Maps.newHashMap(); + for (int i = 0; i < items.size(); i++) { + originalIndex.put(items.get(i), i); + } + + PriorityQueue inDegreeZero = new PriorityQueue(items.size(), + new Comparator() { + @Override + public int compare(T a, T b) { + return originalIndex.get(a).intValue() - + originalIndex.get(b).intValue(); + } + }); + List result = Lists.newArrayList(); + + Multiset inDegree = HashMultiset.create(); + Multimap reverseDeps = ArrayListMultimap.create(); + Multimaps.invertFrom(deps, reverseDeps); + + // First, add all the inputs with in-degree 0. + for (T item : items) { + Collection itemDeps = deps.get(item); + inDegree.add(item, itemDeps.size()); + if (itemDeps.isEmpty()) { + inDegreeZero.add(item); + } + } + + // Then, iterate to a fixed point over the reverse dependency graph. + while (!inDegreeZero.isEmpty()) { + T item = inDegreeZero.remove(); + result.add(item); + for (T inWaiting : reverseDeps.get(item)) { + inDegree.remove(inWaiting, 1); + if (inDegree.count(inWaiting) == 0) { + inDegreeZero.add(inWaiting); + } + } + } + + return result; + } + + /** + * Build a map of dependencies between Parts. + * @param parts The parts to build dependencies from. + */ + private Multimap buildDependencyMap(List parts) { + // Add a Part to act as separator between class initialization + // and static initialization. Statics may depend on the classes + // being properly setup. + parts.add(staticSeparator); + + final Map elementToPartMap = Maps.newHashMap(); + for (Part part : parts) { + elementToPartMap.put(part.element, part); + } + + // Get the direct dependencies. + final Multimap deps = HashMultimap.create(); + for (Part part : parts) { + if (part.superElement != null) { + Part superPart = elementToPartMap.get(part.superElement); + if (superPart != null) { + deps.put(part, superPart); + } + } + + // Don't add a dependency on itself. + if (part != staticSeparator) { + if (part.part.equals(STATICS_PART_NAME)) { + // Push all statics after classes. + deps.put(part, staticSeparator); + } else { + // All classes before statics. + deps.put(staticSeparator, part); + } + } + } + return deps; + } + + /** + * Sort the parts based on their dependencies. + */ + private void sortParts() { + List unsortedParts = parts; + + Multimap deps = buildDependencyMap(unsortedParts); + List sortParts = topologicalStableSort(unsortedParts, deps); + + parts.clear(); + parts.addAll(sortParts); + } + + private long writeParts(DepsCallback callback) + throws IOException { + long charsWritten = 0; + Set seenLibs = new HashSet(); + for (Part part : parts) { + writePart(part, callback, seenLibs); + } + return charsWritten; + } + + private void writePart(Part part, DepsCallback callback, Set seenLibs) + throws IOException { + + // Don't try to do anything with the fake separator part. + if (part == staticSeparator) { + return; + } + + // Avoid cycles. + if (!seenLibs.contains(part.lib)) { + seenLibs.add(part.lib); + // Prepend all native JS for this library. + for (LibraryNode node : part.lib.getNativePaths()) { + callback.visitNative(part.lib, node); + } + } + + callback.visitPart(part); + } + } + + protected final DartMangler mangler = new DollarMangler(); + + protected Map translateToJS(DartUnit unit, DartCompilerContext context, + CoreTypeProvider typeProvider) { + TraceEvent logEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.TRANSLATE_TO_JS, "unit", + unit.getSourceName()) : null; + + OptimizationStrategy optimizationStrategy = shouldOptimize() ? + new BasicOptimizationStrategy(unit, typeProvider) : + new NoOptimizationStrategy(unit, typeProvider); + + try { + TraceEvent normalizeEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.JS_NORMALIZE, "unit", + unit.getSourceName()) : null; + try { + // Normalize front-end AST for back-end consumption. + unit = (DartUnit) (new Normalizer()).exec(unit, typeProvider, optimizationStrategy) + .getNormalizedNode(); + } finally { + Tracer.end(normalizeEvent); + } + + // TODO(floitsch.: Make namer configurable. + JsNamer namer = new JsPrettyNamer(); + + Map parts = new LinkedHashMap(); + + List topNodes = unit.getTopLevelNodes(); + + // Generate an id for this unit that can be used to make globally unique + // identifiers. + String baseUnitId = generateBaseUnitId(unit); + int partIndex = 0; + + // Translate the AST to JS. + JsProgram nonClassStatements = new JsProgram(baseUnitId + partIndex++); + GenerateJavascriptAST nonClassGenerator = null; + TranslationContext nonClassTranslationContext = null; + + + JsProgram staticInitStatements = new JsProgram(baseUnitId + partIndex++); + JsBlock staticInitBlock = staticInitStatements.getGlobalBlock(); + + for (DartNode node : topNodes) { + node = node.getNormalizedNode(); + if (node instanceof DartClass) { + // TODO: Don't write out *.js for interfaces -- there are a lot of them + TraceEvent nodeEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.TRANSLATE_NODE, "unit", + unit.getSourceName(), "node", node.getSymbol().getOriginalSymbolName()) : null; + try { + // Translate the AST to JS. + JsProgram program = new JsProgram(baseUnitId + partIndex++); + TranslationContext translationContext = TranslationContext.createContext(unit, program, + mangler); + + // Generate the Javascript AST. + GenerateJavascriptAST generator = + new GenerateJavascriptAST(unit, typeProvider, context, optimizationStrategy); + generator.translateNode(translationContext, node, staticInitBlock); + + TraceEvent namerEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.NAMER, "unit", + unit.getSourceName()) : null; + try { + namer.exec(program); + } finally { + Tracer.end(namerEvent); + } + + parts.put(((DartClass) node).getClassName(), program); + } finally { + Tracer.end(nodeEvent); + } + } else { + if (nonClassGenerator == null) { + TraceEvent genInitEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.GEN_AST_INIT, "unit", + unit.getSourceName()) : null; + try { + nonClassTranslationContext = TranslationContext.createContext(unit, + nonClassStatements, mangler); + nonClassGenerator = new GenerateJavascriptAST(unit, typeProvider, context, + optimizationStrategy); + } finally { + Tracer.end(genInitEvent); + } + } + + nonClassGenerator.translateNode(nonClassTranslationContext, node, staticInitBlock); + } + } + + TraceEvent namerEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.NAMER, "unit", unit.getSourceName()) + : null; + try { + namer.exec(nonClassStatements); + } finally { + Tracer.end(namerEvent); + } + + // Out-of-date checks rely on the root JS file existing, even if it is empty + parts.put(ROOT_PART_NAME, nonClassStatements); + + // Only add static parts if they are not empty + for (int i = 0; i < staticInitStatements.getFragmentCount(); ++i) { + if (!staticInitStatements.getFragmentBlock(i).getStatements().isEmpty()) { + parts.put(STATICS_PART_NAME, staticInitStatements); + break; + } + } + + return parts; + } finally { + Tracer.end(logEvent); + } + } + + private static String generateBaseUnitId(DartUnit unit) { + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError("Could not find MD5 digest"); + } + StringBuilder sb = new StringBuilder(); + byte[] md5 = md.digest(unit.getSource().getUri().toString().getBytes()); + // Only use the first 6 hex characters of the md5. + for (int i = 0; i < 3; i++) { + sb.append(Integer.toHexString((md5[i] & 0xf0) >> 4)); + sb.append(Integer.toHexString(md5[i] & 0xf)); + } + + return sb.toString(); + } + + protected void writeEntryPointCall(String entry, Writer out) throws IOException { + // Emit entry point call. + // TODO: Actually validate that this method exists. + // Small hack: the V8 arguments object is not an instance of Array. [].concat(arguments) + // copies the elements of the arguments and returns a proper array. + // However in Rhino this operation simply creates an array with 'arguments' as the first (and + // only) element. By calling "arguments.slice()" on it, a new array is returned, and the + // concatenation works again. + // TODO: Use a more robust check to test that the argument is + // array. + out.write("RunEntry(" + entry + ", this.arguments ?" + + " (this.arguments.slice ? [].concat(this.arguments.slice())" + + " : this.arguments) : []);"); + } + + protected String getMangledEntryPoint(DartCompilerContext context) { + MethodElement entry = context.getApplicationUnit().getElement().getEntryPoint(); + if (entry == null) { + return null; + } + + return mangler.mangleEntryPoint(entry, context.getApplicationUnit().getElement()); + } + + protected abstract boolean shouldOptimize(); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/BasicOptimizationStrategy.java b/compiler/java/com/google/dart/compiler/backend/js/BasicOptimizationStrategy.java new file mode 100644 index 00000000000..ee020d03a54 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/BasicOptimizationStrategy.java @@ -0,0 +1,458 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.Sets; +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartBinaryExpression; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNullLiteral; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.backend.common.TypeHeuristic; +import com.google.dart.compiler.backend.common.TypeHeuristic.FieldKind; +import com.google.dart.compiler.backend.common.TypeHeuristicImplementation; +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; + +import java.util.List; +import java.util.Set; + +class BasicOptimizationStrategy implements OptimizationStrategy { + + private final TypeHeuristic typeHeuristic; + private final CoreTypeProvider typeProvider; + private static final String NUMBER_IMPLEMENTATION = "NumberImplementation"; + private static final String STRING_IMPLEMENTATION = "StringImplementation"; + private static final String BOOL_IMPLEMENTATION = "BoolImplementation"; + + public BasicOptimizationStrategy(DartUnit unit, CoreTypeProvider typeProvider) { + this.typeHeuristic = new TypeHeuristicImplementation(unit, typeProvider); + this.typeProvider = typeProvider; + } + + @Override + public boolean canSkipOperatorShim(DartBinaryExpression x) { + // If both expressions are raw js number types, we can elide the operator shim. + Token operator = x.getOperator(); + switch (operator) { + case SHL: + case SAR: + case SHR: + case BIT_AND: + case BIT_OR: + case BIT_XOR: + case LT: + case GT: + case LTE: + case GTE: + case ADD: + case SUB: + case MUL: + case DIV: { + return (isNumericType(x.getArg1()) && isNumericType(x.getArg2())); + } + + case OR: + case AND: { + return ((isNumericType(x.getArg1()) && isNumericType(x.getArg2())) + || (isBooleanType(x.getArg1()) && isBooleanType(x.getArg2()))); + } + + case NE: + case EQ: { + DartExpression lhs = x.getArg1(); + DartExpression rhs = x.getArg2(); + return ((isNumericType(lhs) && (isNumericType(rhs) || isNullLiteral(rhs))) + || (isStringType(lhs) && (isStringType(rhs) || isNullLiteral(rhs))) + || (isBooleanType(lhs) && (isBooleanType(rhs) || isNullLiteral(rhs))) + || isNullLiteral(lhs) + || hasSingleImplementation(x)); + } + + case EQ_STRICT: + case NE_STRICT: { + return true; + } + } + + return false; + } + + @Override + public boolean canSkipOperatorShim(DartUnaryExpression x) { + Token op = x.getOperator(); + switch (op) { + case BIT_NOT: + case SUB: { + return (isNumericType(x.getArg()) && hasSingleImplementation(x)); + } + + case INC: + case DEC: { + assert !op.isUserDefinableOperator(); + return isNumericType(x.getArg()); + } + + case NOT: { + assert !op.isUserDefinableOperator(); + return isBooleanType(x.getArg()); + } + } + return false; + } + + @Override + public boolean canSkipArrayAccessShim(DartArrayAccess array, boolean isAssignee) { + Set impls = typeHeuristic.getImplementationsOf(array); + if (impls != null && impls.size() == 1) { + MethodElement impl = impls.iterator().next(); + Element arrayElement = TypeHeuristicImplementation.maybeGetTargetElement(array); + if (isAssignee && (arrayElement == null || arrayElement.getModifiers().isFinal())) { + return false; + } + return impl.getEnclosingElement().equals(typeProvider.getObjectArrayType().getElement()); + } + return false; + } + + private boolean isStringType(DartExpression expr) { + return isSingleType(expr, STRING_IMPLEMENTATION); + } + + static boolean isStringType(Set types) { + return isSingleType(types, STRING_IMPLEMENTATION); + } + + private boolean isBooleanType(DartExpression expr) { + return isSingleType(expr, BOOL_IMPLEMENTATION); + } + + static boolean isBooleanType(Set types) { + return isSingleType(types, BOOL_IMPLEMENTATION); + } + + private boolean isNumericType(DartExpression expr) { + return isSingleType(typeHeuristic.getTypesOf(expr), NUMBER_IMPLEMENTATION); + } + + static boolean isNumericType(Set types) { + return isSingleType(types, NUMBER_IMPLEMENTATION); + } + + private boolean isSingleType(DartExpression expr, String className) { + Set types = typeHeuristic.getTypesOf(expr); + return isSingleType(types, className); + } + + private boolean hasSingleImplementation(DartBinaryExpression expr) { + Set impl = typeHeuristic.getImplementationsOf(expr); + return ((impl != null) && (impl.size() == 1)); + } + + private boolean isNullLiteral(DartExpression expr) { + return expr instanceof DartNullLiteral; + } + + private static boolean isSingleType(Set types, String className) { + if (types.size() != 1) { + return false; + } + + Type type = types.iterator().next(); + if (type.getKind() == TypeKind.INTERFACE) { + return className.equals(type.getElement().getName()); + } + return false; + } + + /** + * Return {@link FieldElement} if the {@link DartIdentifier} is actually an access to the field + * and we can optimize away the call to the generated getter. + */ + @Override + public FieldElement findOptimizableFieldElementFor(DartExpression expr, FieldKind fieldKind) { + Set visited = Sets.newHashSet(); + FieldElement field = maybeGetInlineableField(expr, fieldKind, visited); + visited = null; + return field; + } + + private FieldElement maybeGetInlineableField(DartExpression expr, FieldKind fieldKind, + Set visited) { + if (visited.contains(expr)) { + // If field references itself, it will cause a cycle. in this case, we return null. + return null; + } + visited.add(expr); + + // if the field is referenced in a function expression we cannot inline it. + if (expr.getParent() instanceof DartFunctionObjectInvocation) { + return null; + } + + Set impls = typeHeuristic.getFieldImplementationsOf(expr, fieldKind); + if ((impls != null) && (impls.size() == 1)) { + FieldElement field = impls.iterator().next(); + + // Check if field is non-abstract and can be 'trivially' inlined. + if (isFieldInlinable(field, fieldKind)) { + return field; + } + + // Check if field is native and whitelisted. + if (isWhitelistedNativeField(field, fieldKind)) { + return field; + } + + // Check if field is abstract and has no side effect. + FieldElement backingField = maybeGetNonAbstractFieldGetter(field, fieldKind, visited); + if (backingField != null) { + return backingField; + } + } + return null; + } + + private FieldElement maybeGetNonAbstractFieldGetter(FieldElement field, FieldKind fieldKind, + Set visited) { + if (field.getModifiers().isAbstractField()) { + if (fieldKind.equals(FieldKind.GETTER) && (field.getGetter() != null)) { + DartMethodDefinition getter = (DartMethodDefinition) field.getGetter().getNode(); + if (getter != null && getter.getFunction() != null) { + DartFunction fnGetter = getter.getFunction(); + if (fnGetter.getBody() != null && fnGetter.getBody().getStatements() != null + && fnGetter.getBody().getStatements().size() == 1) { + DartStatement stmt = fnGetter.getBody().getStatements().iterator().next(); + if (stmt instanceof DartReturnStatement) { + DartReturnStatement returnStmt = (DartReturnStatement) stmt; + return maybeGetInlineableField(returnStmt.getValue(), fieldKind, visited); + } + } + } + } + } + return null; + } + + @Override + public Element findElementFor(DartMethodInvocation expr) { + Set impls = typeHeuristic.getImplementationsOf(expr); + if ((impls != null) && (impls.size() == 1)) { + return impls.iterator().next(); + } + return (Element) expr.getTargetSymbol(); + } + + @Override + public boolean canSkipNormalization(DartBinaryExpression expr) { + Token operator = expr.getOperator(); + Set types = typeHeuristic.getTypesOf(expr.getArg1()); + switch (operator) { + case ASSIGN_ADD: { + if (!canInlineSideEffect(expr.getArg1())) { + return false; + } + return isNumericType(types) || isStringType(types); + } + case ASSIGN_SUB: + case ASSIGN_MUL: + case ASSIGN_DIV: + case ASSIGN_SHR: + case ASSIGN_SAR: + case ASSIGN_SHL: + case ASSIGN_BIT_AND: + case ASSIGN_BIT_OR: + case ASSIGN_BIT_XOR: { + if (!canInlineSideEffect(expr.getArg1())) { + return false; + } + return isNumericType(types); + } + case AND: + case OR: { + return isNumericType(types) || isBooleanType(types); + } + case ASSIGN_TRUNC: + case ASSIGN_MOD: + // TRUNC and MOD cannot skip normalization as there is no 'native' javascript + // equivalent operator. + return false; + default: + throw new AssertionError("Internal Error: Unknown operator " + operator); + } + } + + @Override + public boolean canSkipNormalization(DartUnaryExpression expr) { + Token operator = expr.getOperator(); + Set types = typeHeuristic.getTypesOf(expr.getArg()); + switch (operator) { + case DEC: + case INC: { + // DEC, INC are not user definable. + if (!canInlineSideEffect(expr.getArg())) { + return false; + } + return isNumericType(types); + } + default: + throw new AssertionError("Internal Error: Unknown operator " + operator); + } + } + + @Override + public boolean isWhitelistedNativeField(FieldElement field, FieldKind fieldKind) { + // TODO (fabiomfv) : Given that we only whitelist two types and one field, hardcode the logic + // for now. If the number grows, consider moving to a map. + // Assumes the native field name is the same as the FieldElement name. + if (isNativeFieldWithAccessor(field, fieldKind)) { + Element fieldHolder = field.getEnclosingElement(); + if (fieldHolder.equals(typeProvider.getObjectArrayType().getElement()) + || fieldHolder.equals(typeProvider.getStringImplementationType().getElement())) { + return field.getName().equals("length"); + } + } + return false; + } + + private boolean canInlineSideEffect(DartExpression expr) { + if (expr instanceof DartArrayAccess) { + Set impls = typeHeuristic.getImplementationsOf(expr); + if (impls != null) { + for (MethodElement impl : impls) { + if (ElementKind.of(impl.getEnclosingElement()).equals(ElementKind.CLASS)) { + ClassElement cls = (ClassElement) impl.getEnclosingElement(); + Element indexAssignOp = cls.lookupLocalElement("operator []="); + if (indexAssignOp != null && !cls.getType().equals(typeProvider.getObjectArrayType())) { + return false; + } + } + } + return true; + } + } else { + Element element = TypeHeuristicImplementation.maybeGetTargetElement(expr); + if ((element != null) && element.getModifiers().isFinal()) { + return false; + } + switch (ElementKind.of(element)) { + case FIELD: { + return isFieldInlinable((FieldElement) element); + } + case PARAMETER: + case VARIABLE: + return true; + } + } + return false; + } + + private boolean isFieldInlinable(FieldElement field) { + return !field.isStatic() && !field.isDynamic() && !field.getModifiers().isAbstractField(); + } + + private boolean isFieldInlinable(FieldElement field, FieldKind fieldKind) { + return isFieldInlinable(field) + && (fieldKind != FieldKind.SETTER || !field.getModifiers().isFinal()); + } + + private boolean isNativeFieldWithAccessor(FieldElement field, FieldKind fieldKind) { + if (fieldKind == FieldKind.GETTER && field.getGetter() != null) { + return field.getGetter().getModifiers().isNative(); + } else if (fieldKind == FieldKind.SETTER && field.getSetter() != null) { + return field.getSetter().getModifiers().isNative(); + } + return false; + } + + private boolean hasSingleImplementation(DartExpression expr) { + Set impls = typeHeuristic.getImplementationsOf(expr); + return ((impls != null) && (impls.size() == 1)); + } + + @Override + public boolean canInlineInitializers(ConstructorElement constructorElement) { + // For now we only inline classes that don't have Only immediate subtypes of object that are + // not subclassed. + // We will refine this in the near future to include arbitrary class hierarchies. + ClassElement classElement = (ClassElement) constructorElement.getEnclosingElement(); + if (canEmitOptimizedClassConstructor(classElement)) { + Modifiers modifiers = constructorElement.getModifiers(); + if (modifiers.isRedirectedConstructor() || modifiers.isConstant()) { + return false; + } + return (classElement.isObjectChild() && (classElement.getSubtypes().size() == 1)); + } + return false; + } + + @Override + public boolean canEmitOptimizedClassConstructor(ClassElement classElement) { + // For now we only inline classes that don't have Only immediate subtypes of object that are + // not subclassed. + // We will refine this in the near future to include arbitrary class hierarchies. + if (classElement.getModifiers().isNative()) { + return false; + } + List constructors = classElement.getConstructors(); + if (constructors.size() != 1) { + return false; + } + ConstructorElement constructor = constructors.iterator().next(); + Modifiers modifiers = constructor.getModifiers(); + if (modifiers.isStatic() || modifiers.isConstant() || modifiers.isNative()) { + return false; + } + DartMethodDefinition method = (DartMethodDefinition) constructor.getNode(); + for (DartParameter param : method.getFunction().getParams()) { + if (param.getModifiers().isNamed() || (param.getDefaultExpr() != null)) { + return false; + } + } + for (DartInitializer initializer : method.getInitializers()) { + // TODO (fabiomfv) : + // Function expressions in initializers are being revisited due to the possiblity of having + // closures on 'this' that is not fully created at the time of initialization. Keeping the + // simplest case for now. Will revisit this in the next round. + if (initializer.getValue() instanceof DartFunctionExpression) { + return false; + } + } + return true; + } + + @Override + public boolean canOptimizeFunctionExpressionBind(DartFunctionExpression expr) { + DartFunction fn = expr.getFunction(); + if (fn != null) { + for (DartParameter param : fn.getParams()) { + if (param.getModifiers().isNamed()) { + return false; + } + } + } + return true; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/Cloner.java b/compiler/java/com/google/dart/compiler/backend/js/Cloner.java new file mode 100644 index 00000000000..b0dd804410e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/Cloner.java @@ -0,0 +1,253 @@ +// Copyright 2011, the Dart project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsNullLiteral; +import com.google.dart.compiler.backend.js.ast.JsNumberLiteral; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import com.google.dart.compiler.backend.js.ast.JsRegExp; +import com.google.dart.compiler.backend.js.ast.JsStringLiteral; +import com.google.dart.compiler.backend.js.ast.JsThisRef; +import com.google.dart.compiler.backend.js.ast.JsVisitor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * Implements actual cloning logic. We rely on the JsExpressions to provide + * traversal logic. The {@link #stack} field is used to accumulate + * already-cloned JsExpression instances. One gotcha that falls out of this is + * that argument lists are on the stack in reverse order, so lists should be + * constructed via inserts, rather than appends. + */ +public class Cloner extends JsVisitor { + protected final Stack stack = new Stack(); + private boolean successful = true; + + /** + * @param expression + * @return Return a clone of the expression tree + */ + public static JsExpression clone(JsExpression expression) { + Cloner c = new Cloner(); + c.accept(expression); + return c.getExpression(); + } + + @Override + public void endVisit(JsArrayAccess x, JsContext ctx) { + JsArrayAccess newExpression = new JsArrayAccess(); + newExpression.setIndexExpr(stack.pop()); + newExpression.setArrayExpr(stack.pop()); + stack.push(newExpression); + } + + @Override + public void endVisit(JsArrayLiteral x, JsContext ctx) { + JsArrayLiteral toReturn = new JsArrayLiteral(); + List expressions = toReturn.getExpressions(); + int size = x.getExpressions().size(); + while (size-- > 0) { + expressions.add(0, stack.pop()); + } + stack.push(toReturn); + } + + @Override + public void endVisit(JsBinaryOperation x, JsContext ctx) { + JsBinaryOperation toReturn = new JsBinaryOperation(x.getOperator()); + toReturn.setArg2(stack.pop()); + toReturn.setArg1(stack.pop()); + stack.push(toReturn); + } + + @Override + public void endVisit(JsBooleanLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsConditional x, JsContext ctx) { + JsConditional toReturn = new JsConditional(); + toReturn.setElseExpression(stack.pop()); + toReturn.setThenExpression(stack.pop()); + toReturn.setTestExpression(stack.pop()); + stack.push(toReturn); + } + + /** + * The only functions that would get be visited are those being used as + * first-class objects. + */ + @Override + public void endVisit(JsFunction x, JsContext ctx) { + // Set a flag to indicate that we cannot continue, and push a null so + // we don't run out of elements on the stack. + successful = false; + stack.push(null); + } + + /** + * Cloning the invocation allows us to modify it without damaging other call + * sites. + */ + @Override + public void endVisit(JsInvocation x, JsContext ctx) { + JsInvocation toReturn = new JsInvocation(); + List params = toReturn.getArguments(); + int size = x.getArguments().size(); + while (size-- > 0) { + params.add(0, stack.pop()); + } + toReturn.setQualifier(stack.pop()); + stack.push(toReturn); + } + + /** + * Do a deep clone of a JsNameRef. Because JsNameRef chains are shared + * throughout the AST, you can't just go and change their qualifiers when + * re-writing an invocation. + */ + @Override + public void endVisit(JsNameRef x, JsContext ctx) { + JsNameRef toReturn; + JsName name = x.getName(); + if (name != null) { + toReturn = new JsNameRef(name); + } else { + toReturn = new JsNameRef(x.getIdent()); + } + + if (x.getQualifier() != null) { + toReturn.setQualifier(stack.pop()); + } + stack.push(toReturn); + } + + @Override + public void endVisit(JsNew x, JsContext ctx) { + int size = x.getArguments().size(); + List arguments = new ArrayList(size); + while (size-- > 0) { + arguments.add(0, stack.pop()); + } + JsNew toReturn = new JsNew(stack.pop()); + toReturn.getArguments().addAll(arguments); + stack.push(toReturn); + } + + @Override + public void endVisit(JsNullLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsNumberLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsObjectLiteral x, JsContext ctx) { + JsObjectLiteral toReturn = new JsObjectLiteral(); + List inits = toReturn.getPropertyInitializers(); + + int size = x.getPropertyInitializers().size(); + while (size-- > 0) { + /* + * JsPropertyInitializers are the only non-JsExpression objects that we + * care about, so we just go ahead and create the objects in the loop, + * rather than expecting it to be on the stack and having to perform + * narrowing casts at all stack.pop() invocations. + */ + JsPropertyInitializer newInit = new JsPropertyInitializer(); + newInit.setValueExpr(stack.pop()); + newInit.setLabelExpr(stack.pop()); + + inits.add(0, newInit); + } + stack.push(toReturn); + } + + @Override + public void endVisit(JsPostfixOperation x, JsContext ctx) { + JsPostfixOperation toReturn = new JsPostfixOperation(x.getOperator()); + toReturn.setArg(stack.pop()); + stack.push(toReturn); + } + + @Override + public void endVisit(JsPrefixOperation x, JsContext ctx) { + JsPrefixOperation toReturn = new JsPrefixOperation(x.getOperator()); + toReturn.setArg(stack.pop()); + stack.push(toReturn); + } + + @Override + public void endVisit(JsRegExp x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsStringLiteral x, JsContext ctx) { + stack.push(x); + } + + @Override + public void endVisit(JsThisRef x, JsContext ctx) { + stack.push(new JsThisRef()); + } + + public JsExpression getExpression() { + return (successful && checkStack()) ? stack.peek() : null; + } + + private boolean checkStack() { + if (stack.size() > 1) { + throw new InternalCompilerException("Too many expressions on stack"); + } + + return stack.size() == 1; + } +} + diff --git a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java new file mode 100644 index 00000000000..577e80de4ee --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAst.java @@ -0,0 +1,76 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.Source; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.javascript.jscomp.AbstractCompiler; +import com.google.javascript.jscomp.SourceAst; +import com.google.javascript.jscomp.SourceFile; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.Node; + +/** + * Maps the DartC AST to a Closure Compiler input source. + * + * @author johnlenz@google.com (John Lenz) + */ +public class ClosureJsAst implements SourceAst { + + private static final long serialVersionUID = 1L; + + /* + * Root node of internal JS Compiler AST which represents the same source. + * In order to get the tree, getAstRoot() has to be called. + */ + private Node root; + private final JsProgram program; + private final Source source; + private final InputId inputId; + + public ClosureJsAst(JsProgram program, String inputName, Source source) { + assert(inputName != null); + this.program = program; + this.source = source; + this.inputId = new InputId(inputName); + } + + @Override + public void clearAst() { + root = null; + } + + @Override + public Node getAstRoot(AbstractCompiler compiler) { + if (root == null) { + createAst(compiler); + } + return root; + } + + @Override + public InputId getInputId() { + return inputId; + } + + @Override + public SourceFile getSourceFile() { + return null; + } + + @Override + public void setSourceFile(SourceFile file) { + throw new UnsupportedOperationException( + "ClosureJsAst cannot be associated with a SourceFile instance."); + } + + public String getSourceName() { + return source.getName(); + } + + private void createAst(AbstractCompiler compiler) { + root = new ClosureJsAstTranslator().translate(program, inputId, source); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java new file mode 100644 index 00000000000..160f40eb661 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsAstTranslator.java @@ -0,0 +1,716 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.base.Preconditions; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.backend.js.ast.HasName; +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral; +import com.google.dart.compiler.backend.js.ast.JsBreak; +import com.google.dart.compiler.backend.js.ast.JsCase; +import com.google.dart.compiler.backend.js.ast.JsCatch; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsContinue; +import com.google.dart.compiler.backend.js.ast.JsDebugger; +import com.google.dart.compiler.backend.js.ast.JsDefault; +import com.google.dart.compiler.backend.js.ast.JsDoWhile; +import com.google.dart.compiler.backend.js.ast.JsEmpty; +import com.google.dart.compiler.backend.js.ast.JsExprStmt; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFor; +import com.google.dart.compiler.backend.js.ast.JsForIn; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsIf; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsLabel; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsNode; +import com.google.dart.compiler.backend.js.ast.JsNullLiteral; +import com.google.dart.compiler.backend.js.ast.JsNumberLiteral; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsParameter; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import com.google.dart.compiler.backend.js.ast.JsRegExp; +import com.google.dart.compiler.backend.js.ast.JsReturn; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsStringLiteral; +import com.google.dart.compiler.backend.js.ast.JsSwitch; +import com.google.dart.compiler.backend.js.ast.JsSwitchMember; +import com.google.dart.compiler.backend.js.ast.JsThisRef; +import com.google.dart.compiler.backend.js.ast.JsThrow; +import com.google.dart.compiler.backend.js.ast.JsTry; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; +import com.google.dart.compiler.backend.js.ast.JsVars; +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import com.google.dart.compiler.backend.js.ast.JsWhile; +import com.google.dart.compiler.common.HasSymbol; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.common.Symbol; +import com.google.javascript.jscomp.AstValidator; +import com.google.javascript.rhino.InputId; +import com.google.javascript.rhino.JSDocInfoBuilder; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; +import com.google.javascript.rhino.jstype.SimpleSourceFile; +import com.google.javascript.rhino.jstype.StaticSourceFile; + +import java.util.HashMap; +import java.util.Map; + + +/** + * Translate a Dart JS AST to a Closure Compiler AST. + * @author johnlenz@google.com (John Lenz) + */ +public class ClosureJsAstTranslator { + private final Map sourceCache = + new HashMap(); + + private StaticSourceFile getClosureSourceFile(Source source) { + StaticSourceFile closureSourceFile = sourceCache.get(source); + if (closureSourceFile == null) { + closureSourceFile = new SimpleSourceFile(source.getName(), false); + sourceCache.put(source, closureSourceFile); + } + return closureSourceFile; + } + + public Node translate(JsProgram program, InputId inputId, Source source) { + Node script = new Node(Token.SCRIPT); + script.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); + script.setInputId(inputId); + script.putProp(Node.SOURCENAME_PROP, source.getName()); + script.setStaticSourceFile(getClosureSourceFile(source)); + for (JsStatement s : program.getGlobalBlock().getStatements()) { + script.addChildToBack(transform(s)); + } + // Validate the structural integrity of the AST. + new AstValidator().validateScript(script); + return script; + } + + private Node transform(JsStatement x) { + switch (x.getKind()) { + case BLOCK: + return transform((JsBlock)x); + case BREAK: + return transform((JsBreak)x); + case CONTINUE: + return transform((JsContinue)x); + case DEBUGGER: + return transform((JsDebugger)x); + case DO: + return transform((JsDoWhile)x); + case EMPTY: + return transform((JsEmpty)x); + case EXPR_STMT: + return transform((JsExprStmt)x); + case FOR: + return transform((JsFor)x); + case FOR_IN: + return transform((JsForIn)x); + case IF: + return transform((JsIf)x); + case LABEL: + return transform((JsLabel)x); + case RETURN: + return transform((JsReturn)x); + case SWITCH: + return transform((JsSwitch)x); + case THROW: + return transform((JsThrow)x); + case TRY: + return transform((JsTry)x); + case VARS: + return transform((JsVars)x); + case WHILE: + return transform((JsWhile)x); + default: + throw new IllegalStateException( + "Unexpected statement type: " + x.getClass().getSimpleName()); + } + } + + private Node transform(JsExpression x) { + assert x != null; + switch (x.getKind()) { + case ARRAY: + return transform((JsArrayLiteral)x); + case ARRAY_ACCESS: + return transform((JsArrayAccess)x); + case BINARY_OP: + return transform((JsBinaryOperation)x); + case CONDITIONAL: + return transform((JsConditional)x); + case INVOKE: + return transform((JsInvocation)x); + case FUNCTION: + return transform((JsFunction)x); + case OBJECT: + return transform((JsObjectLiteral)x); + case BOOLEAN: + return transform((JsBooleanLiteral)x); + case NULL: + return transform((JsNullLiteral)x); + case NUMBER: + return transform((JsNumberLiteral)x); + case REGEXP: + return transform((JsRegExp)x); + case STRING: + return transform((JsStringLiteral)x); + case THIS: + return transform((JsThisRef)x); + case NAME_REF: + return transform((JsNameRef)x); + case NEW: + return transform((JsNew)x); + case POSTFIX_OP: + return transform((JsPostfixOperation)x); + case PREFIX_OP: + return transform((JsPrefixOperation)x); + default: + throw new IllegalStateException( + "Unexpected expression type: " + x.getClass().getSimpleName()); + } + } + + private Node transform(JsSwitchMember x) { + switch (x.getKind()) { + case CASE: + return transform((JsCase)x); + case DEFAULT: + return transform((JsDefault)x); + default: + throw new IllegalStateException( + "Unexpected switch member type: " + x.getClass().getSimpleName()); + } + } + + private Node transform(JsArrayAccess x) { + Node n = new Node(Token.GETELEM, + transform(x.getArrayExpr()), + transform(x.getIndexExpr())); + return applySourceInfo(n, x); + } + + private Node transform(JsArrayLiteral x) { + Node n = new Node(Token.ARRAYLIT); + for (Object element : x.getExpressions()) { + JsExpression arg = (JsExpression) element; + n.addChildToBack(transform(arg)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsBinaryOperation x) { + JsBinaryOperator op = x.getOperator(); + Node n = new Node(getTokenForOp(op), + transform(x.getArg1()), + transform(x.getArg2())); + return applySourceInfo(n, x); + } + + private Node transform(JsBlock x) { + Node n = new Node(Token.BLOCK); + for (JsStatement s : x.getStatements()) { + n.addChildToBack(transform(s)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsBooleanLiteral x) { + Node n = new Node(x.getValue() ? Token.TRUE : Token.FALSE); + return applySourceInfo(n, x); + } + + private Node transform(JsBreak x) { + Node n = new Node(Token.BREAK); + + JsNameRef label = x.getLabel(); + if (label != null) { + n.addChildToBack(transformLabel(label)); + } + + return applySourceInfo(n, x); + } + + private Node transform(JsCase x) { + Node n = new Node(Token.CASE); + n.addChildToBack(transform(x.getCaseExpr())); + + Node body = new Node(Token.BLOCK); + body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); + applySourceInfo(body, x); + n.addChildToBack(body); + + for (Object element : x.getStmts()) { + JsStatement stmt = (JsStatement) element; + body.addChildToBack(transform(stmt)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsCatch x) { + Node n = new Node(Token.CATCH, + transformName(x.getParameter().getName()), + transform(x.getBody())); + Preconditions.checkState(x.getCondition() == null); + return applySourceInfo(n, x); + } + + private Node transform(JsConditional x) { + Node n = new Node(Token.HOOK, + transform(x.getTestExpression()), + transform(x.getThenExpression()), + transform(x.getElseExpression())); + return applySourceInfo(n, x); + } + + private Node transform(JsContinue x) { + Node n = new Node(Token.CONTINUE); + + JsNameRef label = x.getLabel(); + if (label != null) { + n.addChildToBack(transformLabel(label)); + } + + return applySourceInfo(n, x); + } + + private Node transform(JsDebugger x) { + Node n = new Node(Token.DEBUGGER); + return applySourceInfo(n, x); + } + + private Node transform(JsDefault x) { + Node n = new Node(Token.DEFAULT); + + Node body = new Node(Token.BLOCK); + body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); + applySourceInfo(body, x); + n.addChildToBack(body); + + for (Object element : x.getStmts()) { + JsStatement stmt = (JsStatement) element; + body.addChildToBack(transform(stmt)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsDoWhile x) { + Node n = new Node(Token.DO, + transformBody(x.getBody(), x), + transform(x.getCondition())); + return applySourceInfo(n, x); + } + + private Node transform(JsEmpty x) { + return new Node(Token.EMPTY); + } + + private Node transform(JsExprStmt x) { + // The Dart JS AST doesn't produce function declarations, instead + // they are expressions statements: + Node expr = transform(x.getExpression()); + if (expr.getType() != Token.FUNCTION) { + return new Node(Token.EXPR_RESULT, expr); + } else { + return expr; + } + } + + private Node transform(JsFor x) { + Node n = new Node(Token.FOR); + + // The init expressions or var decl. + // + if (x.getInitExpr() != null) { + n.addChildToBack(transform(x.getInitExpr())); + } else if (x.getInitVars() != null) { + n.addChildToBack(transform(x.getInitVars())); + } else { + n.addChildToBack(new Node(Token.EMPTY)); + } + + // The loop test. + // + if (x.getCondition() != null) { + n.addChildToBack(transform(x.getCondition())); + } else { + n.addChildToBack(new Node(Token.EMPTY)); + } + + // The incr expression. + // + if (x.getIncrExpr() != null) { + n.addChildToBack(transform(x.getIncrExpr())); + } else { + n.addChildToBack(new Node(Token.EMPTY)); + } + + n.addChildToBack(transformBody(x.getBody(), x)); + return applySourceInfo(n, x); + } + + private Node transform(JsForIn x) { + Node n = new Node(Token.FOR); + + if (x.getIterVarName() != null) { + Node expr = new Node(Token.VAR, + transformName(x.getIterVarName())); + n.addChildToBack(expr); + } else { + // Just a name ref. + // + n.addChildToBack(transform(x.getIterExpr())); + } + + n.addChildToBack(transform(x.getObjExpr())); + n.addChildToBack(transformBody(x.getBody(), x)); + return applySourceInfo(n, x); + } + + private Node transform(JsFunction x) { + Node n = new Node(Token.FUNCTION); + if (x.getName() != null) { + n.addChildToBack(getNameNodeFor(x)); + applyOriginalName(n, x); + } else { + Node emptyName = Node.newString(Token.NAME, ""); + applySourceInfo(emptyName, x); + n.addChildToBack(emptyName); + n.putProp(Node.ORIGINALNAME_PROP, ""); + } + + Node params = new Node(Token.LP); + for (Object element : x.getParameters()) { + JsParameter param = (JsParameter) element; + params.addChildToBack(transform(param)); + } + applySourceInfo(n, x); + n.addChildToBack(params); + + n.addChildToBack(transform(x.getBody())); + + if (x.isConstructor()) { + JSDocInfoBuilder builder = new JSDocInfoBuilder(false); + builder.recordConstructor(); + n.setJSDocInfo(builder.build(n)); + } + + return applySourceInfo(n, x); + } + + private Node transform(JsIf x) { + Node n = new Node(Token.IF, + transform(x.getIfExpr()), + transformBody(x.getThenStmt(), x)); + if (x.getElseStmt() != null) { + n.addChildToBack(transformBody(x.getElseStmt(), x)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsInvocation x) { + Node n = new Node(Token.CALL, + transform(x.getQualifier())); + for (Object element : x.getArguments()) { + JsExpression arg = (JsExpression) element; + n.addChildToBack(transform(arg)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsLabel x) { + Node n = new Node(Token.LABEL, + transformLabel(x.getName()), + transform(x.getStmt())); + + return applySourceInfo(n, x); + } + + private Node transform(JsNameRef x) { + Node n; + if (x.getQualifier() != null) { + n = new Node(Token.GETPROP, + transform(x.getQualifier()), + transformNameAsString(x.getShortIdent(), x)); + } else { + n = transformName(x.getShortIdent(), x); + } + applyOriginalName(n, x); + return applySourceInfo(n, x); + } + + private Node transform(JsNew x) { + Node n = new Node(Token.NEW, + transform(x.getConstructorExpression())); + for (Object element : x.getArguments()) { + JsExpression arg = (JsExpression) element; + n.addChildToBack(transform(arg)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsNullLiteral x) { + return new Node(Token.NULL); + } + + private Node transform(JsNumberLiteral x) { + return Node.newNumber(x.getValue()); + } + + private Node transform(JsObjectLiteral x) { + Node n = new Node(Token.OBJECTLIT); + + for (Object element : x.getPropertyInitializers()) { + JsPropertyInitializer propInit = (JsPropertyInitializer) element; + Node key = transform(propInit.getLabelExpr()); + Preconditions.checkState(key.getType() == Token.STRING); + key.addChildToBack(transform(propInit.getValueExpr())); + n.addChildToBack(key); + } + return applySourceInfo(n, x); + } + + private Node transform(JsParameter x) { + return getNameNodeFor(x); + } + + private Node transform(JsPostfixOperation x) { + Node n = new Node(getTokenForOp(x.getOperator()), + transform(x.getArg())); + n.putBooleanProp(Node.INCRDECR_PROP, true); + return applySourceInfo(n, x); + } + + private Node transform(JsPrefixOperation x) { + Node n = new Node(getTokenForOp(x.getOperator()), + transform(x.getArg())); + return applySourceInfo(n, x); + } + + private Node transform(JsRegExp x) { + String flags = x.getFlags(); + Node n = new Node(Token.REGEXP, + Node.newString(x.getPattern()), + Node.newString(flags != null ? x.getFlags() : "")); + return applySourceInfo(n, x); + } + + private Node transform(JsReturn x) { + Node n = new Node(Token.RETURN); + JsExpression result = x.getExpr(); + if (result != null) { + n.addChildToBack(transform(x.getExpr())); + } + return applySourceInfo(n, x); + } + + private Node transform(JsStringLiteral x) { + return Node.newString(x.getValue()); + } + + private Node transform(JsSwitch x) { + Node n = new Node(Token.SWITCH, + transform(x.getExpr())); + for (JsSwitchMember member : x.getCases()) { + n.addChildToBack(transform(member)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsThisRef x) { + Node n = new Node(Token.THIS); + return applySourceInfo(n, x); + } + + private Node transform(JsThrow x) { + Node n = new Node(Token.THROW, + transform(x.getExpr())); + return applySourceInfo(n, x); + } + + private Node transform(JsTry x) { + Node n = new Node(Token.TRY, + transform(x.getTryBlock())); + + Node catches = new Node(Token.BLOCK); + for (JsCatch catchBlock : x.getCatches()) { + catches.addChildToBack(transform(catchBlock)); + } + n.addChildToBack(catches); + + JsBlock finallyBlock = x.getFinallyBlock(); + if (finallyBlock != null) { + n.addChildToBack(transform(finallyBlock)); + } + + return applySourceInfo(n, x); + } + + private Node transform(JsVar x) { + Node n = getNameNodeFor(x); + JsExpression initExpr = x.getInitExpr(); + if (initExpr != null) { + n.addChildToBack(transform(initExpr)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsVars x) { + Node n = new Node(Token.VAR); + for (JsVar var : x) { + n.addChildToBack(transform(var)); + } + return applySourceInfo(n, x); + } + + private Node transform(JsWhile x) { + Node n = new Node(Token.WHILE, + transform(x.getCondition()), + transformBody(x.getBody(), x)); + return applySourceInfo(n, x); + } + + private Node transformBody(JsStatement x, SourceInfo parent) { + Node n = transform(x); + if (n.getType() != Token.BLOCK) { + Node stmt = n; + n = new Node(Token.BLOCK); + if (n.getType() != Token.EMPTY) { + n.addChildToBack(stmt); + } + applySourceInfo(n, parent); + } + return n; + } + + private Node transformLabel(JsNameRef label) { + Node n = Node.newString(Token.LABEL_NAME, getName(label)); + return applySourceInfo(n, label); + } + + private Node transformLabel(JsName label) { + Node n = Node.newString(Token.LABEL_NAME, getName(label)); + return applySourceInfo(n, label.getStaticRef()); + } + + private Node transformName(JsName name) { + Node n = Node.newString(Token.NAME, getName(name)); + return applySourceInfo(n, name.getStaticRef()); + } + + private Node transformName(String name, SourceInfo info) { + Node n = Node.newString(Token.NAME, name); + return applySourceInfo(n, info); + } + + private Node transformNameAsString(String name, SourceInfo info) { + Node n = Node.newString(name); + return applySourceInfo(n, info); + } + + private Node getNameNodeFor(HasName hasName) { + Node n = Node.newString(Token.NAME, getName(hasName.getName())); + applyOriginalName(n, (JsNode)hasName); + return applySourceInfo(n, (SourceInfo)hasName); + } + + private String getName(JsName name) { + return name.getShortIdent(); + } + + private String getName(JsNameRef name) { + return name.getShortIdent(); + } + + private int getTokenForOp(JsUnaryOperator op) { + switch (op) { + case BIT_NOT: return Token.BITNOT; + case DEC: return Token.DEC; + case DELETE: return Token.DELPROP; + case INC: return Token.INC; + case NEG: return Token.NEG; + case POS: return Token.POS; + case NOT: return Token.NOT; + case TYPEOF: return Token.TYPEOF; + case VOID: return Token.VOID; + } + throw new IllegalStateException(); + } + + private int getTokenForOp(JsBinaryOperator op) { + switch (op) { + case MUL: return Token.MUL; + case DIV: return Token.DIV; + case MOD: return Token.MOD; + case ADD: return Token.ADD; + case SUB: return Token.SUB; + case SHL: return Token.LSH; + case SHR: return Token.RSH; + case SHRU: return Token.URSH; + case LT: return Token.LT; + case LTE: return Token.LE; + case GT: return Token.GT; + case GTE: return Token.GE; + case INSTANCEOF: return Token.INSTANCEOF; + case INOP: return Token.IN; + case EQ: return Token.EQ; + case NEQ: return Token.NE; + case REF_EQ: return Token.SHEQ; + case REF_NEQ: return Token.SHNE; + case BIT_AND: return Token.BITAND; + case BIT_XOR: return Token.BITXOR; + case BIT_OR: return Token.BITOR; + case AND: return Token.AND; + case OR: return Token.OR; + case ASG: return Token.ASSIGN; + case ASG_ADD: return Token.ASSIGN_ADD; + case ASG_SUB: return Token.ASSIGN_SUB; + case ASG_MUL: return Token.ASSIGN_MUL; + case ASG_DIV: return Token.ASSIGN_DIV; + case ASG_MOD: return Token.ASSIGN_MOD; + case ASG_SHL: return Token.ASSIGN_LSH; + case ASG_SHR: return Token.ASSIGN_RSH; + case ASG_SHRU: return Token.ASSIGN_URSH; + case ASG_BIT_AND: return Token.ASSIGN_BITAND; + case ASG_BIT_OR: return Token.ASSIGN_BITOR; + case ASG_BIT_XOR: return Token.ASSIGN_BITXOR; + case COMMA: return Token.COMMA; + } + return 0; + } + + private Node applyOriginalName(Node n, JsNode x) { + if (x instanceof HasSymbol) { + Symbol symbol = ((HasSymbol)x).getSymbol(); + if (symbol != null) { + String originalName = symbol.getOriginalSymbolName(); + n.putProp(Node.ORIGINALNAME_PROP, originalName); + } + } + return n; + } + + private Node applySourceInfo(Node n, SourceInfo info) { + if (info != null && info.getSource() != null) { + n.setStaticSourceFile(getClosureSourceFile(info.getSource())); + n.setLineno(info.getSourceLine()); + n.setCharno(info.getSourceColumn()); + } + return n; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java new file mode 100644 index 00000000000..241822fda54 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java @@ -0,0 +1,537 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; +import com.google.common.io.LimitInputStream; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.javascript.jscomp.CheckLevel; +import com.google.javascript.jscomp.CompilationLevel; +import com.google.javascript.jscomp.Compiler; +import com.google.javascript.jscomp.CompilerInput; +import com.google.javascript.jscomp.CompilerOptions; +import com.google.javascript.jscomp.DiagnosticGroups; +import com.google.javascript.jscomp.JSError; +import com.google.javascript.jscomp.JSModule; +import com.google.javascript.jscomp.JSSourceFile; +import com.google.javascript.jscomp.Result; +import com.google.javascript.jscomp.SourceAst; +import com.google.javascript.jscomp.WarningLevel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * A compiler backend that produces raw Javascript. + * @author johnlenz@google.com (John Lenz) + */ +public class ClosureJsBackend extends AbstractJsBackend { + public static final String EXTENSION_JS = "opt.js"; + public static final String EXTENSION_JS_SRC_MAP = "opt.js.map"; + + // A map of possible input sources to use when building the optimized output. + private Map dartSrcToUnitMap = Maps.newHashMap(); + private long totalJsOutputCharCount; + + // Generate "readable" output for debugging + private boolean generateHumanReadableOutput = false; + + @Override + public boolean isOutOfDate(DartSource src, DartCompilerContext context) { + return true; + } + + @Override + public void compileUnit(DartUnit unit, DartSource src, + DartCompilerContext context, CoreTypeProvider typeProvider) { + dartSrcToUnitMap.put(src.getName(), unit); + } + + private Map createClosureJsAst(Map parts, Source source) { + String name = source.getName(); + Preconditions.checkState(name != null && !name.isEmpty(), "A source name is required"); + + Map translatedParts = new HashMap(); + for (Map.Entry part : parts.entrySet()) { + String partName = part.getKey(); + String inputName = name + ':' + partName; + SourceAst sourceAst = new ClosureJsAst(part.getValue(), inputName, source); + CompilerInput input = new CompilerInput(sourceAst, false); + translatedParts.put(part.getKey(), input); + } + return translatedParts; + } + + private class DepsWritingCallback implements DepsCallback { + private final DartCompilerContext context; + private final CoreTypeProvider typeProvider; + private final List inputs; + private final Map sourcesByName; + private final Map> translatedUnits = Maps.newHashMap(); + + DepsWritingCallback( + DartCompilerContext context, + CoreTypeProvider typeProvider, + List inputs, + Map sourcesByName) { + this.context = context; + this.typeProvider = typeProvider; + this.inputs = inputs; + this.sourcesByName = sourcesByName; + } + + @Override + public void visitNative(LibraryUnit libUnit, LibraryNode node) + throws IOException { + String name = node.getText(); + DartSource nativeSrc = libUnit.getSource().getSourceFor(name); + StringWriter w = new StringWriter(); + Reader r = nativeSrc.getSourceReader(); + CharStreams.copy(r, w); + inputs.add(new CompilerInput(JSSourceFile.fromCode(name, w.toString()), false)); + } + + @Override + public void visitPart(Part part) { + DartSource src = part.unit.getSource(); + Map translatedParts = translatedUnits.get(part.unit); + if (translatedParts == null) { + assert !sourcesByName.containsKey(src.getName()); + sourcesByName.put(src.getName(), src); + Preconditions.checkNotNull(part.unit, "src: " + src.getName()); + translatedParts = translateUnit(part.unit, src, context, typeProvider); + translatedUnits.put(part.unit, translatedParts); + } + inputs.add(translatedParts.get(part.part)); + } + } + + private void packageAppOptimized(LibrarySource app, + Collection libraries, + DartCompilerContext context, + CoreTypeProvider typeProvider) + throws IOException { + + List inputs = Lists.newLinkedList(); + Map sourcesByName = Maps.newHashMap(); + DepsWritingCallback callback = new DepsWritingCallback( + context, typeProvider, inputs, sourcesByName); + DependencyBuilder.build(context.getAppLibraryUnit(), callback); + + // Lastly, add the entry point. + inputs.add( getCompilerInputForEntry(context) ); + + // Currently, there is only a single module, add all the sources to it. + JSModule mainModule = new JSModule("main"); + for (CompilerInput input : inputs) { + if (input != null) { + mainModule.add(input); + } + } + + Writer out = context.getArtifactWriter(app, "", EXTENSION_JS); + boolean failed = true; + try { + Writer srcMapOut = context.getArtifactWriter(app, "", EXTENSION_JS_SRC_MAP); + boolean failed2 = true; + try { + compileModule( + getCompilerOptions(), mainModule, sourcesByName, out, srcMapOut, context); + failed2 = false; + } finally { + Closeables.close(srcMapOut, failed2); + } + failed = false; + } finally { + Closeables.close(out, failed); + } + } + + private Map translateUnit( + DartUnit unit, DartSource src, DartCompilerContext context, CoreTypeProvider typeProvider) { + Map parts = translateToJS(unit, context, typeProvider); + + // Translate the AST and cache it for later use. + return createClosureJsAst(parts, src); + } + + private CompilerInput getCompilerInputForEntry(DartCompilerContext context) + throws IOException { + StringWriter entry = new StringWriter(); + writeEntryPointCall(getMangledEntryPoint(context), entry); + return new CompilerInput( + JSSourceFile.fromCode("entry", entry.toString()), false); + } + + class MockSource implements Source { + private String sourceName; + + MockSource(String sourceName) { + this.sourceName = sourceName; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public String getName() { + return sourceName; + } + + @Override + public Reader getSourceReader() { + return new StringReader(""); + } + + @Override + public URI getUri() { + return null; + } + } + + // Stub source info object for reporting errors coming from the Closure Compiler + static class JSErrorSourceInfo implements SourceInfo { + final JSError error; + final Source source; + + JSErrorSourceInfo(JSError error, Source source) { + this.error = error; + this.source = source; + } + + @Override + public Source getSource() { + return source; + } + + @Override + public int getSourceColumn() { + return error.getCharno(); + } + + @Override + public int getSourceLength() { + return -1; + } + + @Override + public int getSourceLine() { + return error.lineNumber; + } + + @Override + public int getSourceStart() { + return -1; + } + } + + private void compileModule( + CompilerOptions options, JSModule module, + Map sourcesByName, + Writer out, Writer srcMapOut, + DartCompilerContext context) throws IOException { + // Turn off Closure Compiler logging + Logger.getLogger("com.google.javascript.jscomp").setLevel(Level.OFF); + + Compiler compiler = new Compiler(); + + List externs = getDefaultExterns(); + List modules = Lists.newLinkedList(); + modules.add(module); + Result result = compiler.compileModules(externs, modules, options); + + if (processResults(compiler, result, module, out, srcMapOut) != 0) { + for (JSError error : result.errors) { + // Use the real dart source object when we can. + Source source = sourcesByName.get(error.sourceName); + if (source == null) { + // This might be a compiler generate source, whatever it is + // report it. + source = new MockSource(error.sourceName); + } + System.err.println("error optimizing:" + error.toString()); + @SuppressWarnings("deprecation") + DartCompilationError event = new DartCompilationError( + new JSErrorSourceInfo(error, source), error.description); + context.compilationError(event); + } + } + + out.close(); + srcMapOut.close(); + } + + /** + * Processes the results of the compile job, and returns an error code. + */ + private int processResults(Compiler compiler, Result result, JSModule module, + Writer out, Writer srcMapOut) + throws IOException { + if (result.success) { + String output = compiler.toSource(module); + out.append(output); + out.append('\n'); + + compiler.getSourceMap().appendTo(srcMapOut, module.getName()); + + totalJsOutputCharCount = output.length(); + + // TODO(johnlenz): Output the externs declarations + // TODO(johnlenz): Output the manifest. + } + + // return 0 if no errors, the error count otherwise + return Math.min(result.errors.length, 0x7f); + } + + private CompilerOptions getCompilerOptions() { + CompilerOptions options = new CompilerOptions(); + CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); + // TODO(johnlenz): This is overkill as we only care about errors, + // not warnings at this point. But we want the errors. + WarningLevel.VERBOSE.setOptionsForWarningLevel(options); + + // Disable type warnings as we don't provide any type information. + options.setInferTypes(false); + options.checkTypes = false; + options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.OFF); + + // Use the lowest common denominator, ES3 parsing with ES5 Strict + // restrictions. + options.checkEs5Strict = true; + options.setAssumeStrictThis(true); + + options.setCodingConvention(new ClosureJsCodingConvention()); + + // Always output the source map. + options.sourceMapOutputPath = "placeholder"; // anything will do + + // TODO(johnlenz): rewriteFunctionExpressions kills the Richards benchmark, + // it needs some better heuristics. + options.rewriteFunctionExpressions = false; + + // AliasKeywords has a performance hit, disable it. + options.aliasKeywords = false; + + // TODO(johnlenz): These passes use SimpleDefinitionFinder or equivalent, and operate + // based on property name, not object type. DisambiguateProperties helps but is not + // a complete fix even with complete type information. + // See http://code.google.com/p/closure-compiler/issues/detail?id=437. + // Disable them for now until we develop a plan for how to deal with them. + + options.computeFunctionSideEffects = false; + options.devirtualizePrototypeMethods = true; + options.inlineGetters = true; + + + // TODO(johnlenz): Some DOM definitions look like unused prototype property + // definitions because they are only referenced using dynamically generated + // names. + options.removeUnusedPrototypePropertiesInExterns = false; + + // To ease debugging, try enabling these options: + if (generateHumanReadableOutput) { + options.prettyPrint = true; + options.generatePseudoNames = true; + options.printInputDelimiter = true; + options.inputDelimiter = "// Input %name%"; + } + // If those aren't enough, try these: + // options.coalesceVariableNames = false; + // options.setShadowVariables(false); + // options.inlineFunctions = false; + + return options; + } + + // The externs expected in externs.zip, in sorted order. + private static final List DEFAULT_EXTERNS_NAMES = ImmutableList.of( + // JS externs + "es3.js", + "es5.js", + // "json.js", // TODO(johnlenz): add this. + + // Event APIs + "w3c_event.js", + "w3c_event3.js", + "gecko_event.js", + "ie_event.js", + "webkit_event.js", + + // DOM apis + "w3c_dom1.js", + "w3c_dom2.js", + "w3c_dom3.js", + "gecko_dom.js", + "ie_dom.js", + "webkit_dom.js", + + // CSS apis + "w3c_css.js", + "gecko_css.js", + "ie_css.js", + "webkit_css.js", + + // Top-level namespaces + "google.js", + + "deprecated.js", + "fileapi.js", + "flash.js", + "gears_symbols.js", + "gears_types.js", + "gecko_xml.js", + "html5.js", + "ie_vml.js", + "iphone.js", + "webstorage.js", + "w3c_anim_timing.js", + "w3c_css3d.js", + "w3c_elementtraversal.js", + "w3c_geolocation.js", + "w3c_indexeddb.js", + "w3c_navigation_timing.js", + "w3c_range.js", + "w3c_selectors.js", + "w3c_xml.js", + "window.js", + "webkit_notifications.js", + "webgl.js"); + + // Add a declarations for the V8 logging function. + private static final String UNIT_TEST_EXTERN_STUBS = "var write;"; + + // TODO(johnlenz): include json.js in the default set of externs. + private static final String MISSING_EXTERNS = + "var JSON = {};\n" + + "/**\n" + + " * @param {string} jsonStr The string to parse.\n" + + " * @param {(function(string, *) : *)=} opt_reviver\n" + + " * @return {*} The JSON object.\n" + + " */\n" + + "JSON.parse = function(jsonStr, opt_reviver) {};\n" + + "\n" + + "/**\n" + + " * @param {*} jsonObj Input object.\n" + + " * @param {(Array.|(function(string, *) : *)|null)=} opt_replacer\n" + + " * @param {(number|string)=} opt_space\n" + + " * @return {string} json string which represents jsonObj.\n" + + " */\n" + + "JSON.stringify = function(jsonObj, opt_replacer, opt_space) {};" + + "\n"; + + /** + * @return a mutable list + * @throws IOException + */ + public static List getDefaultExterns() throws IOException { + Class clazz = ClosureJsBackend.class; + InputStream input = clazz.getResourceAsStream( + "/com/google/javascript/jscomp/externs.zip"); + if (input == null) { + /* + * HACK - the open source version of the closure compiler maps the + * resource into a different location. + */ + input = clazz.getResourceAsStream("/externs.zip"); + } + ZipInputStream zip = new ZipInputStream(input); + Map externsMap = Maps.newHashMap(); + for (ZipEntry entry = null; (entry = zip.getNextEntry()) != null; ) { + LimitInputStream entryStream = new LimitInputStream(zip, entry.getSize()); + externsMap.put(entry.getName(), + JSSourceFile.fromInputStream( + // Give the files an odd prefix, so that they do not conflict + // with the user's files. + "externs.zip//" + entry.getName(), + entryStream)); + } + + Preconditions.checkState( + externsMap.keySet().equals(Sets.newHashSet(DEFAULT_EXTERNS_NAMES)), + "Externs zip must match our hard-coded list of externs."); + + // Order matters, so the resources must be added to the result list + // in the expected order. + List externs = Lists.newArrayList(); + for (String key : DEFAULT_EXTERNS_NAMES) { + externs.add(externsMap.get(key)); + } + + // Add methods used when running the unit tests. + externs.add(JSSourceFile.fromCode("missingExterns", MISSING_EXTERNS)); + + // Add methods used when running the unit tests. + externs.add(JSSourceFile.fromCode("unitTestStubs", UNIT_TEST_EXTERN_STUBS)); + + return externs; + } + + @Override + public void packageApp(LibrarySource app, + Collection libraries, + DartCompilerContext context, + CoreTypeProvider typeProvider) + throws IOException { + totalJsOutputCharCount = 0; + packageAppOptimized(app, libraries, context, typeProvider); + CompilerMetrics compilerMetrics = context.getCompilerMetrics(); + if (compilerMetrics != null) { + compilerMetrics.packagedJsApplication(totalJsOutputCharCount, -1); + } + } + + @Override + public String getAppExtension() { + return EXTENSION_JS; + } + + @Override + public String getSourceMapExtension() { + return EXTENSION_JS_SRC_MAP; + } + + @Override + protected boolean shouldOptimize() { + return true; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ClosureJsCodingConvention.java b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsCodingConvention.java new file mode 100644 index 00000000000..590c0242a11 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ClosureJsCodingConvention.java @@ -0,0 +1,104 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.base.Preconditions; +import com.google.javascript.jscomp.ClosureCodingConvention; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +/** + * A means of giving hints to the Closure Compiler: + * - Teach the compiler the meaning of "$inherits" so the name removal + * passes understand it is not just a method depending on the class + * definitions and modifying global state. + * @author johnlenz@google.com (John Lenz) + */ +class ClosureJsCodingConvention extends ClosureCodingConvention { + /** + * {@inheritDoc} + * + *

    Understands several different inheritance patterns that occur in + * DartC generated code. + */ + @Override + public SubclassRelationship getClassesDefinedByCall(Node callNode) { + Node callName = callNode.getFirstChild(); + SubclassType type = typeofClassDefiningName(callName); + if (type != null && callNode.getChildCount() == 3) { + // Only one type of call is expected. + // $inherits(SubClass, SuperClass) + Preconditions.checkState(type == SubclassType.INHERITS); + + Node subclass = callName.getNext(); + Node superclass = callNode.getLastChild(); + + // bail out if either of the side of the "inherits" + // isn't a real class name. This prevents us from + // doing something weird in cases like: + // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) + if (subclass != null && + subclass.isUnscopedQualifiedName() && + superclass.isUnscopedQualifiedName()) { + return new SubclassRelationship(type, subclass, superclass); + } + } + + return super.getClassesDefinedByCall(callNode); + } + + /** + * Determines whether the given node is a class-defining name, like + * "inherits". + * @return The type of class-defining name, or null. + */ + private SubclassType typeofClassDefiningName(Node callName) { + // Check if the method name matches one of the class-defining methods. + if (callName.getType() == Token.NAME + && callName.getString().equals("$inherits")) { + return SubclassType.INHERITS; + } + return null; + } + + /** + * Determines whether the given node is a function binding call, like + * "$bind". + * @return The type of Bind definition, or null. + */ + @Override + public Bind describeFunctionBind(Node n) { + // Check for standard stuff first. + Bind result = super.describeFunctionBind(n); + if (result != null) { + return result; + } + + if (n.getType() != Token.CALL) { + return null; + } + + // Check for Dartc generated bind function + Node callTarget = n.getFirstChild(); + if (callTarget.getType() == Token.NAME) { + if (callTarget.getString().equals("$bind")) { + // goog.bind(fn, self, args...); + Node fn = callTarget.getNext(); + Node thisValue = safeNext(fn); + Node parameters = safeNext(thisValue); + return new Bind(fn, thisValue, parameters); + } + } + + return null; + } + + private Node safeNext(Node n) { + if (n != null) { + return n.getNext(); + } + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/DartMangler.java b/compiler/java/com/google/dart/compiler/backend/js/DartMangler.java new file mode 100644 index 00000000000..63280981294 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/DartMangler.java @@ -0,0 +1,138 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.resolver.MethodElement; + +/** + * Mangles dart identifiers so that they don't conflict with JavaScript identifiers. + * This complements the JsScope infrastructure. The JsScope infrastructure deals with obfuscatable + * identifiers, whereas the DartMangler deals with non-obfuscatable identifiers. + * + * @author floitsch@google.com (Florian Loitsch) + */ +public interface DartMangler { + public static final String NEGATE_OPERATOR_NAME = "negate"; + + /** + * Mangles the given className, so that it does not clash with any global variable or other + * mangled identifiers. + * @return a String that identifies the class, and does not clash with any global variable. + */ + public String mangleClassName(ClassElement classElement); + @Deprecated + public String mangleClassNameHack(LibraryElement library, String str); + + /** + * Mangles the given constructor, so that it does not clash with any built-in JS property, + * initializers, factories or other mangled fields.
    + * The given LibraryElement is used if the constructor is library private. + * @return a String that identifies the constructor, and does not clash with any global variable. + */ + public String mangleConstructor(String constructorName, LibraryElement currentLibrary); + + /** + * Returns a mangled identifier for the given method. + */ + public String mangleNativeMethod(MethodElement methodElement); + + /** + * Mangles the given initializer, so that it does not clash with any built-in JS property, + * factories, constructors or other mangled fields.
    + * The given LibraryElement is used if the initializer is library private. + * @return a String that identifies the initializer, and does not clash with any global variable. + */ + public String createInitializerSyntax(String constructorName, LibraryElement currentLibrary); + + /** + * Mangles the given factory, so that it does not clash with any built-in JS property, + * constructors, initializers or other mangled fields.
    + * Note that factories may be unrelated to the containing class. A class A can + * have a factory method returning an instance of class B. In this case + * className equals B.
    + * The given LibraryElement is used if the factory is library private. + * + * @return a String that identifies the factory, and does not clash with any global variable. + */ + public String createFactorySyntax(String className, String constructorName, + LibraryElement currentlibrary); + + + /** + *

    Returns a name for the given closure. The returned name does not + * clash with any global variable or other mangled identifiers.

    + * The closure is identified by the closureIdentifier. Manglers are allowed to discard the + * closureName completely. + * + * @param closureIdentifier must be a valid identifier of the form [a-zA-Z]+[0-9]* + * @param closureName the closures short readable name. May be null. + * @return a String that identifies the hoisted closure, and does not clash with any global + * variable. + */ + public String createHoistedFunctionName(Element holder, + Element classMemberElement, + String closureIdentifier, + String closureName); + + /** + * Mangles the given field, so that it does not clash with any built-in JS property or other + * mangled fields or methods.
    + * The given LibraryElement is used if the field is library private. + * @return a String that identifies the member, and does not clash with built-in JS properties. + */ + public String mangleField(FieldElement field, LibraryElement currentLibrary); + + /** + * Mangles the given method, so that it does not clash with any built-in JS property or other + * mangled fields or methods.
    + * The given LibraryElement is used if the method is library private. + * @return a String that identifies the member, and does not clash with built-in JS properties. + */ + public String mangleMethod(MethodElement method, LibraryElement currentLibrary); + public String mangleMethod(String methodName, LibraryElement currentLibrary); + + /** + * Mangles the given method to its $named form. + * @return a String that identifies the named form of the member. + */ + public String mangleNamedMethod(MethodElement method, LibraryElement currentLibrary); + public String mangleNamedMethod(String methodName, LibraryElement currentLibrary); + + /** + * Mangles the given method, so that it does not clash with any built-in JS property or other + * mangled fields or methods. This method is different than mangleMethod, as it returns + * the fully qualified mangled name. + * @return a String that identifies the entry, and does not clash with built-in JS properties. + */ + public String mangleEntryPoint(MethodElement method, LibraryElement library); + + /** + * @return the JavaScript property identifier for the given operation. + */ + public String createOperatorSyntax(Token token); + + /** + * @return the JavaScript property identifier for the given operation. + */ + public String createOperatorSyntax(String operation); + + /** + * @return the JavaScript getter property for the given member. + */ + public String createGetterSyntax(String member, LibraryElement currentLibrary); + public String createGetterSyntax(FieldElement member, LibraryElement currentLibrary); + public String createGetterSyntax(MethodElement member, LibraryElement currentLibrary); + + /** + * @return the JavaScript setter property for the given member. + */ + public String createSetterSyntax(String member, LibraryElement currentLibrary); + public String createSetterSyntax(FieldElement member, LibraryElement currentLibrary); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/DollarMangler.java b/compiler/java/com/google/dart/compiler/backend/js/DollarMangler.java new file mode 100644 index 00000000000..894859a3d64 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/DollarMangler.java @@ -0,0 +1,411 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.ImmutableSet; +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.resolver.MethodElement; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Set; + +/** + * Mangles classes and members (including constructors and operators). + * These must be accessible from outside the compilation unit and must therefore have a + * predictable mangling. + * + *

    Functions, constructors and initializers to become top-level functions. They + * cannot conflict with other members, but must not conflict with predefined JavaScript globals. + * + *

    Other members (fields, operators, methods) are always accessed through an object. The mangler + * only needs to guard against conflicts with predefined JavaScript properties (like "prototype" + * and "__proto__"). + */ +public class DollarMangler implements DartMangler { + // TODO(floitsch): get rid of the blacklisted libraries. + // Libraries, where class-names must not include the library-name. + private static final Set BLACKLISTED_LIBRARIES = ImmutableSet.of("corelib", + "corelib_impl", "dom"); + + // Helpers for getters/setters/operator name mangling. + private static final String OPERATOR_SUFFIX = "$operator"; + private static final String GETTER_SUFFIX = "$getter"; + private static final String SETTER_SUFFIX = "$setter"; + private static final String FIELD_SUFFIX = "$field"; + private static final String METHOD_SUFFIX = "$member"; + private static final String NAMED_SUFFIX = "$named"; + private static final String CONSTRUCTOR_SUFFIX = "$Constructor"; + private static final String FACTORY_SUFFIX = "$Factory"; + private static final String INITIALIZER_SUFFIX = "$Initializer"; + + private static final String CLASS_SUFFIX = "$Dart"; + private static final String HOISTED_METHOD_SUFFIX = "$Hoisted"; + private static final String HOISTED_OPERATOR_SUFFIX = "$HoistedOperator"; + private static final String HOISTED_CONSTRUCTOR_SUFFIX = "$HoistedConstructor"; + private static final String HOISTED_STATIC_SUFFIX = "$HoistedStatic"; + + + private static final String NATIVE_PREFIX = "native_"; + + private boolean isLibraryPrivate(String id) { + return (id.length() > 0) && (id.charAt(0) == '_'); + } + + private String attachSuffix(String name, String suffix, + boolean isLibraryPrivate, LibraryElement currentLibrary) { + if (isLibraryPrivate) { + return name + '$' + mangleLibraryName(currentLibrary) + suffix + '_'; + } + return name + suffix; + } + + private String attachSuffix(String name, String suffix, LibraryElement currentLibrary) { + return attachSuffix(name, suffix, isLibraryPrivate(name), currentLibrary); + } + + private String mangleLibraryName(LibraryElement element) { + LibraryUnit library = element.getLibraryUnit(); + if (isInBlacklist(library)) { + return ""; + } + + // TODO(floitsch): Replace this libraryName + md5(source) with something more enhanced. + + String libName = library.getName(); + if (libName == null || libName.isEmpty()) { + libName = "unnamed"; + } + + // If the libraryName is a path, cut off everything before the last slash. + int nameStart = libName.lastIndexOf('/') + 1; + + // add space for md5 + trailing '$' (and possible leading 'l'). + StringBuilder sb = new StringBuilder(libName.length() + 8); + + // see if we have a leading number, in which case need to prepend an alpha char + final char startChar = libName.charAt(nameStart); + if ('0' <= startChar && startChar <= '9') { + sb.append('l'); + } + sb.append(libName.substring(nameStart)); + + // Replace all non-word chars ([^a-z_A-Z0-9]) with "_". + replaceNonWordChars(sb); + + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError("Could not find MD5 digest"); + } + byte[] md5 = md.digest(library.getSource().getUri().toString().getBytes()); + // Only use the first 6 hex characters of the md5. + for (int i = 0; i < 3; i++) { + sb.append(Integer.toHexString((md5[i] & 0xf0) >> 4)); + sb.append(Integer.toHexString(md5[i] & 0xf)); + } + + sb.append("$"); + + return sb.toString(); + } + + /* + * Replace all non-word characters with an underscore + */ + private void replaceNonWordChars(StringBuilder sb) { + /* + * This code is implemented as a more efficient implementation than using + * String.replaceAll("\\W", "_") + */ + final int len = sb.length(); + for (int idx = 0; idx < len; idx++) { + final char ch = sb.charAt(idx); + if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))) { + sb.setCharAt(idx, '_'); + } + } + } + + @Override + public String mangleClassName(ClassElement classElement) { + return mangleClassNameHack(classElement.getLibrary(), classElement.getName()); + } + + @Override + @Deprecated + public String mangleClassNameHack(LibraryElement library, String className) { + String libraryToken = library == null ? "" : mangleLibraryName(library); + return libraryToken + className + CLASS_SUFFIX; + } + + private boolean isInBlacklist(LibraryUnit libraryUnit) { + if (libraryUnit.getName() == null) { + return false; + } + if (BLACKLISTED_LIBRARIES.contains(libraryUnit.getName())) { + return true; + } + + // Libraries that use the new syntax (with no name) will have a URI for a name, e.g.: + // file://blah/blah/coreimpl.dart + for (String name : BLACKLISTED_LIBRARIES) { + if (libraryUnit.getName().endsWith(name)) { + return true; + } + } + return false; + } + + @Override + public String mangleConstructor(String constructorName, LibraryElement currentLibrary) { + return attachSuffix(constructorName, CONSTRUCTOR_SUFFIX, currentLibrary); + } + + @Override + public String createInitializerSyntax(String constructorName, LibraryElement currentLibrary) { + return attachSuffix(constructorName, INITIALIZER_SUFFIX, currentLibrary); + } + + @Override + public String createFactorySyntax(String className, String constructor, + LibraryElement currentLibrary) { + // We can't just return the constructor + suffix. + // A class might have factories for different classes. + String factoryName; + if (constructor.equals("")) { + factoryName = className + "$"; + } else { + factoryName = className + "$" + constructor + "$" + className.length(); + } + + return attachSuffix(factoryName, FACTORY_SUFFIX, isLibraryPrivate(constructor), currentLibrary); + } + + private boolean containsDollar(String str) { + return str.indexOf('$') >= 0; + } + + private String createHoistedFunctionName(String holderName, + String elementName, + String closureIdentifier, + String closureName, + String suffix) { + assert closureIdentifier.indexOf('$') == -1; + boolean containsDollar = containsDollar(holderName) + || containsDollar(elementName) + || (closureName != null && containsDollar(closureName)); + String result; + if (closureName != null) { + // Return a mangled id of the form: + // class$method$id$closure$$Hoisted, or + // class$method$id$closure$12_23_45$Hoisted (if any of the strings contains dollars). + result = holderName + "$" + elementName + "$" + closureIdentifier + "$" + closureName + "$"; + if (containsDollar) { + result += holderName.length() + "_" + elementName.length() + "_" + + closureIdentifier.length(); + } + } else { + // Return a mangled id of the form: + // class$method$id$$Hoisted, or + // class$method$id$12_23$Hoisted (if any of the strings contains dollars). + result = holderName + "$" + elementName + "$" + closureIdentifier + "$"; + if (containsDollar) { + result += holderName.length() + "_" + holderName.length(); + } + } + return result + suffix; + } + + @Override + public String createHoistedFunctionName(Element holder, + Element element, + String closureIdentifier, + String closureName) { + String holderName = ""; + switch (ElementKind.of(holder)) { + case CLASS: + holderName = mangleClassName((ClassElement) holder); + break; + + case LIBRARY: + holderName = mangleLibraryName((LibraryElement) holder); + break; + } + + String name = element.getName(); + String suffix; + switch (ElementKind.of(element)) { + case METHOD: + if (element.getModifiers().isOperator()) { + suffix = HOISTED_OPERATOR_SUFFIX; + if (!name.equals(NEGATE_OPERATOR_NAME)) { + name = Token.lookup(name).name(); + } + } else { + suffix = HOISTED_METHOD_SUFFIX; + } + break; + + case CONSTRUCTOR: + suffix = HOISTED_CONSTRUCTOR_SUFFIX; + break; + + default: + // Otherwise we are in a static initializer. + suffix = HOISTED_STATIC_SUFFIX; + } + return createHoistedFunctionName(holderName, name, closureIdentifier, closureName, + suffix); + } + + private String createFieldOrMethodBaseName(Element field, boolean accessor) { + String prefix = ""; + Element enclosing = field.getEnclosingElement(); + if (ElementKind.of(enclosing).equals(ElementKind.LIBRARY)) { + prefix = mangleLibraryName((LibraryElement) enclosing); + } else if (!accessor && field.getModifiers().isStatic()) { + prefix = mangleClassName((ClassElement) enclosing); + } + return prefix + field.getName(); + } + + @Override + public String mangleField(FieldElement field, LibraryElement currentLibrary) { + return attachSuffix(createFieldOrMethodBaseName(field, false), FIELD_SUFFIX, + isLibraryPrivate(field.getName()), currentLibrary); + } + + @Override + public String mangleMethod(MethodElement method, LibraryElement currentLibrary) { + String methodName = method.getName(); + if (method.getModifiers().isOperator()) { + methodName = createOperatorSyntax(methodName); + } else if (method.getModifiers().isGetter()) { + methodName = createGetterSyntax(methodName, currentLibrary); + } else if (method.getModifiers().isSetter()) { + methodName = createSetterSyntax(methodName, currentLibrary); + } else { + methodName = attachSuffix(methodName, METHOD_SUFFIX, currentLibrary); + } + + String prefix = ""; + if (ElementKind.of(method.getEnclosingElement()).equals(ElementKind.LIBRARY)) { + prefix = mangleLibraryName((LibraryElement) method.getEnclosingElement()); + } + return prefix + methodName; + } + + @Override + public String mangleNamedMethod(MethodElement method, LibraryElement currentLibrary) { + // There can be no named shims for operators, getters, or setters. + String methodName = method.getName(); + methodName = attachSuffix(methodName, NAMED_SUFFIX, currentLibrary); + + String prefix = ""; + if (ElementKind.of(method.getEnclosingElement()).equals(ElementKind.LIBRARY)) { + prefix = mangleLibraryName((LibraryElement) method.getEnclosingElement()); + } + return prefix + methodName; + } + + @Override + public String mangleNamedMethod(String methodName, LibraryElement currentLibrary) { + return attachSuffix(methodName, NAMED_SUFFIX, currentLibrary); + } + + @Override + public String mangleEntryPoint(MethodElement method, LibraryElement library) { + Element holder = method.getEnclosingElement(); + switch (ElementKind.of(holder)) { + case CLASS: + String mangledClassName = mangleClassName((ClassElement) holder); + return mangledClassName + "." + mangleMethod(method.getName(), library); + + case LIBRARY: + return mangleMethod(method, library); + } + throw new InternalCompilerException("Unknown entry point kind" + method); + } + + @Override + public String createGetterSyntax(FieldElement field, LibraryElement currentLibrary) { + return attachSuffix(createFieldOrMethodBaseName(field, true), GETTER_SUFFIX, + isLibraryPrivate(field.getName()), currentLibrary); + } + + @Override + public String createGetterSyntax(MethodElement field, LibraryElement currentLibrary) { + return attachSuffix(createFieldOrMethodBaseName(field, true), GETTER_SUFFIX, + isLibraryPrivate(field.getName()), currentLibrary); + } + + @Override + public String createSetterSyntax(FieldElement field, LibraryElement currentLibrary) { + return attachSuffix(createFieldOrMethodBaseName(field, true), SETTER_SUFFIX, + isLibraryPrivate(field.getName()), currentLibrary); + } + + @Override + public String mangleMethod(String methodName, LibraryElement currentLibrary) { + return attachSuffix(methodName, METHOD_SUFFIX, currentLibrary); + } + + private static String getNegateOperator() { + return NEGATE_OPERATOR_NAME + OPERATOR_SUFFIX; + } + + @Override + public String createOperatorSyntax(Token token) { + return token.name() + OPERATOR_SUFFIX; + } + + @Override + public String createOperatorSyntax(String operation) { + if (operation.equals(NEGATE_OPERATOR_NAME)) { + return getNegateOperator(); + } + return Token.lookup(operation).name() + OPERATOR_SUFFIX; + } + + @Override + public String createGetterSyntax(String member, LibraryElement currentLibrary) { + return attachSuffix(member, GETTER_SUFFIX, currentLibrary); + } + + @Override + public String createSetterSyntax(String member, LibraryElement currentLibrary) { + return attachSuffix(member, SETTER_SUFFIX, currentLibrary); + } + + @Override + public String mangleNativeMethod(MethodElement element) { + String elementName = element.getName(); + String encodedName = null; + if (element.getModifiers().isOperator()) { + if ("negate".equals(elementName)) { + encodedName = elementName; + } else { + encodedName = Token.lookup(elementName).name(); + } + } else if (element.getModifiers().isGetter()) { + encodedName = "get$" + elementName; + } else if (element.getModifiers().isSetter()) { + encodedName = "set$" + elementName; + } else { + encodedName = elementName; + } + String holderName = element.getEnclosingElement().getName(); + return NATIVE_PREFIX + holderName + "_" + encodedName; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java b/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java new file mode 100644 index 00000000000..1e5360f2843 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/GenerateJavascriptAST.java @@ -0,0 +1,3583 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartAssertion; +import com.google.dart.compiler.ast.DartBinaryExpression; +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.DartClassMember; +import com.google.dart.compiler.ast.DartConditional; +import com.google.dart.compiler.ast.DartContinueStatement; +import com.google.dart.compiler.ast.DartDefault; +import com.google.dart.compiler.ast.DartDoWhileStatement; +import com.google.dart.compiler.ast.DartDoubleLiteral; +import com.google.dart.compiler.ast.DartEmptyStatement; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIfStatement; +import com.google.dart.compiler.ast.DartImportDirective; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartInvocation; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartLibraryDirective; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMapLiteralEntry; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNamedExpression; +import com.google.dart.compiler.ast.DartNativeBlock; +import com.google.dart.compiler.ast.DartNativeDirective; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartNullLiteral; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartParameterizedNode; +import com.google.dart.compiler.ast.DartParenthesizedExpression; +import com.google.dart.compiler.ast.DartPlainVisitor; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; +import com.google.dart.compiler.ast.DartResourceDirective; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartSourceDirective; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartStringInterpolation; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartSuperConstructorInvocation; +import com.google.dart.compiler.ast.DartSuperExpression; +import com.google.dart.compiler.ast.DartSwitchStatement; +import com.google.dart.compiler.ast.DartSyntheticErrorExpression; +import com.google.dart.compiler.ast.DartSyntheticErrorStatement; +import com.google.dart.compiler.ast.DartThisExpression; +import com.google.dart.compiler.ast.DartThrowStatement; +import com.google.dart.compiler.ast.DartTryStatement; +import com.google.dart.compiler.ast.DartTypeExpression; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartUnqualifiedInvocation; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.DartVariableStatement; +import com.google.dart.compiler.ast.DartWhileStatement; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.backend.common.TypeHeuristic.FieldKind; +import com.google.dart.compiler.backend.js.ScopeRootInfo.DartScope; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsBreak; +import com.google.dart.compiler.backend.js.ast.JsCase; +import com.google.dart.compiler.backend.js.ast.JsCatch; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsContinue; +import com.google.dart.compiler.backend.js.ast.JsDefault; +import com.google.dart.compiler.backend.js.ast.JsDoWhile; +import com.google.dart.compiler.backend.js.ast.JsExprStmt; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFor; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsIf; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsLabel; +import com.google.dart.compiler.backend.js.ast.JsLiteral; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsNode; +import com.google.dart.compiler.backend.js.ast.JsNullLiteral; +import com.google.dart.compiler.backend.js.ast.JsNumberLiteral; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsParameter; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import com.google.dart.compiler.backend.js.ast.JsReturn; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsStringLiteral; +import com.google.dart.compiler.backend.js.ast.JsSwitch; +import com.google.dart.compiler.backend.js.ast.JsSwitchMember; +import com.google.dart.compiler.backend.js.ast.JsThisRef; +import com.google.dart.compiler.backend.js.ast.JsThrow; +import com.google.dart.compiler.backend.js.ast.JsTry; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; +import com.google.dart.compiler.backend.js.ast.JsValueLiteral; +import com.google.dart.compiler.backend.js.ast.JsVars; +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import com.google.dart.compiler.backend.js.ast.JsWhile; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.Elements; +import com.google.dart.compiler.resolver.EnclosingElement; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.resolver.SuperElement; +import com.google.dart.compiler.resolver.VariableElement; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; +import com.google.dart.compiler.type.Types; +import com.google.dart.compiler.util.AstUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Stack; +import java.util.concurrent.Callable; + +/** + * Visitor that generates a Javascript AST from an existing Dart AST. + */ +public class GenerateJavascriptAST { + private final DartCompilerContext context; + private final OptimizationStrategy optStrategy; + private final DartUnit unit; + private CoreTypeProvider typeProvider; + + /** + * Generates the Javascript AST using the names created in {@link GenerateNamesAndScopes}. + */ + static class GenerateJavascriptVisitor + implements DartPlainVisitor, TraversalContextProvider { + + private static boolean isSuperCall(Symbol symbol) { + return ElementKind.of(symbol).equals(ElementKind.SUPER); + } + + /** + * Returns true for members that are static or should be treated + * as if the user had declared them as static. + */ + private static boolean isDeclaredAsStaticOrImplicitlyStatic(Element element) { + Modifiers modifiers = element.getModifiers(); + if (modifiers.isStatic()) { + // Member was actually declared static + return true; + } + + if (Elements.isTopLevel(element)) { + // Top level fields and methods are implicitly static + ElementKind elementKind = ElementKind.of(element); + return elementKind == ElementKind.FIELD || elementKind == ElementKind.METHOD; + } + + return false; + } + + /** + * The name of the javascript function used to intern compile time constants + */ + private static final String INTERN_CONST_FUNCTION = "$intern"; + + /** + * The name of the function used to lookup a id for a constant object + */ + private static final String DART_CONST_ID_JS_FUNC = "$dart_const_id"; + + /** + * The name of the method use to generate the id for an constant object + */ + private static final String CONST_ID_JS_METHOD_NAME = "$const_id"; + + private static final String STATIC_UNINITIALIZED = "static$uninitialized"; + private static final String STATIC_INITIALIZING = "static$initializing"; + private static final String ISOLATE_CURRENT = "isolate$current"; + private static final String ISOLATE_INITS = "isolate$inits"; + private static final String ISOLATE_DEFAULT_FACTORY = "default$factory"; + static final int MAX_SPECIALIZED_BIND_SCOPES = 3; + static final int MAX_SPECIALIZED_BIND_ARGS = 5; + private static final String ISOLATE_ISOLATE_FACTORY = "isolateFactory"; + private static final String ISOLATE_ISOLATE_FACTORY_GETTER = "getIsolateFactory"; + + private final JsScope globalScope; + private final JsBlock globalBlock; + private final List staticInit = Lists.newArrayList(); + private final Deque functionStack = new LinkedList(); + private final Deque> jsNewDeclarationsStack = new LinkedList>(); + private Element currentHolder; + private boolean inFactoryOrStaticContext = false; + private ScopeRootInfo currentScopeInfo; + private JsName traceCounter; + private final RuntimeTypeInjector rtt; + + private final TranslationContext translationContext; + private final OptimizationStrategy optStrategy; + private final DartCompilerContext context; + private final LibraryElement unitLibrary; + private final DartMangler mangler; + private final CoreTypeProvider typeProvider; + private final Types typeUtils; + + public GenerateJavascriptVisitor(DartUnit unit, DartCompilerContext context, + TranslationContext translationContext, + OptimizationStrategy optStrategy, CoreTypeProvider typeProvider) { + this.context = context; + this.translationContext = translationContext; + this.optStrategy = optStrategy; + this.typeProvider = typeProvider; + this.typeUtils = Types.getInstance(typeProvider); + this.unitLibrary = unit.getLibrary().getElement(); + + // Cache the mangler in a field since it is used frequently + mangler = translationContext.getMangler(); + + JsProgram program = translationContext.getProgram(); + globalScope = program.getScope(); + globalBlock = program.getGlobalBlock(); + // setup the global scope. + jsNewDeclarationsStack.push(new HashSet()); + currentHolder = unit.getLibrary().getElement(); + rtt = new RuntimeTypeInjector(this, typeProvider, translationContext); + } + + /** + * @param block The global block to add the static init statements too. + */ + public void addStaticInitsToBlock(JsBlock block) { + if (staticInit.isEmpty()) return; + JsFunction init = new JsFunction(globalScope); + JsBlock body = new JsBlock(); + body.getStatements().addAll(staticInit); + init.setBody(body); + staticInit.clear(); + + // All the static variable initialization code belonging to the + // current compilation unit is appended to the initialization + // list (isolate$inits) through Array.prototype.push. + JsNameRef pushRef = AstUtil.newNameRef(new JsNameRef(ISOLATE_INITS), "push"); + JsInvocation invokePush = AstUtil.newInvocation(pushRef); + invokePush.getArguments().add(init); + block.getStatements().add(new JsExprStmt(invokePush)); + } + + /** + * @return the JsScope that is used to create temporary Js-variables in the current scope. + */ + private JsScope getCurrentFunctionScope() { + return translationContext.getMethods().get(functionStack.peek()).getScope(); + } + + /** + * Adds the given name to the declarations-array. All names in the array will be declared at + * the beginning of the function. The same name can be registered multiple times. + * @param name + */ + private void registerForDeclaration(JsName name) { + jsNewDeclarationsStack.peek().add(name); + } + + + /** + * Creates a temporary variable but does not register it for var-declaration. + * @return the JsName of the temporary. + */ + private JsName createNonVarTempory() { + JsScope scope; + if (!functionStack.isEmpty()) { + scope = getCurrentFunctionScope(); + } else { + scope = globalScope; + } + return scope.declareTemporary(); + } + + /** + * Creates a temporary variable and registers it, so that an declaration statement is + * emitted. + * @return the JsName of the temporary. + */ + @Override + public JsName createTemporary() { + JsName temp = createNonVarTempory(); + registerForDeclaration(temp); + return temp; + } + + /** + * Returns the JsName for the given element. If the element is global and + * hasn't been declared yet, it is done now. + */ + private JsName getJsName(Symbol symbol) { + return translationContext.getNames().getName(symbol); + } + + /** + * Creates a JS function with JS calling conventions and a deterministic name + * so that it can be invoked from JS. The function will then forward + * the call to the given method (with Dart calling conventions). + */ + private void generateJsExportedFunction(MethodElement element, JsName name) { + JsFunction fn = new JsFunction(globalScope); + + JsNameRef dartTarget = makeMethodJsReference(element, name); + JsInvocation callIntoDart = AstUtil.newInvocation(dartTarget); + + List parameters = fn.getParameters(); + List arguments = callIntoDart.getArguments(); + for (VariableElement p : element.getParameters()) { + JsName parameter = fn.getScope().declareFreshName(p.getName()); + parameters.add(new JsParameter(parameter)); + arguments.add(parameter.makeRef()); + } + JsBlock jsBlock = new JsBlock(); + jsBlock.getStatements().add(new JsReturn(callIntoDart)); + jsBlock.setSourceRef(element.getNode()); + + fn.setBody(jsBlock); + + String exportedFunctionName = mangler.mangleNativeMethod(element); + JsName exportedFunctionJsName = globalScope.declareName(exportedFunctionName, + exportedFunctionName, + exportedFunctionName); + fn.setName(exportedFunctionJsName); + fn.setSourceRef(element.getNode()); + globalBlock.getStatements().add(fn.makeStmt()); + } + + /** + * Makes the default-constructor accessible under a deterministic name and + * with JavaScript calling conventions so that it can be invoked from JS. + */ + private void generateIsolateDefaultFactoryMember(MethodElement element, JsName funcName) { + JsName className = getJsName(element.getEnclosingElement()); + JsNameRef unmangledName = AstUtil.newNameRef(className.makeRef(), ISOLATE_DEFAULT_FACTORY); + JsNameRef factoryName = AstUtil.newNameRef(className.makeRef(), funcName); + JsBinaryOperation defaultAsg = AstUtil.newAssignment(unmangledName, factoryName); + defaultAsg.setSourceRef(element.getNode()); + globalBlock.getStatements().add(defaultAsg.makeStmt()); + } + + @Override + public JsNode visitClass(DartClass x) { + assert ElementKind.of(currentHolder).equals(ElementKind.LIBRARY) + : "Nested classes should be impossible"; + Element previousHolder = currentHolder; + currentHolder = x.getSymbol(); + + ClassElement classElement = x.getSymbol(); + JsName classJsName = getJsName(classElement); + + // If there is already a native class we must not create the JS function. + if (classElement.getNativeName() == null) { + if (optStrategy.canEmitOptimizedClassConstructor(classElement)) { + createInlinedClassConstructor(x); + } else { + JsFunction jsClass = new JsFunction(globalScope, classJsName).setSourceRef(x); + jsClass.setIsConstructor(true); + jsClass.setBody(new JsBlock()); + globalBlock.getStatements().add(jsClass.makeStmt()); + } + } + + rtt.generateRuntimeTypeInfo(x); + maybeInjectIsolateMethods(classElement); + + if (classElement.isInterface()) { + // Emit only static final fields for interfaces. + for (Element member : classElement.getMembers()) { + if (ElementKind.of(member).equals(ElementKind.FIELD)) { + Modifiers modifiers = member.getModifiers(); + if (modifiers.isStatic() && !modifiers.isAbstractField()) { + assert modifiers.isFinal(); + generateField((FieldElement) member); + } + } + } + } else { + // Inherits. + if (x.getSuperSymbol() != null) { + JsNameRef superRef = getJsName(x.getSuperSymbol()).makeRef(); + JsInvocation inherits = AstUtil.newInvocation( + new JsNameRef("$inherits"), + classJsName.makeRef(), + superRef); + inherits.setSourceRef(x); + globalBlock.getStatements().add(inherits.makeStmt()); + } + + List classMembers = new ArrayList(); + classMembers.addAll(classElement.getConstructors()); + for (Element element : classElement.getMembers()) { + classMembers.add(element); + } + for (Element member : classMembers) { + switch(ElementKind.of(member)) { + case METHOD: { + MethodElement methodElement = (MethodElement) member; + generateMethodDefinition(methodElement); + if (!methodElement.getModifiers().isOperator()) { + generateMethodGetter(methodElement); + } + break; + } + + case CONSTRUCTOR: + generateMethodDefinition((MethodElement) member); + break; + + case FIELD: + generateField((FieldElement) member); + break; + + default: + throw new AssertionError("Invalid member " + member); + } + } + + // TODO(johnlenz): should we create a stub method to catch + // class without const constructors? As is, the a non-const + // class with get the id of an "const Object". + if (hasConstConstructor(classElement)) { + makeConstIdMethod(classElement); + } + + // Add temporary variable declarations, if any. + // TODO(johnlenz): This isn't always correct: an incremental compile + // might reuse temps, which we don't want. However, static initializations + // (where the temps would be used) aren't quite right either yet. Double + // check this when its done. + Set temps = jsNewDeclarationsStack.peek(); + declareTempsInBlock(globalBlock, temps); + + // Clear the set for the next class. + temps.clear(); + } + + assert currentHolder == x.getSymbol() : "Unbalanced class visitation"; + currentHolder = previousHolder; + + return null; + } + + /** + * @param classElement + * + */ + private void maybeInjectIsolateMethods(ClassElement classElement) { + if (isIsolateClass(classElement)) { + // In order to construct an isolate in another worker, it must be + // referrable by name, this requires a top level method. + generateIsolateFactory(classElement); + + // ... and a way to get the factory from a isolate instance + generateIsolateFactoryGetter(classElement); + } + } + + private JsName getIsolateFactoryFunctionName(ClassElement classElement) { + String fnNameStr = getJsName(classElement).getShortIdent() + "$" + ISOLATE_ISOLATE_FACTORY; + return globalScope.declareName(fnNameStr); + } + + private JsNameRef getIsolateFactoryGetterName(ClassElement classElement) { + return AstUtil.newNameRef( + AstUtil.newPrototypeNameRef(getJsName(classElement).makeRef()), + ISOLATE_ISOLATE_FACTORY_GETTER); + } + + private void generateIsolateFactory(ClassElement classElement) { + // Create static factory function: + // function Foo$IsolateFactory() { + // return Foo.default$Factory(); + // } + + // Build the function + JsName fnName = getIsolateFactoryFunctionName(classElement); + + JsNameRef defaultFactory = AstUtil.newNameRef( + getJsName(classElement).makeRef(), ISOLATE_DEFAULT_FACTORY); + JsInvocation invokeFactory = AstUtil.newInvocation(defaultFactory); + + // TODO(johnlenz): Add runtime type information if necessary. + JsFunction factoryFn = AstUtil.newFunction(globalScope, fnName, null, + new JsReturn(invokeFactory)); + + globalBlock.getStatements().add(factoryFn.makeStmt()); + } + + private void generateIsolateFactoryGetter(ClassElement classElement) { + // Create static factory function: + // function Foo.prototype.getFactory() { + // return Foo$IsolateFactory; + // } + + // Build the function + JsName fnName = getIsolateFactoryFunctionName(classElement); + JsFunction getterFn = AstUtil.newFunction(globalScope, null, null, + new JsReturn(fnName.makeRef())); + + // Declare it. + JsExpression declStmt = AstUtil.newAssignment( + getIsolateFactoryGetterName(classElement), getterFn); + globalBlock.getStatements().add(declStmt.makeStmt()); + } + + private boolean isIsolateClass(ClassElement classElement) { + InterfaceType classType = classElement.getType(); + return TypeKind.of(classType) == TypeKind.INTERFACE + && typeUtils.isSubtype(classType, typeProvider.getIsolateType()); + } + + /** + * Creates a $getter for a method of a class, returning $method as a closure + * bound to the current instance if it is an instance method. + */ + private void generateMethodGetter(MethodElement methodElement) { + // Generate a getter for method binding to a variable + JsNameRef classJsNameRef = getJsName(methodElement.getEnclosingElement()).makeRef(); + String getterName = mangler.createGetterSyntax(methodElement, unitLibrary); + String methodName = methodElement.getName(); + JsName getterJsName = globalScope.declareName(getterName, getterName, methodName); + getterJsName.setObfuscatable(false); + String mangledMethodName = mangler.mangleNamedMethod(methodElement, unitLibrary); + JsFunction func = new JsFunction(globalScope); + JsNameRef getterJsNameRef; + JsExpression methodToCall; + if (methodElement.getModifiers().isStatic()) { + // function() { return $member; } + getterJsNameRef = AstUtil.newNameRef(classJsNameRef, getterJsName); + methodToCall = AstUtil.newNameRef(classJsNameRef, mangledMethodName); + func.setBody(AstUtil.newBlock(new JsReturn(methodToCall))); + } else { + // function() { return $bind(.prototype.$member, this); } + JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(classJsNameRef); + getterJsNameRef = AstUtil.newNameRef(prototypeRef, getterJsName); + methodToCall = AstUtil.newNameRef(prototypeRef, mangledMethodName); + JsExpression bindMethodCall = AstUtil.newInvocation( + new JsNameRef("$bind"), methodToCall, new JsThisRef()); + func.setBody(AstUtil.newBlock(new JsReturn(bindMethodCall))); + } + func.setName(getterJsNameRef.getName()); + func.setSourceRef(methodElement.getNode()); + JsBinaryOperation asg = AstUtil.newAssignment(getterJsNameRef, func); + asg.setSourceRef(methodElement.getNode()); + globalBlock.getStatements().add(asg.makeStmt()); + } + + private boolean hasConstConstructor(ClassElement element) { + for (ConstructorElement ctr : element.getConstructors()) { + if (ctr.getModifiers().isConstant()) { + return true; + } + } + return false; + } + + private void makeConstIdMethod(ClassElement classElement) { + JsNameRef methodRef = makeConstIdMethodRef(classElement); + JsStatement decl = AstUtil.newAssignment(methodRef, + makeConstIdMethodFunction(classElement)).makeStmt(); + this.globalBlock.getStatements().add(decl); + } + + private JsNameRef makeConstIdMethodRef(ClassElement classElement) { + // Instance methods hang from the prototype. + JsNameRef qualifier = AstUtil.newPrototypeNameRef(getJsName(classElement).makeRef()); + JsNameRef methodRef = AstUtil.newNameRef(qualifier, CONST_ID_JS_METHOD_NAME); + return methodRef; + } + + private JsFunction makeConstIdMethodFunction(ClassElement classElement) { + JsFunction func = new JsFunction(this.globalScope); + // Make an id like: + // :field1:field2:...-:field1:field2:... + String s = getJsName(classElement).getShortIdent(); + JsExpression idExpr = string(s); + for (Element member : classElement.getMembers()) { + if (member.getKind() == ElementKind.FIELD) { + if (!member.getModifiers().isStatic()) { + idExpr = addConstIdFieldExpr((FieldElement) member, idExpr); + } + } + } + idExpr = addConstIdSuperExpr(classElement, idExpr); + func.setBody(AstUtil.newBlock(new JsReturn(idExpr))); + return func; + } + + private JsExpression addConstIdFieldExpr( + FieldElement element, JsExpression prevPart) { + JsExpression qualifier = getGetterSetterQualifier(element); + JsName fieldJsName = getJsName(element); + JsNameRef ref = AstUtil.newNameRef(qualifier, fieldJsName); + + // example: prevPart + ":" + $const_id(this.field) + return add(prevPart, add(string(":"), + AstUtil.newInvocation(new JsNameRef(DART_CONST_ID_JS_FUNC), ref))); + } + + private JsExpression addConstIdSuperExpr(ClassElement element, JsExpression prevPart) { + InterfaceType superType = element.getSupertype(); + if (superType == null || superType.getElement().isObject()) { + // The root object doesn't add anything. + return prevPart; + } + JsNameRef superConstIdRef = makeConstIdMethodRef(superType.getElement()); + JsNameRef callRef = AstUtil.newNameRef(superConstIdRef, "call"); + JsInvocation superCall = AstUtil.newInvocation(callRef, new JsThisRef()); + + // example: prevPart + "-" + super.prototype.$const_id.call(this); + return add(prevPart, add(string("-"), superCall)); + } + + private JsExpression add(JsExpression first, JsExpression second) { + return new JsBinaryOperation(JsBinaryOperator.ADD, first, second); + } + + private void generateField(FieldElement element) { + generate(element.getNode()); + } + + private void generateMethodDefinition(MethodElement element) { + JsFunction func = (JsFunction) generate(element.getNode()); + + // makeMethod clears the name of the function. + JsName funcName = func.getName(); + makeMethod(element, func); + + // If the method is the default factory we add the same method under an unmangled name. + // This is necessary for the isolate code. + if (Elements.isNonFactoryConstructor(element) + && "".equals(element.getName()) + && func.getParameters().size() == 0 + && isIsolateClass((ClassElement)element.getEnclosingElement())) { + generateIsolateDefaultFactoryMember(element, funcName); + } + + // If the function is exported to JavaScript make it accessible under its + // (more or less) unmangled name. + DartMethodDefinition method = (DartMethodDefinition) element.getNode(); + DartBlock body = method.getFunction().getBody(); + if (element.getModifiers().isNative() && !(body instanceof DartNativeBlock)) { + generateJsExportedFunction(element, funcName); + } + } + + private void createInlinedClassConstructor(DartClass x) { + ClassElement classElement = x.getSymbol(); + assert classElement.getNativeName() == null; + JsName classJsName = getJsName(classElement); + JsFunction jsClass = new JsFunction(globalScope, classJsName).setSourceRef(x); + jsClass.setIsConstructor(true); + JsBlock block = new JsBlock(); + JsScope scope = new JsScope(globalScope, "temp"); + for (FieldElement fieldElement : getFieldsInClassHierarchy(classElement)) { + String fieldName = translationContext.getMangler().mangleField(fieldElement, unitLibrary); + JsNameRef fieldRef = AstUtil.newNameRef(new JsThisRef(), fieldName); + JsName paramName = scope.declareName("p$" + fieldName); + jsClass.getParameters().add(new JsParameter(paramName)); + JsBinaryOperation asg = AstUtil.newAssignment(fieldRef, new JsNameRef(paramName)); + block.getStatements().add(asg.makeStmt()); + } + jsClass.setBody(block); + globalBlock.getStatements().add(jsClass.makeStmt()); + } + + private List getFieldsInClassHierarchy(ClassElement classElement) { + InterfaceType current = classElement.getType(); + Stack classes = new Stack(); + while ((current != null) && !current.getElement().isObject()) { + classElement = current.getElement(); + classes.push(classElement); + current = classElement.getSupertype(); + } + List fields = Lists.newArrayList(); + while (!classes.isEmpty()) { + classElement = classes.pop(); + for (Element elem : classElement.getMembers()) { + Modifiers modifiers = elem.getModifiers(); + if (ElementKind.of(elem).equals(ElementKind.FIELD) && !modifiers.isStatic() + && !modifiers.isAbstractField()) { + fields.add((FieldElement) elem); + } + } + } + return fields; + } + + private void generateAbstractField(FieldElement fieldElement) { + if (fieldElement.getGetter() != null) { + generateMethodDefinition(fieldElement.getGetter()); + } + if (fieldElement.getSetter() != null) { + generateMethodDefinition(fieldElement.getSetter()); + } + } + + private void declareTempsInBlock(JsBlock block, Collection tempCollection) { + // Add temporary variable declarations, if any. + Iterator temps = tempCollection.iterator(); + if (temps.hasNext()) { + JsVars jsVars = new JsVars(); + while (temps.hasNext()) { + JsName name = temps.next(); + JsVars.JsVar jsVar = new JsVars.JsVar(name); + jsVars.insert(jsVar); + } + block.getStatements().add(0, jsVars); + } + } + + private List getInlineFieldInitializers(ConstructorElement element) { + List fieldInitializers = new ArrayList(); + Iterable classMembers = element.getEnclosingElement().getMembers(); + for (Element member : classMembers) { + Modifiers modifiers = member.getModifiers(); + if (!modifiers.isStatic() + && !modifiers.isAbstractField() + && ElementKind.of(member).equals(ElementKind.FIELD)) { + DartField field = (DartField) member.getNode(); + if (field.getValue() != null) { + fieldInitializers.add(field); + } + } + } + return fieldInitializers; + } + + private JsExpression generateInlineFieldInitializer(DartField field) { + JsNameRef fieldName = AstUtil.newNameRef(new JsThisRef(), getJsName(field.getSymbol())); + JsExpression initExpr = (JsExpression) generate(field.getValue()); + return AstUtil.newAssignment(fieldName, initExpr); + } + + /** + * For a constructor B whose super is A we generate: + * + * FactoryB() { + * var tmp = new B; + * InitB(tmp); + * BodyB(tmp); + * } + * + * BodyB() { + * BodyA(); + * } + * + * InitB() { + * InitA(); + * } + * + * This method creates the InitB method and adds the BodyA call in the BodyB method. + */ + private void addInitializers(DartMethodDefinition constructor, + JsFunction factory, + JsName tempVar) { + ConstructorElement element = (ConstructorElement) constructor.getSymbol(); + JsScope classMemberScope = translationContext.getMemberScopes().get(element.getEnclosingElement()); + JsName curClassJsName = getJsName(element.getEnclosingElement()); + + // Create the initializer function. + String constructorName = element.getName(); + String initName = mangler.createInitializerSyntax(constructorName, unitLibrary); + JsName initJsName = classMemberScope.declareName(initName, initName, constructorName); + // Initializers are called from other class (as part of the super initialization). + initJsName.setObfuscatable(false); + JsFunction initFunction = new JsFunction(globalScope, initJsName).setSourceRef(constructor); + initFunction.setBody(new JsBlock()); + + // Add the initializer as a member of the current class. + makeMethod(element, initFunction); + + // Add the parameters to the initializer function. + List params = constructor.getFunction().getParams(); + for (DartParameter p : params) { + initFunction.getParameters().add( + new JsParameter(getJsName(p.getNormalizedNode().getSymbol()))); + } + + // If there are initializers, or inline field initializers, populate the + // initializer function. + List initializers = constructor.getInitializers(); + List fieldInitializers = getInlineFieldInitializers(element); + + if (!initializers.isEmpty() || !fieldInitializers.isEmpty()) { + // TODO(johnlenz): move this block shares the some of the same setup + // and tear down as the visitFunction method. + + // Give the initializer expressions access to the function parameters + functionStack.push(constructor.getFunction()); + jsNewDeclarationsStack.push(new HashSet()); + + JsInvocation constructorInvocation = maybeGenerateSuperOrRedirectCall(constructor); + boolean hasConstructorInvocation = constructorInvocation != null; + Iterator iterator = initializers.iterator(); + Iterator fieldIterator = fieldInitializers.iterator(); + + if (hasConstructorInvocation) { + // skip the super call + DartInitializer first = iterator.next(); + assert first.isInvocation(); + } + + List jsInitializers = initFunction.getBody().getStatements(); + + // Do the field inline initializers first. If there are any assignments in the initializer + // list, they will be the last assignments. + while (fieldIterator.hasNext()) { + JsExpression initializer = generateInlineFieldInitializer(fieldIterator.next()); + jsInitializers.add(initializer.makeStmt()); + } + + while (iterator.hasNext()) { + jsInitializers.add((JsStatement) generate(iterator.next())); + } + + if (hasConstructorInvocation) { + // Call the super initializer function in the initializer. + // Compute the super constructor initializer to call. + DartInvocation initInvocation = (DartInvocation) initializers.get(0).getValue(); + ConstructorElement superElement = (ConstructorElement) initInvocation.getSymbol(); + // TODO(floitsch): it would be better if we had a js-name and not just a string. + // This way the debugging information would be better. + // We need to generate the JsName (for the initializer/factory) once only and store it + // in some hashtable. Then instead of reusing the mangler, we should reuse those JsNames. + // The debugging information would then contain a link from the property-access to the + // constructor. Without JsName the debugger just assumes we access some random property. + String mangledSuperConstructorName = + mangler.createInitializerSyntax(superElement.getName(), unitLibrary); + Element superClassElement = superElement.getEnclosingElement(); + JsNameRef superInitRef = AstUtil.newNameRef(getJsName(superClassElement).makeRef(), + mangledSuperConstructorName); + JsNameRef callRef = AstUtil.newNameRef(superInitRef, "call"); + JsInvocation superInitCall = AstUtil.newInvocation(callRef); + initFunction.getBody().getStatements().add(0, superInitCall.makeStmt()); + // TODO(floitsch): don't copy the arguments from the super call for the initializer call. + // This will evaluate side-effects twice, and we are reusing nodes (thereby creating a + // DAG instead of a tree). + superInitCall.getArguments().addAll(constructorInvocation.getArguments()); + } + + // Call the initializer in the factory. This must be executed + // before calling the super constructor: .$Initializer.call(this, ...) + JsNameRef initRef = AstUtil.newNameRef(curClassJsName.makeRef(), initJsName); + JsNameRef initCallRef = AstUtil.newNameRef(initRef, "call"); + JsInvocation initCall = AstUtil.newInvocation(initCallRef, tempVar.makeRef()); + for (DartParameter p : params) { + initCall.getArguments().add(getJsName(p.getNormalizedNode().getSymbol()).makeRef()); + } + + factory.getBody().getStatements().add(0, initCall.makeStmt()); + + // Dart does not have an implicit call to a super constructor. + + // Add temporary variable declarations, if any. + declareTempsInBlock(initFunction.getBody(), jsNewDeclarationsStack.pop()); + + // setup the scope alias for the init function + maybeAddFunctionScopeAlias( + currentScopeInfo.getScope(constructor.getFunction()), initFunction); + + // Remove the containing function scope. + functionStack.pop(); + } + } + + private void addSuperOrRedirectConstructorCall(DartMethodDefinition constructor) { + JsInvocation superCall = maybeGenerateSuperOrRedirectCall(constructor); + if (superCall != null) { + // If we have a super constructor call, add it as the first statement + // in the constructor body. + // .$Constructor.call(this, ...). + JsFunction constructorFunction = translationContext.getMethods().get(constructor.getFunction()); + constructorFunction.getBody().getStatements().add(0, superCall.makeStmt()); + } + } + + private JsInvocation maybeGenerateSuperOrRedirectCall(DartMethodDefinition constructor) { + // If there are initializers, populate the initializer function. + List initializers = constructor.getInitializers(); + if (!initializers.isEmpty()) { + DartInitializer firstInit = initializers.get(0); + if (firstInit.isInvocation()) { + JsExprStmt statement = (JsExprStmt) generate(firstInit); + return (JsInvocation) statement.getExpression(); + } + } + return null; + } + + private JsNode generateConstructorDefinition(DartMethodDefinition x) { + assert currentScopeInfo == null : "Nesting a constructor in a method should be impossible"; + currentScopeInfo = ScopeRootInfo.makeScopeInfo(x); + ConstructorElement element = (ConstructorElement) x.getSymbol(); + ClassElement classElement = (ClassElement) element.getEnclosingElement(); + JsScope classMemberScope = translationContext.getMemberScopes().get(classElement); + String constructorName = element.getName(); + JsName curClassJsName = getJsName(classElement); + + JsFunction dartCtor = (JsFunction) generate(x.getFunction()); + + // Add the constructor as a member of the current class. + makeMethod(element, dartCtor); + + // Create the static factory function that allocates the object + // and calls the constructor. + // .ConstructorName$Factory = function (args ...) { + // var tmp = new (); + // tmp.$typeInfo = runtimeType; + // .ConstructorName$Constructor.call(tmp, args ...); + // return tmp; + // } + // Attaching the factory to is done outside this method. We just provide the + // factory-name ("ConstructorName$Factory" here). + + // The factory becomes a member of and should therefore be declared in the same + // scope as all other members. + String className = element.getConstructorType().getName(); + String factoryName = mangler.createFactorySyntax(className, constructorName, + unitLibrary); + JsName factoryJsName = + classMemberScope.declareName(factoryName, factoryName, constructorName); + // Factories are globally accessible. + factoryJsName.setObfuscatable(false); + + JsFunction factoryFunction = new JsFunction(globalScope, factoryJsName).setSourceRef(x); + JsScope factoryScope = factoryFunction.getScope(); + + // We do the constructor invocation before we declare the temporary variable. This is + // necessary to ensure that the created temporary does not conflict with the parameters. + JsInvocation constructorInvocation = new JsInvocation(); + JsName constructorJsName = getJsName(element); + JsNameRef constructorRef = AstUtil.newNameRef(curClassJsName.makeRef(), constructorJsName); + constructorInvocation.setQualifier(AstUtil.newNameRef(constructorRef, "call")); + + // Add the arguments to the constructor invocation. Note that the constructor call is still + // missing the 'tmp' variable. We will add it later. + List params = x.getFunction().getParams(); + for (DartParameter p : params) { + // TODO(ngeoffray): We should actually copy the arguments. See b/4424659. + JsName argName = getJsName(p.getNormalizedNode().getSymbol()); + constructorInvocation.getArguments().add(argName.makeRef()); + } + + JsName tempVar = factoryScope.declareTemporary(); + // Add the 'tmp' var to the constructor call. + constructorInvocation.getArguments().add(0, tempVar.makeRef()); + + factoryFunction.setBody(AstUtil.newBlock( + constructorInvocation.makeStmt(), + new JsReturn(tempVar.makeRef()))); + + if (optStrategy.canInlineInitializers(element)) { + rtt.maybeAddClassRuntimeTypeToConstructor(classElement, factoryFunction, tempVar.makeRef()); + generateInitializersInlined(x, factoryFunction, factoryScope, tempVar); + } else { + addInitializers(x, factoryFunction, tempVar); + rtt.maybeAddClassRuntimeTypeToConstructor(classElement, factoryFunction, tempVar.makeRef()); + JsNew jsNew = new JsNew(curClassJsName.makeRef()); + factoryFunction.getBody().getStatements().add(0, AstUtil.newVar(x, tempVar, jsNew)); + } + + generateAll(x.getFunction().getParams(), factoryFunction.getParameters(), JsParameter.class); + + assert currentScopeInfo != null; + inFactoryOrStaticContext = false; + currentScopeInfo = null; + + return factoryFunction; + } + + private void generateInitializersInlined(DartMethodDefinition x, JsFunction factoryFunction, + JsScope factoryScope, JsName tempVar) { + ConstructorElement element = (ConstructorElement) x.getSymbol(); + JsName curClassJsName = getJsName(element.getEnclosingElement()); + Map initMap = new HashMap(); + JsExpression superInvocation = null; + for (DartInitializer init : x.getInitializers()) { + JsExpression initValue = (JsExpression) generate(init.getValue()); + if (init.isInvocation()) { + superInvocation = initValue; + continue; + } else { + assert ElementKind.of(init.getName().getTargetSymbol()).equals(ElementKind.FIELD); + FieldElement fieldElement = (FieldElement) init.getName().getTargetSymbol(); + initMap.put(fieldElement, initValue); + } + } + List stmts = Lists.newArrayList(); + JsNew jsNew = new JsNew(curClassJsName.makeRef()); + ClassElement classElement = (ClassElement) element.getEnclosingElement(); + for (FieldElement fieldElement : getFieldsInClassHierarchy(classElement)) { + String fieldName = translationContext.getMangler().mangleField(fieldElement, unitLibrary); + JsName tmp = factoryScope.declareName("init$" + fieldName); + JsExpression initValue = initMap.get(fieldElement); + if (initValue == null) { + DartField fieldNode = (DartField) fieldElement.getNode(); + if (fieldNode.getValue() != null) { + initValue = (JsExpression) generate(fieldNode.getValue()); + } else { + initValue = undefined(); + } + } + stmts.add(AstUtil.newVar(x, tmp, initValue)); + jsNew.getArguments().add(new JsNameRef(tmp)); + } + if (superInvocation != null) { + factoryFunction.getBody().getStatements().add(0, new JsExprStmt(superInvocation)); + } + stmts.add(AstUtil.newVar(x, tempVar, jsNew)); + factoryFunction.getBody().getStatements().addAll(0, stmts); + } + + @Override + public JsNode visitMethodDefinition(DartMethodDefinition x) { + assert x == x.getNormalizedNode(); + if (Elements.isNonFactoryConstructor(x.getSymbol())) { + return generateConstructorDefinition(x); + } + + assert currentScopeInfo == null : "Nested methods should be impossible"; + inFactoryOrStaticContext = x.getModifiers().isFactory() + || x.getModifiers().isStatic(); + currentScopeInfo = ScopeRootInfo.makeScopeInfo(x); + + JsFunction func = (JsFunction) generate(x.getFunction()); + + assert currentScopeInfo != null; + inFactoryOrStaticContext = false; + currentScopeInfo = null; + + if (Elements.isTopLevel(x.getSymbol())) { + JsFunction tramp = generateNamedParameterMethodTrampoline(x, func.getName().makeRef()); + String mangled = mangler.mangleNamedMethod(x.getSymbol(), unitLibrary); + JsName trampName = globalScope.declareName(mangled); + tramp.setName(trampName); + + globalBlock.getStatements().add(func.makeStmt()); + globalBlock.getStatements().add(tramp.makeStmt()); + } + + return func; + } + + private JsFunction generateNamedParameterMethodTrampoline(DartMethodDefinition method, + JsNameRef origJsName) { + boolean preserveThis = !(method.getModifiers().isStatic() || + method.getModifiers().isFactory() || + Elements.isTopLevel(method.getSymbol())); + + return generateNamedParameterTrampoline(method.getFunction(), origJsName, 0, preserveThis); + } + + private JsFunction generateNamedParameterTrampoline(DartFunction func, + JsNameRef origJsName, int numClosureScopes, boolean preserveThis) { + // function([$s0, $s1, ...], $n, $o, P0, P1, P2, P3, ...) { + JsFunction tramp = new JsFunction(globalScope); + JsScope scope = tramp.getScope(); + List closureScopeParams = new ArrayList(); + for (int i = 0; i < numClosureScopes; ++i) { + JsParameter param = new JsParameter(scope.declareName("$s" + i)); + tramp.getParameters().add(param); + closureScopeParams.add(param); + } + JsParameter countParam = new JsParameter(scope.declareName("$n")); + tramp.getParameters().add(countParam); + JsParameter namedParam = new JsParameter(scope.declareName("$o")); + tramp.getParameters().add(namedParam); + + List explicitJsParams = new ArrayList(); + for (DartParameter dartParam : func.getParams()) { + String paramName = ((DartIdentifier) dartParam.getName()).getTargetName(); + JsParameter param = new JsParameter(scope.declareName(paramName)); + explicitJsParams.add(param); + tramp.getParameters().add(param); + } + + // var seen = 0, def = 0; + JsBlock body = new JsBlock(); + tramp.setBody(body); + List stmts = body.getStatements(); + + JsName seen = scope.declareName("seen"); + JsName def = scope.declareName("def"); + stmts.add(AstUtil.newVar(null, seen, number(0))); + stmts.add(AstUtil.newVar(null, def, number(0))); + + // switch ($n) { + // case 1: P0 = $o.P0 ? (++seen, $o.P0) : null; // no default value + // case 2: P1 = $o.P1 ? (++seen, $o.P1) : (++def, DEFAULT); // explicit default value + // ... + // } + JsSwitch jsSwitch = new JsSwitch(); + jsSwitch.setExpr(countParam.getName().makeRef()); + for (int i = 0; i < func.getParams().size(); ++i) { + DartParameter param = func.getParams().get(i); + JsParameter jsParam = tramp.getParameters().get(i + 2); + if (!param.getModifiers().isNamed()) { + continue; + } + + JsNameRef ifExpr = AstUtil.newNameRef(namedParam.getName().makeRef(), + jsParam.getName()); + + JsPrefixOperation ppSeen = new JsPrefixOperation(JsUnaryOperator.INC, seen.makeRef()); + JsBinaryOperation thenExpr = new JsBinaryOperation(JsBinaryOperator.COMMA, ppSeen, + AstUtil.newNameRef(namedParam.getName().makeRef(), jsParam.getName())); + + JsExpression elseExpr; + + DartExpression defaultValue = param.getDefaultExpr(); + if (defaultValue != null) { + JsPrefixOperation ppDef = new JsPrefixOperation(JsUnaryOperator.INC, def.makeRef()); + elseExpr = new JsBinaryOperation(JsBinaryOperator.COMMA, ppDef, + generateDefaultValue(defaultValue)); + } else { + elseExpr = nulle(); + } + + JsBinaryOperation asg = assign( + jsParam.getName().makeRef(), + new JsConditional(ifExpr, thenExpr, elseExpr)); + + jsSwitch.getCases().add(AstUtil.newCase(number(i), asg.makeStmt())); + } + if (jsSwitch.getCases().size() > 0) { + stmts.add(jsSwitch); + } + + // if ((seen != $o.$count) || (seen + def + $n != TOTAL)) { + // $nsme(); + // } + { + JsBinaryOperation ifLeft = neq(seen.makeRef(), + AstUtil.newNameRef(namedParam.getName().makeRef(), "count")); + + JsExpression add1 = add(seen.makeRef(), def.makeRef()); + JsExpression add2 = add(add1, countParam.getName().makeRef()); + JsExpression ifRight = neq(add2, number(func.getParams().size())); + + JsExpression ifExpr = or(ifLeft, ifRight); + JsStatement thenStmt = AstUtil.newInvocation(new JsNameRef("$nsme")).makeStmt(); + + stmts.add(new JsIf(ifExpr, thenStmt, null)); + } + + JsInvocation jsInvoke = AstUtil.newInvocation( + AstUtil.newNameRef(origJsName.getQualifier(), origJsName.getName())); + if (preserveThis) { + JsNameRef call = AstUtil.newNameRef(jsInvoke.getQualifier(), "call"); + jsInvoke = AstUtil.newInvocation(call, new JsThisRef()); + } + for (int i = 0; i < numClosureScopes; ++i) { + jsInvoke.getArguments().add(closureScopeParams.get(i).getName().makeRef()); + } + for (JsParameter jsParam : explicitJsParams) { + jsInvoke.getArguments().add(jsParam.getName().makeRef()); + } + stmts.add(new JsReturn(jsInvoke)); + + return tramp; + } + + /** + * If necessary, add object holding aliases for any parameters + * captured by function closures. + */ + private void maybeAddFunctionScopeAlias(DartScope scope, JsFunction function) { + if (scope.definesClosureReferencedSymbols()) { + JsScope jsScope = function.getScope(); + JsBlock body = function.getBody(); + + // Example: + // function f(a,b) { ... } + // to: + // function f(a,b) {var s0={f:f,a:a,b:b} ... }; + JsObjectLiteral aliasInit = new JsObjectLiteral(); + for (Entry entry : scope.getSymbols().entrySet()) { + if (entry.getValue().isReferencedFromClosure()) { + JsName param = getJsName(entry.getKey()); + aliasInit.getPropertyInitializers().add( + new JsPropertyInitializer(string(param.getIdent()), new JsNameRef(param))); + } + } + + JsName aliasName = scope.getAliasForJsScope(jsScope); + // Scope objects are declared (in the JsScope) at first use. By construction scope-objects + // are only created when they are used. Therefore the scope-object must exist in the + // JsScope. + assert aliasName != null; + JsStatement aliasDecl = AstUtil.newVar(null, aliasName, aliasInit); + body.getStatements().add(0, aliasDecl); + } + } + + private JsName getTraceCounter() { + if (traceCounter == null) { + traceCounter = globalScope.declareTemporary(); + JsStatement counterDecl = AstUtil.newVar(null, traceCounter, number(0)); + globalBlock.getStatements().add(0, counterDecl); + } + return traceCounter; + } + + private JsNameRef makeMethodJsReference(Element element, JsName name) { + JsNameRef qualifier; + boolean isNonFactoryConstructor = Elements.isNonFactoryConstructor(element); + Modifiers modifiers = element.getModifiers(); + JsNameRef classJsName = getJsName(element.getEnclosingElement()).makeRef(); + if (modifiers.isStatic() || modifiers.isFactory() || isNonFactoryConstructor) { + // Static methods hang directly from the constructor. + qualifier = classJsName; + } else { + // Instance methods hang from the prototype. + qualifier = AstUtil.newPrototypeNameRef(classJsName); + } + + JsNameRef prop = AstUtil.newNameRef(qualifier, name); + // TODO(johnlenz): This should be the name node reference + prop.setSourceRef(element.getNode()); + return prop; + } + + /** + * Turns a method into a prototype assignment on the JS class. Clears the + * name from the given function. + */ + private void makeMethod(Element element, JsFunction func) { + if (element.getEnclosingElement().getKind().equals(ElementKind.CLASS)) { + JsNameRef prop = makeMethodJsReference(element, func.getName()); + func.setName(null); + JsBinaryOperation asg = AstUtil.newAssignment(prop, func); + + // TODO(johnlenz): This should be the stmt node reference + asg.setSourceRef(element.getNode()); + globalBlock.getStatements().add(asg.makeStmt()); + + // If it's a (non-operator, non-property) method, generate its named trampoline. + if (element.getKind().equals(ElementKind.METHOD) && + !element.getModifiers().isOperator() && + !element.getModifiers().isGetter() && + !element.getModifiers().isSetter()) { + // Declare the mangled trampoline's name in the same scope as its target. + String mangled = mangler.mangleNamedMethod((MethodElement) element, unitLibrary); + JsName namedName = prop.getName().getEnclosing().declareName(mangled); + JsNameRef namedProp = makeMethodJsReference(element, namedName); + + DartMethodDefinition method = (DartMethodDefinition) element.getNode(); + JsFunction tramp = generateNamedParameterMethodTrampoline(method, prop); + + asg = assign(namedProp, tramp); + globalBlock.getStatements().add(asg.makeStmt()); + } + } else { + globalBlock.getStatements().add(func.makeStmt()); + } + } + + private JsExpression getGetterSetterQualifier(Element element) { + if (isDeclaredAsStaticOrImplicitlyStatic(element)) { + // The mangler makes sure that the mangled version of static + // fields names encode the class name so we do not have to + // read the fields through the class function. + return new JsNameRef(ISOLATE_CURRENT); + } else if (Elements.isTopLevel(element)) { + return null; + } else { + return new JsThisRef(); + } + } + + /** + * Creates a getter that returns a JavaScript property. + */ + private void makePropertyGetter(FieldElement element) { + JsExpression qualifier = getGetterSetterQualifier(element); + JsName fieldJsName = getJsName(element); + JsNameRef ref = AstUtil.newNameRef(qualifier, fieldJsName); + makeGetter(element, ref); + } + + /** + * Creates a getter that returns a constant (simple) JavaScript value. + */ + private void makeConstantValueGetter(FieldElement element, JsExpression value) { + assert element.getModifiers().isFinal(); + makeGetter(element, value); + } + + private void makeGetter(FieldElement element, JsExpression expression) { + String getterName = mangler.createGetterSyntax(element, unitLibrary); + String fieldName = element.getName(); + JsName getterJsName = globalScope.declareName(getterName, getterName, fieldName); + getterJsName.setObfuscatable(false); + JsFunction func = new JsFunction(globalScope, getterJsName); + func.setBody(AstUtil.newBlock(new JsReturn(expression))); + makeMethod(element, func); + } + + /** + * Create a shim method for invoking a method through a field. Invoke the + * getter to get the field value, then apply the shim's arguments to + * the returned closure object. + */ + private void makeMethodCallThroughFieldShim(DartField x) { + FieldElement element = x.getSymbol(); + if (Elements.isTopLevel(element)) { + // Don't bother making a call-though-field shim for global methods. They're always + // statically resolved, so we'll never generate a call to one. + return; + } + + String shimName = mangler.mangleNamedMethod(element.getName(), unitLibrary); + String fieldName = element.getName(); + JsName shimJsName = globalScope.declareName(shimName, shimName, fieldName); + shimJsName.setObfuscatable(false); + JsFunction func = new JsFunction(globalScope, shimJsName); + JsExpression qualifier; + if (element.getModifiers().isStatic()) { + Element enclosingElement = element.getEnclosingElement(); + switch (enclosingElement.getKind()) { + case CLASS: + qualifier = AstUtil.newNameRef(null, + mangler.mangleClassName((ClassElement) enclosingElement)); + break; + case LIBRARY: + qualifier = null; + break; + default: + throw new InternalCompilerException( + "Unhandled type of static element making method shim."); + } + } else { + qualifier = getGetterSetterQualifier(element); + } + String getterName = mangler.createGetterSyntax(element, unitLibrary); + JsExpression expression = AstUtil.newInvocation(AstUtil.newNameRef(qualifier, getterName)); + expression = AstUtil.newNameRef(expression, "apply"); + expression = AstUtil.newInvocation(expression, new JsThisRef(), + AstUtil.newNameRef(null, "arguments")); + func.setBody(AstUtil.newBlock(new JsReturn(expression))); + makeMethod(element, func); + } + + /** + * Creates a getter method that lazily initializes the field (if necessary). + */ + private void makeInitializingGetter(FieldElement element, JsExpression initExpression) { + String getterName = mangler.createGetterSyntax(element, unitLibrary); + String fieldName = element.getName(); + JsName getterJsName = globalScope.declareName(getterName, getterName, fieldName); + getterJsName.setObfuscatable(false); + + JsFunction func = new JsFunction(globalScope, getterJsName); + JsScope scope = new JsScope(globalScope, "temp"); + + // Foo.x$getter = function() { + // var t0 = isolate$current.Foo$x; + // var t1 = $initializing; + // if (t0 === t1) throw "circular initialization"; + // if (t0 !== $uninitialized) return t0; + // isolate$current.Foo$x = t1; + // var t2 = ... // initialization expression + // isolate$current.Foo$x = t2; + // return t2; + // } + + JsExpression fieldQualifier = getGetterSetterQualifier(element); + JsName fieldJsName = getJsName(element); + + JsName t0 = scope.declareTemporary(); + JsName t1 = scope.declareTemporary(); + JsName t2 = scope.declareTemporary(); + + JsVars initializeT0 = AstUtil.newVar( + null, t0, AstUtil.newNameRef(fieldQualifier, fieldJsName)); + JsVars initializeT1 = AstUtil.newVar( + null, t1, new JsNameRef(STATIC_INITIALIZING)); + JsStatement checkIfCircular = new JsIf( + new JsBinaryOperation( + JsBinaryOperator.REF_EQ, + t0.makeRef(), + t1.makeRef()), + new JsThrow(string("circular initialization")), + null); + JsStatement checkIfInitialized = new JsIf( + new JsBinaryOperation( + JsBinaryOperator.REF_NEQ, + t0.makeRef(), + new JsNameRef(STATIC_UNINITIALIZED)), + new JsReturn(t0.makeRef()), + null); + JsStatement markField = AstUtil.newAssignment( + AstUtil.newNameRef(fieldQualifier, fieldJsName), t1.makeRef()).makeStmt(); + JsStatement initializeT2 = AstUtil.newVar( + null, t2, initExpression); + JsStatement initializeField = AstUtil.newAssignment( + AstUtil.newNameRef(fieldQualifier, fieldJsName), t2.makeRef()).makeStmt(); + JsStatement returnT2 = new JsReturn(t2.makeRef()); + + // Construct the method from the statements. + func.setBody(AstUtil.newBlock( + initializeT0, + initializeT1, + checkIfCircular, + checkIfInitialized, + markField, + initializeT2, + initializeField, + returnT2)); + makeMethod(element, func); + } + + /** + * Creates a setter and turns it into a prototype assignment on the + * JS class. + */ + private void makeSetter(FieldElement element) { + String fieldName = element.getName(); + String setterName = mangler.createSetterSyntax(element, unitLibrary); + JsName setterJsName = globalScope.declareName(setterName, setterName, fieldName); + setterJsName.setObfuscatable(false); + JsFunction func = new JsFunction(globalScope, setterJsName); + + JsScope scope = new JsScope(globalScope, "temp"); + JsName parameter = scope.declareTemporary(); + func.getParameters().add(0, new JsParameter(parameter)); + + JsExpression qualifier = getGetterSetterQualifier(element); + + JsName fieldJsName = getJsName(element); + JsNameRef ref = AstUtil.newNameRef(qualifier, fieldJsName); + JsBinaryOperation asg = AstUtil.newAssignment(ref, parameter.makeRef()); + func.setBody(AstUtil.newBlock(new JsExprStmt(asg))); + + makeMethod(element, func); + } + + @Override + public JsNode visitInitializer(DartInitializer x) { + JsExpression e = (JsExpression) generate(x.getValue()); + if (!x.isInvocation()) { + JsName fieldJsName = getJsName(x.getName().getTargetSymbol()); + assert fieldJsName != null : "Field name must have been resolved."; + JsNameRef field = AstUtil.newNameRef(new JsThisRef(), fieldJsName); + e = AstUtil.newAssignment(field, e); + e.setSourceRef(x); + } + return new JsExprStmt(e); + } + + @Override + public JsNode visitFieldDefinition(DartFieldDefinition node) { + assert ElementKind.of(currentHolder).equals(ElementKind.LIBRARY); + for (DartField field : node.getFields()) { + generateTopLevelField(field); + } + return null; + } + + private void generateTopLevelField(DartField field) { + if (field.getSymbol().getModifiers().isAbstractField()) { + generate(field.getAccessor()); + } else { + generate(field); + } + } + + @Override + public JsNode visitField(DartField x) { + makeMethodCallThroughFieldShim(x); + FieldElement element = x.getSymbol(); + Modifiers modifiers = element.getModifiers(); + if (modifiers.isAbstractField()) { + generateAbstractField(element); + return null; + } + + DartExpression initializer = x.getValue(); + JsExprStmt result = null; + + if (initializer != null || Elements.isTopLevel(element)) { + currentScopeInfo = ScopeRootInfo.makeScopeInfo(x); + inFactoryOrStaticContext = true; + + // There's an initializer, so emit an assignment statement. + JsNameRef fieldName; + if (isDeclaredAsStaticOrImplicitlyStatic(element)) { + JsExpression qualifier = getGetterSetterQualifier(element); + fieldName = AstUtil.newNameRef(qualifier, translationContext.getNames().getName(element)); + } else { + fieldName = AstUtil.newNameRef(new JsThisRef(), getJsName(element)); + } + + JsExpression initExpr; + if (initializer == null) { + initExpr = undefined(); + } else { + initExpr = (JsExpression) generate(initializer); + } + + boolean emitStaticInitialization = true; + if (x.getModifiers().isFinal() && + (initializer == null || initExpr instanceof JsValueLiteral)) { + makeConstantValueGetter(element, initExpr); + emitStaticInitialization = false; + } else if (initializer == null || initExpr instanceof JsLiteral) { + makePropertyGetter(element); + } else { + makeInitializingGetter(element, initExpr); + initExpr = new JsNameRef(STATIC_UNINITIALIZED); + } + + if (emitStaticInitialization) { + JsBinaryOperation assignment = AstUtil.newAssignment(fieldName, initExpr); + assignment.setSourceRef(x); + result = new JsExprStmt(assignment); + staticInit.add(result); + } + + assert currentScopeInfo != null; + currentScopeInfo = null; + inFactoryOrStaticContext = false; + } else { + makePropertyGetter(element); + } + + if (!element.getModifiers().isFinal()) { + makeSetter(element); + } + return result; + } + + @Override + public JsNode visitFunction(DartFunction x) { + if (x.getBody() == null) { + if (ElementKind.of(currentHolder).equals(ElementKind.CLASS) + && ((ClassElement) currentHolder).isInterface()) { + return null; + } + } + + functionStack.push(x); + jsNewDeclarationsStack.push(new HashSet()); + + // The JsFunction was already created and pushed in visit(DartFunction). + JsFunction jsFunc = translationContext.getMethods().get(x); + + // Generate and set the body. + JsBlock body; + if (x.getBody() == null) { + // The resolution has checked already that it is valid for this method + // to not have a body. + body = new JsBlock(); + } else { + body = (JsBlock) generate(x.getBody()); + } + jsFunc.setBody(body); + + // Add temporary variable declarations, if any. + declareTempsInBlock(body, jsNewDeclarationsStack.pop()); + + DartNode parent = x.getParent(); + assert parent != null; + if (parent instanceof DartMethodDefinition) { + DartMethodDefinition method = (DartMethodDefinition) parent; + if (isFactory(method)) { + rtt.maybeAddTypeParameterToFactory(method, jsFunc); + } + if (Elements.isNonFactoryConstructor((Element) parent.getSymbol())) { + this.addSuperOrRedirectConstructorCall(method); + } + } + + // Call the function prologue setup functions in the reserve order that + // their output need to appear as each adds to the front of the function + // body. + // 3. setup the scope aliases (after default init) + maybeAddFunctionScopeAlias(currentScopeInfo.getScope(x), + translationContext.getMethods().get(x)); + + // 2. setup parameter values + generateAll(x.getParams(), jsFunc.getParameters(), JsParameter.class); + + // 1. call function trace before anything else. + maybeAddFunctionTracing(x); + + functionStack.pop(); + return jsFunc.setSourceRef(x); + } + + private boolean isFactory(DartMethodDefinition method) { + return method.getModifiers().isFactory(); + } + + private void maybeAddFunctionTracing(DartFunction dartFunction) { + // TODO(floitsch): temporary way to enable tracing is by setting a system property. + String tracingCallTarget = System.getProperty("Trace"); + if (tracingCallTarget != null) { + JsFunction function = translationContext.getMethods().get(dartFunction); + + // Example: + // function f(a,b) { ... } + // to: + // function f(a, b) { + // nestingCounter++; + // (nestingCounter, "f(a, b)", a, b); + // try { ... } + // finally { nestingCounter--; } + // } + JsExpression increment = new JsPostfixOperation(JsUnaryOperator.INC, + new JsNameRef(getTraceCounter())); + JsExpression decrement = new JsPostfixOperation(JsUnaryOperator.DEC, + new JsNameRef(getTraceCounter())); + + JsInvocation tracerCall = new JsInvocation(); + tracerCall.setQualifier(new JsNameRef(tracingCallTarget)); + List traceArguments = tracerCall.getArguments(); + traceArguments.add(new JsNameRef(getTraceCounter())); + traceArguments.add(null); // Reserve space for string description. + StringBuffer description = new StringBuffer(); + JsName name = function.getName(); + if (name != null) { + description.append(function.getName().toString()); + } else { + description.append(""); + } + description.append("("); + dartFunction.getParams(); + for (DartParameter param : dartFunction.getParams()) { + JsName paramName = getJsName(param.getSymbol()); + description.append(paramName.toString()); + traceArguments.add(new JsNameRef(paramName)); + } + description.append(")"); + // Update string description in argument list. + traceArguments.set(1, string(description.toString())); + + JsTry countingTry = new JsTry(); + countingTry.setTryBlock(function.getBody()); + countingTry.setFinallyBlock(AstUtil.newBlock(decrement.makeStmt())); + JsBlock newBody = AstUtil.newBlock(increment.makeStmt(), + tracerCall.makeStmt(), + countingTry); + function.setBody(newBody); + } + } + + @Override + public JsNode visitParameter(DartParameter x) { + if (x.getSymbol() != null) { + return new JsParameter(getJsName(x.getSymbol())).setSourceRef(x); + } else { + // TODO(ngeoffray): A parameter in a function type does not have a symbol. + return null; + } + } + + @Override + public JsNode visitBlock(DartBlock x) { + // Basic block handling + JsBlock jsBlock = new JsBlock(); + // TODO(johnlenz): merge redundant JsBlock nodes. + generateAll(x.getStatements(), jsBlock.getStatements(), JsStatement.class); + + // + // For names defined in this scope that are captured by a function + // closure rewrite, inject an object to hold the aliases for the + // value for use by the closure. This simulates lexically scoped names + // in JavaScript and once the closures are hoisted out of the scope + // prevents the capture of value that would otherwise be protected in + // another scope. + // + + // Inject scope alias initialization and clean up. + ScopeRootInfo methodInfo = currentScopeInfo; + if (methodInfo != null) { + DartScope scope = methodInfo.getScope(x); + if (scope.definesClosureReferencedSymbols()) { + // Make sure the alias is defined in the scope. + JsScope currentFunctionScope = getCurrentFunctionScope(); + JsName aliasName = scope.findAliasForJsScope(currentFunctionScope); + // Scope objects are declared (in the JsScope) at first use. By construction scope-objects + // are only created when they are used. Therefore the scope-object must exist in the + // JsScope. + assert aliasName != null; + registerForDeclaration(aliasName); + + // Init and clean up the scope alias + // TODO(johnlenz): this really should be in a finally block, + // debate the runtime cost of doing this, version the possibility of + // a memory leak (It is only really needed if there are closures + // outside this DartScope). + // Alternately, once the closures have been hoisted out, the cleanup + // code can be removed completely. + List list = jsBlock.getStatements(); + JsStatement init = AstUtil.newAssignment( + new JsNameRef(aliasName), new JsObjectLiteral()) + .makeStmt(); + JsStatement cleanup = AstUtil.newAssignment( + new JsNameRef(aliasName), undefined()) + .makeStmt(); + list.add(0, init); + list.add(cleanup); + } + } + return jsBlock.setSourceRef(x); + } + + @Override + public JsNode visitIfStatement(DartIfStatement x) { + JsExpression jsCondition = (JsExpression) generate(x.getCondition()); + JsStatement jsThenStmt = (JsStatement) generate(x.getThenStatement()); + JsStatement jsElseStmt = null; + if (x.getElseStatement() != null) { + jsElseStmt = (JsStatement) generate(x.getElseStatement()); + } + return new JsIf(jsCondition, jsThenStmt, jsElseStmt).setSourceRef(x); + } + + @Override + public JsNode visitSwitchStatement(DartSwitchStatement x) { + JsSwitch jsSwitch = new JsSwitch(); + jsSwitch.setExpr((JsExpression) generate(x.getExpression())); + generateAll(x.getMembers(), jsSwitch.getCases(), JsSwitchMember.class); + return jsSwitch.setSourceRef(x); + } + + @Override + public JsNode visitCase(DartCase x) { + JsCase jsCase = new JsCase(); + jsCase.setCaseExpr((JsExpression) generate(x.getExpr())); + generateAll(x.getStatements(), jsCase.getStmts(), JsStatement.class); + return jsCase.setSourceRef(x); + } + + @Override + public JsNode visitDefault(DartDefault x) { + JsDefault jsDefault = new JsDefault(); + generateAll(x.getStatements(), jsDefault.getStmts(), JsStatement.class); + return jsDefault.setSourceRef(x); + } + + @Override + public JsNode visitWhileStatement(DartWhileStatement x) { + JsExpression condition = (JsExpression) generate(x.getCondition()); + JsBlock body = (JsBlock) generate(x.getBody()); + return new JsWhile(condition, body).setSourceRef(x); + } + + @Override + public JsNode visitDoWhileStatement(DartDoWhileStatement x) { + JsExpression condition = (JsExpression) generate(x.getCondition()); + JsBlock body = (JsBlock) generate(x.getBody()); + return new JsDoWhile(condition, body).setSourceRef(x); + } + + @Override + public JsNode visitForStatement(DartForStatement x) { + // Dart AST normalization removes init expressions. + assert x.getInit() == null; + + JsFor jsFor = new JsFor().setSourceRef(x); + if (x.getCondition() != null) { + jsFor.setCondition((JsExpression) generate(x.getCondition())); + } + if (x.getIncrement() != null) { + jsFor.setIncrExpr((JsExpression) generate(x.getIncrement())); + } + jsFor.setBody((JsStatement) generate(x.getBody())); + return jsFor.setSourceRef(x); + } + + @Override + public JsNode visitForInStatement(DartForInStatement x) { + DartStatement normalizedNode = x.getNormalizedNode(); + if (normalizedNode == null) { + throw new InternalCompilerException("For-in statement should have been normalized."); + } + return normalizedNode.accept(this); + } + + @Override + public JsNode visitContinueStatement(DartContinueStatement x) { + JsContinue jsContinue = null; + if (x.getTargetSymbol() != null) { + jsContinue = new JsContinue(getJsName(x.getTargetSymbol()).makeRef()); + } else { + jsContinue = new JsContinue(); + } + return jsContinue.setSourceRef(x); + } + + @Override + public JsNode visitBreakStatement(DartBreakStatement x) { + JsBreak jsBreak = null; + if (x.getTargetSymbol() != null) { + jsBreak = new JsBreak(getJsName(x.getTargetSymbol()).makeRef()); + } else { + jsBreak = new JsBreak(); + } + return jsBreak.setSourceRef(x); + } + + @Override + public JsNode visitReturnStatement(DartReturnStatement x) { + JsReturn jsRet = new JsReturn(); + if (x.getValue() != null) { + jsRet.setExpr((JsExpression) generate(x.getValue())); + } + return jsRet.setSourceRef(x); + } + + @Override + public JsNode visitTryStatement(DartTryStatement x) { + JsTry jsTry = new JsTry(); + jsTry.setTryBlock((JsBlock) generate(x.getTryBlock())); + + // TODO(jgw): The Javascript AST allows multiple catch blocks for some reason, + // even though that makes no sense. Sort this out once structured exceptions are + // worked out in Dart. + List catchBlocks = x.getCatchBlocks(); + if (catchBlocks != null && !catchBlocks.isEmpty()) { + // Transform a sequence of catch blocks into nested if-statements. + // Example: + // try { + // } catch (SomeException e1) { + // } catch (OtherException e2) { + // } + // becomes + // try { + // } catch(tmpVar) { + // if (tmpVar instanceof SomeException) { var e1 = tmpVar; + // } else if (tmpVar instanceof OtherException) { var e2 = tmpVar; + // } else { throw tmpVar; } + // } + // + // Note that when no type is given for the Dart exception, it catches always. Then we + // don't need a rethrow. + // The JsCatch scope only contains one variable. There is hence no clash possible. + JsCatch jsCatch = new JsCatch(getCurrentFunctionScope(), "e"); + JsName exceptionVar = jsCatch.getScope().findExistingName("e"); + + jsTry.getCatches().add(jsCatch); + JsBlock jsCatchBody = new JsBlock(); + // Tease out browser built-in exceptions + JsExpression filterBuiltin = AstUtil.newAssignment(exceptionVar.makeRef(), + AstUtil.newInvocation(AstUtil.newNameRef(null, "$transformBrowserException"), + exceptionVar.makeRef())); + jsCatchBody.getStatements().add(filterBuiltin.makeStmt()); + jsCatch.setBody(jsCatchBody); + JsStatement jsElse = new JsThrow(new JsNameRef(exceptionVar)); + + for (int i = catchBlocks.size() - 1; i >= 0; i--) { + DartCatchBlock catchBlock = catchBlocks.get(i); + JsBlock jsClauseBody = (JsBlock) generate(catchBlock.getBlock()); + if (catchBlock.getStackTrace() != null) { + // TODO(ngeoffray): do something with the stackTrace. + JsParameter jsStackParam = (JsParameter) generate(catchBlock.getStackTrace()); + registerForDeclaration(jsStackParam.getName()); + } + JsParameter jsClauseParam = (JsParameter) generate(catchBlock.getException()); + JsName jsClauseParamName = jsClauseParam.getName(); + + JsExpression assignment = AstUtil.newAssignment(new JsNameRef(jsClauseParamName), + new JsNameRef(exceptionVar)); + jsClauseBody.getStatements().add(0, assignment.makeStmt()); + // The exception variable is not declared by the catch-block anymore. Register for + // declaration so that it becomes a local variable. + // Note that the name could already be in the list (if two catch-clauses share the same + // name). In this case the declaration-clause will declare the same variable multiple + // times (ex: var e, e, e;). + registerForDeclaration(jsClauseParamName); + + DartParameter exception = catchBlock.getException(); + DartTypeNode exceptionType = exception.getTypeNode(); + if (exceptionType == null) { + // No type has been given. This clause catches everything. + jsElse = jsClauseBody; + continue; + } + + JsExpression instanceCheck = rtt.generateInstanceOfComparison( + getCurrentClass(), + new JsNameRef(exceptionVar), + exceptionType, + exceptionType).setSourceRef(exception); + jsElse = new JsIf(instanceCheck, jsClauseBody, jsElse); + } + jsCatchBody.getStatements().add(jsElse); + } + + if (x.getFinallyBlock() != null) { + JsBlock jsFinallyBlock = (JsBlock) generate(x.getFinallyBlock()); + jsTry.setFinallyBlock(jsFinallyBlock); + } + + // Allow a try block to be a target of a label by surrounding it with a block. + JsNode result = jsTry.setSourceRef(x); + if (x.getParent() instanceof DartLabel) { + result = new JsBlock(jsTry).setSourceRef(x); + } + return result; + } + + @Override + public JsNode visitThrowStatement(DartThrowStatement x) { + JsNameRef error = new JsNameRef("$Dart$ThrowException"); + JsInvocation invoc = AstUtil.newInvocation(error); + if (x.getException() != null) { + invoc.getArguments().add((JsExpression) generate(x.getException())); + } + return new JsExprStmt(invoc.setSourceRef(x)); + } + + @Override + public JsNode visitVariableStatement(DartVariableStatement x) { + + // Dart AST Normalization creates one declaration per VAR. + assert x.getVariables().size() == 1; + + JsNode node = generate(x.getVariables().get(0)); + if (node instanceof JsVar) { + JsVars jsVars = new JsVars(); + jsVars.insert((JsVar)node); + return jsVars.setSourceRef(x); + } else { + + // Variables captured by closures may be transformed to property + // assignments. + assert node instanceof JsStatement; + + return node; + } + } + + @Override + public JsNode visitVariable(DartVariable x) { + Symbol targetSymbol = x.getSymbol(); + + // If the name is referenced by a closure use the scope alias. + JsNameRef scopeAliasRef = maybeMakeScopeAliasReference(targetSymbol); + JsNode result = null; + if (scopeAliasRef != null) { + if (x.getValue() != null) { + JsExpression initExpr = (JsExpression) generate(x.getValue()); + result = AstUtil.newAssignment(scopeAliasRef, initExpr).setSourceRef(x).makeStmt(); + } else { + // we need to put some statement in to keep the expected number + // of values on the stack. + result = translationContext.getProgram().getEmptyStmt(); + } + } else { + JsVars.JsVar jsVar = new JsVars.JsVar(getJsName(targetSymbol)); + if (x.getValue() != null) { + JsExpression initExpr = (JsExpression) generate(x.getValue()); + jsVar.setInitExpr(initExpr); + } else { + jsVar.setInitExpr(undefined()); + } + result = jsVar.setSourceRef(x); + } + + return result; + } + + @Override + public JsNode visitEmptyStatement(DartEmptyStatement x) { + x.visitChildren(this); + // TODO(johnlenz): Set source info? + return translationContext.getProgram().getEmptyStmt(); + } + + @Override + public JsNode visitSyntheticErrorExpression(DartSyntheticErrorExpression node) { + String name = node.getSource().getName(); + int line = node.getSourceLine(); + int col = node.getSourceColumn(); + throw new AssertionError("Generating JS with parse error at " + name + ":" + + line + ":" + col); + } + + @Override + public JsNode visitSyntheticErrorStatement(DartSyntheticErrorStatement node) { + String name = node.getSource().getName(); + int line = node.getSourceLine(); + int col = node.getSourceColumn(); + throw new AssertionError("Generating JS with parse error at " + name + ":" + + line + ":" + col); + } + + @Override + public JsNode visitLabel(DartLabel x) { + JsStatement jsStmt = (JsStatement) generate(x.getStatement()); + JsLabel jsLabel = new JsLabel(getJsName(x.getSymbol())); + jsLabel.setStmt(jsStmt); + return jsLabel.setSourceRef(x); + } + + @Override + public JsNode visitExprStmt(DartExprStmt x) { + JsNode node = generate(x.getExpression()); + if (node instanceof JsVars) { + // Function statements maybe transformed to var statements. + // TODO(johnlenz): Create a JsFunctionStatement so that those statements + // aren't wrapped in expressions. + // Note(floitsch): When removing this special case please update the comments + // in 'endVisit(DartFunctionExpression, ...)'. + return node; + } else { + JsExpression expr = (JsExpression) node; + return new JsExprStmt(expr).setSourceRef(x); + } + } + + @Override + public JsNode visitConditional(DartConditional x) { + JsExpression testExpr = (JsExpression) generate(x.getCondition()); + JsExpression thenExpr = (JsExpression) generate(x.getThenExpression()); + JsExpression elseExpr = (JsExpression) generate(x.getElseExpression()); + return new JsConditional(testExpr, thenExpr, elseExpr).setSourceRef(x); + } + + @Override + public JsNode visitBinaryExpression(DartBinaryExpression x) { + assert x == x.getNormalizedNode(); + + Token operator = x.getOperator(); + + if (operator == Token.IS) { + return generateInstanceOfComparison(x); + } + + JsExpression rhs = (JsExpression) generate(x.getArg2()); + if (operator == Token.ASSIGN) { + return x.getArg1().accept(new Assignment(x, rhs)); + } + + assert !operator.isUserDefinableOperator() || !operator.isAssignmentOperator() : x; + + // We can skip shims for non-user-definable operators (NE is a special case because it's not + // user-definable, but still has to be shimmed). + boolean skipShim = (!operator.isUserDefinableOperator() && (operator != Token.NE)); + if (!skipShim) { + // For user-defined operators, the optimization strategy can choose to skip the shim. + skipShim = optStrategy.canSkipOperatorShim(x); + } + + JsExpression lhs = (JsExpression) generate(x.getArg1()); + Token op = x.getOperator(); + if (skipShim) { + if (op.isEqualityOperator()) { + op = mapToStrictEquals(op); + // TODO (fabiomfv) - This optimization targets a v8 perf issue. V8 double equals + // comparison to undefined is up to 4 times slower than == null. It seems that it was + // fixed on v8 3.5. once we move to 3.5 and the fix confirmed, this should be revisited. + if (x.getArg2() instanceof DartNullLiteral) { + op = mapToNonStrictEquals(op); + rhs = nulle(); + } + if (x.getArg1() instanceof DartNullLiteral) { + JsExpression tmp = lhs; + lhs = rhs; + rhs = tmp; + op = mapToNonStrictEquals(op); + rhs = nulle(); + } + } + JsExpression binOp = new JsBinaryOperation(mapBinaryOp(op), lhs, rhs); + binOp.setSourceRef(x); + return binOp; + } else { + JsNameRef ref = new JsNameRef(mangler.createOperatorSyntax(operator)); + return AstUtil.newInvocation(ref, lhs, rhs).setSourceRef(x); + } + } + + private JsExpression generateInstanceOfComparison(DartBinaryExpression x) { + JsExpression lhs = (JsExpression) generate(x.getArg1()); + DartExpression rhs = x.getArg2(); + boolean isNot = false; + if (rhs instanceof DartUnaryExpression) { + isNot = true; + rhs = ((DartUnaryExpression) rhs).getArg(); + } + JsExpression expr = rtt.generateInstanceOfComparison(getCurrentClass(), + lhs, ((DartTypeExpression) rhs).getTypeNode(), rhs).setSourceRef(x); + if (isNot) { + expr = new JsPrefixOperation(JsUnaryOperator.NOT, expr); + } + return expr; + } + + private JsNameRef nameref(JsName qualifier, String prop) { + return AstUtil.newNameRef(qualifier.makeRef(), prop); + } + + private JsBinaryOperation assign(JsNameRef op1, JsExpression op2) { + return AstUtil.newAssignment(op1, op2); + } + + private JsBinaryOperation neq(JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.NEQ, op1, op2); + } + + private JsBinaryOperation or(JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2); + } + + private JsPrefixOperation not(JsExpression op1) { + return new JsPrefixOperation(JsUnaryOperator.NOT, op1); + } + + private JsBinaryOperation and(JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2); + } + + private JsNumberLiteral number(double num) { + return translationContext.getProgram().getNumberLiteral(num); + } + + private JsStringLiteral string(String str) { + return translationContext.getProgram().getStringLiteral(str); + } + + private JsNullLiteral nulle() { + return translationContext.getProgram().getNullLiteral(); + } + + private JsNameRef undefined() { + return translationContext.getProgram().getUndefinedLiteral(); + } + + @Override + public JsNode visitTypeNode(DartTypeNode x) { + // This backend does not need types. + return null; + } + + @Override + public JsNode visitTypeParameter(DartTypeParameter x) { + // This backend does not need types. + return null; + } + + @Override + public JsNode visitTypeExpression(DartTypeExpression x) { + throw new AssertionError("Unreachable"); + } + + @Override + public JsNode visitUnaryExpression(DartUnaryExpression x) { + assert x == x.getNormalizedNode(); + Token operator = x.getOperator(); + JsNode result; + JsExpression arg = (JsExpression) generate(x.getArg()); + boolean canSkipUnaryOpShim = optStrategy.canSkipOperatorShim(x); + if (operator == Token.SUB) { + if (canSkipUnaryOpShim) { + JsExpression unaryMinus = new JsPrefixOperation(JsUnaryOperator.NEG, arg); + unaryMinus.setSourceRef(x); + return unaryMinus; + } else { + JsNameRef ref = + new JsNameRef(mangler.createOperatorSyntax(DartMangler.NEGATE_OPERATOR_NAME)); + result = (AstUtil.newInvocation(ref, arg)); + return result.setSourceRef(x); + } + } else if (operator.isUserDefinableOperator()) { + if (canSkipUnaryOpShim) { + JsExpression expr = new JsPrefixOperation(mapUnaryOp(operator), arg); + expr.setSourceRef(x); + return expr; + } else { + JsNameRef ref = new JsNameRef(mangler.createOperatorSyntax(operator)); + result = (AstUtil.newInvocation(ref, arg)); + return result.setSourceRef(x); + } + } else { + JsUnaryOperator jsUnaryOperator; + switch (operator) { + case INC: + jsUnaryOperator = JsUnaryOperator.INC; + break; + case DEC: + jsUnaryOperator = JsUnaryOperator.DEC; + break; + case NOT: + jsUnaryOperator = JsUnaryOperator.NOT; + break; + default: + throw new AssertionError("Unexpected unary operator " + operator.name()); + } + + if (x.isPrefix()) { + result = new JsPrefixOperation(jsUnaryOperator, arg); + } else { + result = new JsPostfixOperation(jsUnaryOperator, arg); + } + + return result.setSourceRef(x); + } + } + + @Override + public JsNode visitPropertyAccess(DartPropertyAccess x) { + Element element = optStrategy.findOptimizableFieldElementFor(x, FieldKind.GETTER); + return generateLoad(x.getQualifier(), x.getName(), element).setSourceRef(x); + } + + @Override + public JsNode visitArrayAccess(DartArrayAccess x) { + JsExpression target = (JsExpression) generate(x.getTarget()); + JsExpression key = (JsExpression) generate(x.getKey()); + if (optStrategy.canSkipArrayAccessShim(x, false /* isAssignee */)) { + return AstUtil.newArrayAccess(target, inlineArrayIndexCheck(target, key)); + } else { + JsNameRef ref = AstUtil.newNameRef(target, mangler.createOperatorSyntax(Token.INDEX)); + JsInvocation invoke = AstUtil.newInvocation(ref, key); + return invoke.setSourceRef(x); + } + } + + @Override + public JsNode visitUnqualifiedInvocation(DartUnqualifiedInvocation x) { + DartIdentifier target = x.getTarget(); + Element element = target.getTargetSymbol(); + ElementKind kind = ElementKind.of(element); + JsExpression qualifier; + String mangledName; + MethodElement method = null; + switch (kind) { + case FIELD: + case FUNCTION_OBJECT: + case PARAMETER: + case VARIABLE: + mangledName = null; + qualifier = (JsExpression) generate(target); + EnclosingElement enclosingElement = element.getEnclosingElement(); + if ((kind == ElementKind.FUNCTION_OBJECT) && (element.getEnclosingElement() != null)) { + // Function-object invocations can be made directly, unless they're closures (in which + // case their enclosing-element will be null). + method = (MethodElement) element; + } + break; + + case NONE: + mangledName = mangler.mangleMethod(x.getTarget().getTargetName(), unitLibrary); + qualifier = new JsThisRef(); + break; + + case METHOD: + method = (MethodElement) element; + mangledName = mangler.mangleMethod(method, unitLibrary); + if (element.getModifiers().isStatic()) { + qualifier = referenceName(element.getEnclosingElement(), x.getTarget()); + } else if (Elements.isTopLevel(element)) { + qualifier = null; + } else { + qualifier = new JsThisRef(); + } + break; + + default: + throw new AssertionError("Cannot be an unqualified invocation " + kind); + } + return generateInvocation(x, qualifier, false, mangledName, method); + } + + @Override + public JsNode visitFunctionObjectInvocation(DartFunctionObjectInvocation x) { + DartExpression target = x.getTarget(); + if (target instanceof DartFunctionExpression) { + DartFunctionExpression functionExpression = (DartFunctionExpression) target; + if (functionExpression.getSymbol().getModifiers().isInlinable()) { + return new FunctionExpressionInliner(functionExpression, x.getArgs()).call(); + } + } + JsExpression qualifier = (JsExpression) generate(target); + return generateInvocation(x, qualifier, false, null, null); + } + + /** + * Takes a function expression and inlines it with the given arguments, for + * example: + *

    {@code
    +     *   function(parameter) { return parameter; }(argument)
    +     * }
    + * becomes: + *
    {@code
    +     *   ($1 = argument, $1)
    +     * }
    + */ + private class FunctionExpressionInliner implements Callable { + private final List arguments; + private final List parameters; + private final Map parameterMap = new HashMap(); + private final List statements; + private final JsExpression[] expressions; + + FunctionExpressionInliner(DartFunctionExpression functionExpression, + List arguments) { + final DartFunction function = functionExpression.getFunction(); + this.arguments = arguments; + parameters = function.getParams(); + assert arguments.size() == parameters.size(); + statements = function.getBody().getStatements(); + expressions = new JsExpression[parameters.size() + statements.size()]; + } + + @Override + public JsExpression call() { + int i = 0; + Iterator argumentsIterator = arguments.iterator(); + for (DartParameter parameter : parameters) { + // Assign each argument to a new temporary. + // For example: "arg" becomes: "$i = arg" + expressions[i++] = rewriteArgument(parameter, argumentsIterator.next()); + } + for (DartStatement statement : statements) { + // Inline each statement after rewriting references to the parameters. + // For example: "return parameter_i;" becomes: "$i" + expressions[i++] = rewriteStatement(statement); + } + if (i == 1) { + return expressions[0]; + } else { + return AstUtil.newSequence(expressions); + } + } + + private JsExpression rewriteArgument(DartParameter parameter, DartExpression argument) { + JsName temporary = createTemporary(); + VariableElement element = Elements.makeVariable(temporary.getIdent()); + parameterMap.put(parameter.getSymbol(), element); + translationContext.getNames().setName(element, temporary); + return AstUtil.newAssignment(temporary.makeRef(), (JsExpression) generate(argument)); + } + + private JsExpression rewriteStatement(DartStatement node) { + node.accept(new ParameterRewriter()); + JsNode jsNode = generate(node); + if (jsNode instanceof JsExprStmt) { + return ((JsExprStmt) jsNode).getExpression(); + } else if (jsNode instanceof JsReturn) { + return ((JsReturn) jsNode).getExpr(); + } else { + throw new AssertionError(node); + } + } + + private class ParameterRewriter extends DartNodeTraverser { + @Override + public Void visitIdentifier(DartIdentifier node) { + Element element = parameterMap.get(node.getTargetSymbol()); + if (element != null) { + DartIdentifier identifier = new DartIdentifier(element.getName()); + identifier.setSourceInfo(node); + identifier.setSymbol(element); + node.setNormalizedNode(identifier); + } + return null; + } + } + } + + @Override + public JsNode visitMethodInvocation(DartMethodInvocation x) { + Element element = optStrategy.findElementFor(x); + MethodElement method = null; + JsExpression qualifier; + String mangledName; + + if (element == null) { + mangledName = mangler.mangleNamedMethod(x.getFunctionNameString(), unitLibrary); + qualifier = (JsExpression)generate(x.getTarget()); + } else { + switch (element.getKind()) { + case METHOD: { + mangledName = mangler.mangleMethod((MethodElement) element, unitLibrary); + if (element.getModifiers().isStatic()) { + qualifier = referenceName(element.getEnclosingElement(), x.getTarget()); + } else { + qualifier = (JsExpression) generate(x.getTarget()); + } + method = (MethodElement) element; + break; + } + + case FIELD: { + mangledName = mangler.mangleNamedMethod(x.getFunctionNameString(), unitLibrary); + qualifier = (JsExpression)generate(x.getTarget()); + break; + } + + default: { + throw new AssertionError("Unexpected invocation target."); + } + } + } + + boolean isSuperCall = isSuperCall(x.getTarget().getSymbol()); + return generateInvocation(x, qualifier, isSuperCall, mangledName, method); + } + + private JsExpression generateConstructorInvocation( + DartNewExpression x, JsExpression qualifier, + MethodElement method) { + JsInvocation invoke = (JsInvocation)generateInvocation(x, qualifier, false, null, method); + // TODO(johnlenz): if generateInvocation generates a "noSuchMethod" call. This will add + // useless parameters to the call, this is harmless at the moment. + rtt.mayAddRuntimeTypeToConstrutorOrFactoryCall(getCurrentClass(), x, invoke); + return invoke; + } + + private JsExpression generateInvocation(DartInvocation x, + JsExpression qualifier, + boolean isSuperCall, + String mangledName, + MethodElement method) { + JsInvocation jsInvoke = new JsInvocation(); + + if (method != null) { + if (!generateDirectCallArgs(x, method, jsInvoke)) { + // Call cannot succeed. Return false to generate $nsme() in its place. + return AstUtil.newInvocation(new JsNameRef("$nsme")); + } + } else { + generateNamedCallArgs(x, jsInvoke); + } + + JsExpression explicitReceiver = null; + int argsLength = jsInvoke.getArguments().size(); + qualifier = referenceMethodMember(qualifier, mangledName); + + // If it's a super-call, and we need to adjust the 'this'. + if (isSuperCall) { + qualifier = AstUtil.newNameRef(qualifier, "call"); + } + + if (isSuperCall) { + assert explicitReceiver == null; + explicitReceiver = new JsThisRef(); + } + if (explicitReceiver != null) { + jsInvoke.getArguments().add(0, explicitReceiver); + } + jsInvoke.setQualifier(qualifier); + return jsInvoke.setSourceRef(x); + } + + /** + * @return false if the invocation cannot succeed + */ + private boolean generateDirectCallArgs(DartInvocation x, MethodElement target, + JsInvocation jsInvoke) { + // Direct call. Standard calling convention. + List args = x.getArgs(); + List jsArgs = jsInvoke.getArguments(); + + // Reorder named parameters. + List posArgs = new ArrayList(); + Map namedArgs = new HashMap(); + for (DartExpression arg : args) { + if (arg instanceof DartNamedExpression) { + DartNamedExpression named = (DartNamedExpression) arg; + namedArgs.put(named.getName().getTargetName(), named.getExpression()); + } else { + posArgs.add(arg); + } + } + + int idx = 0, posUsed = 0; + for (VariableElement param : target.getParameters()) { + String name = param.getName(); + if (name != null) { + DartExpression namedArg = namedArgs.get(param.getName()); + if (namedArg != null) { + if (!param.getModifiers().isNamed()) { + // Provided a named argument to a positional parameter. + return false; + } + jsArgs.add((JsExpression) generate(namedArg)); + } else if (idx < posArgs.size()) { + ++posUsed; + jsArgs.add((JsExpression) generate(posArgs.get(idx))); + } else if (param.getDefaultValue() != null) { + jsArgs.add(generateDefaultValue(param.getDefaultValue())); + } else { + // Call cannot succeed; bail out. + return false; + } + } + ++idx; + } + + if (posUsed != posArgs.size()) { + // Unused positional arguments. + return false; + } + + return true; + } + + private JsExpression generateDefaultValue(DartExpression defaultValue) { + if (defaultValue != null) { + if (defaultValue instanceof DartFunctionExpression) { + // This should be caught much earlier and rejected. This check avoids an NPE later. + return nulle(); + } + } + return (JsExpression) generate(defaultValue); + } + + private void generateNamedCallArgs(DartInvocation invoke, JsInvocation jsInvoke) { + // Indirect call. Named-parameter calling convention. + // method(parg_count, { na0:NA0, na1:NA1, ..., count:N }, pa0, pa1, ...); + List args = invoke.getArgs(); + List jsArgs = jsInvoke.getArguments(); + + int namedCount = 0; + for (DartExpression arg : args) { + if (arg instanceof DartNamedExpression) { + ++namedCount; + } + } + + JsExpression argmap; + if (namedCount == 0) { + argmap = new JsNameRef("$noargs"); + } else { + JsObjectLiteral bag = new JsObjectLiteral(); + for (DartExpression arg : args) { + if (arg instanceof DartNamedExpression) { + DartNamedExpression namedExpr = ((DartNamedExpression) arg); + String targetName = namedExpr.getName().getTargetName(); + JsPropertyInitializer propInit = new JsPropertyInitializer( + string(targetName), + (JsExpression) generate(namedExpr.getExpression())); + bag.getPropertyInitializers().add(propInit); + } + } + JsPropertyInitializer countProp = new JsPropertyInitializer(string("count"), + number(namedCount)); + bag.getPropertyInitializers().add(countProp); + argmap = bag; + } + + jsArgs.add(number(args.size() - namedCount)); + jsArgs.add(argmap); + for (DartExpression arg : args) { + if (!(arg instanceof DartNamedExpression)) { + jsArgs.add((JsExpression) generate(arg)); + } + } + } + + private JsExpression referenceMethodMember(JsExpression qualifier, + String mangledName) { + if (mangledName != null) { + qualifier = AstUtil.newNameRef(qualifier, mangledName); + } + return qualifier; + } + + @Override + public JsNode visitThisExpression(DartThisExpression x) { + return new JsThisRef().setSourceRef(x); + } + + @Override + public JsNode visitSuperExpression(DartSuperExpression x) { + ClassElement element = x.getSymbol().getClassElement(); + JsNameRef superRef = AstUtil.newPrototypeNameRef(getJsName(element).makeRef()); + return superRef.setSourceRef(x); + } + + @Override + public JsNode visitSuperConstructorInvocation(DartSuperConstructorInvocation x) { + return generateSuperConstructorInvocation(x); + } + + @Override + public JsNode visitNativeBlock(DartNativeBlock x) { + JsBlock jsBlock = new JsBlock(); + + DartMethodDefinition method = + (DartMethodDefinition) currentScopeInfo.getContainingClassMember(); + String name = mangler.mangleNativeMethod(method.getSymbol()); + + JsNameRef nativeRef = new JsNameRef(name); + JsInvocation nativeCall; + if (method.getModifiers().isStatic()) { + nativeCall = AstUtil.newInvocation(nativeRef); + } else { + JsNameRef callRef = AstUtil.newNameRef(nativeRef, "call"); + nativeCall = AstUtil.newInvocation(callRef, new JsThisRef()); + } + + for (DartParameter p : method.getFunction().getParams()) { + nativeCall.getArguments().add(getJsName(p.getSymbol()).makeRef()); + } + + jsBlock.getStatements().add(new JsReturn(nativeCall)); + return jsBlock.setSourceRef(x); + } + + @Override + public JsNode visitNewExpression(DartNewExpression x) { + ConstructorElement element = x.getSymbol(); + String className = element.getEnclosingElement().getName(); + // TODO(floitsch): We should have a JsNames instead of creating the string representations. + String name = mangler.createFactorySyntax(className, element.getName(), unitLibrary); + // We add the class name of the holder of the constructor as a qualifier. + JsName classJsName = getJsName(element.getEnclosingElement()); + JsNameRef consName = AstUtil.newNameRef(classJsName.makeRef(), name); + + JsExpression newExpr = generateConstructorInvocation(x, consName, element); + if (x.isConst()) { + newExpr = maybeInternConst(newExpr, Types.constructorType(x).getArguments()); + } + return newExpr; + } + + // Compile time constants expressions must be canonicalized. + // We do this with the javascript native "$intern" method. + private JsExpression maybeInternConst(JsExpression newExpr, List typeParams) { + JsInvocation intern = AstUtil.newInvocation(new JsNameRef(INTERN_CONST_FUNCTION), newExpr); + if (typeParams != null && typeParams.size() != 0) { + JsArrayLiteral arr = new JsArrayLiteral(); + for (Type t : typeParams) { + String typeName = ""; + if (t.getKind() != TypeKind.DYNAMIC) { + typeName = getJsName(t.getElement()).getShortIdent(); + } + arr.getExpressions().add(string(typeName)); + } + intern.getArguments().add(arr); + } + return intern; + } + + @Override + public JsNode visitFunctionExpression(DartFunctionExpression x) { + JsFunction fn = (JsFunction) generate(x.getFunction()); + + JsName fnDeclaredName; + JsName hoistedName; + + // TODO(johnlenz): values used in super class init methods are currently + // evaluated twice (once for the init and once for the constructor), but + // this is problematic. We won't need to keep track of the hoisted + // state once the re-evaluation problem is fixed. + boolean fnWasPreviouslyHoisted = fn.isHoisted(); + if (fnWasPreviouslyHoisted) { + fnDeclaredName = fn.getName(); + hoistedName = fn.getName(); + } else { + + // 0) Save off the original name + fnDeclaredName = fn.getName(); + + // 1) Create a global name for this method + hoistedName = makeClosureHoistedJsName(currentHolder, currentScopeInfo, x); + + // 2) Give it a unique name. + fn.setName(hoistedName); + + // 3) Insert it into global scope + fn.rebaseScope(globalScope); + + // 4) Make it statement, if it isn't already + globalBlock.getStatements().add(fn.makeStmt()); + + // 5) Mark the function as hoisted + fn.setHoisted(); + } + + ScopeRootInfo.ClosureInfo info = currentScopeInfo.getClosureInfo(x.getFunction()); + List list = info.getSortedReferencedScopeList(); + + // TODO(jgw): See johnlenz' comment above about re-evaluation. This guard can go away once + // that problem is fixed. + if (!fnWasPreviouslyHoisted) { + // Generate the named-parameter trampoline. + boolean includesClosureScope = !list.isEmpty(); + boolean preserveThis = !inFactoryOrStaticContext && info.referencesThis; + JsFunction tramp = generateNamedParameterTrampoline(x.getFunction(), + hoistedName.makeRef(), + list.size(), preserveThis); + String mangled = mangler.mangleNamedMethod(hoistedName.getIdent(), unitLibrary); + hoistedName = globalScope.declareName(mangled); + tramp.setName(hoistedName); + globalBlock.getStatements().add(tramp.makeStmt()); + } + + // 5) Bind the necessary scope references and possibly "this". + JsExpression replacement; + + if (list.isEmpty() && inFactoryOrStaticContext) { + + // Simply replace the function + replacement = new JsNameRef(hoistedName); + + } else { + // Replace "function (){}" with "bind(hoistedName, this, scope1, scope2, ...)" + // so that references to class fields can be resolved. + + JsExpression thisRef = undefined(); + // Only bind 'this' if 'this' is referenced + if (!inFactoryOrStaticContext && info.referencesThis) { + thisRef = new JsThisRef(); + } + + // Replace the definition with a reference to the + // hoisted function, and bind the necessary values + // to it. + int scopeCount = list.size(); + int argCount = fn.getParameters().size() + 2; // +2 => Named-parameter calling convention + String jsBindName = "$bind"; + if (optStrategy.canOptimizeFunctionExpressionBind(x)) { + if (scopeCount <= MAX_SPECIALIZED_BIND_SCOPES && argCount <= MAX_SPECIALIZED_BIND_ARGS) { + // Use specialized forms. + jsBindName = "$bind" + scopeCount + "_" + argCount; + } + } + + JsInvocation invoke = + AstUtil.newInvocation(new JsNameRef(jsBindName), new JsNameRef(hoistedName), thisRef); + + // Add the scope alias to the bind call and function parameter list + int parameterIndex = 0; + for (DartScope s : list) { + // Add the scope-object as argument to the bind call. The scope-object is referenced + // in the outer function (currentFunctionScope). + JsName aliasJsName = s.getAliasForJsScope(getCurrentFunctionScope()); + invoke.getArguments().add(new JsNameRef(aliasJsName)); + // Add the scope-object as parameter to the hoisted signature. The scope-object is + // referenced from the inner function. + JsName jsName = s.findAliasForJsScope(fn.getScope()); + // Scope objects are declared (in the JsScope) at first use. By construction scope-objects + // are only created when they are used. Therefore the scope-object must exist in the + // JsScope. + assert jsName != null; + // TODO(johnlenz): remove this hoisted check once the constructor/init + // parameters aren't reused. + if (!fnWasPreviouslyHoisted) { + fn.getParameters().add(parameterIndex, new JsParameter(jsName)); + } + parameterIndex += 1; + } + + replacement = invoke; + } + + // 6) If this is a named function expression, then we need to build the scope object. + if (!x.isStatement() && x.getName() != null) { + ScopeRootInfo scopeInfo = currentScopeInfo; + DartScope scope = scopeInfo.getScope(x); + // If the function is not used by name, then we might not need to create a scope. + if (scope.definesClosureReferencedSymbols()) { + // Make sure the alias is defined in the scope. + JsScope currentFunctionScope = getCurrentFunctionScope(); + // This must be the second use of the scope in this function scope. The first use was + // as argument to the bind-invocation above. + JsName aliasName = scope.findAliasForJsScope(currentFunctionScope); + assert aliasName != null; + registerForDeclaration(aliasName); + + // Assume that the initial expression was x = function f() { }. + // Up to this point the function has been hoisted and replaced by a binding call: + // x = bind(hoistedName, this, scope1, ...) + // The variable "replacement" is equal to the bind call. + // + // One of the scopes - say "scope2" - is the FunctionExpressionScope that defines 'f' + // itself. We now need to set up this scope. + // Transform: + // x = bind(hoistedName, this, scope1, ...) into + // x = (scope2 = {}, scope2.f = bind(hoistedName, this, scope1, ...)). + JsExpression init = + AstUtil.newAssignment(new JsNameRef(aliasName), new JsObjectLiteral()); + JsNameRef scopeF = makeScopeAliasNameRef(scope, x.getSymbol()); + JsExpression assig = AstUtil.newAssignment(scopeF, replacement); + replacement = new JsBinaryOperation(JsBinaryOperator.COMMA, init, assig); + // TODO(floitsch): we need to clear the scope object. + } + } + + if (x.isStatement()) { + // If the name is referenced by a closure use the scope alias. + JsNameRef scopeAliasRef = maybeMakeScopeAliasReference(x.getSymbol()); + if (scopeAliasRef != null) { + JsExpression assig = AstUtil.newAssignment(scopeAliasRef, replacement); + // We must not return a statement. The parent has a check and handles JsVars differently. + // By default it actually expects an expression. + return assig.setSourceRef(x); + } else { + // The parent expects an expression, but handles Var statements separately. + assert !hoistedName.equals(fnDeclaredName); + JsVars vars = AstUtil.newVar(x.getName(), fnDeclaredName, replacement); + return vars.setSourceRef(x); + } + } else { + return replacement.setSourceRef(x); + } + } + + private JsName makeClosureHoistedJsName( + Element holder, ScopeRootInfo info, DartFunctionExpression x) { + Element element = info.getContainingElement(); + String closureIdentifier = info.getNextClosureName(); + String closureName = x.getFunctionName(); + String hoistedName = + mangler.createHoistedFunctionName(holder, element, closureIdentifier, closureName); + return globalScope.declareName(hoistedName, hoistedName, closureName); + } + + @Override + public JsNode visitIdentifier(DartIdentifier x) { + DartExpression normalizedNode = x.getNormalizedNode(); + if (normalizedNode != x) { + return normalizedNode.accept(this); + } + + Element element = optStrategy.findOptimizableFieldElementFor(x, FieldKind.GETTER); + return generateLoad(null, x, element).setSourceRef(x); + } + + /** + * @return A NameRef to the scoped alias if needed. + */ + private JsNameRef maybeMakeScopeAliasReference(Symbol targetSymbol) { + if (!functionStack.isEmpty()) { + /* + * Currently, you must be inside of a DartFunction in order to be able to generate a + * scope alias. + */ + ScopeRootInfo methodInfo = currentScopeInfo; + if (methodInfo != null) { + DartScope.DartSymbolInfo symbolInfo = methodInfo.getSymbolInfo(targetSymbol); + if (symbolInfo != null && symbolInfo.isReferencedFromClosure()) { + return makeScopeAliasNameRef(symbolInfo.getOwningScope(), targetSymbol); + } + } + } + return null; + } + + private JsNameRef makeScopeAliasNameRef(DartScope scope, Symbol targetSymbol) { + JsName qualifier = scope.getAliasForJsScope(getCurrentFunctionScope()); + return AstUtil.newNameRef(new JsNameRef(qualifier), getJsName(targetSymbol).getIdent()); + } + + @Override + public JsNode visitNullLiteral(DartNullLiteral x) { + // TODO(johnlenz): set source location? + return undefined(); + } + + @Override + public JsNode visitStringLiteral(DartStringLiteral x) { + // TODO(johnlenz): properly set source location? + return string(x.getValue()).setSourceRef(x); + } + + @Override + public JsNode visitStringInterpolation(DartStringInterpolation x) { + List strings = x.getStrings(); + List expressions = x.getExpressions(); + + JsExpression res = null; + Iterator eIter = expressions.iterator(); + boolean first = true; + for (DartStringLiteral lit : strings) { + if (first) { + first = false; + res = (JsExpression) generate(lit); + } else { + assert eIter.hasNext() : "DartStringInterpolation invariant broken."; + JsExpression expr = (JsExpression) generate(eIter.next()); + JsInvocation exprToString = new JsInvocation(); + exprToString.setQualifier(new JsNameRef("$toString")); + exprToString.getArguments().add(expr); + res = new JsBinaryOperation(JsBinaryOperator.ADD, + new JsBinaryOperation(JsBinaryOperator.ADD, res, exprToString), + (JsExpression) generate(lit)).setSourceRef(x); + } + } + assert res != null; + return res; + } + + @Override + public JsNode visitBooleanLiteral(DartBooleanLiteral x) { + // TODO(johnlenz): set source location? + return x.getValue() ? translationContext.getProgram().getTrueLiteral() : translationContext.getProgram().getFalseLiteral(); + } + + @Override + public JsNode visitIntegerLiteral(DartIntegerLiteral x) { + // TODO(johnlenz): set source location? + return number(x.getValue().doubleValue()); + } + + @Override + public JsNode visitDoubleLiteral(DartDoubleLiteral x) { + // TODO(johnlenz): set source location? + return number(x.getValue()); + } + + @Override + public JsNode visitArrayLiteral(DartArrayLiteral x) { + JsArrayLiteral jsArray = new JsArrayLiteral(); + generateAll(x.getExpressions(), jsArray.getExpressions(), JsExpression.class); + jsArray.setSourceRef(x); + JsExpression result = rtt.maybeAddRuntimeTypeForArrayLiteral(getCurrentClass(), x, jsArray); + if (x.isConst()) { + result = this.maybeInternConst(result, x.getType().getArguments()); + } + return result; + } + + @Override + @SuppressWarnings("deprecation") + public JsNode visitMapLiteral(DartMapLiteral x) { + // Map { 'a': 3, 'b': "foo" } to + // (tmp = new Map(), tmp.a = 3, tmp.b = "foo", tmp) + // TODO(floitsch): optimize map-literal creation. + JsName tmpVar = createTemporary(); + // TODO(floitsch): hardcoded reference to "LinkedHashMapImplementation". + // We should instead get the element from the DartMapLiteral x. + String name = "LinkedHashMapImplementation"; + String mangledMap = mangler.mangleClassNameHack(null, name); + String mangledFactory = mangler.createFactorySyntax(name, "", unitLibrary); + JsNameRef runtimeMap = AstUtil.newNameRef(new JsNameRef(mangledMap), mangledFactory); + JsInvocation invoke = AstUtil.newInvocation(runtimeMap); + rtt.maybeAddRuntimeTypeToMapLiteralConstructor(getCurrentClass(), x, invoke); + JsExpression assig = AstUtil.newAssignment(tmpVar.makeRef(), invoke.setSourceRef(x)); + JsExpression result = assig; + for (DartMapLiteralEntry entry : x.getEntries()) { + result = AstUtil.newSequence(result, visitMapLiteralEntry(entry, tmpVar)); + } + result = AstUtil.newSequence(result, tmpVar.makeRef()); + if (x.isConst()) { + result = this.maybeInternConst(result, x.getType().getArguments()); + } + return result; + } + + private JsExpression visitMapLiteralEntry(DartMapLiteralEntry x, JsName map) { + String addMethod = mangler.createOperatorSyntax(Token.ASSIGN_INDEX); + JsExpression value = (JsExpression) generate(x.getValue()); + JsExpression key = (JsExpression) generate(x.getKey()); + JsNameRef methodName = AstUtil.newNameRef(map.makeRef(), addMethod); + return AstUtil.newInvocation(methodName, key, value).setSourceRef(x); + } + + @Override + public JsNode visitMapLiteralEntry(DartMapLiteralEntry x) { + throw new InternalCompilerException("MapLiteralEntries are handled by the 2-arg variant."); + } + + @Override + public JsNode visitNamedExpression(DartNamedExpression node) { + return generate(node.getExpression()); + } + + @Override + public JsNode visitRedirectConstructorInvocation(DartRedirectConstructorInvocation x) { + return generateSuperConstructorInvocation(x); + } + + private JsNode generateSuperConstructorInvocation(DartInvocation x) { + // Must use SuperClass.call(this, ...) to get the correct 'this' context in the callee: + // .$Constructor.call(this, ...). + ConstructorElement element = (ConstructorElement) x.getSymbol(); + // TODO(floitsch): it would be good, if we could get a js-name instead of just a string. + // This way the debugging information would be better. + // We need to generate the JsName (for the initializer/factory) once only and store it + // in some hashtable. Then instead of reusing the mangler, we should reuse those JsNames. + // The debugging information would then contain a link from the property-access to the + // constructor. Without JsName the debugger just assumes we access some random property. + String name = mangler.mangleConstructor(element.getName(), unitLibrary); + Element classElement = element.getEnclosingElement(); + JsNameRef constructorRef = AstUtil.newNameRef(getJsName(classElement).makeRef(), name); + return generateInvocation(x, constructorRef, true, null, element); + } + + private JsBinaryOperator mapBinaryOp(Token operator) { + switch (operator) { + /* Assignment operators. */ + case ASSIGN: return JsBinaryOperator.ASG; + case ASSIGN_BIT_OR: return JsBinaryOperator.ASG_BIT_OR; + case ASSIGN_BIT_XOR: return JsBinaryOperator.ASG_BIT_XOR; + case ASSIGN_BIT_AND: return JsBinaryOperator.ASG_BIT_AND; + case ASSIGN_SHL: return JsBinaryOperator.ASG_SHL; + case ASSIGN_SAR: return JsBinaryOperator.ASG_SHR; + case ASSIGN_SHR: return JsBinaryOperator.ASG_SHRU; + case ASSIGN_ADD: return JsBinaryOperator.ASG_ADD; + case ASSIGN_SUB: return JsBinaryOperator.ASG_SUB; + case ASSIGN_MUL: return JsBinaryOperator.ASG_MUL; + case ASSIGN_DIV: return JsBinaryOperator.ASG_DIV; + case ASSIGN_MOD: return JsBinaryOperator.ASG_MOD; + + /* Binary operators sorted by precedence. */ + case OR: return JsBinaryOperator.OR; + case AND: return JsBinaryOperator.AND; + case BIT_OR: return JsBinaryOperator.BIT_OR; + case BIT_XOR: return JsBinaryOperator.BIT_XOR; + case BIT_AND: return JsBinaryOperator.BIT_AND; + case SHL: return JsBinaryOperator.SHL; + case SAR: return JsBinaryOperator.SHR; + case SHR: return JsBinaryOperator.SHRU; + case ADD: return JsBinaryOperator.ADD; + case SUB: return JsBinaryOperator.SUB; + case MUL: return JsBinaryOperator.MUL; + case DIV: return JsBinaryOperator.DIV; + case MOD: return JsBinaryOperator.MOD; + + /* Compare operators sorted by precedence. */ + case EQ: return JsBinaryOperator.EQ; + case NE: return JsBinaryOperator.NEQ; + case EQ_STRICT: return JsBinaryOperator.REF_EQ; + case NE_STRICT: return JsBinaryOperator.REF_NEQ; + case LT: return JsBinaryOperator.LT; + case GT: return JsBinaryOperator.GT; + case LTE: return JsBinaryOperator.LTE; + case GTE: return JsBinaryOperator.GTE; + + // Only used by 'for'. + case COMMA: return JsBinaryOperator.COMMA; + + default: + throw new InternalCompilerException("Invalid binary operator"); + } + } + + private JsUnaryOperator mapUnaryOp(Token operator) { + switch (operator) { + case BIT_NOT: + return JsUnaryOperator.BIT_NOT; + case NOT: + return JsUnaryOperator.NOT; + case SUB: + return JsUnaryOperator.NEG; + case INC: + return JsUnaryOperator.INC; + case DEC: + return JsUnaryOperator.DEC; + default: + throw new InternalCompilerException("Invalid unary operator."); + } + } + + private Token mapToStrictEquals(Token op) { + switch (op) { + case EQ: + return Token.EQ_STRICT; + case NE: + return Token.NE_STRICT; + case EQ_STRICT: + return Token.EQ_STRICT; + case NE_STRICT: + return Token.NE_STRICT; + default: + throw new InternalCompilerException("Invalid equals operator."); + } + } + + private Token mapToNonStrictEquals(Token op) { + switch (op) { + case EQ_STRICT: + return Token.EQ; + case NE_STRICT: + return Token.NE; + case EQ: + return Token.EQ; + case NE: + return Token.NE; + default: + throw new InternalCompilerException("Invalid equals operator."); + } + } + + class Assignment extends DartNodeTraverser { + private final DartNode info; + private final JsExpression rhs; + + public Assignment(DartNode info, JsExpression rhs) { + this.info = info; + this.rhs = rhs; + } + + @Override + public JsNode visitNode(DartNode lhs) { + throw new AssertionError(lhs.getClass().getSimpleName()); + } + + @Override + public JsNode visitIdentifier(DartIdentifier lhs) { + DartExpression normalizedNode = lhs.getNormalizedNode(); + if (lhs != normalizedNode) { + return normalizedNode.accept(this); + } + Element element = optStrategy.findOptimizableFieldElementFor(lhs, FieldKind.SETTER); + // On the form e1.name = rhs. + return generateStore(null, lhs, rhs, element).setSourceRef(info); + } + + @Override + public JsNode visitPropertyAccess(DartPropertyAccess lhs) { + Element element = optStrategy.findOptimizableFieldElementFor(lhs, FieldKind.SETTER); + // On the form e1.name = rhs. + return generateStore(lhs.getQualifier(), lhs.getName(), rhs, element).setSourceRef(info); + } + + @Override + public JsNode visitArrayAccess(DartArrayAccess lhs) { + // On the form e1[key] = argument. + // Generate: e1.$set(key, $0 = argument), $0 + + JsExpression key = (JsExpression) generate(lhs.getKey()); + JsExpression e1 = (JsExpression) generate(lhs.getTarget()); + if (optStrategy.canSkipArrayAccessShim(lhs, true /* isAssignee */)) { + JsBinaryOperation assign = new JsBinaryOperation(JsBinaryOperator.ASG); + assign.setArg1(AstUtil.newArrayAccess(e1, inlineArrayIndexCheck(e1, key))); + assign.setArg2(rhs); + return assign.setSourceRef(info); + } else { + JsNameRef $0 = new JsNameRef(createTemporary()); + String $set = mangler.createOperatorSyntax(Token.ASSIGN_INDEX); + // Generate: $0 = rhs + JsExpression e = AstUtil.newAssignment($0, rhs); + // Generate: e1.$set(key, $0 = rhs) + e = AstUtil.newInvocation(AstUtil.newNameRef(e1, $set), key, e); + // Generate: e, $0 + return new JsBinaryOperation(JsBinaryOperator.COMMA, e, $0).setSourceRef(info); + } + } + } + + private final JsNode generate(DartNode node) { + if (node != null) { + try { + return node.getNormalizedNode().accept(this); + } catch (AssertionError e) { + reportError(node, e); + // Wrap assertion error to prevent repeated messages for the same error. + throw new RuntimeException(e); + } + } else { + return null; + } + } + + private JsExpression inlineArrayIndexCheck(JsExpression array, JsExpression index) { + return AstUtil.newInvocation(new JsNameRef("$inlineArrayIndexCheck"), array, index); + } + + private void reportError(DartNode node, Throwable exception) { + context.compilationError(new DartCompilationError(node, DartCompilerErrorCode.INTERNAL_ERROR, + exception.getLocalizedMessage())); + } + + private final void generateAll(List nodes, List result, + Class cls) { + for (DartNode node : nodes) { + result.add(cls.cast(generate(node))); + } + } + + JsNameRef referenceName(Symbol symbol, SourceInfo info) { + // If the value if captured by a closure, change the reference to + // use the alias of the value. + JsNameRef jsNode = maybeMakeScopeAliasReference(symbol); + if (jsNode == null) { + jsNode = getJsName(symbol).makeRef(); + } + jsNode.setSourceRef(info); + return jsNode; + } + + @Override + public void visit(List nodes) { + if (nodes != null) { + for (DartNode node : nodes) { + node.accept(this); + } + } + } + + @Override + public JsNode visitAssertion(DartAssertion node) { + JsExpression expression = (JsExpression) generate(node.getExpression()); + JsExpression message = (JsExpression) generate(node.getMessage()); + JsNameRef assertName = new JsNameRef("assert"); + JsInvocation jsInvoke; + if (message == null) { + jsInvoke = AstUtil.newInvocation(assertName, expression); + } else { + jsInvoke = AstUtil.newInvocation(assertName, expression, message); + } + return new JsExprStmt(jsInvoke).setSourceRef(node); + } + + @Override + public JsNode visitParenthesizedExpression(DartParenthesizedExpression node) { + return node.getExpression().accept(this); + } + + @Override + public JsNode visitCatchBlock(DartCatchBlock node) { + throw new AssertionError("should never be called directly"); + } + + @Override + public JsNode visitUnit(DartUnit unit) { + throw new AssertionError("should never be called directly"); + /* + unit.visitChildren(this); + // Initialize static fields after declaring every method, getters & + // setters (b/4101270) + // TODO(johnlenz): canonicalize statics values + globalBlock.getStatements().addAll(staticInit); + */ + } + + @Override + public JsNode visitFunctionTypeAlias(DartFunctionTypeAlias node) { + return null; + } + + private JsExpression generateQualifiedFieldAccess(DartNode qualifier, + String accessorName, boolean accessThroughShim) { + // Generate this.ACCESSOR(); + JsExpression jsQualifier; + if (qualifier == null || (qualifier instanceof DartThisExpression)) { + jsQualifier = new JsThisRef(); + } else { + jsQualifier = (JsExpression) generate(qualifier); + } + + jsQualifier.setSourceRef(qualifier); + JsNameRef nameRef = AstUtil.newNameRef(jsQualifier, accessorName); + if (accessThroughShim) { + return AstUtil.newInvocation(nameRef); + } else { + return nameRef; + } + } + + private JsExpression generateUnresolvedAccess(DartNode qualifier, + String accessorName) { + if (qualifier == null) { + return generateQualifiedFieldAccess(qualifier, accessorName, true); + } + // Generate qualifier.ACCESSOR(); + JsExpression jsQualifier = (JsExpression) generate(qualifier); + jsQualifier.setSourceRef(qualifier); + JsNameRef method = AstUtil.newNameRef(jsQualifier, accessorName); + return AstUtil.newInvocation(method); + } + + private JsInvocation generateSuperFieldAccess(DartNode qualifier, + String accessorName) { + // Generate CLASS.prototype.ACCESSOR.call(this); + ClassElement superClass = ((SuperElement) qualifier.getSymbol()).getClassElement(); + JsExpression jsQualifier = AstUtil.newPrototypeNameRef(getJsName(superClass).makeRef()); + jsQualifier.setSourceRef(qualifier); + JsNameRef method = AstUtil.newNameRef(jsQualifier, accessorName); + method = AstUtil.newNameRef(method, "call"); + JsInvocation jsInvoke = AstUtil.newInvocation(method); + jsInvoke.getArguments().add(0, new JsThisRef()); + return jsInvoke; + } + + private JsInvocation generateStaticFieldAccess(FieldElement element, + DartNode qualifier, + String accessorName) { + // Generate CLASS.ACCESSOR(); + JsExpression jsQualifier = referenceName(element.getEnclosingElement(), qualifier); + jsQualifier.setSourceRef(qualifier); + JsNameRef method = AstUtil.newNameRef(jsQualifier, accessorName); + return AstUtil.newInvocation(method); + } + + private JsInvocation generateLibraryFieldAccess(String accessorName) { + // Generate ACCESSOR(); + return AstUtil.newInvocation(new JsNameRef(accessorName)); + } + + private JsExpression generateFieldAccess(FieldElement field, DartNode qualifier, + String accessorName, boolean accessThroughShim) { + boolean isSuperCall = (qualifier != null) && isSuperCall(qualifier.getSymbol()); + if (isSuperCall) { + return generateSuperFieldAccess(qualifier, accessorName); + } else if (Elements.isTopLevel(field)) { + return generateLibraryFieldAccess(accessorName); + } else if (field.isStatic()) { + return generateStaticFieldAccess(field, qualifier, accessorName); + } else { + return generateQualifiedFieldAccess(qualifier, accessorName, accessThroughShim); + } + } + + /* + * A method is accessed as if a closure object + * class A { foo() { return 1; }) + * Function f = A.foo; + */ + private JsExpression generateMethodBoundToVariable(DartIdentifier methodNode, + MethodElement methodElement, DartNode qualifier) { + JsExpression boundMethod; + String mangledName = mangler.mangleNamedMethod(methodElement, unitLibrary); + boolean isSuperCall = (qualifier != null) && isSuperCall(qualifier.getSymbol()); + if (isSuperCall) { + boundMethod = generateSuperFieldAccess(qualifier, + mangler.createGetterSyntax(methodElement.getName(), unitLibrary)); + } else if (Elements.isTopLevel(methodElement)) { + boundMethod = AstUtil.newNameRef(null, mangledName); + } else if (methodElement.isStatic()) { + if (qualifier == null) { + qualifier = methodElement.getEnclosingElement().getNode(); + assert (qualifier instanceof DartClass); + boundMethod = AstUtil.newNameRef(getJsName(qualifier.getSymbol()).makeRef(), mangledName); + } else { + assert (qualifier instanceof DartIdentifier); + boundMethod = AstUtil.newNameRef((JsExpression) generate(qualifier), mangledName); + } + } else { + // Should be an invocation on an instance + if (qualifier == null) { + qualifier = DartThisExpression.get(); + } + JsExpression methodQualifier = (JsExpression) generate(qualifier); + ClassElement classElement = (ClassElement) methodElement.getEnclosingElement(); + String className = mangler.mangleClassName(classElement); + JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(new JsNameRef(className)); + JsExpression methodToCall = AstUtil.newNameRef(prototypeRef, mangledName); + boundMethod = AstUtil.newInvocation(new JsNameRef("$bind"), methodToCall, methodQualifier); + } + boundMethod.setSourceRef(methodNode); + return boundMethod; + } + + private JsExpression generateLoadTemporary(Element element, DartIdentifier node) { + return referenceName(element, node); + } + + private JsExpression generateLoad(DartNode qualifier, DartIdentifier node, Element element) { + boolean accessThroughShim = true; + if (element != null) { + accessThroughShim = false; + } else { + element = node.getTargetSymbol(); + } + + switch (ElementKind.of(element)) { + case VARIABLE: + case PARAMETER: + case FUNCTION_OBJECT: + // TODO(5089961): we should not generate code for class expressions. + case CLASS: + return generateLoadTemporary(element, node); + + case NONE: + if (qualifier != null && isSuperCall(qualifier.getSymbol())) { + return generateSuperFieldAccess(qualifier, + mangler.createGetterSyntax(node.getTargetName(), unitLibrary)); + } + return generateUnresolvedAccess(qualifier, + mangler.createGetterSyntax(node.getTargetName(), unitLibrary)); + + case FIELD: { + FieldElement field = (FieldElement) element; + String accessorName; + if (accessThroughShim) { + accessorName = mangler.createGetterSyntax(field, unitLibrary); + } else { + if (optStrategy.isWhitelistedNativeField(field, FieldKind.GETTER)) { + accessorName = field.getName(); + } else { + accessorName = mangler.mangleField(field, unitLibrary); + } + } + return generateFieldAccess(field, qualifier, accessorName, accessThroughShim); + } + + case METHOD: { + MethodElement method = (MethodElement) element; + return generateMethodBoundToVariable(node, method, qualifier); + } + + default: + throw new AssertionError("I do not know how to load: " + ElementKind.of(element)); + } + } + + private JsExpression generateStoreTemporary(Element element, + DartIdentifier node, + JsExpression rhs) { + JsNameRef jsName = referenceName(element, node); + return AstUtil.newAssignment(jsName, rhs); + } + + private JsExpression generateStoreField(JsExpression fieldAccess, + JsExpression rhs) { + if (fieldAccess instanceof JsInvocation) { + JsNameRef $0 = new JsNameRef(createTemporary()); + // Generate: $0 = rhs + JsExpression e = AstUtil.newAssignment($0, rhs); + + // Add ($0 = rhs) as parameter of the field access. + ((JsInvocation) fieldAccess).getArguments().add(e); + // Generate: e1.set$name($0 = rhs), $0 + return new JsBinaryOperation(JsBinaryOperator.COMMA, fieldAccess, $0); + } else { + assert (fieldAccess instanceof JsNameRef); + return AstUtil.newAssignment((JsNameRef) fieldAccess, rhs); + } + } + + private JsExpression generateStore(DartNode qualifier, DartIdentifier node, JsExpression rhs, + Element element) { + boolean accessThroughShim = true; + if (element != null) { + accessThroughShim = false; + } else { + element = node.getTargetSymbol(); + } + + switch (ElementKind.of(element)) { + case VARIABLE: + case PARAMETER: + return generateStoreTemporary(element, node, rhs); + + case NONE: { + JsExpression invoke = + generateUnresolvedAccess(qualifier, + mangler.createSetterSyntax(node.getTargetName(), unitLibrary)); + return generateStoreField(invoke, rhs); + } + + case FIELD: { + FieldElement field = (FieldElement) element; + String accessorName; + + if (accessThroughShim) { + accessorName = mangler.createSetterSyntax(field, unitLibrary); + } else { + if (optStrategy.isWhitelistedNativeField(field, FieldKind.SETTER)) { + accessorName = field.getName(); + } else { + accessorName = mangler.mangleField(field, unitLibrary); + } + } + + JsExpression invoke = + generateFieldAccess(field, qualifier, accessorName, accessThroughShim); + return generateStoreField(invoke, rhs); + } + + default: + throw new AssertionError("I do not know how to store into: " + ElementKind.of(element)); + } + } + + @Override + public JsNode visitParameterizedNode(DartParameterizedNode node) { + return node.getExpression().accept(this); + } + + @Override + public JsNode visitImportDirective(DartImportDirective node) { + throw new AssertionError("should never be called directly"); + } + + @Override + public JsNode visitLibraryDirective(DartLibraryDirective node) { + throw new AssertionError("should never be called directly"); + } + + @Override + public JsNode visitNativeDirective(DartNativeDirective node) { + throw new AssertionError("should never be called directly"); + } + + @Override + public JsNode visitResourceDirective(DartResourceDirective node) { + throw new AssertionError("should never be called directly"); + } + + @Override + public JsNode visitSourceDirective(DartSourceDirective node) { + throw new AssertionError("should never be called directly"); + } + + @Override + public DartClassMember getCurrentClassMember() { + if (this.currentScopeInfo != null) { + return currentScopeInfo.getContainingClassMember(); + } + return null; + } + + private ClassElement getCurrentClass() { + if (currentHolder.getKind() == ElementKind.CLASS) { + return (ClassElement) currentHolder; + } + return null; + } + } + + GenerateJavascriptAST(DartUnit unit, CoreTypeProvider typeProvider, DartCompilerContext context, + OptimizationStrategy optimizationStrategy) { + this.unit = unit; + this.context = context; + this.optStrategy = optimizationStrategy; + this.typeProvider = typeProvider; + } + + public void translateNode(TranslationContext translationContext, DartNode node, + JsBlock blockStatics) { + GenerateJavascriptVisitor generator = + new GenerateJavascriptVisitor(unit, context, translationContext, + optStrategy, typeProvider); + // Generate the Javascript AST. + node.accept(generator); + // Set aside the static initializations + generator.addStaticInitsToBlock(blockStatics); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/GenerateNamesAndScopes.java b/compiler/java/com/google/dart/compiler/backend/js/GenerateNamesAndScopes.java new file mode 100644 index 00000000000..84880481ac7 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/GenerateNamesAndScopes.java @@ -0,0 +1,189 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartContext; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.Elements; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.resolver.MethodElement; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * This visitor generates Javascript scopes and names for all the Dart nodes, filling in the + * node->name map in 'names'. + */ +class GenerateNamesAndScopes extends NormalizedVisitor { + + /** + * A JsScope used to manage fields and methods. A MemberJsScope can become + * parentless. + */ + private static class MemberJsScope extends JsScope { + private MemberJsScope(JsScope parent, String description) { + super(parent, description); + } + + @Override + protected void detachFromParent() { + super.detachFromParent(); + } + } + + private final Deque scopes = new LinkedList(); + private DartClass currentClass = null; + private int nextLabelId = 0; + + private final TranslationContext translationContext; + private final LibraryElement unitLibrary; + + private JsScope getGlobalScope() { + return translationContext.getProgram().getScope(); + } + + public GenerateNamesAndScopes(TranslationContext data, LibraryElement unitLibrary) { + this.translationContext = data; + this.unitLibrary = unitLibrary; + scopes.push(getGlobalScope()); + } + + @Override + public boolean visit(DartClass x, DartContext ctx) { + assert currentClass == null; + // Global variables are declared lazily. We don't declare the class now. + currentClass = x; + // We add the member scope into the hierarchy, so that the resolution works on unqualified + // identifiers. Once the resolution is done, we can rip out the scope from the hierarchy. + scopes.push(new MemberJsScope(scopes.peek(), x.getClassName())); + return true; + } + + @Override + public boolean visit(DartField x, DartContext ctx) { + FieldElement element = x.getSymbol(); + String mangledFieldName = translationContext.getMangler().mangleField(element, unitLibrary); + JsName fieldName = declare(x.getSymbol(), mangledFieldName, element.getName()); + fieldName.setObfuscatable(false); + return true; + } + + public boolean generateConstructorName(DartMethodDefinition x) { + ConstructorElement element = (ConstructorElement) x.getSymbol(); + String name = translationContext.getMangler().mangleConstructor(element.getName(), unitLibrary); + JsName jsName = function(x.getSymbol(), name, element.getName(), x.getFunction()); + // Constructors are globally accessible. + jsName.setObfuscatable(false); + return true; + } + + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + MethodElement element = x.getSymbol(); + if (Elements.isNonFactoryConstructor(element)) { + return generateConstructorName(x); + } + if (x.getModifiers().isFactory()) { + String className = element.getEnclosingElement().getName(); + String name = translationContext.getMangler().createFactorySyntax(className, element.getName(), unitLibrary); + JsName jsName = function(x.getSymbol(), name, element.getName(), x.getFunction()); + // Factories are globally accessible. + jsName.setObfuscatable(false); + return true; + } + + String mangledName = translationContext.getMangler().mangleMethod(element, unitLibrary); + JsName methodName = function(x.getSymbol(), mangledName, element.getName(), x.getFunction()); + methodName.setObfuscatable(false); + return true; + } + + @Override + public boolean visit(DartFunctionExpression x, DartContext ctx) { + function(x.getSymbol(), x.getFunctionName(), x.getFunctionName(), x.getFunction()); + return true; + } + + @Override + public boolean visit(DartParameter x, DartContext ctx) { + // TODO(ngeoffray): A parameter in a function type does not have a symbol. + if (x.getSymbol() != null) { + declare(x.getSymbol(), x.getParameterName()); + } + return true; + } + + @Override + public boolean visit(DartVariable x, DartContext ctx) { + declare(x.getSymbol(), x.getVariableName()); + return true; + } + + @Override + public boolean visit(DartLabel x, DartContext ctx) { + declare(x.getSymbol(), "L" + nextLabelId++); + return true; + } + + @Override + public void endVisit(DartMethodDefinition x, DartContext ctx) { + scopes.pop(); + } + + @Override + public void endVisit(DartFunctionExpression x, DartContext ctx) { + scopes.pop(); + } + + @Override + public void endVisit(DartClass x, DartContext ctx) { + currentClass = null; + // Rip out the member scope. Members are always accessed through an object and don't clash + // with other variables. + GenerateNamesAndScopes.MemberJsScope memberScope = (GenerateNamesAndScopes.MemberJsScope) scopes.pop(); + memberScope.rebaseChildScopes(memberScope.getParent()); + memberScope.detachFromParent(); + translationContext.getMemberScopes().put(x.getSymbol(), memberScope); + } + + private JsName function(Symbol symbol, String name, String originalName, DartFunction func) { + JsName jsName = name != null ? declare(symbol, name, originalName) : null; + JsFunction jsFunc = new JsFunction(scopes.peek(), jsName); + jsFunc.setFromDart(true); + scopes.push(jsFunc.getScope()); + translationContext.getMethods().put(func, jsFunc); + return jsName; + } + + private JsName declare(Symbol x, String name, String originalName) { + return declareInScope(scopes.peek(), x, name, originalName); + } + + private JsName declare(Symbol x, String name) { + return declareInScope(scopes.peek(), x, name, name); + } + + private JsName declareInScope(JsScope scope, Symbol x, String name, String originalName) { + JsName jsName = scope.declareName(name, name, originalName); + jsName.getClass(); // Fast null check. + x.getClass(); // Fast null check. + translationContext.getNames().setName(x, jsName); + return jsName; + } +} \ No newline at end of file diff --git a/compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java b/compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java new file mode 100644 index 00000000000..6978aa0a33f --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JavascriptBackend.java @@ -0,0 +1,298 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.Lists; +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.common.GenerateSourceMap; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.metrics.DartEventType; +import com.google.dart.compiler.metrics.Tracer; +import com.google.dart.compiler.metrics.Tracer.TraceEvent; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.util.DefaultTextOutput; +import com.google.dart.compiler.util.TextOutput; +import com.google.debugging.sourcemap.FilePosition; +import com.google.debugging.sourcemap.SourceMapSection; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * A compiler backend that produces raw Javascript. + */ +public class JavascriptBackend extends AbstractJsBackend { + + public static final String EXTENSION_JS = "js"; + public static final String EXTENSION_APP_JS = "app.js"; + public static final String EXTENSION_JS_SRC_MAP = "js.map"; + public static final String EXTENSION_APP_JS_SRC_MAP = "app.js.map"; + + /** + * Wraps an Appendable and keeps track of the current offset as line/columns. + */ + static class CountingAppendable implements Appendable { + + private int line = 0; + private int column = 0; + private Appendable out; + + FilePosition getOffset() { + return new FilePosition(line, column); + } + + CountingAppendable(Appendable out) { + this.out = out; + } + + @Override + public Appendable append(CharSequence csq) throws IOException { + incCount(csq, 0, csq.length()); + return out.append(csq); + } + + @Override + public Appendable append(char c) throws IOException { + incCount(c); + return out.append(c); + } + + @Override + public Appendable append(CharSequence csq, int start, int end) + throws IOException { + incCount(csq, start, end); + return out.append(csq, start, end); + } + + private void incCount(CharSequence cs, int start, int end) { + for (int i = 0; i < cs.length(); i++) { + incCount(cs.charAt(i)); + } + } + + private void incCount(char c) { + if (c == '\n') { + line++; + column = 0; + } else { + column++; + } + } + } + + private static class DepsWritingCallback implements DepsCallback { + private final DartCompilerContext context; + private long charsWritten = 0; + private long nativeCharsWritten = 0; + private CountingAppendable out; + private final List appSections; + + DepsWritingCallback( + DartCompilerContext context, + CountingAppendable out, + List appSections) { + this.out = out; + this.context = context; + this.appSections = appSections; + } + + /** + * @return the charsWritten + */ + public long getCharsWritten() { + return charsWritten; + } + + /** + * @return the nativeCharsWritten + */ + public long getNativeCharsWritten() { + return nativeCharsWritten; + } + + @Override + public void visitNative(LibraryUnit libUnit, LibraryNode node) + throws IOException { + DartSource nativeSrc = libUnit.getSource().getSourceFor(node.getText()); + Reader r = nativeSrc.getSourceReader(); + long charsWrittenForFile = CharStreams.copy(r, out); + nativeCharsWritten += charsWrittenForFile; + charsWritten += charsWrittenForFile; + } + + @Override + public void visitPart(Part part) throws IOException { + DartSource src = part.unit.getSource(); + assert(src != null); + Reader r = context.getArtifactReader(src, part.part, EXTENSION_JS); + if (r == null) { + return; + } + FilePosition offset = out.getOffset(); + assert(src != null); + + long partSize = 0; + boolean failed = true; + try { + partSize = CharStreams.copy(r, out); + charsWritten += partSize; + failed = false; + } finally { + Closeables.close(r, failed); + } + + if (partSize > 0) { + String mapUrl = context.getArtifactUri(src, part.part, EXTENSION_JS_SRC_MAP).toString(); + SourceMapSection sourceMapSection = + SourceMapSection.forURL(mapUrl, offset.getLine(), offset.getColumn()); + appSections.add(sourceMapSection); + } + } + } + + private static void packageLibs(Writer w, + List appSections, + DartCompilerContext context) throws IOException { + final CountingAppendable out = new CountingAppendable(w); + + DepsWritingCallback callback = new DepsWritingCallback(context, out, appSections); + DependencyBuilder.build(context.getAppLibraryUnit(), callback); + + CompilerMetrics compilerMetrics = context.getCompilerMetrics(); + if (compilerMetrics != null) { + compilerMetrics.packagedJsApplication( + callback.getCharsWritten(), callback.getNativeCharsWritten()); + } + } + + @Override + public boolean isOutOfDate(DartSource src, DartCompilerContext context) { + return context.isOutOfDate(src, src, EXTENSION_JS); + } + + @Override + public void compileUnit(DartUnit unit, DartSource src, DartCompilerContext context, + CoreTypeProvider typeProvider) throws IOException { + // Translate the AST to JS. + Map parts = translateToJS(unit, context, typeProvider); + String srcName = src.getName(); + + for (Map.Entry entry : parts.entrySet()) { + // Generate Javascript output. + TextOutput out = new DefaultTextOutput(false); + JsToStringGenerationVisitor srcGenerator; + String name = entry.getKey(); + boolean failed = true; + Writer w; + + JsProgram program = entry.getValue(); + JsBlock globalBlock = program.getGlobalBlock(); + + TraceEvent srcEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.JS_SOURCE_GEN, "src", srcName, "name", + name) : null; + try { + srcGenerator = new JsSourceGenerationVisitor(out); + + // TODO(johnlenz): Make source maps optional. + srcGenerator.generateSourceMap(true); + + srcGenerator.accept(globalBlock); + w = context.getArtifactWriter(src, name, EXTENSION_JS); + try { + w.write(out.toString()); + failed = false; + } finally { + Closeables.close(w, failed); + } + } finally { + Tracer.end(srcEvent); + } + + /* + * Currently, out of date checks require that we write a JS file even if it is empty. + * However, we should not write a map file if it is. + */ + if (!globalBlock.getStatements().isEmpty()) { + TraceEvent sourcemapEvent = + Tracer.canTrace() ? Tracer.start(DartEventType.WRITE_SOURCE_MAP, "src", srcName, + "name", name) : null; + try { + // Write out the source map. + w = context.getArtifactWriter(src, name, EXTENSION_JS_SRC_MAP); + failed = true; + try { + srcGenerator.writeSourceMap(w, src.getName()); + failed = false; + } finally { + Closeables.close(w, failed); + } + } finally { + Tracer.end(sourcemapEvent); + } + } + } + } + + @Override + public void packageApp(LibrarySource app, + Collection libraries, + DartCompilerContext context, + CoreTypeProvider typeProvider) + throws IOException { + List appSections = Lists.newArrayList(); + Writer out = context.getArtifactWriter(app, "", EXTENSION_APP_JS); + boolean failed = true; + try { + // Emit the concatenated Javascript sources in dependency order. + packageLibs(out, appSections, context); + + writeEntryPointCall(getMangledEntryPoint(context), out); + failed = false; + } finally { + Closeables.close(out, failed); + } + + Writer srcMapOut = context.getArtifactWriter(app, "", EXTENSION_APP_JS_SRC_MAP); + failed = true; + try { + // TODO(johnlenz): settle how we want to get a reference to the app + // output. Do we want this to be a filename, a URL, both? + new GenerateSourceMap().appendIndexMapTo(srcMapOut, app.getName() + "." + + EXTENSION_JS, appSections); + failed = false; + } finally { + Closeables.close(srcMapOut, failed); + } + } + + @Override + public String getAppExtension() { + return EXTENSION_APP_JS; + } + + @Override + public String getSourceMapExtension() { + return EXTENSION_APP_JS_SRC_MAP; + } + + @Override + protected boolean shouldOptimize() { + return false; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java new file mode 100644 index 00000000000..8d70657a31e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java @@ -0,0 +1,114 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsVisitable; +import com.google.dart.compiler.backend.js.ast.JsVisitor; + +/** + * Searches for method invocations in constructor expressions that would not + * normally be surrounded by parentheses. + */ +public class JsConstructExpressionVisitor extends JsVisitor { + + public static boolean exec(JsExpression expression) { + if (JsPrecedenceVisitor.exec(expression) < JsPrecedenceVisitor.PRECEDENCE_NEW) { + return true; + } + JsConstructExpressionVisitor visitor = new JsConstructExpressionVisitor(); + visitor.accept(expression); + return visitor.containsInvocation; + } + + private boolean containsInvocation = false; + + private JsConstructExpressionVisitor() { + } + + /** + * We only look at the array expression since the index has its own scope. + */ + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + accept(x.getArrayExpr()); + return false; + } + + /** + * Array literals have their own scoping. + */ + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + return false; + } + + /** + * Functions have their own scoping. + */ + @Override + public boolean visit(JsFunction x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + containsInvocation = true; + return false; + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + if (!x.isLeaf()) { + accept(x.getQualifier()); + } + return false; + } + + /** + * New constructs bind to the nearest set of parentheses. + */ + @Override + public boolean visit(JsNew x, JsContext ctx) { + return false; + } + + /** + * Object literals have their own scope. + */ + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + return false; + } + + /** + * We only look at nodes that would not normally be surrounded by parentheses. + */ + @Override + protected T doAccept(T node) { + // Assign to Object to prevent 'inconvertible types' compile errors due + // to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6548436 + // reproducible in jdk1.6.0_02. + Object o = node; + if (o instanceof JsExpression) { + JsExpression expression = (JsExpression) o; + int precedence = JsPrecedenceVisitor.exec(expression); + // Only visit expressions that won't automatically be surrounded by + // parentheses + if (precedence < JsPrecedenceVisitor.PRECEDENCE_NEW) { + return node; + } + } + return super.doAccept(node); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java new file mode 100644 index 00000000000..6fb4dc4f82b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java @@ -0,0 +1,134 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsExprStmt; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsRegExp; +import com.google.dart.compiler.backend.js.ast.JsVisitor; + +/** + * Determines if an expression statement needs to be surrounded by parentheses. + * + * The statement or the left-most expression needs to be surrounded by + * parentheses if the left-most expression is an object literal or a function + * object. Function declarations do not need parentheses. + * + * For example the following require parentheses:
    + *
      + *
    • { key : 'value'}
    • + *
    • { key : 'value'}.key
    • + *
    • function () {return 1;}()
    • + *
    • function () {return 1;}.prototype
    • + *
    + * + * The following do not require parentheses:
    + *
      + *
    • var x = { key : 'value'}
    • + *
    • "string" + { key : 'value'}.key
    • + *
    • function func() {}
    • + *
    • function() {}
    • + *
    + */ +public class JsFirstExpressionVisitor extends JsVisitor { + + public static boolean exec(JsExprStmt statement) { + JsFirstExpressionVisitor visitor = new JsFirstExpressionVisitor(); + JsExpression expression = statement.getExpression(); + // Pure function declarations do not need parentheses + if (expression instanceof JsFunction) { + return false; + } + visitor.accept(statement.getExpression()); + return visitor.needsParentheses; + } + + private boolean needsParentheses = false; + + private JsFirstExpressionVisitor() { + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + accept(x.getArrayExpr()); + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsBinaryOperation x, JsContext ctx) { + accept(x.getArg1()); + return false; + } + + @Override + public boolean visit(JsConditional x, JsContext ctx) { + accept(x.getTestExpression()); + return false; + } + + @Override + public boolean visit(JsFunction x, JsContext ctx) { + needsParentheses = true; + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + accept(x.getQualifier()); + return false; + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + if (!x.isLeaf()) { + accept(x.getQualifier()); + } + return false; + } + + @Override + public boolean visit(JsNew x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + needsParentheses = true; + return false; + } + + @Override + public boolean visit(JsPostfixOperation x, JsContext ctx) { + accept(x.getArg()); + return false; + } + + @Override + public boolean visit(JsPrefixOperation x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsRegExp x, JsContext ctx) { + return false; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsNameProvider.java b/compiler/java/com/google/dart/compiler/backend/js/JsNameProvider.java new file mode 100644 index 00000000000..8857ee978e0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsNameProvider.java @@ -0,0 +1,86 @@ +// Copyright 2011, the Dart project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ElementKind; + +import java.util.HashMap; +import java.util.Map; + +/** + * A helper class for managing global names. + * @author johnlenz@google.com (John Lenz) + */ +class JsNameProvider { + private final DartMangler mangler; + private Map names = new HashMap(); + private JsScope globalScope; + + JsNameProvider(JsProgram program, DartMangler mangler) { + this.globalScope = program.getScope(); + this.mangler = mangler; + } + + /** + * Returns the JsName for the given element. If the element is global and + * hasn't been declared yet, it is done now. + */ + JsName getName(Symbol symbol) { + JsName jsName = names.get(symbol); + if (jsName != null) { + assert !jsName.getShortIdent().equals("Object$Dart"); + return jsName; + } + assert ElementKind.of(symbol).equals(ElementKind.CLASS) + : "Only classes can be lazily declared. Undeclared: " + + symbol.getOriginalSymbolName(); + ClassElement classElement = (ClassElement) symbol; + String name = classElement.getName(); + String nativeName = classElement.getNativeName(); + if (nativeName == null) { + String mangledClassName = mangler.mangleClassName(classElement); + jsName = globalScope.declareName(mangledClassName, mangledClassName, name); + } else { + jsName = globalScope.declareName(nativeName); + } + // Class names are globally accessible. + jsName.setObfuscatable(false); + names.put(symbol, jsName); + assert !jsName.getShortIdent().equals("Object$Dart") : "unexpected " + ((ClassElement) symbol).getNode().getSource().getName(); + return jsName; + } + + void setName(Symbol symbol, JsName name) { + names.put(symbol, name); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsNamer.java b/compiler/java/com/google/dart/compiler/backend/js/JsNamer.java new file mode 100644 index 00000000000..a3f75852528 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsNamer.java @@ -0,0 +1,25 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsProgram; + +/** + * @author floitsch@google.com (Florian Loitsch) + * + * A namer runs through a program and renames the short names of JsNames. + * Namers must assign short names that don't clash and that are valid + * JS-identifiers. Nested JsScopes must not shadow JsNames from outer + * scopes. + * If a JsName is marked as non-obfuscatable then it must retain its short + * name. + */ +public interface JsNamer { + /** + * Names the shortNames of all JsNames of the program so that they are valid + * JS-identifiers and that there are no clashes and no shadowing. + */ + public void exec(JsProgram program); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsNormalizer.java b/compiler/java/com/google/dart/compiler/backend/js/JsNormalizer.java new file mode 100644 index 00000000000..5eeadf69bd1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsNormalizer.java @@ -0,0 +1,112 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsModVisitor; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperation; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; + +/** + * Fixes any semantic errors introduced by JS AST gen. + * + *
      + *
    • Creating clinit calls can put comma expressions as lvalues; the modifying + * operation must be moved inside the comma expression to the last argument.
    • + *
    + */ +public class JsNormalizer { + + /** + * Resolves any unresolved JsNameRefs. + */ + private static class JsNormalizing extends JsModVisitor { + + @Override + public void endVisit(JsBinaryOperation x, JsContext ctx) { + maybeShuffleModifyingBinary(x, ctx); + } + + @Override + public void endVisit(JsPostfixOperation x, JsContext ctx) { + maybeShuffleModifyingUnary(x, ctx); + } + + @Override + public void endVisit(JsPrefixOperation x, JsContext ctx) { + maybeShuffleModifyingUnary(x, ctx); + } + + /** + * Due to the way clinits are constructed, you can end up with a comma + * operation as the argument to a modifying operation, which is illegal. + * Juggle things to put the operator inside of the comma expression. + */ + private void maybeShuffleModifyingBinary(JsBinaryOperation x, JsContext ctx) { + JsBinaryOperator myOp = x.getOperator(); + JsExpression lhs = x.getArg1(); + + if (myOp.isAssignment() && (lhs instanceof JsBinaryOperation)) { + // Find the rightmost comma operation + JsBinaryOperation curLhs = (JsBinaryOperation) lhs; + assert (curLhs.getOperator() == JsBinaryOperator.COMMA); + while (curLhs.getArg2() instanceof JsBinaryOperation) { + curLhs = (JsBinaryOperation) curLhs.getArg2(); + assert (curLhs.getOperator() == JsBinaryOperator.COMMA); + } + // curLhs is now the rightmost comma operation; slide our operation in + x.setArg1(curLhs.getArg2()); + curLhs.setArg2(x); + // replace myself with the comma expression + ctx.replaceMe(lhs); + } + } + + /** + * Due to the way clinits are constructed, you can end up with a comma + * operation as the argument to a modifying operation, which is illegal. + * Juggle things to put the operator inside of the comma expression. + */ + private void maybeShuffleModifyingUnary(JsUnaryOperation x, JsContext ctx) { + JsUnaryOperator myOp = x.getOperator(); + JsExpression arg = x.getArg(); + if (myOp.isModifying() && (arg instanceof JsBinaryOperation)) { + // Find the rightmost comma operation + JsBinaryOperation curArg = (JsBinaryOperation) arg; + assert (curArg.getOperator() == JsBinaryOperator.COMMA); + while (curArg.getArg2() instanceof JsBinaryOperation) { + curArg = (JsBinaryOperation) curArg.getArg2(); + assert (curArg.getOperator() == JsBinaryOperator.COMMA); + } + // curArg is now the rightmost comma operation; slide our operation in + x.setArg(curArg.getArg2()); + curArg.setArg2(x); + // replace myself with the comma expression + ctx.replaceMe(arg); + } + } + } + + public static void exec(JsProgram program) { + new JsNormalizer(program).execImpl(); + } + + private final JsProgram program; + + private JsNormalizer(JsProgram program) { + this.program = program; + } + + private void execImpl() { + JsNormalizing normalizer = new JsNormalizing(); + normalizer.accept(program); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsParserException.java b/compiler/java/com/google/dart/compiler/backend/js/JsParserException.java new file mode 100644 index 00000000000..79b53c1d69e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsParserException.java @@ -0,0 +1,92 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +/** + * Indicates inability to parse JavaScript source. + */ +public class JsParserException extends Exception { + + /** + * Represents the location of a parser exception. + */ + public static class SourceDetail { + private final String fileName; + private final int line; + private final int lineOffset; + private final String lineSource; + + public SourceDetail(int line, String lineSource, int lineOffset, String fileName) { + this.line = line; + this.lineSource = lineSource; + this.lineOffset = lineOffset; + this.fileName = fileName; + } + + public String getFileName() { + return fileName; + } + + public int getLine() { + return line; + } + + public int getLineOffset() { + return lineOffset; + } + + public String getLineSource() { + return lineSource; + } + } + + private static String createMessageWithDetail(String msg, SourceDetail sourceDetail) { + if (sourceDetail == null) { + return msg; + } + StringBuffer sb = new StringBuffer(); + sb.append(sourceDetail.getFileName()); + sb.append('('); + sb.append(sourceDetail.getLine()); + sb.append(')'); + sb.append(": "); + sb.append(msg); + if (sourceDetail.getLineSource() != null) { + sb.append("\n> "); + sb.append(sourceDetail.getLineSource()); + sb.append("\n> "); + for (int i = 0, n = sourceDetail.getLineOffset(); i < n; ++i) { + sb.append('-'); + } + sb.append('^'); + } + return sb.toString(); + } + + private final SourceDetail sourceDetail; + + public JsParserException(String msg) { + this(msg, null); + } + + public JsParserException(String msg, int line, String lineSource, int lineOffset, String fileName) { + this(msg, new SourceDetail(line, lineSource, lineOffset, fileName)); + } + + public JsParserException(String msg, SourceDetail sourceDetail) { + super(createMessageWithDetail(msg, sourceDetail)); + this.sourceDetail = sourceDetail; + } + + /** + * Provides additional source detail in some cases. + * + * @return additional detail regarding the error, or null if no + * additional detail is available + */ + public SourceDetail getSourceDetail() { + return sourceDetail; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java new file mode 100644 index 00000000000..4b306872f7e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java @@ -0,0 +1,317 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral; +import com.google.dart.compiler.backend.js.ast.JsBreak; +import com.google.dart.compiler.backend.js.ast.JsCase; +import com.google.dart.compiler.backend.js.ast.JsCatch; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsContinue; +import com.google.dart.compiler.backend.js.ast.JsDebugger; +import com.google.dart.compiler.backend.js.ast.JsDefault; +import com.google.dart.compiler.backend.js.ast.JsDoWhile; +import com.google.dart.compiler.backend.js.ast.JsEmpty; +import com.google.dart.compiler.backend.js.ast.JsExprStmt; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFor; +import com.google.dart.compiler.backend.js.ast.JsForIn; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsIf; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsLabel; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsNullLiteral; +import com.google.dart.compiler.backend.js.ast.JsNumberLiteral; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsParameter; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import com.google.dart.compiler.backend.js.ast.JsRegExp; +import com.google.dart.compiler.backend.js.ast.JsReturn; +import com.google.dart.compiler.backend.js.ast.JsStringLiteral; +import com.google.dart.compiler.backend.js.ast.JsSwitch; +import com.google.dart.compiler.backend.js.ast.JsThisRef; +import com.google.dart.compiler.backend.js.ast.JsThrow; +import com.google.dart.compiler.backend.js.ast.JsTry; +import com.google.dart.compiler.backend.js.ast.JsVars; +import com.google.dart.compiler.backend.js.ast.JsVisitor; +import com.google.dart.compiler.backend.js.ast.JsWhile; +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; + +/** + * Precedence indices from "JavaScript - The Definitive Guide" 4th Edition (page + * 57) + * + * Precedence 17 is for indivisible primaries that either don't have children, + * or provide their own delimiters. + * + * Precedence 16 is for really important things that have their own AST classes. + * + * Precedence 15 is for the new construct. + * + * Precedence 14 is for unary operators. + * + * Precedences 12 through 4 are for non-assigning binary operators. + * + * Precedence 3 is for the tertiary conditional. + * + * Precedence 2 is for assignments. + * + * Precedence 1 is for comma operations. + */ +class JsPrecedenceVisitor extends JsVisitor { + + static final int PRECEDENCE_NEW = 15; + + public static int exec(JsExpression expression) { + JsPrecedenceVisitor visitor = new JsPrecedenceVisitor(); + visitor.accept(expression); + if (visitor.answer < 0) { + throw new RuntimeException("Precedence must be >= 0!"); + } + return visitor.answer; + } + + private int answer = -1; + + private JsPrecedenceVisitor() { + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + answer = 16; + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsBinaryOperation x, JsContext ctx) { + answer = x.getOperator().getPrecedence(); + return false; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsBooleanLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsCase x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsCatch x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsConditional x, JsContext ctx) { + answer = 3; + return false; + } + + @Override + public boolean visit(JsContinue x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsDefault x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsEmpty x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsExprStmt x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsFunction x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + answer = 16; + return false; + } + + @Override + public boolean visit(JsLabel x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + if (x.isLeaf()) { + answer = 17; // primary + } else { + answer = 16; // property access + } + return false; + } + + @Override + public boolean visit(JsNew x, JsContext ctx) { + answer = PRECEDENCE_NEW; + return false; + } + + @Override + public boolean visit(JsNullLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsNumberLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsParameter x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsPostfixOperation x, JsContext ctx) { + answer = x.getOperator().getPrecedence(); + return false; + } + + @Override + public boolean visit(JsPrefixOperation x, JsContext ctx) { + answer = x.getOperator().getPrecedence(); + return false; + } + + @Override + public boolean visit(JsProgram x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsPropertyInitializer x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsRegExp x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsReturn x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsStringLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsSwitch x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsThisRef x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsVar x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsVars x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } + + @Override + public boolean visit(JsWhile x, JsContext ctx) { + throw new RuntimeException("Only expressions have precedence."); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsPrettyNamer.java b/compiler/java/com/google/dart/compiler/backend/js/JsPrettyNamer.java new file mode 100644 index 00000000000..4d18fdbf1be --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsPrettyNamer.java @@ -0,0 +1,127 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsRootScope; +import com.google.dart.compiler.backend.js.ast.JsScope; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * A namer that uses short, readable idents to maximize reability. + */ +public class JsPrettyNamer implements JsNamer { + + public JsPrettyNamer() { + this.program = null; + } + + @Override + public void exec(JsProgram program) { + new JsPrettyNamer(program).execImpl(); + } + + /** + * Communicates to a parent scope all the idents used by all child scopes. + */ + private Set childIdents = null; + + private final JsProgram program; + + /** + * A map containing the next integer to try as an identifier suffix for a + * given JsScope. + */ + private IdentityHashMap> startIdentForScope = + new IdentityHashMap>(); + + protected JsPrettyNamer(JsProgram program) { + this.program = program; + } + + private void execImpl() { + visit(program.getRootScope()); + } + + private boolean isLegal(JsScope scope, Set childIdents, String newIdent) { + if (JsReservedIdentifiers.isKeyword(newIdent)) { + return false; + } + if (childIdents.contains(newIdent)) { + // one of my children already claimed this ident + return false; + } + /* + * Never obfuscate a name into an identifier that conflicts with an existing + * unobfuscatable name! It's okay if it conflicts with an existing + * obfuscatable name; that name will get obfuscated out of the way. + */ + return (scope.findExistingUnobfuscatableName(newIdent) == null); + } + + private void visit(JsScope scope) { + HashMap startIdent = startIdentForScope.get(scope); + if (startIdent == null) { + startIdent = new HashMap(); + startIdentForScope.put(scope, startIdent); + } + + // Save off the childIdents which is currently being computed for my parent. + Set myChildIdents = childIdents; + + /* + * Visit my children first. Reset childIdents so that my children will get a + * clean slate: I do not communicate to my children. + */ + childIdents = new HashSet(); + List children = scope.getChildren(); + for (Iterator it = children.iterator(); it.hasNext();) { + visit(it.next()); + } + + JsRootScope rootScope = program.getRootScope(); + if (scope == rootScope) { + return; + } + + // Visit all my idents. + for (Iterator it = scope.getAllNames(); it.hasNext();) { + JsName name = it.next(); + if (!name.isObfuscatable()) { + // Unobfuscatable names become themselves. + name.setShortIdent(name.getIdent()); + continue; + } + + String newIdent = name.getShortIdent(); + if (!isLegal(scope, childIdents, newIdent)) { + String checkIdent; + + // Start searching using a suffix hint stored in the scope. + // We still do a search in case there is a collision with + // a user-provided identifier + Integer s = startIdent.get(newIdent); + int suffix = (s == null) ? 0 : s.intValue(); + do { + checkIdent = newIdent + "_" + suffix++; + } while (!isLegal(scope, childIdents, checkIdent)); + startIdent.put(newIdent, suffix); + name.setShortIdent(checkIdent); + } else { + // nothing to do; the short name is already good + } + childIdents.add(name.getShortIdent()); + } + myChildIdents.addAll(childIdents); + childIdents = myChildIdents; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java new file mode 100644 index 00000000000..b099959ad1a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java @@ -0,0 +1,158 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsBreak; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsDebugger; +import com.google.dart.compiler.backend.js.ast.JsDoWhile; +import com.google.dart.compiler.backend.js.ast.JsEmpty; +import com.google.dart.compiler.backend.js.ast.JsExprStmt; +import com.google.dart.compiler.backend.js.ast.JsFor; +import com.google.dart.compiler.backend.js.ast.JsForIn; +import com.google.dart.compiler.backend.js.ast.JsIf; +import com.google.dart.compiler.backend.js.ast.JsLabel; +import com.google.dart.compiler.backend.js.ast.JsReturn; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsSwitch; +import com.google.dart.compiler.backend.js.ast.JsThrow; +import com.google.dart.compiler.backend.js.ast.JsTry; +import com.google.dart.compiler.backend.js.ast.JsVars; +import com.google.dart.compiler.backend.js.ast.JsVisitor; +import com.google.dart.compiler.backend.js.ast.JsWhile; + +/** + * Determines if a statement at the end of a block requires a semicolon. + * + * For example, the following statements require semicolons:
    + *
      + *
    • if (cond);
    • + *
    • while (cond);
    • + *
    + * + * The following do not require semicolons:
    + *
      + *
    • return 1
    • + *
    • do {} while(true)
    • + *
    + */ +public class JsRequiresSemiVisitor extends JsVisitor { + + public static boolean exec(JsStatement lastStatement) { + JsRequiresSemiVisitor visitor = new JsRequiresSemiVisitor(); + visitor.accept(lastStatement); + return visitor.needsSemicolon; + } + + private boolean needsSemicolon = false; + + private JsRequiresSemiVisitor() { + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsEmpty x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsExprStmt x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + JsStatement thenStmt = x.getThenStmt(); + JsStatement elseStmt = x.getElseStmt(); + JsStatement toCheck = thenStmt; + if (elseStmt != null) { + toCheck = elseStmt; + } + if (toCheck instanceof JsEmpty) { + needsSemicolon = true; + } else { + // Must recurse to determine last statement (possible if-else chain). + accept(toCheck); + } + return false; + } + + @Override + public boolean visit(JsLabel x, JsContext ctx) { + if (x.getStmt() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } + + @Override + public boolean visit(JsReturn x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsSwitch x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsVars x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsWhile x, JsContext ctx) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + return false; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java b/compiler/java/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java new file mode 100644 index 00000000000..56ae5428a3d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java @@ -0,0 +1,223 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.util.HashSet; +import java.util.Set; + +/** + * Determines whether or not a particular string is a JavaScript keyword or not. + */ +public class JsReservedIdentifiers { + + private static Set javaScriptKeywords; + private static Set reservedGlobalSymbols; + private static Set reservedPropertySymbols; + + static { + javaScriptKeywords = new HashSet(); + reservedGlobalSymbols = new HashSet(); + reservedPropertySymbols = new HashSet(); + initJavaScriptKeywords(); + initReservedGlobalSymbols(); + initReservedPropertySymbols(); + } + + public static boolean isKeyword(String s) { + return javaScriptKeywords.contains(s); + } + + private static void initJavaScriptKeywords() { + String[] keywords = new String[] { + // These are current keywords + "break", "delete", "function", "return", "typeof", "case", "do", "if", "switch", "var", + "catch", "else", "in", "this", "void", "continue", "false", "instanceof", "throw", + "while", "debugger", "finally", "new", "true", "with", "default", "for", + "null", "try", + + // These are future keywords + "abstract", "double", "goto", "native", "static", "boolean", "enum", "implements", + "package", "super", "byte", "export", "import", "private", "synchronized", "char", + "extends", "int", "protected", "throws", "class", "final", "interface", "public", + "transient", "const", "float", "long", "short", "volatile" + }; + + for (int i = 0; i < keywords.length; i++) { + javaScriptKeywords.add(keywords[i]); + } + } + + /** + * @return a set containing all known reserved global identifiers. This set must not be modified. + */ + public static Set getReservedGlobalSymbols() { + return reservedGlobalSymbols; + } + + /** + * Returns true if the string s can not be used as a global identifier. The check includes + * JavaScript keywords (as they must not be used either). + * @param s + * @return true if the given String must not be used as a global identifier. + */ + public static boolean isReservedGlobalSymbol(String s) { + return isKeyword(s) || reservedGlobalSymbols.contains(s); + } + + private static void initReservedGlobalSymbols() { + // Section references are from Ecma-262 + // (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) + String[] commonBuiltins = new String[] { + // 15.1.1 Value Properties of the Global Object + "NaN", "Infinity", "undefined", + + // 15.1.2 Function Properties of the Global Object + "eval", "parseInt", "parseFloat", "isNan", "isFinite", + + // 15.1.3 URI Handling Function Properties + "decodeURI", "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + + // 15.1.4 Constructor Properties of the Global Object + "Object", "Function", "Array", "String", "Boolean", "Number", "Date", + "RegExp", "Error", "EvalError", "RangeError", "ReferenceError", + "SyntaxError", "TypeError", "URIError", + + // 15.1.5 Other Properties of the Global Object + "Math", + + // 10.1.6 Activation Object + "arguments", + + // B.2 Additional Properties (non-normative) + "escape", "unescape", + + // Window props (https://developer.mozilla.org/en/DOM/window) + "applicationCache", "closed", "Components", "content", "controllers", + "crypto", "defaultStatus", "dialogArguments", "directories", + "document", "frameElement", "frames", "fullScreen", "globalStorage", + "history", "innerHeight", "innerWidth", "length", + "location", "locationbar", "localStorage", "menubar", + "mozInnerScreenX", "mozInnerScreenY", "mozScreenPixelsPerCssPixel", + "name", "navigator", "opener", "outerHeight", "outerWidth", + "pageXOffset", "pageYOffset", "parent", "personalbar", "pkcs11", + "returnValue", "screen", "scrollbars", "scrollMaxX", "scrollMaxY", + "self", "sessionStorage", "sidebar", "status", "statusbar", "toolbar", + "top", "window", + + // Window methods (https://developer.mozilla.org/en/DOM/window) + "alert", "addEventListener", "atob", "back", "blur", "btoa", + "captureEvents", "clearInterval", "clearTimeout", "close", "confirm", + "disableExternalCapture", "dispatchEvent", "dump", + "enableExternalCapture", "escape", "find", "focus", "forward", + "GeckoActiveXObject", "getAttention", "getAttentionWithCycleCount", + "getComputedStyle", "getSelection", "home", "maximize", "minimize", + "moveBy", "moveTo", "open", "openDialog", "postMessage", "print", + "prompt", "QueryInterface", "releaseEvents", "removeEventListener", + "resizeBy", "resizeTo", "restore", "routeEvent", "scroll", "scrollBy", + "scrollByLines", "scrollByPages", "scrollTo", "setInterval", + "setResizeable", "setTimeout", "showModalDialog", "sizeToContent", + "stop", "uuescape", "updateCommands", "XPCNativeWrapper", + "XPCSafeJSOjbectWrapper", + + // Mozilla Window event handlers, same cite + "onabort", "onbeforeunload", "onchange", "onclick", "onclose", + "oncontextmenu", "ondragdrop", "onerror", "onfocus", "onhashchange", + "onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown", + "onmousemove", "onmouseout", "onmouseover", "onmouseup", + "onmozorientation", "onpaint", "onreset", "onresize", "onscroll", + "onselect", "onsubmit", "onunload", + + // Safari Web Content Guide + // http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf + // WebKit Window member data, from WebKit DOM Reference + // (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html) + // TODO(fredsa) Many, many more functions and member data to add + "ontouchcancel", "ontouchend", "ontouchmove", "ontouchstart", + "ongesturestart", "ongesturechange", "ongestureend", + + // extra window methods + "uneval", + + // keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7, + // https://developer.mozilla.org/en/New_in_JavaScript_1.8.1 + "getPrototypeOf", "let", "yield", + + // "future reserved words" + "abstract", "int", "short", "boolean", "interface", "static", "byte", + "long", "char", "final", "native", "synchronized", "float", "package", + "throws", "goto", "private", "transient", "implements", "protected", + "volatile", "double", "public", + + // IE methods + // (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#) + "attachEvent", "clientInformation", "clipboardData", "createPopup", + "dialogHeight", "dialogLeft", "dialogTop", "dialogWidth", + "onafterprint", "onbeforedeactivate", "onbeforeprint", + "oncontrolselect", "ondeactivate", "onhelp", "onresizeend", + + // Common browser-defined identifiers not defined in ECMAScript + "event", "external", "Debug", "Enumerator", "Global", "Image", + "ActiveXObject", "VBArray", "Components", + + // Functions commonly defined on Object + "toString", "getClass", "constructor", "prototype", "valueOf", + + // Client-side JavaScript identifiers, which are needed for linkers + // that don't ensure GWT's window != $wnd, document != $doc, etc. + // Taken from the Rhino book, pg 715 + "Anchor", "Applet", "Attr", "Canvas", "CanvasGradient", + "CanvasPattern", "CanvasRenderingContext2D", "CDATASection", + "CharacterData", "Comment", "CSS2Properties", "CSSRule", + "CSSStyleSheet", "Document", "DocumentFragment", "DocumentType", + "DOMException", "DOMImplementation", "DOMParser", "Element", "Event", + "ExternalInterface", "FlashPlayer", "Form", "Frame", "History", + "HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image", + "Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType", + "MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin", + "ProcessingInstruction", "Range", "RangeException", "Screen", "Select", + "Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea", + "UIEvent", "Window", "XMLHttpRequest", "XMLSerializer", + "XPathException", "XPathResult", "XSLTProcessor", + + // These keywords trigger the loading of the java-plugin. For the + // next-generation plugin, this results in starting a new Java process. + "java", "Packages", "netscape", "sun", "JavaObject", "JavaClass", + "JavaArray", "JavaMember", + + // GWT-defined identifiers + "$wnd", "$doc", "$entry", "$moduleName", "$moduleBase", "$gwt_version", "$sessionId", + + // Identifiers used by JsStackEmulator; later set to obfuscatable + "$stack", "$stackDepth", "$location", + + // TODO: prove why this is necessary or remove it + "call" + }; + for (int i = 0; i < commonBuiltins.length; i++) { + reservedGlobalSymbols.add(commonBuiltins[i]); + } + } + + /** + * Returns true if the given string can not be used as property symbol. The check excludes + * keywords as JavaScript allow keywords as properties. + * @param s + * @return true if the given string must not be used as a property. + */ + public static boolean isReservedPropertySymbol(String s) { + return reservedPropertySymbols.contains(s); + } + + private static void initReservedPropertySymbols() { + // TODO(floitsch): fill in reserved property symbols. + reservedPropertySymbols.add("__PROTO__"); + reservedPropertySymbols.add("prototype"); + } + + private JsReservedIdentifiers() { + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java new file mode 100644 index 00000000000..ebb836a7228 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java @@ -0,0 +1,39 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsProgramFragment; +import com.google.dart.compiler.util.TextOutput; + +/** + * Generates JavaScript source from an AST. + */ +public class JsSourceGenerationVisitor extends JsToStringGenerationVisitor { + + public JsSourceGenerationVisitor(TextOutput out) { + super(out); + } + + @Override + public boolean visit(JsProgram x, JsContext ctx) { + // Descend naturally. + return true; + } + + @Override + public boolean visit(JsProgramFragment x, JsContext ctx) { + // Descend naturally. + return true; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + printJsBlock(x, false, true); + return false; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java new file mode 100644 index 00000000000..937f9d60fc1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java @@ -0,0 +1,1390 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.HasName; +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral; +import com.google.dart.compiler.backend.js.ast.JsBreak; +import com.google.dart.compiler.backend.js.ast.JsCase; +import com.google.dart.compiler.backend.js.ast.JsCatch; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsContinue; +import com.google.dart.compiler.backend.js.ast.JsDebugger; +import com.google.dart.compiler.backend.js.ast.JsDefault; +import com.google.dart.compiler.backend.js.ast.JsDoWhile; +import com.google.dart.compiler.backend.js.ast.JsEmpty; +import com.google.dart.compiler.backend.js.ast.JsExprStmt; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFor; +import com.google.dart.compiler.backend.js.ast.JsForIn; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsIf; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsLabel; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsNullLiteral; +import com.google.dart.compiler.backend.js.ast.JsNumberLiteral; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsOperator; +import com.google.dart.compiler.backend.js.ast.JsParameter; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsProgramFragment; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import com.google.dart.compiler.backend.js.ast.JsRegExp; +import com.google.dart.compiler.backend.js.ast.JsReturn; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsStringLiteral; +import com.google.dart.compiler.backend.js.ast.JsSwitch; +import com.google.dart.compiler.backend.js.ast.JsThisRef; +import com.google.dart.compiler.backend.js.ast.JsThrow; +import com.google.dart.compiler.backend.js.ast.JsTry; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; +import com.google.dart.compiler.backend.js.ast.JsVars; +import com.google.dart.compiler.backend.js.ast.JsVisitable; +import com.google.dart.compiler.backend.js.ast.JsVisitor; +import com.google.dart.compiler.backend.js.ast.JsWhile; +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import com.google.dart.compiler.common.GenerateSourceMap; +import com.google.dart.compiler.common.HasSourceInfo; +import com.google.dart.compiler.common.SourceMapping; +import com.google.dart.compiler.util.TextOutput; +import com.google.debugging.sourcemap.FilePosition; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * Produces text output from a JavaScript AST. + */ +public class JsToStringGenerationVisitor extends JsVisitor { + + private static final char[] CHARS_BREAK = "break".toCharArray(); + private static final char[] CHARS_CASE = "case".toCharArray(); + private static final char[] CHARS_CATCH = "catch".toCharArray(); + private static final char[] CHARS_CONTINUE = "continue".toCharArray(); + private static final char[] CHARS_DEBUGGER = "debugger".toCharArray(); + private static final char[] CHARS_DEFAULT = "default".toCharArray(); + private static final char[] CHARS_DO = "do".toCharArray(); + private static final char[] CHARS_ELSE = "else".toCharArray(); + private static final char[] CHARS_FALSE = "false".toCharArray(); + private static final char[] CHARS_FINALLY = "finally".toCharArray(); + private static final char[] CHARS_FOR = "for".toCharArray(); + private static final char[] CHARS_FUNCTION = "function".toCharArray(); + private static final char[] CHARS_IF = "if".toCharArray(); + private static final char[] CHARS_IN = "in".toCharArray(); + private static final char[] CHARS_NEW = "new".toCharArray(); + private static final char[] CHARS_NULL = "null".toCharArray(); + private static final char[] CHARS_RETURN = "return".toCharArray(); + private static final char[] CHARS_SWITCH = "switch".toCharArray(); + private static final char[] CHARS_THIS = "this".toCharArray(); + private static final char[] CHARS_THROW = "throw".toCharArray(); + private static final char[] CHARS_TRUE = "true".toCharArray(); + private static final char[] CHARS_TRY = "try".toCharArray(); + private static final char[] CHARS_VAR = "var".toCharArray(); + private static final char[] CHARS_WHILE = "while".toCharArray(); + private static final char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * How many lines of code to print inside of a JsBlock when printing terse. + */ + private static final int JSBLOCK_LINES_TO_PRINT = 3; + + /** + * A variable name is valid if it contains only letters, numbers, _, $ and + * does not begin with a number. There are actually other valid variable + * names, such as ones that contain escaped Unicode characters, but we + * surround those names with quotes in property initializers to be safe. + */ + private static final Pattern VALID_NAME_PATTERN = Pattern.compile("[a-zA-Z_$][\\w$]*"); + + public static String javaScriptString(String value) { + return javaScriptString(value, false); + } + + /** + * Generate JavaScript code that evaluates to the supplied string. Adapted + * from {@link ScriptRuntime#escapeString(String)} + * . The difference is that we quote with either " or ' depending on + * which one is used less inside the string. + */ + public static String javaScriptString(String value, boolean forceDoubleQuote) { + char[] chars = value.toCharArray(); + final int n = chars.length; + int quoteCount = 0; + int aposCount = 0; + for (int i = 0; i < n; ++i) { + switch (chars[i]) { + case '"': + ++quoteCount; + break; + case '\'': + ++aposCount; + break; + } + } + + StringBuffer result = new StringBuffer(value.length() + 16); + + char quoteChar = (quoteCount < aposCount || forceDoubleQuote) ? '"' : '\''; + result.append(quoteChar); + + for (int i = 0; i < n; ++i) { + char c = chars[i]; + + if (' ' <= c && c <= '~' && c != quoteChar && c != '\\') { + // an ordinary print character (like C isprint()) + result.append(c); + continue; + } + + int escape = -1; + switch (c) { + case '\b': + escape = 'b'; + break; + case '\f': + escape = 'f'; + break; + case '\n': + escape = 'n'; + break; + case '\r': + escape = 'r'; + break; + case '\t': + escape = 't'; + break; + case '"': + escape = '"'; + break; // only reach here if == quoteChar + case '\'': + escape = '\''; + break; // only reach here if == quoteChar + case '\\': + escape = '\\'; + break; + } + + if (escape >= 0) { + // an \escaped sort of character + result.append('\\'); + result.append((char) escape); + } else { + /* + * Emit characters from 0 to 31 that don't have a single character + * escape sequence in octal where possible. This saves one or two + * characters compared to the hexadecimal format '\xXX'. + * + * These short octal sequences may only be used at the end of the string + * or where the following character is a non-digit. Otherwise, the + * following character would be incorrectly interpreted as belonging to + * the sequence. + */ + if (c < ' ' && (i == n - 1 || chars[i + 1] < '0' || chars[i + 1] > '9')) { + result.append('\\'); + if (c > 0x7) { + result.append((char) ('0' + (0x7 & (c >> 3)))); + } + result.append((char) ('0' + (0x7 & c))); + } else { + int hexSize; + if (c < 256) { + // 2-digit hex + result.append("\\x"); + hexSize = 2; + } else { + // Unicode. + result.append("\\u"); + hexSize = 4; + } + // append hexadecimal form of ch left-padded with 0 + for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { + int digit = 0xf & (c >> shift); + result.append(HEX_DIGITS[digit]); + } + } + } + } + result.append(quoteChar); + escapeClosingTags(result); + String resultString = result.toString(); + return resultString; + } + + /** + * Escapes any closing XML tags embedded in str, which could + * potentially cause a parse failure in a browser, for example, embedding a + * closing <script> tag. + * + * @param str an unescaped literal; May be null + */ + private static void escapeClosingTags(StringBuffer str) { + if (str == null) { + return; + } + + int index = 0; + + while ((index = str.indexOf(" globalBlocks = new HashSet(); + private final TextOutput p; + private ArrayList statementEnds = new ArrayList(); + private ArrayList statementStarts = new ArrayList(); + private boolean buildMappings; + private List mappings = Lists.newArrayList(); + + public JsToStringGenerationVisitor(TextOutput out) { + this.p = out; + } + + /** + * @param generate Whether to generate the source map. + */ + public void generateSourceMap(boolean generate) { + this.buildMappings = generate; + } + + public void writeSourceMap(Appendable out, String name) throws IOException { + GenerateSourceMap generator = new GenerateSourceMap(); + for (SourceMapping m : mappings) { + generator.addMapping(m.getNode(), m.getStart(), m.getEnd()); + } + generator.appendTo(out, name); + } + + @Override + public void doTraverse(JsVisitable x, JsContext ctx) { + SourceMapping m = null; + // TODO(johnlenz): filter out uninteresting node types + if (buildMappings) { + m = new SourceMapping((HasSourceInfo) x, new FilePosition(p.getLine(), p.getColumn())); + mappings.add(m); + } + + super.doTraverse(x, ctx); + + if (buildMappings) { + m.setEnd(new FilePosition(p.getLine(), p.getColumn())); + } + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + JsExpression arrayExpr = x.getArrayExpr(); + _parenPush(x, arrayExpr, false); + accept(arrayExpr); + _parenPop(x, arrayExpr, false); + _lsquare(); + accept(x.getIndexExpr()); + _rsquare(); + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + _lsquare(); + boolean sep = false; + for (Object element : x.getExpressions()) { + JsExpression arg = (JsExpression) element; + sep = _sepCommaOptSpace(sep); + _parenPushIfCommaExpr(arg); + accept(arg); + _parenPopIfCommaExpr(arg); + } + _rsquare(); + return false; + } + + @Override + public boolean visit(JsBinaryOperation x, JsContext ctx) { + JsBinaryOperator op = x.getOperator(); + JsExpression arg1 = x.getArg1(); + _parenPush(x, arg1, !op.isLeftAssociative()); + accept(arg1); + if (op.isKeyword()) { + _parenPopOrSpace(x, arg1, !op.isLeftAssociative()); + } else { + _parenPop(x, arg1, !op.isLeftAssociative()); + _spaceOpt(); + } + p.print(op.getSymbol()); + JsExpression arg2 = x.getArg2(); + if (_spaceCalc(op, arg2)) { + _parenPushOrSpace(x, arg2, op.isLeftAssociative()); + } else { + _spaceOpt(); + _parenPush(x, arg2, op.isLeftAssociative()); + } + accept(arg2); + _parenPop(x, arg2, op.isLeftAssociative()); + return false; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + printJsBlock(x, true, true); + return false; + } + + @Override + public boolean visit(JsBooleanLiteral x, JsContext ctx) { + if (x.getValue()) { + _true(); + } else { + _false(); + } + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + _break(); + + JsNameRef label = x.getLabel(); + if (label != null) { + _space(); + _nameRef(label); + } + + return false; + } + + @Override + public boolean visit(JsCase x, JsContext ctx) { + _case(); + _space(); + accept(x.getCaseExpr()); + _colon(); + _newlineOpt(); + + indent(); + for (Object element : x.getStmts()) { + JsStatement stmt = (JsStatement) element; + needSemi = true; + accept(stmt); + if (needSemi) { + _semi(); + } + _newlineOpt(); + } + outdent(); + needSemi = false; + return false; + } + + @Override + public boolean visit(JsCatch x, JsContext ctx) { + _spaceOpt(); + _catch(); + _spaceOpt(); + _lparen(); + _nameDef(x.getParameter().getName()); + + // Optional catch condition. + // + JsExpression catchCond = x.getCondition(); + if (catchCond != null) { + _space(); + _if(); + _space(); + accept(catchCond); + } + + _rparen(); + _spaceOpt(); + accept(x.getBody()); + + return false; + } + + @Override + public boolean visit(JsConditional x, JsContext ctx) { + // Associativity: for the then and else branches, it is safe to insert + // another + // ternary expression, but if the test expression is a ternary, it should + // get parentheses around it. + { + JsExpression testExpression = x.getTestExpression(); + _parenPush(x, testExpression, true); + accept(testExpression); + _parenPop(x, testExpression, true); + } + _questionMark(); + { + JsExpression thenExpression = x.getThenExpression(); + _parenPush(x, thenExpression, false); + accept(thenExpression); + _parenPop(x, thenExpression, false); + } + _colon(); + { + JsExpression elseExpression = x.getElseExpression(); + _parenPush(x, elseExpression, false); + accept(elseExpression); + _parenPop(x, elseExpression, false); + } + return false; + } + + @Override + public boolean visit(JsContinue x, JsContext ctx) { + _continue(); + + JsNameRef label = x.getLabel(); + if (label != null) { + _space(); + _nameRef(label); + } + + return false; + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + _debugger(); + return false; + } + + @Override + public boolean visit(JsDefault x, JsContext ctx) { + _default(); + _colon(); + + indent(); + for (Object element : x.getStmts()) { + JsStatement stmt = (JsStatement) element; + needSemi = true; + accept(stmt); + if (needSemi) { + _semi(); + } + _newlineOpt(); + } + outdent(); + needSemi = false; + return false; + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + _do(); + _nestedPush(x.getBody(), true); + accept(x.getBody()); + _nestedPop(x.getBody()); + if (needSemi) { + _semi(); + _newlineOpt(); + } else { + _spaceOpt(); + needSemi = true; + } + _while(); + _spaceOpt(); + _lparen(); + accept(x.getCondition()); + _rparen(); + return false; + } + + @Override + public boolean visit(JsEmpty x, JsContext ctx) { + return false; + } + + @Override + public boolean visit(JsExprStmt x, JsContext ctx) { + boolean surroundWithParentheses = JsFirstExpressionVisitor.exec(x); + if (surroundWithParentheses) { + _lparen(); + } + JsExpression expr = x.getExpression(); + accept(expr); + if (surroundWithParentheses) { + _rparen(); + } + return false; + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + _for(); + _spaceOpt(); + _lparen(); + + // The init expressions or var decl. + // + if (x.getInitExpr() != null) { + accept(x.getInitExpr()); + } else if (x.getInitVars() != null) { + accept(x.getInitVars()); + } + + _semi(); + + // The loop test. + // + if (x.getCondition() != null) { + _spaceOpt(); + accept(x.getCondition()); + } + + _semi(); + + // The incr expression. + // + if (x.getIncrExpr() != null) { + _spaceOpt(); + accept(x.getIncrExpr()); + } + + _rparen(); + _nestedPush(x.getBody(), false); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + _for(); + _spaceOpt(); + _lparen(); + + if (x.getIterVarName() != null) { + _var(); + _space(); + _nameDef(x.getIterVarName()); + + if (x.getIterExpr() != null) { + _spaceOpt(); + _assignment(); + _spaceOpt(); + accept(x.getIterExpr()); + } + } else { + // Just a name ref. + // + accept(x.getIterExpr()); + } + + _space(); + _in(); + _space(); + accept(x.getObjExpr()); + + _rparen(); + _nestedPush(x.getBody(), false); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + // function foo(a, b) { + // stmts... + // } + // + @Override + public boolean visit(JsFunction x, JsContext ctx) { + _function(); + + // Functions can be anonymous. + // + if (x.getName() != null) { + _space(); + _nameOf(x); + } + + _lparen(); + boolean sep = false; + for (Object element : x.getParameters()) { + JsParameter param = (JsParameter) element; + sep = _sepCommaOptSpace(sep); + accept(param); + } + _rparen(); + + accept(x.getBody()); + needSemi = true; + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + _if(); + _spaceOpt(); + _lparen(); + accept(x.getIfExpr()); + _rparen(); + JsStatement thenStmt = x.getThenStmt(); + _nestedPush(thenStmt, false); + accept(thenStmt); + _nestedPop(thenStmt); + JsStatement elseStmt = x.getElseStmt(); + if (elseStmt != null) { + if (needSemi) { + _semi(); + _newlineOpt(); + } else { + _spaceOpt(); + needSemi = true; + } + _else(); + boolean elseIf = elseStmt instanceof JsIf; + if (!elseIf) { + _nestedPush(elseStmt, true); + } else { + _space(); + } + accept(elseStmt); + if (!elseIf) { + _nestedPop(elseStmt); + } + } + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + JsExpression qualifier = x.getQualifier(); + _parenPush(x, qualifier, false); + accept(qualifier); + _parenPop(x, qualifier, false); + + _lparen(); + boolean sep = false; + for (Object element : x.getArguments()) { + JsExpression arg = (JsExpression) element; + sep = _sepCommaOptSpace(sep); + _parenPushIfCommaExpr(arg); + accept(arg); + _parenPopIfCommaExpr(arg); + } + _rparen(); + return false; + } + + @Override + public boolean visit(JsLabel x, JsContext ctx) { + _nameOf(x); + _colon(); + _spaceOpt(); + accept(x.getStmt()); + return false; + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + JsExpression q = x.getQualifier(); + if (q != null) { + _parenPush(x, q, false); + if (q instanceof JsNumberLiteral) { + /** + * Fix for Issue #3796. "42.foo" is not allowed, but "(42).foo" is. + */ + _lparen(); + } + accept(q); + if (q instanceof JsNumberLiteral) { + _rparen(); + } + _parenPop(x, q, false); + _dot(); + } + _nameRef(x); + return false; + } + + @Override + public boolean visit(JsNew x, JsContext ctx) { + _new(); + _space(); + + JsExpression ctorExpr = x.getConstructorExpression(); + boolean needsParens = JsConstructExpressionVisitor.exec(ctorExpr); + if (needsParens) { + _lparen(); + } + accept(ctorExpr); + if (needsParens) { + _rparen(); + } + + /* + * If a constructor call has no arguments, it may simply be replaced with + * "new Constructor" with no parentheses. + */ + List args = x.getArguments(); + if (args.size() > 0) { + _lparen(); + boolean sep = false; + for (JsExpression arg : args) { + sep = _sepCommaOptSpace(sep); + _parenPushIfCommaExpr(arg); + accept(arg); + _parenPopIfCommaExpr(arg); + } + _rparen(); + } + + return false; + } + + @Override + public boolean visit(JsNullLiteral x, JsContext ctx) { + _null(); + return false; + } + + @Override + public boolean visit(JsNumberLiteral x, JsContext ctx) { + double dvalue = x.getValue(); + long lvalue = (long) dvalue; + if (lvalue == dvalue) { + p.print(Long.toString(lvalue)); + } else { + p.print(Double.toString(dvalue)); + } + return false; + } + + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + _lbrace(); + boolean sep = false; + for (Object element : x.getPropertyInitializers()) { + sep = _sepCommaOptSpace(sep); + JsPropertyInitializer propInit = (JsPropertyInitializer) element; + printLabel : { + JsExpression labelExpr = propInit.getLabelExpr(); + // labels can be either string, integral, or decimal literals + if (labelExpr instanceof JsStringLiteral) { + String propName = ((JsStringLiteral) labelExpr).getValue(); + if (VALID_NAME_PATTERN.matcher(propName).matches() + && !JsReservedIdentifiers.isKeyword(propName)) { + p.print(propName); + break printLabel; + } + } + accept(labelExpr); + } + _colon(); + JsExpression valueExpr = propInit.getValueExpr(); + _parenPushIfCommaExpr(valueExpr); + accept(valueExpr); + _parenPopIfCommaExpr(valueExpr); + } + _rbrace(); + return false; + } + + @Override + public boolean visit(JsParameter x, JsContext ctx) { + _nameOf(x); + return false; + } + + @Override + public boolean visit(JsPostfixOperation x, JsContext ctx) { + JsUnaryOperator op = x.getOperator(); + JsExpression arg = x.getArg(); + // unary operators always associate correctly (I think) + _parenPush(x, arg, false); + accept(arg); + _parenPop(x, arg, false); + p.print(op.getSymbol()); + return false; + } + + @Override + public boolean visit(JsPrefixOperation x, JsContext ctx) { + JsUnaryOperator op = x.getOperator(); + p.print(op.getSymbol()); + JsExpression arg = x.getArg(); + if (_spaceCalc(op, arg)) { + _space(); + } + // unary operators always associate correctly (I think) + _parenPush(x, arg, false); + accept(arg); + _parenPop(x, arg, false); + return false; + } + + @Override + public boolean visit(JsProgram x, JsContext ctx) { + p.print(""); + return false; + } + + @Override + public boolean visit(JsProgramFragment x, JsContext ctx) { + p.print(""); + return false; + } + + @Override + public boolean visit(JsPropertyInitializer x, JsContext ctx) { + // Since there are separators, we actually print the property init + // in visit(JsObjectLiteral). + // + return false; + } + + @Override + public boolean visit(JsRegExp x, JsContext ctx) { + _slash(); + p.print(x.getPattern()); + _slash(); + String flags = x.getFlags(); + if (flags != null) { + p.print(flags); + } + return false; + } + + @Override + public boolean visit(JsReturn x, JsContext ctx) { + _return(); + JsExpression expr = x.getExpr(); + if (expr != null) { + _space(); + accept(expr); + } + return false; + } + + @Override + public boolean visit(JsStringLiteral x, JsContext ctx) { + printStringLiteral(x.getValue()); + return false; + } + + @Override + public boolean visit(JsSwitch x, JsContext ctx) { + _switch(); + _spaceOpt(); + _lparen(); + accept(x.getExpr()); + _rparen(); + _spaceOpt(); + _blockOpen(); + acceptList(x.getCases()); + _blockClose(); + return false; + } + + @Override + public boolean visit(JsThisRef x, JsContext ctx) { + _this(); + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + _throw(); + _space(); + accept(x.getExpr()); + return false; + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + _try(); + _spaceOpt(); + accept(x.getTryBlock()); + + acceptList(x.getCatches()); + + JsBlock finallyBlock = x.getFinallyBlock(); + if (finallyBlock != null) { + _spaceOpt(); + _finally(); + _spaceOpt(); + accept(finallyBlock); + } + + return false; + } + + @Override + public boolean visit(JsVar x, JsContext ctx) { + _nameOf(x); + JsExpression initExpr = x.getInitExpr(); + if (initExpr != null) { + _spaceOpt(); + _assignment(); + _spaceOpt(); + _parenPushIfCommaExpr(initExpr); + accept(initExpr); + _parenPopIfCommaExpr(initExpr); + } + return false; + } + + @Override + public boolean visit(JsVars x, JsContext ctx) { + _var(); + _space(); + boolean sep = false; + for (JsVar var : x) { + sep = _sepCommaOptSpace(sep); + accept(var); + } + return false; + } + + @Override + public boolean visit(JsWhile x, JsContext ctx) { + _while(); + _spaceOpt(); + _lparen(); + accept(x.getCondition()); + _rparen(); + _nestedPush(x.getBody(), false); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + // CHECKSTYLE_NAMING_OFF + protected void _newline() { + p.newline(); + } + + protected void _newlineOpt() { + p.newlineOpt(); + } + + protected void printJsBlock(JsBlock x, boolean truncate, boolean finalNewline) { + boolean needBraces = !x.isGlobalBlock(); + + if (needBraces) { + // Open braces. + // + _blockOpen(); + } + + int count = 0; + for (Iterator iter = x.getStatements().iterator(); iter.hasNext(); ++count) { + boolean isGlobal = x.isGlobalBlock() || globalBlocks.contains(x); + + if (truncate && count > JSBLOCK_LINES_TO_PRINT) { + p.print("[...]"); + _newlineOpt(); + break; + } + JsStatement stmt = iter.next(); + needSemi = true; + boolean shouldRecordPositions = isGlobal && !(stmt instanceof JsBlock); + boolean stmtIsGlobalBlock = false; + if (isGlobal) { + if (stmt instanceof JsBlock) { + // A block inside a global block is still considered global + stmtIsGlobalBlock = true; + globalBlocks.add((JsBlock) stmt); + } + } + if (shouldRecordPositions) { + statementStarts.add(p.getPosition()); + } + accept(stmt); + if (stmtIsGlobalBlock) { + globalBlocks.remove(stmt); + } + if (needSemi) { + /* + * Special treatment of function decls: If they are the only item in a + * statement (i.e. not part of an assignment operation), just give them + * a newline instead of a semi. + */ + boolean functionStmt = + stmt instanceof JsExprStmt && ((JsExprStmt) stmt).getExpression() instanceof JsFunction; + /* + * Special treatment of the last statement in a block: only a few + * statements at the end of a block require semicolons. + */ + boolean lastStatement = !iter.hasNext() && needBraces && !JsRequiresSemiVisitor.exec(stmt); + if (functionStmt) { + if (lastStatement) { + _newlineOpt(); + } else { + _newline(); + } + } else { + if (lastStatement) { + _semiOpt(); + } else { + _semi(); + } + _newlineOpt(); + } + } + if (shouldRecordPositions) { + assert (statementStarts.size() == statementEnds.size() + 1); + statementEnds.add(p.getPosition()); + } + } + + if (needBraces) { + // _blockClose() modified + p.indentOut(); + p.print('}'); + if (finalNewline) { + _newlineOpt(); + } + } + needSemi = false; + } + + private void _assignment() { + p.print('='); + } + + private void _blockClose() { + p.indentOut(); + p.print('}'); + _newlineOpt(); + } + + private void _blockOpen() { + p.print('{'); + p.indentIn(); + _newlineOpt(); + } + + private void _break() { + p.print(CHARS_BREAK); + } + + private void _case() { + p.print(CHARS_CASE); + } + + private void _catch() { + p.print(CHARS_CATCH); + } + + private void _colon() { + p.print(':'); + } + + private void _continue() { + p.print(CHARS_CONTINUE); + } + + private void _debugger() { + p.print(CHARS_DEBUGGER); + } + + private void _default() { + p.print(CHARS_DEFAULT); + } + + private void _do() { + p.print(CHARS_DO); + } + + private void _dot() { + p.print('.'); + } + + private void _else() { + p.print(CHARS_ELSE); + } + + private void _false() { + p.print(CHARS_FALSE); + } + + private void _finally() { + p.print(CHARS_FINALLY); + } + + private void _for() { + p.print(CHARS_FOR); + } + + private void _function() { + p.print(CHARS_FUNCTION); + } + + private void _if() { + p.print(CHARS_IF); + } + + private void _in() { + p.print(CHARS_IN); + } + + private void _lbrace() { + p.print('{'); + } + + private void _lparen() { + p.print('('); + } + + private void _lsquare() { + p.print('['); + } + + private void _nameDef(JsName name) { + p.print(name.getShortIdent()); + } + + private void _nameOf(HasName hasName) { + _nameDef(hasName.getName()); + } + + private void _nameRef(JsNameRef nameRef) { + p.print(nameRef.getShortIdent()); + } + + private boolean _nestedPop(JsStatement statement) { + boolean pop = !(statement instanceof JsBlock); + if (pop) { + p.indentOut(); + } + return pop; + } + + private boolean _nestedPush(JsStatement statement, boolean needSpace) { + boolean push = !(statement instanceof JsBlock); + if (push) { + if (needSpace) { + _space(); + } + p.indentIn(); + _newlineOpt(); + } else { + _spaceOpt(); + } + return push; + } + + private void _new() { + p.print(CHARS_NEW); + } + + private void _null() { + p.print(CHARS_NULL); + } + + private boolean _parenCalc(JsExpression parent, JsExpression child, boolean wrongAssoc) { + int parentPrec = JsPrecedenceVisitor.exec(parent); + int childPrec = JsPrecedenceVisitor.exec(child); + return (parentPrec > childPrec || (parentPrec == childPrec && wrongAssoc)); + } + + private boolean _parenPop(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPop = _parenCalc(parent, child, wrongAssoc); + if (doPop) { + _rparen(); + } + return doPop; + } + + private boolean _parenPopIfCommaExpr(JsExpression x) { + boolean doPop = + x instanceof JsBinaryOperation + && ((JsBinaryOperation) x).getOperator() == JsBinaryOperator.COMMA; + if (doPop) { + _rparen(); + } + return doPop; + } + + private boolean _parenPopOrSpace(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPop = _parenCalc(parent, child, wrongAssoc); + if (doPop) { + _rparen(); + } else { + _space(); + } + return doPop; + } + + private boolean _parenPush(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPush = _parenCalc(parent, child, wrongAssoc); + if (doPush) { + _lparen(); + } + return doPush; + } + + private boolean _parenPushIfCommaExpr(JsExpression x) { + boolean doPush = + x instanceof JsBinaryOperation + && ((JsBinaryOperation) x).getOperator() == JsBinaryOperator.COMMA; + if (doPush) { + _lparen(); + } + return doPush; + } + + private boolean _parenPushOrSpace(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPush = _parenCalc(parent, child, wrongAssoc); + if (doPush) { + _lparen(); + } else { + _space(); + } + return doPush; + } + + private void _questionMark() { + p.print('?'); + } + + private void _rbrace() { + p.print('}'); + } + + private void _return() { + p.print(CHARS_RETURN); + } + + private void _rparen() { + p.print(')'); + } + + private void _rsquare() { + p.print(']'); + } + + private void _semi() { + p.print(';'); + } + + private void _semiOpt() { + p.printOpt(';'); + } + + private boolean _sepCommaOptSpace(boolean sep) { + if (sep) { + p.print(','); + _spaceOpt(); + } + return true; + } + + private void _slash() { + p.print('/'); + } + + private void _space() { + p.print(' '); + } + + /** + * Decide whether, if op is printed followed by arg, + * there needs to be a space between the operator and expression. + * + * @return true if a space needs to be printed + */ + private boolean _spaceCalc(JsOperator op, JsExpression arg) { + if (op.isKeyword()) { + return true; + } + if (arg instanceof JsBinaryOperation) { + JsBinaryOperation binary = (JsBinaryOperation) arg; + /* + * If the binary operation has a higher precedence than op, then it won't + * be parenthesized, so check the first argument of the binary operation. + */ + if (binary.getOperator().getPrecedence() > op.getPrecedence()) { + return _spaceCalc(op, binary.getArg1()); + } + return false; + } + if (arg instanceof JsPrefixOperation) { + JsOperator op2 = ((JsPrefixOperation) arg).getOperator(); + return (op == JsBinaryOperator.SUB || op == JsUnaryOperator.NEG) + && (op2 == JsUnaryOperator.DEC || op2 == JsUnaryOperator.NEG) + || (op == JsBinaryOperator.ADD && op2 == JsUnaryOperator.INC); + } + if (arg instanceof JsNumberLiteral) { + JsNumberLiteral literal = (JsNumberLiteral) arg; + return (op == JsBinaryOperator.SUB || op == JsUnaryOperator.NEG) && (literal.getValue() < 0); + } + return false; + } + + private void _spaceOpt() { + p.printOpt(' '); + } + + private void _switch() { + p.print(CHARS_SWITCH); + } + + private void _this() { + p.print(CHARS_THIS); + } + + private void _throw() { + p.print(CHARS_THROW); + } + + private void _true() { + p.print(CHARS_TRUE); + } + + private void _try() { + p.print(CHARS_TRY); + } + + private void _var() { + p.print(CHARS_VAR); + } + + private void _while() { + p.print(CHARS_WHILE); + } + + // CHECKSTYLE_NAMING_ON + + private void indent() { + p.indentIn(); + } + + private void outdent() { + p.indentOut(); + } + + private void printStringLiteral(String value) { + String resultString = javaScriptString(value); + p.print(resultString); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/NoOptimizationStrategy.java b/compiler/java/com/google/dart/compiler/backend/js/NoOptimizationStrategy.java new file mode 100644 index 00000000000..2398d9bf3f1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/NoOptimizationStrategy.java @@ -0,0 +1,80 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartBinaryExpression; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.backend.common.TypeHeuristic.FieldKind; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.FieldElement; + +class NoOptimizationStrategy implements OptimizationStrategy { + + public NoOptimizationStrategy(DartUnit unit, CoreTypeProvider typeProvider) { + } + + @Override + public boolean canSkipOperatorShim(DartBinaryExpression x) { + return false; + } + + @Override + public boolean canSkipArrayAccessShim(DartArrayAccess array, boolean isAssignee) { + return false; + } + + @Override + public FieldElement findOptimizableFieldElementFor(DartExpression expr, FieldKind fieldKind) { + return null; + } + + @Override + public Element findElementFor(DartMethodInvocation expr) { + return (Element) expr.getTargetSymbol(); + } + + @Override + public boolean canSkipOperatorShim(DartUnaryExpression x) { + return false; + } + + @Override + public boolean canSkipNormalization(DartBinaryExpression receiver) { + return false; + } + + @Override + public boolean canSkipNormalization(DartUnaryExpression expr) { + return false; + } + + @Override + public boolean canInlineInitializers(ConstructorElement constructorElement) { + return false; + } + + @Override + public boolean canEmitOptimizedClassConstructor(ClassElement classElement) { + return false; + } + + @Override + public boolean isWhitelistedNativeField(FieldElement field, FieldKind fieldKind) { + return false; + } + + @Override + public boolean canOptimizeFunctionExpressionBind(DartFunctionExpression expr) { + return false; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/NormalizedVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/NormalizedVisitor.java new file mode 100644 index 00000000000..9ca13af9647 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/NormalizedVisitor.java @@ -0,0 +1,21 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.ast.DartContext; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartVisitable; +import com.google.dart.compiler.ast.DartVisitor; + +/** + * A visitor that always visits the normalized node. + */ +class NormalizedVisitor extends DartVisitor { + @Override + protected void doTraverse(DartVisitable visitable, DartContext ctx) { + DartNode node = (DartNode) visitable; + super.doTraverse(node.getNormalizedNode(), ctx); + } +} \ No newline at end of file diff --git a/compiler/java/com/google/dart/compiler/backend/js/Normalizer.java b/compiler/java/com/google/dart/compiler/backend/js/Normalizer.java new file mode 100644 index 00000000000..72914eec6b7 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/Normalizer.java @@ -0,0 +1,924 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartBinaryExpression; +import com.google.dart.compiler.ast.DartBlock; +import com.google.dart.compiler.ast.DartCase; +import com.google.dart.compiler.ast.DartClassMember; +import com.google.dart.compiler.ast.DartContext; +import com.google.dart.compiler.ast.DartDefault; +import com.google.dart.compiler.ast.DartDoWhileStatement; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIfStatement; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartModVisitor; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartParenthesizedExpression; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartSwitchMember; +import com.google.dart.compiler.ast.DartSwitchStatement; +import com.google.dart.compiler.ast.DartThrowStatement; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.DartVariableStatement; +import com.google.dart.compiler.ast.DartWhileStatement; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.Elements; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.LabelElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.resolver.VariableElement; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Normalization phase of Dart compiler. Rewrites the AST to simplify later + * phases. + *
      + *
    • Split 'type' declarations such as "int a = 0, b = 0" + *
    • Introduce block statements for control structures such as IF, WHILE, and + * FOR + *
    • normalize case statements, including adding FallThroughError throws + *
    • pull initializers out of FOR loops + *
    • remove useless statement labels + *
    • remove parenthesized expression nodes + *
    + */ +public class Normalizer { + + public DartUnit exec(DartUnit unit, CoreTypeProvider typeProvider, + OptimizationStrategy optimizationStrategy) { + new ParenNormalizer().accept(unit); + new BlockNormalizer(typeProvider).accept(unit); + new ForLoopInitNormalizer().accept(unit); + // Now that the have been inserted and the For VAR has been + // pulled out. It is easy to split up the VAR declarations. + new VarNormalizer().accept(unit); + unit.accept(new NormalizerVisitor(optimizationStrategy)); + // debugPrint(unit); + return unit; + } + + @SuppressWarnings("unused") + private void debugPrint(DartUnit unit) { + DartNodeTraverser debugPrinter = new DartNodeTraverser() { + @Override + public Void visitClassMember(DartClassMember node) { + System.err.println(node); + return super.visitClassMember(node); + } + + @Override + public Void visitNode(DartNode node) { + DartNode normalizedNode = node.getNormalizedNode(); + if (node != normalizedNode) { + System.err.println("orig: " + node); + System.err.println("norm: " + normalizedNode); + System.err.println(); + } + return super.visitNode(node); + } + }; + unit.accept(debugPrinter); + } + + /** + * Minimize the complexity of adding statements to the AST by inserting BLOCK + * nodes where there can be statements, after this pass there are only + * three statement holders: BLOCK, FOR, and LABEL. + *

    + * This also simplifies scope handling. + *

    + * The complexity of dealing with LABEL statements is reduced by minimizing + * the number of places that a label can be used to four statement types: + * BLOCK, FOR, WHILE, and DO-WHILE. + * BLOCK could also be removed by using "label:do { statement } while (false);" + * should we choose to. + */ + private static class BlockNormalizer extends DartModVisitor { + + private final ConstructorElement fallThroughError; + + private static ConstructorElement getFallThroughError( + CoreTypeProvider typeProvider) { + ClassElement element = typeProvider.getFallThroughError().getElement(); + // TODO(fabiomfv): remove local resolution once we settle on the approach. + ConstructorElement constructor = element.lookupConstructor(""); + if (constructor == null) { + throw new InternalCompilerException("FallThroughError does not have unnamed constructor."); + } + return constructor; + } + + public BlockNormalizer(CoreTypeProvider typeProvider) { + fallThroughError = getFallThroughError(typeProvider); + } + + @Override + public void endVisit(DartLabel x, DartContext ctx) { + DartStatement body = x.getStatement(); + if (body instanceof DartVariableStatement) { + // TODO(johnlenz): I'm assuming labelled statements don't introduce + // a new scope. + // Don't push a single VAR into a BLOCK, the label can't be referenced + // so drop it entirely. + ctx.replaceMe(body); + } else if (!(body instanceof DartBlock) && !canContinueControlStructure(body)) { + DartIdentifier label = x.getLabel(); + DartLabel replacement = new DartLabel(label, maybeAddBlock(body)); + LabelElement element = (LabelElement) x.getSymbol(); + element.setNode(replacement); + replacement.setSymbol(element); + replacement.setSourceInfo(x); + ctx.replaceMe(replacement); + } + } + + /** + * @return Whether this is a control structure that can be used + * with a named "continue" statement. + */ + private boolean canContinueControlStructure(DartStatement stmt) { + if (stmt instanceof DartForStatement + || stmt instanceof DartWhileStatement + || stmt instanceof DartDoWhileStatement) { + return true; + } + return false; + } + + @Override + public void endVisit(DartForStatement x, DartContext ctx) { + DartStatement body = x.getBody(); + if (!(body instanceof DartBlock)) { + DartStatement init = x.getInit(); + DartExpression condition = x.getCondition(); + DartExpression increment = x.getIncrement(); + + DartStatement replacement = new DartForStatement( + init, condition, increment, maybeAddBlock(body)); + replacement.setSourceInfo(x); + ctx.replaceMe(replacement); + } + } + + @Override + public void endVisit(DartIfStatement x, DartContext ctx) { + DartStatement thenStmt = x.getThenStatement(); + DartStatement elseStmt = x.getElseStatement(); + if (!(thenStmt instanceof DartBlock) || !(elseStmt instanceof DartBlock)) { + DartExpression condition = x.getCondition(); + + // TODO(johnlenz): Preserve source location? + DartIfStatement replacement = new DartIfStatement( + condition, + maybeAddBlock(thenStmt), + (elseStmt != null) ? maybeAddBlock(elseStmt) : null); + replacement.setSourceInfo(x); + ctx.replaceMe(replacement); + } + } + + @Override + public void endVisit(DartWhileStatement x, DartContext ctx) { + DartStatement body = x.getBody(); + if (!(body instanceof DartBlock)) { + DartExpression condition = x.getCondition(); + + // TODO(johnlenz): Preserve source location? + DartWhileStatement replacement = new DartWhileStatement( + condition, maybeAddBlock(body)); + replacement.setSourceInfo(x); + ctx.replaceMe(replacement); + } + } + + @Override + public void endVisit(DartDoWhileStatement x, DartContext ctx) { + DartStatement body = x.getBody(); + if (!(body instanceof DartBlock)) { + DartExpression condition = x.getCondition(); + + // TODO(johnlenz): Preserve source location? + DartDoWhileStatement replacement = new DartDoWhileStatement( + condition, maybeAddBlock(body)); + replacement.setSourceInfo(x); + ctx.replaceMe(replacement); + } + } + + /** + * Normalize case statements. There are two main things to be accomplished: + *

      + *
    1. add throw new FallThroughError at the end of non-empty cases which + * may fall through to the next block + *
    2. wrap individual statements or empty cases in blocks + *
    + * + * For example: + *
    +     * case 1:
    +     * case 2: ...
    +     * 
    + * becomes + *
    +     * case 1: {}
    +     * case 2: ...
    +     * 
    + * while this: + *
    +     * case 1: {}
    +     * case 2: ...
    +     * 
    + * becomes + *
    +     * case 1: { throw new FallThroughError(); }
    +     * case 2: ...
    +     * 
    + * The last case (and any empty labels that fall through into it) does not get + * a throw added, but does get changed to an empty block. + * + * @param caseStmt the "case: stmt;*" block being normalized + * @param ctx {@link DartContext} + */ + @Override + public void endVisit(DartCase caseStmt, DartContext ctx) { + List stmts = caseStmt.getStatements(); + if (stmts.size() == 0) { + replaceWithBlock(stmts, Lists.newArrayList(stmts)); + return; + } + + // unpack an outer block if present + List innerStatements = stmts; + if (stmts.get(0) instanceof DartBlock) { + innerStatements = ((DartBlock) stmts.get(0)).getStatements(); + } + + // check if we need to add a throw + boolean needsThrow = !isLastSwitchMember(caseStmt); + if (needsThrow) { + // TODO(jat): should we only look at the last statement? + // DartParser.parseCaseStatemets seems to stop as soon as it hits an + // AbruptCompletingStatement. + for (DartStatement stmt : innerStatements) { + if (stmt.isAbruptCompletingStatement()) { + needsThrow = false; + break; + } + } + } + + // if we don't need to modify the block, nothing to do + if (!needsThrow && stmts.get(0) instanceof DartBlock) { + return; + } + + // copy the list of statements and add a throw + List newStmts = Lists.newArrayList(innerStatements); + if (needsThrow) { + newStmts.add(buildThrow(fallThroughError)); + } + caseStmt.setNormalizedNode(new DartCase(caseStmt.getExpr(), caseStmt.getLabel(), newStmts)); + replaceWithBlock(stmts, newStmts); + } + + /** + * Check to see if the supplied switch member is the last one in the switch + * statement, or is allowed to fall through to the last one. + * + * @param member + * @return true if the specified member is the last one in the switch + * statement or is allowed to fall through to the l + */ + private boolean isLastSwitchMember(DartSwitchMember member) { + /* + * We are replacing empty switch members with empty blocks as we go, but + * that is ok because we start from the end here. + */ + DartSwitchStatement switchStmt = (DartSwitchStatement) member.getParent(); + List members = switchStmt.getMembers(); + int i = members.size() - 1; + if (members.get(i) == member) { + // last switch member + return true; + } + // now we only want ones that are empty and have no non-empty switch members + // between them and the last member + while (i >= 0) { + DartSwitchMember curMember = members.get(i); + if (curMember.getStatements().size() > 0) { + // non-empty, so no earlier members can fall through + return false; + } + if (curMember == member) { + return true; + } + i--; + } + return false; + } + + /** + * Create a throw new ExceptionCtor() statement. + * + * @param exceptionCtor constructor to use to create exception instance + * @param args zero or more arguments for the supplied constructor + * @return a {@link DartStatement} representing a throw of the supplied + * exception + */ + private static DartStatement buildThrow(ConstructorElement exceptionCtor, + DartExpression... args) { + // Create AST nodes representing 'throw new FallThroughException();'. + DartNewExpression newExpr = new DartNewExpression(new DartTypeNode(new DartIdentifier( + exceptionCtor.getName())), Arrays.asList(args), false); + newExpr.setSymbol(exceptionCtor); + return new DartThrowStatement(newExpr); + } + + /** + * @param stmts + * @param newStmts + */ + private void replaceWithBlock(List stmts, List newStmts) { + DartBlock block = new DartBlock(newStmts); + stmts.clear(); + stmts.add(block); + } + + @Override + public void endVisit(DartDefault member, DartContext ctx) { + // default labels must be last, so they do not need to throw FallThroughErrors + // So, all we need to do is make sure they are blocks + List stmts = member.getStatements(); + if (stmts.size() == 0 || !(stmts.get(0) instanceof DartBlock)) { + replaceWithBlock(stmts, Lists.newArrayList(stmts)); + } + } + + private DartBlock maybeAddBlock(DartStatement statement) { + if (statement instanceof DartBlock) { + return (DartBlock)statement; + } + return new DartBlock(Lists.newArrayList(statement)); + } + } + + /** + * Extract the any initializer statements out of the FOR loop. This is done + * to minimize the amount of code that needs to be special cased for handling + * expressions in a FOR loop. + */ + private static class ForLoopInitNormalizer extends DartModVisitor { + @Override + public boolean visit(DartLabel x, DartContext ctx) { + // Pulled any FOR loop initializer expressions up above the label, + // we do this in the visit to allow the DartForStatement to handle + // the unlabelled case. + + LabeledForVisitor labelVisitor = new LabeledForVisitor(); + labelVisitor.accept(x); + if (labelVisitor.forInit != null) { + // FOR loop initializer need to be scoped, put them in a block. + List stmts = Lists.newArrayList(labelVisitor.forInit, x); + DartBlock replacement = new DartBlock(stmts); + ctx.replaceMe(replacement); + + // The removed expression hasn't be visited yet, do it now so it isn't + // skipped. + accept(replacement); + } + return true; + } + + @Override + public void endVisit(DartForStatement x, DartContext ctx) { + DartStatement init = x.getInit(); + if (init != null) { + DartStatement body = x.getBody(); + DartExpression condition = x.getCondition(); + DartExpression increment = x.getIncrement(); + + DartStatement newFor = new DartForStatement( + null, condition, increment, body); + newFor.setSourceInfo(x); + + // FOR loop initializer need to be scoped, put them in a block. + DartStatement replacementBlock = new DartBlock( + Lists.newArrayList(init, newFor)); + + ctx.replaceMe(replacementBlock); + } + } + + private static class LabeledForVisitor extends DartModVisitor { + DartStatement forInit = null; + + @Override + public boolean visit(DartForStatement x, DartContext ctx) { + if (x.getInit() != null) { + DartStatement init = x.getInit(); + if (init != null) { + DartStatement body = x.getBody(); + DartExpression condition = x.getCondition(); + DartExpression increment = x.getIncrement(); + + // TODO(johnlenz): Preserve source location? + DartStatement newFor = new DartForStatement( + null, condition, increment, body); + newFor.setSourceInfo(x); + ctx.replaceMe(newFor); + } + forInit = init; + } + return false; + } + + @Override + public boolean visit(DartLabel x, DartContext ctx) { + DartStatement stmt = x.getStatement(); + return (stmt instanceof DartLabel) || (stmt instanceof DartForStatement); + } + } + } + + /** + * Remove parenthesized expression nodes. This simplifies all of the rest of + * the normalizers by not requiring them to deal with this special case. + */ + private static class ParenNormalizer extends DartModVisitor { + @Override + public void endVisit(DartParenthesizedExpression x, DartContext ctx) { + ctx.replaceMe(x.getExpression()); + } + } + + /** + * Split VAR declarations so that there is only one VAR declaration per + * statement. This simplifies rewriting of VAR statements. + */ + private static class VarNormalizer extends DartModVisitor { + @Override + public void endVisit(DartVariableStatement x, DartContext ctx) { + List vars = x.getVariables(); + if (vars.size() > 1) { + for (DartVariable v : vars) { + DartVariableStatement stmt = new DartVariableStatement( + Lists.newArrayList(v), x.getTypeNode()); + stmt.setSourceInfo(v); + ctx.insertBefore(stmt); + } + ctx.removeMe(); + } + } + } + + /** + * The actual normalization. + */ + private static class NormalizerVisitor extends DartNodeTraverser { + // Collects names to avoid conflicts with synthesized variables. + private final Set usedNames = new HashSet(); + private final OptimizationStrategy optimizationStrategy; + + NormalizerVisitor(OptimizationStrategy optimizationStrategy) { + this.optimizationStrategy = optimizationStrategy; + } + + @Override + public DartNode visitClassMember(DartClassMember node) { + usedNames.clear(); + return super.visitClassMember(node); + } + + @Override + public DartNode visitForInStatement(DartForInStatement node) { + node.visitChildren(this); + + // Normalize for (var? name in expression) { ... } into: + // { + // var i = expression.iterator(); + // while (i.hasNext()) { + // var? name = i.next(); + // ... + // } + // } + List topLevelStatements = new ArrayList(); + + // Generate the call to expression.iterator(). + DartMethodInvocation iteratorCall = call(node.getIterable(), "iterator"); + + // Create and add the iterator variable to the statements. + DartVariableStatement iteratorVariable = makeTempVariable(0, iteratorCall); + topLevelStatements.add(iteratorVariable); + + // Generate the call to i.hasNext(); + DartIdentifier iterator = ref(iteratorVariable); + DartMethodInvocation hasNext = call(iterator, "hasNext"); + + // Generate the call to i.next(); + iterator = ref(iteratorVariable); + DartMethodInvocation next = call(iterator, "next"); + + DartStatement setup = normalizeForInSetup(node, next); + DartWhileStatement whileStatement = whileStmt(hasNext, setup, node.getBody()); + topLevelStatements.add(whileStatement); + + DartBlock newBlock = new DartBlock(topLevelStatements); + node.setNormalizedNode(newBlock); + return node; + } + + private DartStatement normalizeForInSetup(DartForInStatement node, DartExpression next) { + if (node.introducesVariable()) { + // Since we're going to change the variable to have an initializer expression, + // we have to create a new variable statement around it too. Make sure it has + // the right type and modifiers. + DartVariableStatement variableStatement = node.getVariableStatement(); + DartVariable oldVariable = variableStatement.getVariables().get(0); + DartVariable newVariable = new DartVariable(oldVariable.getName(), next); + newVariable.setSymbol(oldVariable.getSymbol()); + return new DartVariableStatement(Lists.newArrayList(newVariable), + variableStatement.getTypeNode(), variableStatement.getModifiers()); + } else { + return exprStmt(assign(node.getIdentifier(), next)); + } + } + + @Override + public DartExpression visitBinaryExpression(DartBinaryExpression node) { + node.visitChildren(this); + Token operator = node.getOperator(); + if (operator.isAssignmentOperator() && operator != Token.ASSIGN + && shouldNormalizeOperator(node)) { + node.setNormalizedNode(normalizeCompoundAssignment(mapAssignableOp(operator), false, + node.getArg1().getNormalizedNode(), + node.getArg2().getNormalizedNode())); + } + return node; + } + + @Override + public DartExpression visitUnaryExpression(DartUnaryExpression node) { + node.visitChildren(this); + Token operator = node.getOperator(); + if (operator.isCountOperator() && shouldNormalizeOperator(node)) { + DartExpression lhs = node.getArg().getNormalizedNode(); + DartIntegerLiteral rhs = DartIntegerLiteral.one(); + node.setNormalizedNode(normalizeCompoundAssignment(mapAssignableOp(operator), + !node.isPrefix(), lhs, rhs)); + } + return node; + } + + @Override + public DartMethodDefinition visitMethodDefinition(DartMethodDefinition node) { + super.visitMethodDefinition(node); + if (Elements.isNonFactoryConstructor(node.getSymbol())) { + normalizeParameterInitializer(node); + } + return node; + } + + @Override + public DartNode visitSwitchStatement(DartSwitchStatement node) { + node.getExpression().accept(this); + for (DartNode member : node.getMembers()) { + member.accept(this); + } + return node; + } + + // Normalize parameter initializer. + // transforms: class A { A(this.x) { } } + // into: class A { A(this.x) : this.x = x { } + private void normalizeParameterInitializer(DartMethodDefinition node) { + List nInit = new ArrayList(); + for (DartParameter param : node.getFunction().getParams()) { + FieldElement fieldElement = param.getSymbol().getParameterInitializerElement(); + if (fieldElement != null) { + DartIdentifier left = new DartIdentifier(param.getParameterName()); + left.setSymbol(fieldElement); + left.setSourceInfo(param); + Element ve = Elements.makeVariable(param.getParameterName()); + DartParameter nParam = new DartParameter(param.getName(), param.getTypeNode(), + param.getFunctionParameters(), param.getDefaultExpr(), param.getModifiers()); + nParam.setSymbol(ve); + param.setNormalizedNode(nParam); + DartIdentifier right = new DartIdentifier(param.getParameterName()); + right.setSymbol(ve); + right.setSourceInfo(param); + DartInitializer di = new DartInitializer(left, right); + di.setSourceInfo(param); + nInit.add(di); + } + } + if (!nInit.isEmpty()) { + if (!node.getInitializers().isEmpty()) { + nInit.addAll(0, node.getInitializers()); + } + DartMethodDefinition nConstructor = DartMethodDefinition.create( + node.getName(), node.getFunction(), node.getModifiers(), nInit, null); + nConstructor.setSymbol(node.getSymbol()); + nConstructor.setSourceInfo(node.getSourceInfo()); + node.setNormalizedNode(nConstructor); + } + } + + private DartExpression normalizeCompoundAssignment(Token operator, + boolean isPostfix, + DartExpression operand1, + DartExpression operand2) { + return operand1.accept(new CompoundAssignmentNormalizer(operator, isPostfix, operand2)); + } + + private String makeTempName(int i) { + String name; + do { + name = "$" + i; + i++; + } while (usedNames.contains(name)); + usedNames.add(name); + return name; + } + + private DartVariableStatement makeTempVariable(int i, DartExpression init) { + String variableName = makeTempName(i); + DartIdentifier variableIdentifier = new DartIdentifier(variableName); + DartVariable variable = new DartVariable(variableIdentifier, init); + VariableElement element = Elements.variableElement(variable, + variableName, + Modifiers.NONE); + variable.setSymbol(element); + return new DartVariableStatement(Lists.newArrayList(variable), null); + } + + private class Let { + private final List arguments; + final DartParameter[] parameters; + + Let(DartExpression... arguments) { + this.arguments = Arrays.asList(arguments); + parameters = new DartParameter[arguments.length]; + for (int i = 0; i < arguments.length; i++) { + parameters[i] = makeTempParameter(i); + } + } + + DartExpression expression() { + DartBlock body = new DartBlock(Arrays.asList(body())); + return call(makeFunctionExpression(body), arguments); + } + + private DartFunctionExpression makeFunctionExpression(DartBlock body) { + DartFunction function = new DartFunction(Arrays.asList(parameters), body , null); + DartFunctionExpression expression = new DartFunctionExpression(null, function, false); + MethodElement element = + Elements.methodFromFunctionExpression(expression, Modifiers.NONE.makeInlinable()); + expression.setSymbol(element); + for (DartParameter parameter : parameters) { + Elements.addParameter(element, parameter.getSymbol()); + } + return expression; + } + + DartExpression p(int i) { + return ref(parameters[i]); + } + + DartStatement[] body() { + return new DartStatement[0]; + } + + private DartParameter makeTempParameter(int i) { + String name = makeTempName(i); + DartIdentifier identifier = new DartIdentifier(name); + DartParameter parameter = new DartParameter(identifier, null, null, null, Modifiers.NONE); + VariableElement element = Elements.parameterElement(parameter, name, Modifiers.NONE); + parameter.setSymbol(element); + return parameter; + } + } + + private class CompoundAssignmentNormalizer extends DartNodeTraverser { + final Token operator; + final boolean isPostfix; + final DartExpression rhs; + + CompoundAssignmentNormalizer(Token operator, boolean isPostfix, + DartExpression rhs) { + this.operator = operator; + this.isPostfix = isPostfix; + this.rhs = rhs; + } + + @Override + public DartExpression visitNode(DartNode lhs) { + throw new AssertionError(lhs); + } + + @Override + public DartExpression visitIdentifier(DartIdentifier id) { + return rewriteExpression(id); + } + + private DartExpression rewriteExpression(final DartExpression lhs) { + if (!isPostfix) { + // Turns: lhs += rhs + // Into: lhs = lhs + rhs + // Turns: ++lhs + // Into: lhs = lhs + 1 + return assign(lhs, bin(operator, lhs, rhs)); + } else { + // Turns: lhs++ + // Into: function($2) { id = $2 + 1; return $2; }(lhs) + return new Let(lhs) { + @Override DartStatement[] body() { + return statements(exprStmt(assign(lhs, bin(operator, p(0), rhs))), + retrn(p(0))); + } + }.expression(); + } + } + + @Override + public DartExpression visitPropertyAccess(DartPropertyAccess access) { + Element element = access.getTargetSymbol(); + if (element != null && element.getModifiers().isStatic()) { + return rewriteExpression(access); + } + final DartIdentifier name = access.getName(); + return new RewriteAccess((DartExpression) access.getQualifier()) { + @Override + DartExpression operand1() { + return access(p(0), name); + } + }.expression(); + } + + @Override + public DartExpression visitArrayAccess(DartArrayAccess access) { + DartExpression target = access.getTarget(); + DartExpression key = access.getKey(); + // TODO(5408710): Evaluation order of target vs. key? (order passed to constructor) + return new RewriteAccess(target, key) { + @Override + DartExpression operand1() { + return arrayAccess(p(0), p(1)); + } + }.expression(); + } + + private abstract class RewriteAccess extends Let { + public RewriteAccess(DartExpression... arguments) { + super(arguments); + } + + abstract DartExpression operand1(); + + @Override + DartStatement[] body() { + final DartExpression operand1 = operand1(); + if (isPostfix) { + // Turns: this[0.0]-- + // Into: function($0, $1) { + // return function($2) { $0[$1] = $2 - 1; return $2; }($0[$1]); + // }(this, 0.0) + Let let = new Let(operand1) { + @Override DartStatement[] body() { + return statements(exprStmt(assign(operand1, bin(operator, p(0), rhs))), + retrn(p(0))); + } + }; + return statements(retrn(let.expression())); + } else { + // Turns: this[0.0] += $0 + // Into: function($3, $4) { return $3[$4] = $3[$4] + $0; }(this, 0.0) + // Turns: ++this[0.0] + // Into: function($3, $4) { return $3[$4] = $3[$4] + 1; }(this, 0.0) + return statements(retrn(assign(operand1, bin(operator, operand1, rhs)))); + } + } + } + } + + private DartStatement[] statements(DartStatement... statements) { + return statements; + } + + private DartWhileStatement whileStmt(DartExpression condition, DartStatement... statements) { + return new DartWhileStatement(condition, new DartBlock(Arrays.asList(statements))); + } + + private DartExpression call(DartFunctionExpression function, List args) { + return new DartFunctionObjectInvocation(function, args); + } + + private DartMethodInvocation call(DartExpression receiver, String name) { + return new DartMethodInvocation(receiver, + new DartIdentifier(name), + Collections.emptyList()); + } + + private boolean shouldNormalizeOperator(DartBinaryExpression node) { + return !optimizationStrategy.canSkipNormalization(node); + } + + private boolean shouldNormalizeOperator(DartUnaryExpression node) { + return !optimizationStrategy.canSkipNormalization(node); + } + + private DartArrayAccess arrayAccess(DartExpression target, DartExpression key) { + return new DartArrayAccess(target, key); + } + + private DartReturnStatement retrn(DartExpression value) { + return new DartReturnStatement(value); + } + + private DartBinaryExpression bin(Token operator, DartExpression lhs, DartExpression rhs) { + return new DartBinaryExpression(operator, lhs, rhs); + } + + private DartBinaryExpression assign(DartExpression lhs, DartExpression rhs) { + return bin(Token.ASSIGN, lhs, rhs); + } + + private DartExprStmt exprStmt(DartExpression expression) { + return new DartExprStmt(expression); + } + + private DartPropertyAccess access(DartExpression qualifier, DartIdentifier name) { + return new DartPropertyAccess(qualifier, name); + } + + private DartExpression ref(DartParameter parameter) { + DartIdentifier identifier = new DartIdentifier(parameter.getParameterName()); + identifier.setSymbol(parameter.getSymbol()); + return identifier; + } + + private DartIdentifier ref(DartVariableStatement variableStatement) { + DartVariable variable = variableStatement.getVariables().get(0); + DartIdentifier identifier = new DartIdentifier(variable.getVariableName()); + identifier.setSymbol(variable.getSymbol()); + return identifier; + } + + private Token mapAssignableOp(Token operator) { + switch (operator) { + case ASSIGN_BIT_OR: return Token.BIT_OR; + case ASSIGN_BIT_XOR: return Token.BIT_XOR; + case ASSIGN_BIT_AND: return Token.BIT_AND; + case ASSIGN_SHL: return Token.SHL; + case ASSIGN_SAR: return Token.SAR; + case ASSIGN_SHR: return Token.SHR; + case ASSIGN_ADD: return Token.ADD; + case ASSIGN_SUB: return Token.SUB; + case ASSIGN_MUL: return Token.MUL; + case ASSIGN_DIV: return Token.DIV; + case ASSIGN_MOD: return Token.MOD; + case ASSIGN_TRUNC: return Token.TRUNC; + case INC: return Token.ADD; + case DEC: return Token.SUB; + + default: + throw new InternalCompilerException("Invalid assignment operator"); + } + } + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/OptimizationStrategy.java b/compiler/java/com/google/dart/compiler/backend/js/OptimizationStrategy.java new file mode 100644 index 00000000000..69dfb6ca11b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/OptimizationStrategy.java @@ -0,0 +1,42 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartBinaryExpression; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.backend.common.TypeHeuristic.FieldKind; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.FieldElement; + +interface OptimizationStrategy { + + boolean canSkipOperatorShim(DartBinaryExpression expr); + + boolean canSkipOperatorShim(DartUnaryExpression expr); + + boolean canSkipArrayAccessShim(DartArrayAccess array, boolean isAssignee); + + FieldElement findOptimizableFieldElementFor(DartExpression expr, FieldKind fieldKind); + + Element findElementFor(DartMethodInvocation expr); + + boolean canSkipNormalization(DartBinaryExpression expr); + + boolean canSkipNormalization(DartUnaryExpression expr); + + boolean canInlineInitializers(ConstructorElement constructorElement); + + boolean canEmitOptimizedClassConstructor(ClassElement classElement); + + boolean isWhitelistedNativeField(FieldElement field, FieldKind fieldKind); + + boolean canOptimizeFunctionExpressionBind(DartFunctionExpression expr); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java b/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java new file mode 100644 index 00000000000..4399656ac06 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java @@ -0,0 +1,802 @@ +// Copyright 2011, the Dart project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartClassMember; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsParameter; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsReturn; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsThisRef; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; +import com.google.dart.compiler.type.TypeVariable; +import com.google.dart.compiler.type.Types; + +import static com.google.dart.compiler.util.AstUtil.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A helper class that contains the logic necessary for injecting runtime type references. + * + * @author johnlenz@google.com (John Lenz) + */ +public class RuntimeTypeInjector { + private final TraversalContextProvider context; + private final JsBlock globalBlock; + private final JsScope globalScope; + // Maps builtin types to Javascript types implementations. + private final Map builtinTypes; + private CoreTypeProvider typeProvider; + private final TranslationContext translationContext; + + RuntimeTypeInjector( + TraversalContextProvider context, + CoreTypeProvider typeProvider, + TranslationContext translationContext) { + this.context = context; + this.translationContext = translationContext; + JsProgram program = translationContext.getProgram(); + this.globalBlock = program.getGlobalBlock(); + this.globalScope = program.getScope(); + this.builtinTypes = makeBuiltinTypes(typeProvider); + this.typeProvider = typeProvider; + } + + private Map makeBuiltinTypes(CoreTypeProvider typeProvider) { + Map builtinTypes = Maps.newHashMap(); + builtinTypes.put(typeProvider.getBoolType().getElement(), "Boolean"); + builtinTypes.put(typeProvider.getIntType().getElement(), "Number"); + builtinTypes.put(typeProvider.getDoubleType().getElement(), "Number"); + builtinTypes.put(typeProvider.getStringType().getElement(), "String"); + return builtinTypes; + } + + /** + * Generate the code necessary to allow for runtime type checks + */ + void generateRuntimeTypeInfo(DartClass x) { + generateRuntimeTypeInfoMethods(x); + + ClassElement classElement = x.getSymbol(); + if (!classElement.isInterface()) { + injectInterfaceMarkers(classElement, x); + } + } + + private void injectInterfaceMarkers(ClassElement classElement, SourceInfo srcRef) { + JsProgram program = translationContext.getProgram(); + JsName classJsName = translationContext.getNames().getName(classElement); + for (InterfaceType iface : getAllInterfaces(classElement)) { + JsStatement assignment = (JsStatement) newAssignment( + newNameRef( + newNameRef(new JsNameRef(classJsName), "prototype"), + "$implements$" + translationContext.getMangler().mangleClassName(iface.getElement())), + program.getNumberLiteral(1)).makeStmt().setSourceRef(srcRef); + globalBlock.getStatements().add(assignment); + } + } + + private Set getAllInterfaces(ClassElement classElement) { + // TODO(johnlenz): All interfaces here should not include the super class implemented interfaces + // those are handled by the super-class definition. + Set interfaces = Sets.newLinkedHashSet(); + if (classElement.getType() == null) { + throw new InternalCompilerException("type is null on ClassElement " + classElement); + } + // A class needs to implement its own implied interface so the "is" + // implementation works properly. + interfaces.add(classElement.getType()); + + for (InterfaceType current = classElement.getType(); current != null; + current = current.getElement().getSupertype()) { + // TODO(johnlenz): Maybe use "getAllSupertypes" on the interface element instead + addAllInterfaces(interfaces, current); + } + return interfaces; + } + + private void addAllInterfaces(Set interfaces, InterfaceType t) { + interfaces.add(t); + for (InterfaceType current : t.getElement().getInterfaces()) { + addAllInterfaces(interfaces, current); + } + } + + /** + * Insert the function or method necessary to implement runtime type information + * for the provided class. + */ + private void generateRuntimeTypeInfoMethods(DartClass x) { + // 1) create static type information lookup function + generateRTTLookupMethod(x); + + // 2) create a method to fill in the type information for the class + ClassElement classElement = x.getSymbol(); + if (hasRTTImplements(classElement)) { + generateRTTImplementsMethod(x); + } + + // 3) create "addTo" method for use by classes or interfaces that inherit from this class + generateRTTAddToMethod(x); + } + + private void generateRTTLookupMethod(DartClass x) { + ClassElement classElement = x.getSymbol(); + boolean hasTypeParams = hasTypeParameters(classElement); + + // 1) create static type information construction function + // function Foo$lookupOrCreateRTT(typeargs) { + // return $createRTT(name, Foo$RTTimplements, typeargs) ; + // } + + // Build the function + JsFunction lookupFn = new JsFunction(globalScope); + lookupFn.setBody(new JsBlock()); + List body = lookupFn.getBody().getStatements(); + JsScope scope = new JsScope(globalScope, "temp"); + + JsProgram program = translationContext.getProgram(); + + JsInvocation invokeCreate = call(null, + newQualifiedNameRef("RTT.create"), getRTTClassId(classElement)); + List callArgs = invokeCreate.getArguments(); + if (hasRTTImplements(classElement)) { + callArgs.add(getRTTImplementsMethodName(classElement)); + } else if (hasTypeParams) { + // need a placeholder param if the typeArgs are needed. + callArgs.add(program.getNullLiteral()); + } + + if (hasTypeParams) { + JsName typeArgs = scope.declareName("typeArgs"); + lookupFn.getParameters().add(new JsParameter(typeArgs)); + callArgs.add(typeArgs.makeRef()); + } + + body.add(new JsReturn(invokeCreate)); + + // Finally, Add the function + JsExpression fnDecl = assign(null, + getRTTLookupMethodName(classElement), lookupFn); + globalBlock.getStatements().add(fnDecl.makeStmt()); + } + + private void generateRTTImplementsMethod(DartClass x) { + ClassElement classElement = x.getSymbol(); + + // 1) create static type information construction function + // function Foo$lookupOrCreateRTT(rtt, typeArgs) { + // + // // superclass + // FooSuper$addTo(rtt, superTypeArg1, ...); + // // interfaces + // FooInterface1$addTo(rtt, interface1TypeArg1, ...); + // + // // fill in derived types + // rtt.derivedTypes = [ + // FirstRef$lookupOrCreateRTT(typearg1, ...), + // ... + // ] + // } + + boolean hasTypeParams = classElement.getTypeParameters().size() > 0; + + // Build the function + JsFunction implementsFn = new JsFunction(globalScope); + implementsFn.setBody(new JsBlock()); + List body = implementsFn.getBody().getStatements(); + JsScope scope = new JsScope(globalScope, "temp"); + + JsName rtt = scope.declareName("rtt"); + implementsFn.getParameters().add(new JsParameter(rtt)); + JsName typeArgs = null; + if (hasTypeParams) { + typeArgs = scope.declareName("typeArgs"); + implementsFn.getParameters().add(new JsParameter(typeArgs)); + } + + JsInvocation callAddTo = newInvocation(getRTTAddToMethodName(classElement), rtt.makeRef()); + if (hasTypeParams) { + typeArgs = scope.declareName("typeArgs"); + callAddTo.getArguments().add(typeArgs.makeRef()); + } + body.add(callAddTo.makeStmt()); + + // Add the derived types + + if (hasTypeParams) { + // Populated the list of derived types + JsArrayLiteral derivedTypesArray = new JsArrayLiteral(); + // TODO(johnlenz): Add needed types here. + JsExpression addDerivedTypes = assign(null, + nameref(null, rtt.makeRef(), "derivedTypes"), + derivedTypesArray); + body.add(addDerivedTypes.makeStmt()); + } + + // Finally, Add the function + JsExpression fnDecl = assign(null, + getRTTImplementsMethodName(classElement), implementsFn); + globalBlock.getStatements().add(fnDecl.makeStmt()); + } + + private void generateRTTAddToMethod(DartClass x) { + ClassElement classElement = x.getSymbol(); + + // 2) create "addTo" method + // Foo$Type$addTo(target, typeargs) { + // var rtt = Foo$lookupOrCreateRTT(typeargs) + // target.implementedTypes[rtt.classkey] = rtt; + // } + + // Build the function + JsFunction addToFn = new JsFunction(globalScope); + addToFn.setBody(new JsBlock()); + JsScope scope = new JsScope(globalScope, "temp"); + + JsName targetType = scope.declareName("target"); + addToFn.getParameters().add(new JsParameter(targetType)); + + // Get the RTT info object + JsName rtt = scope.declareName("rtt"); + List body = addToFn.getBody().getStatements(); + JsInvocation callLookup = newInvocation( + getRTTLookupMethodName(classElement)); + + if (hasTypeParameters(classElement)) { + JsName typeArgs = scope.declareName("typeArgs"); + addToFn.getParameters().add(new JsParameter(typeArgs)); + callLookup.getArguments().add(new JsNameRef(typeArgs)); + } + + JsStatement rttLookup = newVar((SourceInfo)null, rtt, callLookup); + body.add(rttLookup); + + // store it. + JsExpression addToTypes = newAssignment( + new JsArrayAccess( + newNameRef(targetType.makeRef(), "implementedTypes"), + newNameRef(rtt.makeRef(), "classKey")), + rtt.makeRef()); + body.add(addToTypes.makeStmt()); + + InterfaceType superType = classElement.getSupertype(); + if (superType != null && !superType.getElement().isObject()) { + ClassElement interfaceElement = superType.getElement(); + JsInvocation callAddTo = newInvocation( + getRTTAddToMethodName(interfaceElement), targetType.makeRef()); + if (hasTypeParameters(interfaceElement) && !superType.hasDynamicTypeArgs()) { + JsArrayLiteral superTypeArgs = new JsArrayLiteral(); + List typeParams = classElement.getTypeParameters(); + for (Type arg : superType.getArguments()) { + superTypeArgs.getExpressions().add( + buildTypeLookupExpression(arg, typeParams, + nameref(null, targetType.makeRef(), "typeArgs"))); + } + callAddTo.getArguments().add(superTypeArgs); + } + body.add(callAddTo.makeStmt()); + } + + // Add the interfaces + + for (InterfaceType interfaceType : classElement.getInterfaces() ) { + ClassElement interfaceElement = interfaceType.getElement(); + JsInvocation callAddTo = call(null, + getRTTAddToMethodName(interfaceElement), targetType.makeRef()); + if (hasTypeParameters(interfaceElement) && !interfaceType.hasDynamicTypeArgs()) { + JsArrayLiteral interfaceTypeArgs = new JsArrayLiteral(); + List typeParams = classElement.getTypeParameters(); + for (Type arg : interfaceType.getArguments()) { + interfaceTypeArgs.getExpressions().add( + buildTypeLookupExpression(arg, typeParams, + nameref(null, targetType.makeRef(), "typeArgs"))); + } + callAddTo.getArguments().add(interfaceTypeArgs); + } + body.add(callAddTo.makeStmt()); + } + + // Add the function statement + JsExpression fnDecl = newAssignment( + getRTTAddToMethodName(classElement), addToFn); + globalBlock.getStatements().add(fnDecl.makeStmt()); + } + + private JsExpression getRTTClassId(ClassElement classElement) { + JsName classJsName = translationContext.getNames().getName(classElement); + JsProgram program = translationContext.getProgram(); + return program.getStringLiteral(classJsName.getShortIdent()); + } + + private JsNameRef getRTTLookupMethodName(ClassElement classElement) { + return nameref(null, translationContext.getNames().getName(classElement), "$lookupRTT"); + } + + private JsNameRef getRTTImplementsMethodName(ClassElement classElement) { + return nameref(null, translationContext.getNames().getName(classElement), "$RTTimplements"); + } + + private JsNameRef getRTTAddToMethodName(ClassElement classElement) { + return nameref(null, translationContext.getNames().getName(classElement), "$addTo"); + } + + /** + * Build a class relative type arguments expression + * @param classElement The class whose type arguments to refer to. + */ + private JsExpression buildTypeArgsReference(ClassElement classElement) { + // build: $getTypeArgsFor(this, 'class') + // Here build a reference to the type parameter for this class instance, this needs + // be looked up on a per-class basis. + return call(null, + newQualifiedNameRef( + "RTT.getTypeArgsFor"), new JsThisRef(), getRTTClassId(classElement)); + } + + private JsExpression buildFactoryTypeInfoReference() { + // There is no inheritence involved with generic factory methods, + // so we simply use a hard reference to the type info parameter to the + // factory. + return nameref(null, "$typeArgs"); + } + + /** + * @return a JsArrayLiteral listing the type arguments for the interface instance. + */ + private JsExpression buildTypeArgs( + InterfaceType instanceType, + List listTypeVars, + JsExpression contextTypeArgs) { + ClassElement classElement = instanceType.getElement(); + if (!hasTypeParameters(classElement)) { + return null; + } + + if (instanceType.hasDynamicTypeArgs()) { + JsProgram program = translationContext.getProgram(); + return program.getNullLiteral(); + } + + JsArrayLiteral arr = new JsArrayLiteral(); + assert instanceType.getArguments().size() > 0; + for (Type type : instanceType.getArguments()) { + JsExpression typeExpr = buildTypeLookupExpression( + type, listTypeVars, contextTypeArgs); + arr.getExpressions().add(typeExpr); + } + + return arr; + } + + /** + * @return a JsArrayLiteral listing the type arguments for the interface instance. + */ + private JsExpression buildTypeArgsForFactory( + FunctionType functionType, + InterfaceType instanceType, + List listTypeVars, + JsExpression contextTypeArgs) { + if (functionType.getTypeVariables().size() == 0) { + return null; + } + + if (instanceType.hasDynamicTypeArgs()) { + return translationContext.getProgram().getNullLiteral(); + } + + JsArrayLiteral arr = new JsArrayLiteral(); + for (Type type : instanceType.getArguments()) { + JsExpression typeExpr = buildTypeLookupExpression( + type, listTypeVars, contextTypeArgs); + arr.getExpressions().add(typeExpr); + } + + return arr; + } + + /** + * @return an expression for looking up the RTT information for the given RAW type. + */ + private JsExpression generateRawRTTLookup(ClassElement classElement) { + JsInvocation invokeLookup = call(null, getRTTLookupMethodName(classElement)); + return invokeLookup; + } + + private JsExpression generateRTTLookup( + InterfaceType instanceType, ClassElement contextClassElement) { + return generateRTTLookup(instanceType.getElement(), instanceType, contextClassElement); + } + + private JsExpression generateRTTLookup( + ClassElement classElement, InterfaceType instanceType, ClassElement contextClassElement) { + JsInvocation invokeLookup = call(null, getRTTLookupMethodName(classElement)); + if (hasTypeParameters(instanceType.getElement()) && !instanceType.hasDynamicTypeArgs()) { + JsExpression typeArgs = generateTypeArgsArray(instanceType, contextClassElement); + assert typeArgs != null; + invokeLookup.getArguments().add(typeArgs); + } + return invokeLookup; + } + + private JsExpression generateTypeArgsArray( + InterfaceType instanceType, ClassElement contextClassElement) { + JsExpression typeArgs; + if (inFactoryOrStatic(contextClassElement)) { + if (inFactory()) { + // When building a type list in a static context like a factory, type variables are + // resolved from the type parameters to the static method. + DartClassMember member = context.getCurrentClassMember(); + DartMethodDefinition containingMethod = (DartMethodDefinition)member; + ConstructorElement contextElement = (ConstructorElement)containingMethod.getSymbol(); + typeArgs = buildTypeArgs( + instanceType, + ((FunctionType)contextElement.getType()).getTypeVariables(), + buildFactoryTypeInfoReference()); + } else { + typeArgs = buildTypeArgs(instanceType, null, null); + } + } else { + // Build type args in a class context: + // When building a type list in a class instance, type variables are + // resolved from the runtime type information on the instance of the object. + JsExpression typeArgContextExpr = buildTypeArgsReference(contextClassElement); + typeArgs = buildTypeArgs( + instanceType, + contextClassElement.getTypeParameters(), + typeArgContextExpr); + } + return typeArgs; + } + + private JsExpression generateTypeArgsArrayForFactory(FunctionType functionType, + InterfaceType instanceType, + ClassElement contextClassElement) { + JsExpression typeArgs; + if (inFactoryOrStatic(contextClassElement)) { + if (inFactory()) { + // When building a type list in a static context like a factory, type + // variables are + // resolved from the type parameters to the static method. + DartClassMember member = context.getCurrentClassMember(); + DartMethodDefinition containingMethod = (DartMethodDefinition) member; + ConstructorElement contextElement = (ConstructorElement) containingMethod + .getSymbol(); + typeArgs = buildTypeArgsForFactory(functionType, instanceType, + ((FunctionType) contextElement.getType()).getTypeVariables(), + buildFactoryTypeInfoReference()); + } else { + typeArgs = buildTypeArgsForFactory(functionType, instanceType, null, null); + } + } else { + // Build type args in a class context: + // When building a type list in a class instance, type variables are + // resolved from the runtime type information on the instance of the + // object. + JsExpression typeArgContextExpr = buildTypeArgsReference(contextClassElement); + typeArgs = buildTypeArgsForFactory(functionType, instanceType, contextClassElement + .getTypeParameters(), typeArgContextExpr); + } + return typeArgs; + } + + /** + * @param classElement + * @return Whether the class has type parameters. + */ + private boolean hasTypeParameters(ClassElement classElement) { + return classElement.getTypeParameters().size() > 0; + } + + private boolean hasRTTImplements(ClassElement classElement) { + InterfaceType superType = classElement.getSupertype(); + return ((superType != null + && !superType.getElement().isObject()) + || !classElement.getInterfaces().isEmpty()); + } + + /** + * @return js The expression used to lookup the RTT for the given type. + */ + private JsExpression buildTypeLookupExpression( + Type type, List list, JsExpression contextTypeArgs) { + switch (TypeKind.of(type)) { + case INTERFACE: + InterfaceType interfaceType = (InterfaceType) type; + JsInvocation callLookup = call(null, + getRTTLookupMethodName(interfaceType.getElement())); + if (hasTypeParameters(interfaceType.getElement()) && !interfaceType.hasDynamicTypeArgs()) { + JsArrayLiteral typeArgs = new JsArrayLiteral(); + for (Type arg : interfaceType.getArguments()) { + typeArgs.getExpressions().add(buildTypeLookupExpression(arg, list, contextTypeArgs)); + } + callLookup.getArguments().add(typeArgs); + } + return callLookup; + + case FUNCTION_ALIAS: + // TODO(johnlenz): implement this + return newQualifiedNameRef("RTT.placeholderType"); + + case VARIABLE: + TypeVariable var = (TypeVariable)type; + JsProgram program = translationContext.getProgram(); + int varIndex = 0; + for (Type t : list) { + if (t.equals(type)) { + return call(null, newQualifiedNameRef("RTT.getTypeArg"), + Cloner.clone(contextTypeArgs), + program.getNumberLiteral(varIndex)); + } + varIndex++; + } + throw new AssertionError("unresolved type variable:" + var); + + default: + throw new AssertionError("unexpected type kind:" + type.getKind()); + } + } + + /** + * Add the appropriate code to a class's constructing factory, if necessary. + */ + void maybeAddClassRuntimeTypeToConstructor( + ClassElement classElement, JsFunction factory, JsExpression thisRef) { + // TODO(johnlenz):in optimized mode, only add this where it is needed. + JsScope factoryScope = factory.getScope(); + JsExpression typeInfo; + if (hasTypeParameters(classElement)) { + JsName typeinfoParameter = factoryScope.declareName("$rtt"); + factory.getParameters().add(0, new JsParameter(typeinfoParameter)); + typeInfo = typeinfoParameter.makeRef(); + } else { + // TODO(johnlenz): this is a constant value, it only needs to be evaluated once. + typeInfo = generateRawRTTLookup(classElement); + } + JsExpression setTypeInfo = assign(null, + nameref(null, Cloner.clone(thisRef), "$typeInfo"), typeInfo); + factory.getBody().getStatements().add(0, setTypeInfo.makeStmt()); + } + + /** + * Add a type arguments parameter to a factory method, if necessary. + */ + void maybeAddTypeParameterToFactory(DartMethodDefinition method, JsFunction factory) { + // TODO(johnlenz):in optimized mode, only add this where it is needed. + if (isParameterizedFactoryMethod(method)) { + JsScope scope = factory.getScope(); + JsName typeArgs = scope.declareName("$typeArgs"); + factory.getParameters().add(0, new JsParameter(typeArgs)); + } + } + + private boolean isParameterizedFactoryMethod(DartMethodDefinition method) { + assert method.getModifiers().isFactory(); + return !method.getTypeParameters().isEmpty(); + } + + /** + * Add a runtime type information to a array literal, if necessary. + */ + JsExpression maybeAddRuntimeTypeForArrayLiteral(ClassElement enclosingClass, + DartArrayLiteral x, JsArrayLiteral jsArray) { + // TODO(johnlenz):in optimized mode, only add this where it is needed. + InterfaceType instanceType = typeProvider.getArrayLiteralType( + x.getType().getArguments().get(0)); + JsExpression rtt = generateRTTLookup(instanceType, enclosingClass); + // Bind the runtime type information to the native array expression + return call(x, newQualifiedNameRef("RTT.setTypeInfo"), jsArray, rtt); + } + + /** + * Add a runtime type information to a map literal, if necessary. + */ + void maybeAddRuntimeTypeToMapLiteralConstructor(ClassElement enclosingClass, + DartMapLiteral x, JsInvocation invoke) { + // TODO(johnlenz):in optimized mode, only add this where it is needed. + + // Fixup the type for map literal type to be the implementing type. + List typeArgs = x.getType().getArguments(); + InterfaceType instanceType = typeProvider.getMapLiteralType( + typeArgs.get(0), typeArgs.get(1)); + JsExpression rtt = generateRTTLookup(instanceType, enclosingClass); + invoke.getArguments().add(rtt); + } + + /** + * Add runtime type information to a "new" expression, if necessary. + */ + void mayAddRuntimeTypeToConstrutorOrFactoryCall( + ClassElement enclosingClass, DartNewExpression x, JsInvocation invoke) { + // TODO(johnlenz):in optimized mode, only add this where it is needed. + + InterfaceType instanceType = Types.constructorType(x); + if (instanceType == null) { + // TODO(johnlenz): HackHack. Currently the "new FallThroughError" injected by the + // Normalizer does not have the instance type attached. But in this + // case we know it does not have any type parameters. + // reportError(x.getParent().getParent(), new AssertionError("missing type information")); + assert typeProvider.getFallThroughError().getElement().lookupConstructor("").equals( + x.getSymbol()); + } else if (constructorHasTypeParameters(x)) { + ConstructorElement constructor = x.getSymbol(); + ClassElement containingClassElement = enclosingClass; + if (constructor.getModifiers().isFactory()) { + // We are calling a factory, this is either in a class + FunctionType functionType = (FunctionType) constructor.getType(); + JsExpression typeArgs = generateTypeArgsArrayForFactory( + functionType, instanceType, containingClassElement); + assert typeArgs != null; + invoke.getArguments().add(0, typeArgs); + } else { + ClassElement constructorClassElement = constructor.getConstructorType(); + invoke.getArguments().add(0, + generateRTTLookup(constructorClassElement, instanceType, containingClassElement)); + } + } + } + + private boolean constructorHasTypeParameters(DartNewExpression x) { + ConstructorElement element = x.getSymbol(); + if (element.getModifiers().isFactory()) { + return isParameterizedFactoryMethod((DartMethodDefinition)element.getNode()); + } else { + InterfaceType instanceType = Types.constructorType(x); + return hasTypeParameters(instanceType.getElement()); + } + } + + /** + * @return an expression that implements a runtime "is" operation + */ + JsExpression generateInstanceOfComparison( + ClassElement enclosingClass, + JsExpression lhs, + DartTypeNode typeNode, + SourceInfo src) { + ClassElement currentClass = enclosingClass; + Type type = typeNode.getType(); + switch (TypeKind.of(type)) { + case INTERFACE: + InterfaceType interfaceType = (InterfaceType) type; + if (hasTypeParameters(interfaceType.getElement()) + && !interfaceType.hasDynamicTypeArgs()) { + return generateRefiedInterfaceTypeComparison(lhs, interfaceType, currentClass, src); + } else { + // TODO(johnlenz): special case "is Object"? + return generateRawInterfaceTypeComparison(lhs, typeNode, src); + } + case VARIABLE: + TypeVariable typeVar = (TypeVariable) type; + return generateRefiedTypeVariableComparison(lhs, typeVar, currentClass, src); + case DYNAMIC: + JsProgram program = translationContext.getProgram(); + return program.getTrueLiteral(); + default: + throw new IllegalStateException("unexpected"); + } + } + + private JsExpression generateRefiedInterfaceTypeComparison( + JsExpression lhs, InterfaceType type, ClassElement contextClassElement, SourceInfo src) { + JsExpression rtt = generateRTTLookup(type, contextClassElement); + return call(src, nameref(src, rtt, "implementedBy"), lhs); + } + + private JsExpression generateRefiedTypeVariableComparison( + JsExpression lhs, TypeVariable type, ClassElement contextClassElement, SourceInfo src) { + JsExpression rttContext; + if (!inFactory()) { + // build: this.typeinfo.implementedTypes['class'].typeArgs; + rttContext = buildTypeArgsReference(contextClassElement); + } else { + // build: $typeArgs + rttContext = buildFactoryTypeInfoReference(); + } + // rtt = rttContext.typeArgs[x] + JsExpression rtt = buildTypeLookupExpression(type, contextClassElement.getTypeParameters(), rttContext); + return call(src, nameref(src, rtt, "implementedBy"), lhs); + } + + private JsExpression generateRawInterfaceTypeComparison( + JsExpression lhs, DartTypeNode typeNode, + SourceInfo src) { + ClassElement element = (ClassElement) typeNode.getType().getElement(); + if (element.equals(typeProvider.getObjectType().getElement())) { + // Everything is an object, including null + return this.translationContext.getProgram().getTrueLiteral(); + } + String builtin = builtinTypes.get(element); + if (builtin != null) { + return call(src, + nameref(src, + nameref(src, builtin), "$instanceOf"), + lhs); + } + + // Due to implementing implied interfaces of classes, we always have to + // use $implements$ rather than using JS instanceof for classes. + + // Inject: !!(tmp = target, tmp && tmp.$implements$type) + JsProgram program = translationContext.getProgram(); + JsName tmp = context.createTemporary(); + String mangledClass = translationContext.getMangler().mangleClassName(element); + return not(src, + not(src, + comma(src, + assign(src, tmp.makeRef(), lhs), + and(src, + neq(src, tmp.makeRef().setSourceRef(src), program.getNullLiteral()), + nameref(src, + tmp, + "$implements$" + mangledClass))))); + } + + private boolean inFactory() { + DartClassMember member = context.getCurrentClassMember(); + return member != null && member.getModifiers().isFactory(); + } + + private boolean inFactoryOrStatic(ClassElement containingClass) { + DartClassMember member = context.getCurrentClassMember(); + return containingClass == null + || containingClass.getKind() != ElementKind.CLASS + || member == null + || member.getModifiers().isFactory() + || member.getModifiers().isStatic(); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ScopeRootInfo.java b/compiler/java/com/google/dart/compiler/backend/js/ScopeRootInfo.java new file mode 100644 index 00000000000..b7e0fbb33b4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ScopeRootInfo.java @@ -0,0 +1,528 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.dart.compiler.ast.DartBlock; +import com.google.dart.compiler.ast.DartCatchBlock; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartClassMember; +import com.google.dart.compiler.ast.DartContext; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartThisExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.resolver.Element; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Information relating to a methods scope, symbols, and closures + */ +class ScopeRootInfo { + /** + * Defines a relationship between a DartSymbol and a scope. + */ + static class DartScope { + /** + * A simple class for storing information about a symbol, specifically + * whether the symbol is referenced by a closure. + */ + static class DartSymbolInfo { + private final DartScope owningScope; + private boolean referencedFromClosure = false; + + public DartSymbolInfo(DartScope owningScope) { + this.owningScope = owningScope; + } + + public DartScope getOwningScope() { + return owningScope; + } + + public boolean isReferencedFromClosure() { + return referencedFromClosure; + } + + public void setReferencedFromClosure(boolean referencedFromClosure) { + this.referencedFromClosure = referencedFromClosure; + } + } + + private final DartScope parent; + private Map symbols = Maps.newLinkedHashMap(); + private Map jsAliasNames = Maps.newHashMap(); + // The scope's depth from the initial method definition scope. + private final int depth; + + public DartScope(DartScope parent) { + this.parent = parent; + this.depth = (parent != null) ? parent.getDepth() + 1 : 0; + } + + public void declare(Symbol symbol) { + symbols.put(symbol, new DartSymbolInfo(this)); + } + + public int getDepth() { + return depth; + } + + public DartScope getParent() { + return parent; + } + + public DartScope findSymbolScope(Symbol symbol) { + DartScope current = this; + while (current != null) { + DartScope.DartSymbolInfo info = current.getSymbolInfo(symbol); + if (info != null) { + return current; + } + current = current.getParent(); + } + return null; + } + + public DartScope.DartSymbolInfo getSymbolInfo(Symbol symbol) { + return this.symbols.get(symbol); + } + + public Map getSymbols() { + return symbols; + } + + public boolean definesClosureReferencedSymbols() { + for (DartScope.DartSymbolInfo symbol : symbols.values()) { + if (symbol.isReferencedFromClosure()) { + return true; + } + } + return false; + } + + private String getScopeAliasName() { + return "dartc_scp$" + depth; + } + + /** + * Returns the alias-object-JsName of this DartScope in the given JsScope. If none exists yet, + * creates a new one. + * + * @param jsScope + * @return the JsName representing the alias object for this DartScope. + */ + public JsName getAliasForJsScope(JsScope jsScope) { + JsName result = findAliasForJsScope(jsScope); + if (result == null) { + result = jsScope.declareFreshName(getScopeAliasName()); + jsAliasNames.put(jsScope, result); + } + return result; + } + + /** + * Returns the alias-object-JsName of this DartScope in the given JsScope. If none exists yet, + * null is returned. + * + * @param jsScope + * @return the JsName representing the alias object for this DartScope. + */ + public JsName findAliasForJsScope(JsScope jsScope) { + return jsAliasNames.get(jsScope); + } + } + + /** + * A simple class to keep track of the scope referenced by a closure, + * also whether the closure references "this". + */ + public static class ClosureInfo { + final Set referencedScopes = Sets.newHashSet(); + + boolean referencesThis = false; + + public List getSortedReferencedScopeList() { + ArrayList sortedScopes = Lists.newArrayList( + referencedScopes); + Collections.sort(sortedScopes, new Comparator(){ + @Override + public int compare(DartScope s1, DartScope s2) { + return s1.getDepth() - s2.getDepth(); + }}); + return sortedScopes; + } + } + + /** + * A generic helper class for visiting DartScope definitions. + */ + static private abstract class DartScopesVisitor extends NormalizedVisitor { + + // A place to store the constructor initializer list to the initializers + // can be visited within the scope of the constructor's parameters. + private List pendingConstructorInitList = null; + + @Override + public boolean visit(DartUnit x, DartContext ctx) { + return enterScope(x, ctx); + } + + @Override + public boolean visit(DartClass x, DartContext ctx) { + return enterScope(x, ctx); + } + + @Override + public boolean visit(DartMethodDefinition x, DartContext ctx) { + this.pendingConstructorInitList = x.getInitializers(); + accept(x.getFunction()); + return false; + } + + @Override + public void endVisit(DartMethodDefinition x, DartContext ctx) { + assert this.pendingConstructorInitList == null; + } + + @Override + public boolean visit(DartFunctionExpression x, DartContext ctx) { + // For function statements, the function's name belong in the outer scope. + // For function expressions, it is part of the function's own scope. + if (!x.isStatement()) { + return enterScope(x, ctx); + } + return true; + } + + @Override + public boolean visit(DartFunction x, DartContext ctx) { + boolean enter = enterScope(x, ctx); + if (enter) { + // Save and clear the cached init lists before processing + // any function as default parameters or in init list. + List inits = pendingConstructorInitList; + pendingConstructorInitList = null; + acceptList(x.getParams()); + if (inits != null) { + acceptList(inits); + } + if (x.getBody() != null) { + accept(x.getBody()); + } + } + return false; + } + + @Override + public boolean visit(DartBlock x, DartContext ctx) { + return enterScope(x, ctx); + } + + @Override + public boolean visit(DartCatchBlock x, DartContext ctx) { + return enterScope(x, ctx); + } + + @Override + public boolean visit(DartForInStatement x, DartContext ctx) { + return enterScope(x, ctx); + } + + @Override + public boolean visit(DartForStatement x, DartContext ctx) { + return enterScope(x, ctx); + } + + @Override + public void endVisit(DartUnit x, DartContext ctx) { + exitScope(x, ctx); + } + + @Override + public void endVisit(DartClass x, DartContext ctx) { + exitScope(x, ctx); + } + + @Override + public void endVisit(DartFunctionExpression x, DartContext ctx) { + if (!x.isStatement()) { + exitScope(x, ctx); + } + } + + @Override + public void endVisit(DartFunction x, DartContext ctx) { + exitScope(x, ctx); + } + + @Override + public void endVisit(DartBlock x, DartContext ctx) { + exitScope(x, ctx); + } + + @Override + public void endVisit(DartCatchBlock x, DartContext ctx) { + exitScope(x, ctx); + } + + @Override + public void endVisit(DartForStatement x, DartContext ctx) { + exitScope(x, ctx); + } + + abstract boolean enterScope(DartNode x, DartContext ctx); + abstract void exitScope(DartNode x, DartContext ctx); + } + + /** + * Build a set of ClosureInfo objects for the method, + * find and mark any variable referenced by a closure. + */ + private static class ClosureRefenceMapBuilder extends ScopeRootInfo.DartScopesVisitor { + private final Map scopes; + private Deque scopeStack = Lists.newLinkedList(); + private final Map closures = Maps.newHashMap(); + private Deque closureStack = Lists.newLinkedList(); + + ClosureRefenceMapBuilder(Map scopes) { + this.scopes = scopes; + } + + @Override + boolean enterScope(DartNode x, DartContext ctx) { + scopeStack.push(scopes.get(x)); + return true; + } + + @Override + void exitScope(DartNode x, DartContext ctx) { + scopeStack.pop(); + } + + @Override + public boolean visit(DartFunctionObjectInvocation x, DartContext ctx) { + DartExpression target = x.getTarget(); + if (target instanceof DartFunctionExpression) { + DartFunctionExpression functionExpression = (DartFunctionExpression) target; + if (functionExpression.getSymbol().getModifiers().isInlinable()) { + acceptList(x.getArgs()); + return traverseFunction(functionExpression.getFunction(), ctx); + } + } + return super.visit(x, ctx); + } + + // Inlined from DartFunction#traverse(DartVisitor, DartContext). + private boolean traverseFunction(DartFunction function, DartContext ctx) { + for (DartParameter parameter : function.getParams()) { + doTraverse(parameter, ctx); + } + if (function.getBody() != null) { + doTraverse(function.getBody(), ctx); + } + // Ignore the return type. + return false; + } + + @Override + public boolean visit(DartFunction x, DartContext ctx) { + this.closures.put(x, new ClosureInfo()); + this.closureStack.push(x); + return super.visit(x, ctx); + } + + @Override + public void endVisit(DartFunction x, DartContext ctx) { + closureStack.pop(); + super.endVisit(x, ctx); + } + + @Override + public void endVisit(DartIdentifier x, DartContext ctx) { + processSymbol(x.getTargetSymbol()); + super.endVisit(x, ctx); + } + + @Override + public void endVisit(DartThisExpression x, DartContext ctx) { + for (DartFunction closure : closureStack) { + ScopeRootInfo.ClosureInfo info = closures.get(closure); + info.referencesThis = true; + } + } + + private void processSymbol(Symbol targetSymbol) { + if (targetSymbol != null) { + DartNode node = targetSymbol.getNode(); + if (node instanceof DartClassMember) { + // Special case: implicit instance/static member references. + DartClassMember member = (DartClassMember) node; + if (!member.getModifiers().isStatic()) { + // A member reference implies a reference to the current "this" + // object, the closure will need to pass it through. + for (DartFunction closure : closureStack) { + ScopeRootInfo.ClosureInfo info = closures.get(closure); + info.referencesThis = true; + } + } + } + + if (closureStack.size() > 0) { + DartScope currentScope = this.scopeStack.peek(); + DartScope symbolScope = currentScope.findSymbolScope(targetSymbol); + if (symbolScope != null) { + boolean referencedFromClosure = false; + for (DartFunction closure : closureStack) { + DartScope closureScope = scopes.get(closure); + // For each of the closures, determine if the value is defined + // outside outside the current closure, if so record its use. + if (symbolScope.getDepth() < closureScope.getDepth()) { + ScopeRootInfo.ClosureInfo info = closures.get(closure); + info.referencedScopes.add(symbolScope); + referencedFromClosure = true; + } + } + if (referencedFromClosure) { + symbolScope.getSymbolInfo(targetSymbol) + .setReferencedFromClosure(true); + } + } + } + } + } + } + + /** + * Build the DartScope objects for a method. + */ + static private class MethodScopeMapBuilder extends ScopeRootInfo.DartScopesVisitor { + private Map scopes = Maps.newLinkedHashMap(); + private Deque scopeStack = Lists.newLinkedList(); + + @Override + boolean enterScope(DartNode x, DartContext ctx) { + scopeStack.push(new DartScope(scopeStack.peek())); + scopes.put(x, scopeStack.peek()); + return true; + } + + @Override + void exitScope(DartNode x, DartContext ctx) { + scopeStack.pop(); + } + + // TODO(johnlenz): Handle catch exception declarations. + + @Override + public void endVisit(DartParameter x, DartContext ctx) { + DartScope currentScope = scopeStack.peek(); + currentScope.declare(x.getSymbol()); + super.endVisit(x, ctx); + } + + @Override + public void endVisit(DartVariable x, DartContext ctx) { + DartScope currentScope = scopeStack.peek(); + currentScope.declare(x.getSymbol()); + super.endVisit(x, ctx); + } + + @Override + public boolean visit(DartFunctionExpression x, DartContext ctx) { + DartScope currentScope = scopeStack.peek(); + boolean visit = super.visit(x, ctx); + if (!x.isStatement()) { + // Declare the symbol in the new scope + currentScope = scopeStack.peek(); + } + currentScope.declare(x.getSymbol()); + return visit; + } + } + + private final DartClassMember classMember; + private final Map symbols = Maps.newHashMap(); + private final Map closures; + private final Map scopes; + private int closureIds = 0; + + static ScopeRootInfo makeScopeInfo(DartMethodDefinition x) { + return makeScopeInfoImpl(x); + } + + static ScopeRootInfo makeScopeInfo(DartField x) { + return makeScopeInfoImpl(x); + } + + private static ScopeRootInfo makeScopeInfoImpl(DartClassMember x) { + ScopeRootInfo.MethodScopeMapBuilder scopeBuilder = new MethodScopeMapBuilder(); + scopeBuilder.accept(x); + ScopeRootInfo.ClosureRefenceMapBuilder closureBuilder = new ClosureRefenceMapBuilder( + scopeBuilder.scopes); + closureBuilder.accept(x); + return new ScopeRootInfo(x, scopeBuilder.scopes, closureBuilder.closures); + } + + ScopeRootInfo( + DartClassMember x, Map scopes, + Map closures) { + this.classMember = x; + this.closures = closures; + this.scopes = scopes; + for (DartScope scope : scopes.values()) { + this.symbols.putAll(scope.getSymbols()); + } + } + + Element getContainingElement() { + return classMember.getSymbol(); + } + + DartClassMember getContainingClassMember() { + return classMember; + } + + DartScope.DartSymbolInfo getSymbolInfo(Symbol targetSymbol) { + return symbols.get(targetSymbol); + } + + public ScopeRootInfo.ClosureInfo getClosureInfo(DartFunction x) { + return closures.get(x); + } + + public DartScope getScope(DartNode x) { + return scopes.get(x); + } + + /** + * @return A name for a closure unique within this method. + */ + public String getNextClosureName() { + return "c" + closureIds++; + } +} \ No newline at end of file diff --git a/compiler/java/com/google/dart/compiler/backend/js/TranslationContext.java b/compiler/java/com/google/dart/compiler/backend/js/TranslationContext.java new file mode 100644 index 00000000000..19fe3af30d4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/TranslationContext.java @@ -0,0 +1,62 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.LibraryElement; + +import java.util.HashMap; +import java.util.Map; + +/** + * Information generated by {@link GenerateNamesAndScopes} and consumed by + * {@link GenerateJavascriptAST}. + */ +public class TranslationContext { + private final DartMangler mangler; + private final Map memberScopes = new HashMap(); + private final Map methods = new HashMap(); + private final JsNameProvider names; + private final JsProgram program; + + private TranslationContext(JsProgram program, DartMangler mangler) { + this.program = program; + this.mangler = mangler; + this.names = new JsNameProvider(program, mangler); + } + + public DartMangler getMangler() { + return mangler; + } + + public Map getMemberScopes() { + return memberScopes; + } + + public Map getMethods() { + return methods; + } + + public JsNameProvider getNames() { + return names; + } + + public JsProgram getProgram() { + return program; + } + + public static TranslationContext createContext(DartUnit unit, JsProgram program, + DartMangler mangler) { + TranslationContext translationContext = new TranslationContext(program, mangler); + LibraryElement unitLibrary = unit.getLibrary().getElement(); + new GenerateNamesAndScopes(translationContext, unitLibrary).accept(unit); + return translationContext; + } +} \ No newline at end of file diff --git a/compiler/java/com/google/dart/compiler/backend/js/TraversalContextProvider.java b/compiler/java/com/google/dart/compiler/backend/js/TraversalContextProvider.java new file mode 100644 index 00000000000..dca7a06109a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/TraversalContextProvider.java @@ -0,0 +1,40 @@ +// Copyright 2011, the Dart project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.ast.DartClassMember; +import com.google.dart.compiler.backend.js.ast.JsName; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public interface TraversalContextProvider { + DartClassMember getCurrentClassMember(); + + JsName createTemporary(); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/UncheckedJsParserException.java b/compiler/java/com/google/dart/compiler/backend/js/UncheckedJsParserException.java new file mode 100644 index 00000000000..c2db8120537 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/UncheckedJsParserException.java @@ -0,0 +1,21 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +/** + * An unchecked wrapper exception to interop with Rhino. + */ +class UncheckedJsParserException extends RuntimeException { + + private final JsParserException parserException; + + public UncheckedJsParserException(JsParserException parserException) { + this.parserException = parserException; + } + + public JsParserException getParserException() { + return parserException; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java b/compiler/java/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java new file mode 100644 index 00000000000..8f4364d94f2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * An interface that describes the boolean evaluation of an expression. + */ +public interface CanBooleanEval { + + boolean isBooleanFalse(); + + boolean isBooleanTrue(); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/HasArguments.java b/compiler/java/com/google/dart/compiler/backend/js/ast/HasArguments.java new file mode 100644 index 00000000000..2b880f7c86c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/HasArguments.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.List; + +/** + * Implemented by JavaScript objects that accept arguments. + */ +public interface HasArguments { + + List getArguments(); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/HasCondition.java b/compiler/java/com/google/dart/compiler/backend/js/ast/HasCondition.java new file mode 100644 index 00000000000..082f4e15eb3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/HasCondition.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Implemented by JavaScript objects with conditional execution. + */ +public interface HasCondition { + + JsExpression getCondition(); + + void setCondition(JsExpression condition); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/HasName.java b/compiler/java/com/google/dart/compiler/backend/js/ast/HasName.java new file mode 100644 index 00000000000..bdd87970929 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/HasName.java @@ -0,0 +1,14 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.HasSymbol; + +/** + * Implemented by JavaScript objects that have a name. + */ +public interface HasName extends HasSymbol { + JsName getName(); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java new file mode 100644 index 00000000000..46df5108086 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java @@ -0,0 +1,68 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a javascript expression for array access. + */ +public final class JsArrayAccess extends JsExpression { + + private JsExpression arrayExpr; + private JsExpression indexExpr; + + public JsArrayAccess() { + super(); + } + + public JsArrayAccess(JsExpression arrayExpr, JsExpression indexExpr) { + this.arrayExpr = arrayExpr; + this.indexExpr = indexExpr; + } + + public JsExpression getArrayExpr() { + return arrayExpr; + } + + public JsExpression getIndexExpr() { + return indexExpr; + } + + @Override + public boolean hasSideEffects() { + return arrayExpr.hasSideEffects() || indexExpr.hasSideEffects(); + } + + @Override + public boolean isDefinitelyNotNull() { + return false; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public void setArrayExpr(JsExpression arrayExpr) { + this.arrayExpr = arrayExpr; + } + + public void setIndexExpr(JsExpression indexExpr) { + this.indexExpr = indexExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + arrayExpr = v.accept(arrayExpr); + indexExpr = v.accept(indexExpr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.ARRAY_ACCESS; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java new file mode 100644 index 00000000000..8cb9f7e6d52 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java @@ -0,0 +1,67 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a JavaScript expression for array literals. + */ +public final class JsArrayLiteral extends JsLiteral { + + private final List exprs = new ArrayList(); + + public JsArrayLiteral() { + super(); + } + + public List getExpressions() { + return exprs; + } + + @Override + public boolean hasSideEffects() { + for (JsExpression expr : exprs) { + if (expr.hasSideEffects()) { + return true; + } + } + return false; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(exprs); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.ARRAY; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java new file mode 100644 index 00000000000..b09d50c1d05 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java @@ -0,0 +1,107 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript binary operation. + */ +public final class JsBinaryOperation extends JsExpression { + + private JsExpression arg1; + private JsExpression arg2; + private final JsBinaryOperator op; + + public JsBinaryOperation(JsBinaryOperator op) { + this(op, null, null); + } + + public JsBinaryOperation(JsBinaryOperator op, JsExpression arg1, JsExpression arg2) { + this.op = op; + this.arg1 = arg1; + this.arg2 = arg2; + } + + public JsExpression getArg1() { + return arg1; + } + + public JsExpression getArg2() { + return arg2; + } + + public JsBinaryOperator getOperator() { + return op; + } + + @Override + public boolean hasSideEffects() { + return op.isAssignment() || arg1.hasSideEffects() || arg2.hasSideEffects(); + } + + @Override + public boolean isDefinitelyNotNull() { + // Precarious coding, but none of these can have null results. + if (op.getPrecedence() > 5) { + return true; + } + if (op == JsBinaryOperator.OR) { + if (arg1 instanceof CanBooleanEval) { + if (((CanBooleanEval) arg1).isBooleanTrue()) { + assert arg1.isDefinitelyNotNull(); + return true; + } + } + } + // AND and OR can return nulls + if (op.isAssignment()) { + if (op == JsBinaryOperator.ASG) { + return arg2.isDefinitelyNotNull(); + } else { + // All other ASG's are math ops. + return true; + } + } + + if (op == JsBinaryOperator.COMMA) { + return arg2.isDefinitelyNotNull(); + } + + return false; + } + + @Override + public boolean isDefinitelyNull() { + if (op == JsBinaryOperator.AND) { + return arg1.isDefinitelyNull(); + } + return false; + } + + public void setArg1(JsExpression arg1) { + this.arg1 = arg1; + } + + public void setArg2(JsExpression arg2) { + this.arg2 = arg2; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (op.isAssignment()) { + arg1 = v.acceptLvalue(arg1); + } else { + arg1 = v.accept(arg1); + } + arg2 = v.accept(arg2); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.BINARY_OP; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java new file mode 100644 index 00000000000..b54647f3a46 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java @@ -0,0 +1,120 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the operator in a JavaScript binary operation. + */ +public enum JsBinaryOperator implements JsOperator { + + /* + * Precedence indices from "JavaScript - The Definitive Guide" 4th Edition + * (page 57) + * + * + * Precedence 15 is for really important things that have their own AST + * classes. + * + * Precedence 14 is for unary operators. + */ + + MUL("*", 13, LEFT | INFIX), DIV("/", 13, LEFT | INFIX), MOD("%", 13, LEFT + | INFIX), + + ADD("+", 12, LEFT | INFIX), SUB("-", 12, LEFT | INFIX), + + SHL("<<", 11, LEFT | INFIX), SHR(">>", 11, LEFT | INFIX), SHRU(">>>", 11, + LEFT | INFIX), + + LT("<", 10, LEFT | INFIX), LTE("<=", 10, LEFT | INFIX), GT(">", 10, LEFT + | INFIX), GTE(">=", 10, LEFT | INFIX), INSTANCEOF("instanceof", 10, LEFT + | INFIX), INOP("in", 10, LEFT | INFIX), + + EQ("==", 9, LEFT | INFIX), NEQ("!=", 9, LEFT | INFIX), REF_EQ("===", 9, LEFT + | INFIX), REF_NEQ("!==", 9, LEFT | INFIX), + + BIT_AND("&", 8, LEFT | INFIX), + + BIT_XOR("^", 7, LEFT | INFIX), + + BIT_OR("|", 6, LEFT | INFIX), + + AND("&&", 5, LEFT | INFIX), + + OR("||", 4, LEFT | INFIX), + + // Precedence 3 is for the condition operator. + + // These assignment operators are right-associative. + ASG("=", 2, INFIX), ASG_ADD("+=", 2, INFIX), ASG_SUB("-=", 2, INFIX), ASG_MUL( + "*=", 2, INFIX), ASG_DIV("/=", 2, INFIX), ASG_MOD("%=", 2, INFIX), ASG_SHL( + "<<=", 2, INFIX), ASG_SHR(">>=", 2, INFIX), ASG_SHRU(">>>=", 2, INFIX), ASG_BIT_AND( + "&=", 2, INFIX), ASG_BIT_OR("|=", 2, INFIX), ASG_BIT_XOR("^=", 2, INFIX), + + COMMA(",", 1, LEFT | INFIX); + + private final int mask; + private final int precedence; + private final String symbol; + + private JsBinaryOperator(String symbol, int precedence, int mask) { + this.symbol = symbol; + this.precedence = precedence; + this.mask = mask; + } + + @Override + public int getPrecedence() { + return precedence; + } + + @Override + public String getSymbol() { + return symbol; + } + + public boolean isAssignment() { + /* + * Beware, flaky! Maybe I should have added Yet Another Field to + * BinaryOperator? + */ + return (getPrecedence() == ASG.getPrecedence()); + } + + @Override + public boolean isKeyword() { + return this == INSTANCEOF || this == INOP; + } + + @Override + public boolean isLeftAssociative() { + return (mask & LEFT) != 0; + } + + @Override + public boolean isPrecedenceLessThan(JsOperator other) { + return precedence < other.getPrecedence(); + } + + @Override + public boolean isValidInfix() { + return (mask & INFIX) != 0; + } + + @Override + public boolean isValidPostfix() { + return (mask & POSTFIX) != 0; + } + + @Override + public boolean isValidPrefix() { + return (mask & PREFIX) != 0; + } + + @Override + public String toString() { + return symbol; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsBlock.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBlock.java new file mode 100644 index 00000000000..39d1e356b2d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBlock.java @@ -0,0 +1,44 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a JavaScript block statement. + */ +public class JsBlock extends JsStatement { + + private final List stmts = new ArrayList(); + + public JsBlock() { + } + + public JsBlock(JsStatement stmt) { + stmts.add(stmt); + } + + public List getStatements() { + return stmts; + } + + public boolean isGlobalBlock() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(stmts); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.BLOCK; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsBooleanLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBooleanLiteral.java new file mode 100644 index 00000000000..12238ddbb02 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBooleanLiteral.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript literal boolean expression. + */ +public final class JsBooleanLiteral extends JsValueLiteral { + + private final boolean value; + + // Should be interned by JsProgram + JsBooleanLiteral(boolean value) { + this.value = value; + } + + public boolean getValue() { + return value; + } + + @Override + public boolean isBooleanFalse() { + return value == false; + } + + @Override + public boolean isBooleanTrue() { + return value == true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.BOOLEAN; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsBreak.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBreak.java new file mode 100644 index 00000000000..363f294df78 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsBreak.java @@ -0,0 +1,46 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the JavaScript break statement. + */ +public final class JsBreak extends JsStatement { + + private final JsNameRef label; + + public JsBreak() { + this(null); + } + + public JsBreak(JsNameRef label) { + super(); + this.label = label; + } + + public JsNameRef getLabel() { + return label; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (label != null) { + v.accept(label); + } + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.BREAK; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsCase.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsCase.java new file mode 100644 index 00000000000..43d2fc41230 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsCase.java @@ -0,0 +1,39 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the JavaScript case statement. + */ +public final class JsCase extends JsSwitchMember { + + private JsExpression caseExpr; + + public JsCase() { + super(); + } + + public JsExpression getCaseExpr() { + return caseExpr; + } + + public void setCaseExpr(JsExpression caseExpr) { + this.caseExpr = caseExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + caseExpr = v.accept(caseExpr); + v.acceptWithInsertRemove(stmts); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.CASE; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsCatch.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsCatch.java new file mode 100644 index 00000000000..f992545b944 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsCatch.java @@ -0,0 +1,66 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript catch clause. + */ +public class JsCatch extends JsNode implements HasCondition { + + protected final JsCatchScope scope; + private JsBlock body; + private JsExpression condition; + private JsParameter param; + + public JsCatch(JsScope parent, String ident) { + super(); + assert (parent != null); + scope = new JsCatchScope(parent, ident); + param = new JsParameter(scope.findExistingName(ident)); + } + + public JsBlock getBody() { + return body; + } + + @Override + public JsExpression getCondition() { + return condition; + } + + public JsParameter getParameter() { + return param; + } + + public JsScope getScope() { + return scope; + } + + public void setBody(JsBlock body) { + this.body = body; + } + + @Override + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + param = v.accept(param); + if (condition != null) { + condition = v.accept(condition); + } + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.CATCH; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsCatchScope.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsCatchScope.java new file mode 100644 index 00000000000..6c9ce22a0e8 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsCatchScope.java @@ -0,0 +1,75 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A special scope used only for catch blocks. It only holds a single symbol: + * the catch argument's name. + */ +public class JsCatchScope extends JsScope { + + private final JsName name; + + public JsCatchScope(JsScope parent, String ident) { + super(parent, "Catch scope"); + this.name = new JsName(this, ident, ident, ident); + } + + @Override + public JsName declareName(String ident) { + // Declare into parent scope! + return getParent().declareName(ident); + } + + @Override + public JsName declareName(String ident, String shortIdent) { + // Declare into parent scope! + return getParent().declareName(ident, shortIdent); + } + + @Override + public Iterator getAllNames() { + return new Iterator() { + private boolean didIterate = false; + + @Override + public boolean hasNext() { + return !didIterate; + } + + @Override + public JsName next() { + if (didIterate) { + throw new NoSuchElementException(); + } + didIterate = true; + return name; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + } + + @Override + protected JsName doCreateName(String ident, String shortIdent, String originalName) { + throw new UnsupportedOperationException("Cannot create a name in a catch scope"); + } + + @Override + protected JsName findExistingNameNoRecurse(String ident) { + if (name.getIdent().equals(ident)) { + return name; + } else { + return null; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsConditional.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsConditional.java new file mode 100644 index 00000000000..b1171bac2c0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsConditional.java @@ -0,0 +1,78 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript conditional expression. + */ +public final class JsConditional extends JsExpression { + + private JsExpression elseExpr; + private JsExpression testExpr; + private JsExpression thenExpr; + + public JsConditional() { + } + + public JsConditional(JsExpression testExpr, JsExpression thenExpr, JsExpression elseExpr) { + this.testExpr = testExpr; + this.thenExpr = thenExpr; + this.elseExpr = elseExpr; + } + + public JsExpression getElseExpression() { + return elseExpr; + } + + public JsExpression getTestExpression() { + return testExpr; + } + + public JsExpression getThenExpression() { + return thenExpr; + } + + @Override + public boolean hasSideEffects() { + return testExpr.hasSideEffects() || thenExpr.hasSideEffects() || elseExpr.hasSideEffects(); + } + + @Override + public boolean isDefinitelyNotNull() { + return thenExpr.isDefinitelyNotNull() && elseExpr.isDefinitelyNotNull(); + } + + @Override + public boolean isDefinitelyNull() { + return thenExpr.isDefinitelyNull() && elseExpr.isDefinitelyNull(); + } + + public void setElseExpression(JsExpression elseExpr) { + this.elseExpr = elseExpr; + } + + public void setTestExpression(JsExpression testExpr) { + this.testExpr = testExpr; + } + + public void setThenExpression(JsExpression thenExpr) { + this.thenExpr = thenExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + testExpr = v.accept(testExpr); + thenExpr = v.accept(thenExpr); + elseExpr = v.accept(elseExpr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.CONDITIONAL; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsContext.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsContext.java new file mode 100644 index 00000000000..ddb170052d3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsContext.java @@ -0,0 +1,27 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * The context in which a JsNode visitation occurs. This represents the set of + * possible operations a JsVisitor subclass can perform on the currently visited + * node. + */ +public interface JsContext { + + boolean canInsert(); + + boolean canRemove(); + + void insertAfter(JsVisitable node); + + void insertBefore(JsVisitable node); + + boolean isLvalue(); + + void removeMe(); + + void replaceMe(JsVisitable node); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsContinue.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsContinue.java new file mode 100644 index 00000000000..d761815fe76 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsContinue.java @@ -0,0 +1,46 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the JavaScript continue statement. + */ +public final class JsContinue extends JsStatement { + + private final JsNameRef label; + + public JsContinue() { + this(null); + } + + public JsContinue(JsNameRef label) { + super(); + this.label = label; + } + + public JsNameRef getLabel() { + return label; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (label != null) { + v.accept(label); + } + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.CONTINUE; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsDebugger.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsDebugger.java new file mode 100644 index 00000000000..34cfae43f48 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsDebugger.java @@ -0,0 +1,25 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript debugger statement. + */ +public class JsDebugger extends JsStatement { + + public JsDebugger() { + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.DEBUGGER; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsDefault.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsDefault.java new file mode 100644 index 00000000000..a76987a8e5d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsDefault.java @@ -0,0 +1,28 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the default option in a JavaScript swtich statement. + */ +public final class JsDefault extends JsSwitchMember { + + public JsDefault() { + super(); + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(stmts); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.DEFAULT; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsDoWhile.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsDoWhile.java new file mode 100644 index 00000000000..1dd4a47e8d4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsDoWhile.java @@ -0,0 +1,54 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript do..while statement. + */ +public class JsDoWhile extends JsStatement { + + private JsStatement body; + private JsExpression condition; + + public JsDoWhile() { + super(); + } + + public JsDoWhile(JsExpression condition, JsStatement body) { + super(); + this.condition = condition; + this.body = body; + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getCondition() { + return condition; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + body = v.accept(body); + condition = v.accept(condition); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.DO; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsEmpty.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsEmpty.java new file mode 100644 index 00000000000..c2ac99e3ac0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsEmpty.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents an empty statement in JavaScript. + */ +public class JsEmpty extends JsStatement { + + // Interned by JsProgram + JsEmpty() { + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.EMPTY; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsExprStmt.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsExprStmt.java new file mode 100644 index 00000000000..5b9fc1218c3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsExprStmt.java @@ -0,0 +1,36 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript expression statement. + */ +public final class JsExprStmt extends JsStatement { + + private JsExpression expr; + + public JsExprStmt(JsExpression expr) { + super(); + this.expr = expr; + this.setSourceInfo(expr); + } + + public JsExpression getExpression() { + return expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + expr = v.accept(expr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.EXPR_STMT; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsExpression.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsExpression.java new file mode 100644 index 00000000000..43545ec98dc --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsExpression.java @@ -0,0 +1,52 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; + +/** + * An abstract base class for all JavaScript expressions. + */ +public abstract class JsExpression extends JsNode { + + protected JsExpression() { + } + + /** + * Determines whether the expression can cause side effects. + */ + public abstract boolean hasSideEffects(); + + /** + * True if the target expression is definitely not null. + */ + public abstract boolean isDefinitelyNotNull(); + + /** + * True if the target expression is definitely null. + */ + public abstract boolean isDefinitelyNull(); + + /** + * Determines whether or not this expression is a leaf, such as a + * {@link JsNameRef}, {@link JsBooleanLiteral}, and so on. Leaf expressions + * never need to be parenthesized. + */ + public boolean isLeaf() { + // Conservatively say that it isn't a leaf. + // Individual subclasses can speak for themselves if they are a leaf. + return false; + } + + public JsExprStmt makeStmt() { + return new JsExprStmt(this); + } + + @Override + public JsExpression setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsFor.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsFor.java new file mode 100644 index 00000000000..b980d4be1a2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsFor.java @@ -0,0 +1,105 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; + +/** + * A for statement. If specified at all, the initializer part is + * either a declaration of one or more variables, in which case + * {@link #getInitVars()} is used, or an expression, in which case + * {@link #getInitExpr()} is used. In the latter case, the comma operator is + * often used to create a compound expression. + * + *

    + * Note that any of the parts of the for loop header can be + * null, although the body will never be null. + */ +public class JsFor extends JsStatement { + + private JsStatement body; + private JsExpression condition; + private JsExpression incrExpr; + private JsExpression initExpr; + private JsVars initVars; + + public JsFor() { + super(); + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getCondition() { + return condition; + } + + public JsExpression getIncrExpr() { + return incrExpr; + } + + public JsExpression getInitExpr() { + return initExpr; + } + + public JsVars getInitVars() { + return initVars; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + public void setIncrExpr(JsExpression incrExpr) { + this.incrExpr = incrExpr; + } + + public void setInitExpr(JsExpression initExpr) { + this.initExpr = initExpr; + } + + public void setInitVars(JsVars initVars) { + this.initVars = initVars; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + assert (!(initExpr != null && initVars != null)); + + if (initExpr != null) { + initExpr = v.accept(initExpr); + } else if (initVars != null) { + initVars = v.accept(initVars); + } + + if (condition != null) { + condition = v.accept(condition); + } + + if (incrExpr != null) { + incrExpr = v.accept(incrExpr); + } + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public JsFor setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + @Override + public NodeKind getKind() { + return NodeKind.FOR; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsForIn.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsForIn.java new file mode 100644 index 00000000000..646e349885c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsForIn.java @@ -0,0 +1,71 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript for..in statement. + */ +public class JsForIn extends JsStatement { + + private JsStatement body; + private JsExpression iterExpr; + private JsExpression objExpr; + + // Optional: the name of a new iterator variable to introduce + private final JsName iterVarName; + + public JsForIn() { + this(null); + } + + public JsForIn(JsName iterVarName) { + this.iterVarName = iterVarName; + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getIterExpr() { + return iterExpr; + } + + public JsName getIterVarName() { + return iterVarName; + } + + public JsExpression getObjExpr() { + return objExpr; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setIterExpr(JsExpression iterExpr) { + this.iterExpr = iterExpr; + } + + public void setObjExpr(JsExpression objExpr) { + this.objExpr = objExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (iterExpr != null) { + iterExpr = v.acceptLvalue(iterExpr); + } + objExpr = v.accept(objExpr); + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.FOR_IN; + } +} \ No newline at end of file diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsFunction.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsFunction.java new file mode 100644 index 00000000000..956f114e477 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsFunction.java @@ -0,0 +1,225 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.common.Symbol; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a JavaScript function expression. + */ +public final class JsFunction extends JsLiteral implements HasName { + + private static void trace(String title, String code) { + System.out.println("---------------------------"); + System.out.println(title + ":"); + System.out.println("---------------------------"); + System.out.println(code); + } + + protected JsBlock body; + protected final List params = new ArrayList(); + protected final JsScope scope; + private boolean artificiallyRescued; + private boolean executeOnce; + private boolean fromDart; + private JsFunction impliedExecute; + private JsName name; + private boolean trace = false; + private boolean traceFirst = true; + private boolean hoisted = false; + private boolean constructor = false; + + /** + * Creates an anonymous function. + */ + public JsFunction(JsScope parent) { + this(parent, null, false); + } + + /** + * Creates a function that is not derived from Dart source. + */ + public JsFunction(JsScope parent, JsName name) { + this(parent, name, false); + } + + /** + * Creates a named function, possibly derived from Dart source. + */ + public JsFunction(JsScope parent, JsName name, boolean fromDart) { + assert (parent != null); + this.fromDart = fromDart; + setName(name); + String scopeName = (name == null) ? "" : name.getIdent(); + scopeName = "function " + scopeName; + this.scope = new JsScope(parent, scopeName); + } + + public JsBlock getBody() { + return body; + } + + /** + * If true, this indicates that only the first invocation of the function will + * have any effects. Subsequent invocations may be considered to be no-op + * calls whose return value is ignored. + */ + public boolean getExecuteOnce() { + return executeOnce; + } + + public JsFunction getImpliedExecute() { + return impliedExecute; + } + + @Override + public JsName getName() { + return name; + } + + @Override + public Symbol getSymbol() { + return name; + } + + public List getParameters() { + return params; + } + + public JsScope getScope() { + return scope; + } + + @Override + public boolean hasSideEffects() { + // If there's a name, the name is assigned to. + return name != null; + } + + public boolean isArtificiallyRescued() { + return artificiallyRescued; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public boolean isFromDart() { + return fromDart; + } + + public void setArtificiallyRescued(boolean rescued) { + this.artificiallyRescued = rescued; + } + + public void setBody(JsBlock body) { + this.body = body; + } + + public void setExecuteOnce(boolean executeOnce) { + this.executeOnce = executeOnce; + } + + public void setFromDart(boolean fromDart) { + this.fromDart = fromDart; + } + + public void setImpliedExecute(JsFunction impliedExecute) { + this.impliedExecute = impliedExecute; + } + + public void setName(JsName name) { + this.name = name; + if (name != null) { + if (isFromDart()) { + name.setStaticRef(this); + } + } + } + + public void setTrace() { + this.trace = true; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + String before = null; + if (trace && v instanceof JsModVisitor) { + before = this.toSource(); + if (traceFirst) { + traceFirst = false; + trace("SCRIPT INITIAL", before); + } + } + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(params); + body = v.accept(body); + } + v.endVisit(this, ctx); + if (trace && v instanceof JsModVisitor) { + String after = this.toSource(); + if (!after.equals(before)) { + String title = v.getClass().getSimpleName(); + trace(title, after); + } + } + } + + public void setHoisted() { + hoisted = true; + } + + /** Whether the function has been hoisted */ + public boolean isHoisted() { + return hoisted; + } + + /** + * Rebase the function to a new scope. + * @param newScopeParent The scope to add the function to. + */ + public void rebaseScope(JsScope newScopeParent) { + this.scope.rebase(newScopeParent); + } + + @Override + public JsFunction setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + public boolean isConstructor() { + return this.constructor; + } + + public boolean setIsConstructor(boolean constructor) { + return this.constructor = constructor; + } + + @Override + public NodeKind getKind() { + return NodeKind.FUNCTION; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java new file mode 100644 index 00000000000..9a14004e14c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java @@ -0,0 +1,19 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represnts a JavaScript block in the global scope. + */ +public class JsGlobalBlock extends JsBlock { + + public JsGlobalBlock() { + } + + @Override + public boolean isGlobalBlock() { + return true; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsIf.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsIf.java new file mode 100644 index 00000000000..478538235ab --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsIf.java @@ -0,0 +1,65 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript if statement. + */ +public final class JsIf extends JsStatement { + + private JsExpression ifExpr; + private JsStatement thenStmt; + private JsStatement elseStmt; + + public JsIf() { + } + + public JsIf(JsExpression ifExpr, JsStatement thenStmt, JsStatement elseStmt) { + this.ifExpr = ifExpr; + this.thenStmt = thenStmt; + this.elseStmt = elseStmt; + } + + public JsStatement getElseStmt() { + return elseStmt; + } + + public JsExpression getIfExpr() { + return ifExpr; + } + + public JsStatement getThenStmt() { + return thenStmt; + } + + public void setElseStmt(JsStatement elseStmt) { + this.elseStmt = elseStmt; + } + + public void setIfExpr(JsExpression ifExpr) { + this.ifExpr = ifExpr; + } + + public void setThenStmt(JsStatement thenStmt) { + this.thenStmt = thenStmt; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + ifExpr = v.accept(ifExpr); + thenStmt = v.accept(thenStmt); + if (elseStmt != null) { + elseStmt = v.accept(elseStmt); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.IF; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsInvocation.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsInvocation.java new file mode 100644 index 00000000000..beb10a1eeed --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsInvocation.java @@ -0,0 +1,62 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a JavaScript invocation. + */ +public final class JsInvocation extends JsExpression implements HasArguments { + + private final List args = new ArrayList(); + private JsExpression qualifier; + + public JsInvocation() { + } + + @Override + public List getArguments() { + return args; + } + + public JsExpression getQualifier() { + return qualifier; + } + + @Override + public boolean hasSideEffects() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return false; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public void setQualifier(JsExpression qualifier) { + this.qualifier = qualifier; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + qualifier = v.accept(qualifier); + v.acceptList(args); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.INVOKE; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsLabel.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsLabel.java new file mode 100644 index 00000000000..ec044fea415 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsLabel.java @@ -0,0 +1,52 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.Symbol; + +/** + * Represents a JavaScript label statement. + */ +public class JsLabel extends JsStatement implements HasName { + + private final JsName label; + + private JsStatement stmt; + + public JsLabel(JsName label) { + this.label = label; + } + + @Override + public JsName getName() { + return label; + } + + @Override + public Symbol getSymbol() { + return label; + } + + public JsStatement getStmt() { + return stmt; + } + + public void setStmt(JsStatement stmt) { + this.stmt = stmt; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + stmt = v.accept(stmt); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.LABEL; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsLiteral.java new file mode 100644 index 00000000000..c87ae0c8af1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsLiteral.java @@ -0,0 +1,14 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript string literal expression. + */ +public abstract class JsLiteral extends JsExpression implements CanBooleanEval { + + protected JsLiteral() { + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsModVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsModVisitor.java new file mode 100644 index 00000000000..ef6e75ce994 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsModVisitor.java @@ -0,0 +1,189 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.util.Hack; + +import java.util.List; + +/** + * A visitor for iterating through and modifying an AST. + */ +public class JsModVisitor extends JsVisitor { + + private class ListContext implements JsContext { + + private List collection; + private int index; + private boolean removed; + private boolean replaced; + + @Override + public boolean canInsert() { + return true; + } + + @Override + public boolean canRemove() { + return true; + } + + @Override + public void insertAfter(JsVisitable node) { + checkRemoved(); + collection.add(index + 1, Hack.cast(node)); + didChange = true; + } + + @Override + public void insertBefore(JsVisitable node) { + checkRemoved(); + collection.add(index++, Hack.cast(node)); + didChange = true; + } + + @Override + public boolean isLvalue() { + return false; + } + + @Override + public void removeMe() { + checkState(); + collection.remove(index--); + didChange = removed = true; + } + + @Override + public void replaceMe(JsVisitable node) { + checkState(); + checkReplacement(collection.get(index), node); + collection.set(index, Hack.cast(node)); + didChange = replaced = true; + } + + protected void traverse(List collection) { + this.collection = collection; + for (index = 0; index < collection.size(); ++index) { + removed = replaced = false; + doTraverse(collection.get(index), this); + } + } + + private void checkRemoved() { + if (removed) { + throw new RuntimeException("Node was already removed"); + } + } + + private void checkState() { + checkRemoved(); + if (replaced) { + throw new RuntimeException("Node was already replaced"); + } + } + } + + private class LvalueContext extends NodeContext { + @Override + public boolean isLvalue() { + return true; + } + } + + private class NodeContext implements JsContext { + private T node; + private boolean replaced; + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return false; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(JsVisitable node) { + if (replaced) { + throw new RuntimeException("Node was already replaced"); + } + checkReplacement(this.node, node); + this.node = Hack.cast(node); + didChange = replaced = true; + } + + protected T traverse(T node) { + this.node = node; + replaced = false; + doTraverse(node, this); + return this.node; + } + } + + protected static void checkReplacement(T origNode, T newNode) { + if (newNode == null) { + throw new RuntimeException("Cannot replace with null"); + } + if (newNode == origNode) { + throw new RuntimeException("The replacement is the same as the original"); + } + } + + protected boolean didChange = false; + + @Override + public boolean didChange() { + return didChange; + } + + @Override + protected T doAccept(T node) { + return new NodeContext().traverse(node); + } + + @Override + protected void doAcceptList(List collection) { + NodeContext ctx = new NodeContext(); + for (int i = 0, c = collection.size(); i < c; ++i) { + ctx.traverse(collection.get(i)); + if (ctx.replaced) { + collection.set(i, ctx.node); + } + } + } + + @Override + protected JsExpression doAcceptLvalue(JsExpression expr) { + return new LvalueContext().traverse(expr); + } + + @Override + protected void doAcceptWithInsertRemove(List collection) { + new ListContext().traverse(collection); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsName.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsName.java new file mode 100644 index 00000000000..97fab4dfe8c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsName.java @@ -0,0 +1,114 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.common.Symbol; + +import java.io.Serializable; + +/** + * An abstract base class for named JavaScript objects. + */ +public class JsName implements Symbol, Serializable { + private final JsScope enclosing; + private final String ident; + private boolean isObfuscatable; + private String shortIdent; + private String originalName; + + /** + * A back-reference to the JsNode that the JsName refers to. + */ + private JsNode staticRef; + + /** + * @param ident the unmangled ident to use for this name + */ + JsName(JsScope enclosing, String ident, String shortIdent, String originalName) { + this.enclosing = enclosing; + this.ident = ident; + this.shortIdent = shortIdent; + if (originalName != null) { + this.originalName = originalName; + } + this.isObfuscatable = true; + } + + public JsScope getEnclosing() { + return enclosing; + } + + public String getIdent() { + return ident; + } + + public String getShortIdent() { + return shortIdent; + } + + public String getOriginalName() { + return originalName; + } + + public JsNode getStaticRef() { + return staticRef; + } + + public boolean isObfuscatable() { + return isObfuscatable; + } + + public JsNameRef makeRef() { + return new JsNameRef(this); + } + + public void setObfuscatable(boolean isObfuscatable) { + this.isObfuscatable = isObfuscatable; + } + + public void setShortIdent(String shortIdent) { + this.shortIdent = shortIdent; + } + + /** + * Should never be called except on immutable stuff. + */ + public void setStaticRef(JsNode node) { + this.staticRef = node; + } + + @Override + public String toString() { + return ident; + } + + @Override + public String getOriginalSymbolName() { + return getOriginalName(); + } + + @Override + public int hashCode() { + return ident.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof JsName)) { + return false; + } + JsName other = (JsName) obj; + return ident.equals(other.ident) && enclosing == other.enclosing; + } + + @Override + public DartNode getNode() { + throw new UnsupportedOperationException(); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsNameRef.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNameRef.java new file mode 100644 index 00000000000..fd07125e98b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNameRef.java @@ -0,0 +1,120 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.Symbol; + +/** + * Represents a JavaScript expression that references a name. + */ +public final class JsNameRef extends JsExpression implements CanBooleanEval, HasName { + + private String ident; + private JsName name; + private JsExpression qualifier; + + public JsNameRef(JsName name) { + this.name = name; + } + + public JsNameRef(String ident) { + this.ident = ident; + } + + public String getIdent() { + return (name == null) ? ident : name.getIdent(); + } + + @Override + public JsName getName() { + return name; + } + + @Override + public Symbol getSymbol() { + return name; + } + + public JsExpression getQualifier() { + return qualifier; + } + + public String getShortIdent() { + return (name == null) ? ident : name.getShortIdent(); + } + + @Override + public boolean hasSideEffects() { + if (qualifier == null) { + return false; + } + if (!qualifier.isDefinitelyNotNull()) { + // Could trigger NPE. + return true; + } + return qualifier.hasSideEffects(); + } + + @Override + public boolean isBooleanFalse() { + return isDefinitelyNull(); + } + + @Override + public boolean isBooleanTrue() { + return false; + } + + @Override + public boolean isDefinitelyNotNull() { + // TODO: look for single-assignment of stuff from Java? + return false; + } + + @Override + public boolean isDefinitelyNull() { + if (name != null) { + return (name.getEnclosing().getProgram().getUndefinedLiteral().getName() == name); + } + return false; + } + + @Override + public boolean isLeaf() { + if (qualifier == null) { + return true; + } else { + return false; + } + } + + public boolean isResolved() { + return name != null; + } + + public void resolve(JsName name) { + this.name = name; + this.ident = null; + } + + public void setQualifier(JsExpression qualifier) { + this.qualifier = qualifier; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (qualifier != null) { + qualifier = v.accept(qualifier); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NAME_REF; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsNew.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNew.java new file mode 100644 index 00000000000..19b6f5d6c90 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNew.java @@ -0,0 +1,61 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents the JavaScript new expression. + */ +public final class JsNew extends JsExpression implements HasArguments { + + private final List args = new ArrayList(); + private JsExpression ctorExpr; + + public JsNew(JsExpression ctorExpr) { + this.ctorExpr = ctorExpr; + } + + @Override + public List getArguments() { + return args; + } + + public JsExpression getConstructorExpression() { + return ctorExpr; + } + + @Override + public boolean hasSideEffects() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + // Sadly, in JS it can be! + // TODO: analysis could probably determine most instances cannot be null. + return false; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + ctorExpr = v.accept(ctorExpr); + v.acceptList(args); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NEW; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsNode.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNode.java new file mode 100644 index 00000000000..2856ad8a008 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNode.java @@ -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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.backend.js.JsSourceGenerationVisitor; +import com.google.dart.compiler.backend.js.JsToStringGenerationVisitor; +import com.google.dart.compiler.common.AbstractNode; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.util.DefaultTextOutput; + +/** + * Base class for all JS AST elements. + */ +public abstract class JsNode extends AbstractNode implements JsVisitable { + + protected JsNode() { + } + + // Causes source generation to delegate to the one visitor + public final String toSource() { + DefaultTextOutput out = new DefaultTextOutput(false); + JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out); + v.accept(this); + return out.toString(); + } + + // Causes source generation to delegate to the one visitor + @Override + public final String toString() { + DefaultTextOutput out = new DefaultTextOutput(false); + JsToStringGenerationVisitor v = new JsToStringGenerationVisitor(out); + v.accept(this); + return out.toString(); + } + + @Override + public SourceInfo getSourceInfo() { + return this; + } + + public JsNode setSourceRef(SourceInfo info) { + if (info != null) { + this.setSourceInfo(info); + } + return this; + } + + public abstract NodeKind getKind(); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java new file mode 100644 index 00000000000..506b2e3f330 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java @@ -0,0 +1,46 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript null literal. + */ +public final class JsNullLiteral extends JsValueLiteral { + + // Should only be instantiated in JsProgram + JsNullLiteral() { + } + + @Override + public boolean isBooleanFalse() { + return true; + } + + @Override + public boolean isBooleanTrue() { + return false; + } + + @Override + public boolean isDefinitelyNotNull() { + return false; + } + + @Override + public boolean isDefinitelyNull() { + return true; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NULL; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java new file mode 100644 index 00000000000..726c6b0ed27 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript literal decimal expression. + */ +public final class JsNumberLiteral extends JsValueLiteral { + + private final double value; + + // Should be interned by JsProgram + JsNumberLiteral(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + + @Override + public boolean isBooleanFalse() { + return value == 0.0; + } + + @Override + public boolean isBooleanTrue() { + return value != 0.0; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NUMBER; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java new file mode 100644 index 00000000000..4f70265624b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java @@ -0,0 +1,66 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A JavaScript object literal. + */ +public final class JsObjectLiteral extends JsLiteral { + + private final List props = new ArrayList(); + + public JsObjectLiteral() { + } + + public List getPropertyInitializers() { + return props; + } + + @Override + public boolean hasSideEffects() { + for (JsPropertyInitializer prop : props) { + if (prop.hasSideEffects()) { + return true; + } + } + return false; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(props); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.OBJECT; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsOperator.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsOperator.java new file mode 100644 index 00000000000..e4440cbe9ab --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsOperator.java @@ -0,0 +1,32 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript operator. + */ +public interface JsOperator { + + int INFIX = 0x02; + int LEFT = 0x01; + int POSTFIX = 0x04; + int PREFIX = 0x08; + + int getPrecedence(); + + String getSymbol(); + + boolean isKeyword(); + + boolean isLeftAssociative(); + + boolean isPrecedenceLessThan(JsOperator other); + + boolean isValidInfix(); + + boolean isValidPostfix(); + + boolean isValidPrefix(); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsParameter.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsParameter.java new file mode 100644 index 00000000000..758767d62bc --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsParameter.java @@ -0,0 +1,41 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.Symbol; + +/** + * A JavaScript parameter. + */ +public final class JsParameter extends JsNode implements HasName { + + private final JsName name; + + public JsParameter(JsName name) { + this.name = name; + name.setStaticRef(this); + } + + @Override + public JsName getName() { + return name; + } + + @Override + public Symbol getSymbol() { + return name; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PARAMETER; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java new file mode 100644 index 00000000000..a33536f6c0d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java @@ -0,0 +1,42 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript postfix operation. + */ +public final class JsPostfixOperation extends JsUnaryOperation { + + public JsPostfixOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsPostfixOperation(JsUnaryOperator op, JsExpression arg) { + super(op, arg); + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + super.traverse(v, ctx); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.POSTFIX_OP; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java new file mode 100644 index 00000000000..8ce12c7bacd --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java @@ -0,0 +1,69 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript prefix operation. + */ +public final class JsPrefixOperation extends JsUnaryOperation implements CanBooleanEval { + + public JsPrefixOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsPrefixOperation(JsUnaryOperator op, JsExpression arg) { + super(op, arg); + } + + @Override + public boolean isBooleanFalse() { + if (getOperator() == JsUnaryOperator.VOID) { + return true; + } + if (getOperator() == JsUnaryOperator.NOT && getArg() instanceof CanBooleanEval) { + CanBooleanEval eval = (CanBooleanEval) getArg(); + return eval.isBooleanTrue(); + } + return false; + } + + @Override + public boolean isBooleanTrue() { + if (getOperator() == JsUnaryOperator.NOT && getArg() instanceof CanBooleanEval) { + CanBooleanEval eval = (CanBooleanEval) getArg(); + return eval.isBooleanFalse(); + } + if (getOperator() == JsUnaryOperator.TYPEOF) { + return true; + } + return false; + } + + @Override + public boolean isDefinitelyNotNull() { + if (getOperator() == JsUnaryOperator.TYPEOF) { + return true; + } + return getOperator() != JsUnaryOperator.VOID; + } + + @Override + public boolean isDefinitelyNull() { + return getOperator() == JsUnaryOperator.VOID; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + super.traverse(v, ctx); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PREFIX_OP; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsProgram.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsProgram.java new file mode 100644 index 00000000000..1d2c902d27a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsProgram.java @@ -0,0 +1,172 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.HashMap; +import java.util.Map; + +/** + * A JavaScript program. + */ +public final class JsProgram extends JsNode { + + private final JsStatement debuggerStmt; + private final JsEmpty emptyStmt; + private final JsBooleanLiteral falseLiteral; + private JsProgramFragment[] fragments; + private final Map indexedFunctions = new HashMap(); + private final JsNullLiteral nullLiteral; + private final Map numberLiteralMap = + new HashMap(); + private final JsScope objectScope; + private final JsRootScope rootScope; + private final Map stringLiteralMap = + new HashMap(); + private final JsScope topScope; + private final JsBooleanLiteral trueLiteral; + + /** + * Constructs a JavaScript program object. + */ + public JsProgram(String unitId) { + rootScope = new JsRootScope(this); + topScope = new JsScope(rootScope, "Global", unitId); + objectScope = new JsScope(rootScope, "Object"); + setFragmentCount(1); + + debuggerStmt = new JsDebugger(); + emptyStmt = new JsEmpty(); + falseLiteral = new JsBooleanLiteral(false); + nullLiteral = new JsNullLiteral(); + trueLiteral = new JsBooleanLiteral(true); + } + + public JsBooleanLiteral getBooleanLiteral(boolean truth) { + if (truth) { + return getTrueLiteral(); + } + return getFalseLiteral(); + } + + /** + * Gets the {@link JsStatement} to use whenever parsed source include a + * debugger statement. + */ + public JsStatement getDebuggerStmt() { + return debuggerStmt; + } + + public JsEmpty getEmptyStmt() { + return emptyStmt; + } + + public JsBooleanLiteral getFalseLiteral() { + return falseLiteral; + } + + public JsBlock getFragmentBlock(int fragment) { + if (fragment < 0 || fragment >= fragments.length) { + throw new IllegalArgumentException("Invalid fragment: " + fragment); + } + return fragments[fragment].getGlobalBlock(); + } + + public int getFragmentCount() { + return this.fragments.length; + } + + /** + * Gets the one and only global block. + */ + public JsBlock getGlobalBlock() { + return getFragmentBlock(0); + } + + public JsFunction getIndexedFunction(String name) { + return indexedFunctions.get(name); + } + + public JsNullLiteral getNullLiteral() { + return nullLiteral; + } + + public JsNumberLiteral getNumberLiteral(double value) { + JsNumberLiteral lit = numberLiteralMap.get(value); + if (lit == null) { + lit = new JsNumberLiteral(value); + numberLiteralMap.put(value, lit); + } + + return lit; + } + + public JsScope getObjectScope() { + return objectScope; + } + + /** + * Gets the quasi-mythical root scope. This is not the same as the top scope; + * all unresolvable identifiers wind up here, because they are considered + * external to the program. + */ + public JsRootScope getRootScope() { + return rootScope; + } + + /** + * Gets the top level scope. This is the scope of all the statements in the + * main program. + */ + public JsScope getScope() { + return topScope; + } + + /** + * Creates or retrieves a JsStringLiteral from an interned object pool. + */ + public JsStringLiteral getStringLiteral(String value) { + JsStringLiteral lit = stringLiteralMap.get(value); + if (lit == null) { + lit = new JsStringLiteral(value); + stringLiteralMap.put(value, lit); + } + return lit; + } + + public JsBooleanLiteral getTrueLiteral() { + return trueLiteral; + } + + public JsNameRef getUndefinedLiteral() { + return new JsNameRef("$Dart$Null"); + } + + public void setFragmentCount(int fragments) { + this.fragments = new JsProgramFragment[fragments]; + for (int i = 0; i < fragments; i++) { + this.fragments[i] = new JsProgramFragment(); + } + } + + public void setIndexedFunctions(Map indexedFunctions) { + this.indexedFunctions.clear(); + this.indexedFunctions.putAll(indexedFunctions); + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + for (JsProgramFragment fragment : fragments) { + v.accept(fragment); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PROGRAM; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java new file mode 100644 index 00000000000..3c6859e2150 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java @@ -0,0 +1,34 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * One independently loadable fragment of a {@link JsProgram}. + */ +public class JsProgramFragment extends JsNode { + + private final JsGlobalBlock globalBlock; + + public JsProgramFragment() { + this.globalBlock = new JsGlobalBlock(); + } + + public JsBlock getGlobalBlock() { + return globalBlock; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.accept(globalBlock); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PROGRAM_FRAGMENT; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java new file mode 100644 index 00000000000..d68d8d01af4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java @@ -0,0 +1,56 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Used in object literals to specify property values by name. + */ +public class JsPropertyInitializer extends JsNode { + + private JsExpression labelExpr; + private JsExpression valueExpr; + + public JsPropertyInitializer() { + } + + public JsPropertyInitializer(JsExpression labelExpr, JsExpression valueExpr) { + this.labelExpr = labelExpr; + this.valueExpr = valueExpr; + } + + public JsExpression getLabelExpr() { + return labelExpr; + } + + public JsExpression getValueExpr() { + return valueExpr; + } + + public boolean hasSideEffects() { + return labelExpr.hasSideEffects() || valueExpr.hasSideEffects(); + } + + public void setLabelExpr(JsExpression labelExpr) { + this.labelExpr = labelExpr; + } + + public void setValueExpr(JsExpression valueExpr) { + this.valueExpr = valueExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + labelExpr = v.accept(labelExpr); + valueExpr = v.accept(valueExpr); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.PROPERTY_INIT; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsRegExp.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsRegExp.java new file mode 100644 index 00000000000..158c7a5e092 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsRegExp.java @@ -0,0 +1,64 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript regular expression. + */ +public final class JsRegExp extends JsValueLiteral { + + private String flags; + private String pattern; + + public JsRegExp() { + } + + public String getFlags() { + return flags; + } + + public String getPattern() { + return pattern; + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + public void setFlags(String suffix) { + this.flags = suffix; + } + + public void setPattern(String re) { + this.pattern = re; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.REGEXP; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsReturn.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsReturn.java new file mode 100644 index 00000000000..16d90e4b353 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsReturn.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript return statement. + */ +public final class JsReturn extends JsStatement { + + private JsExpression expr; + + public JsReturn() { + } + + public JsReturn(JsExpression expr) { + this.expr = expr; + } + + public JsExpression getExpr() { + return expr; + } + + public void setExpr(JsExpression expr) { + this.expr = expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (expr != null) { + expr = v.accept(expr); + } + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.RETURN; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsRootScope.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsRootScope.java new file mode 100644 index 00000000000..25c50b2c9ad --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsRootScope.java @@ -0,0 +1,50 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.backend.js.JsReservedIdentifiers; + +/** + * The root scope is the parent of every scope. All identifiers in this scope + * are not obfuscatable. This scope is prefilled with reserved global + * JavaScript symbols. + */ +public final class JsRootScope extends JsScope { + + private final JsProgram program; + + public JsRootScope(JsProgram program) { + super("Root"); + this.program = program; + } + + @Override + public JsProgram getProgram() { + return program; + } + + @Override + protected JsName doCreateName(String ident, String shortIdent, String originalName) { + JsName name = super.doCreateName(ident, shortIdent, originalName); + name.setObfuscatable(false); + return name; + } + + @Override + protected JsName findExistingNameNoRecurse(String ident) { + JsName name = super.findExistingNameNoRecurse(ident); + if (name == null) { + if (JsReservedIdentifiers.getReservedGlobalSymbols().contains(ident)) { + /* + * Lazily add JsNames for reserved identifiers. Since a JsName for a reserved global symbol + * must report a legitimate enclosing scope, we can't simply have a shared set of symbol + * names. + */ + name = doCreateName(ident, ident, ident); + } + } + return name; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsScope.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsScope.java new file mode 100644 index 00000000000..178f85e51f6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsScope.java @@ -0,0 +1,298 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.util.Lists; +import com.google.dart.compiler.util.Maps; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * A scope is a factory for creating and allocating + * {@link com.google.dart.compiler.backend.js.ast.JsName}s. A JavaScript AST is + * built in terms of abstract name objects without worrying about obfuscation, + * keyword/identifier blacklisting, and so on. + * + *

    + * + * Scopes are associated with + * {@link com.google.dart.compiler.backend.js.ast.JsFunction}s, but the two are + * not equivalent. Functions have scopes, but a scope does not + * necessarily have an associated Function. Examples of this include the + * {@link com.google.dart.compiler.backend.js.ast.JsRootScope} and synthetic + * scopes that might be created by a client. + * + *

    + * + * Scopes can have parents to provide constraints when allocating actual + * identifiers for names. Specifically, names in child scopes are chosen such + * that they do not conflict with names in their parent scopes. The ultimate + * parent is usually the global scope (see + * {@link com.google.dart.compiler.backend.js.ast.JsProgram#getRootScope()}), + * but parentless scopes are useful for managing names that are always accessed + * with a qualifier and could therefore never be confused with the global scope + * hierarchy. + */ +public class JsScope implements Serializable { + + private List children = Collections.emptyList(); + private final String description; + private Map names = Collections.emptyMap(); + private JsScope parent; + protected int tempIndex = 0; + private final String scopeId; + + /* + * Create a scope with parent. + */ + public JsScope(JsScope parent, String description) { + this(parent, description, null); + } + + /** + * Create a scope with parent. + */ + public JsScope(JsScope parent, String description, String scopeId) { + assert (parent != null); + this.scopeId = scopeId; + this.description = description; + this.parent = parent; + parent.children = Lists.add(parent.children, this); + } + + /** + * Rebase the function to a new scope. + * @param newParent The scope to add the function to. + */ + public void rebase(JsScope newParent) { + detachFromParent(); + parent = newParent; + parent.children = Lists.add(parent.children, this); + } + + /** + * Rebase the function's children to a new scope. + * @param newParent + */ + public void rebaseChildScopes(JsScope newParent) { + if (newParent == this) { + return; + } + parent.children = Lists.addAll(parent.children, children); + for (JsScope child : children) { + child.parent = newParent; + } + children = Collections.emptyList(); + } + + /** + * Subclasses can detach and become parentless. + */ + protected void detachFromParent() { + JsScope oldParent = parent; + + oldParent.children = Lists.remove( + parent.children, oldParent.children.indexOf(this)); + + parent = null; + } + + /** + * Subclasses can be parentless. + */ + protected JsScope(String description) { + this.description = description; + this.parent = null; + this.scopeId = null; + } + + /** + * Gets a name object associated with the specified ident in this scope, + * creating it if necessary.
    + * If the JsName does not exist yet, a new JsName is created. The ident, + * short name, and original name of the newly created JsName are equal to + * the given ident. + * + * @param ident An identifier that is unique within this scope. + */ + public JsName declareName(String ident) { + JsName name = findExistingNameNoRecurse(ident); + if (name != null) { + return name; + } + return doCreateName(ident, ident, ident); + } + + /** + * Creates a new variable with an unique ident in this scope. + * The generated JsName is guaranteed to have an identifier (but not short + * name) that does not clash with any existing variables in the scope. + * Future declarations of variables might however clash with the temporary + * (unless they use this function). + */ + public JsName declareFreshName(String shortName) { + String ident = shortName; + int counter = 0; + while (findExistingNameNoRecurse(ident) != null) { + ident = shortName + "_" + counter++; + } + return doCreateName(ident, shortName, shortName); + } + + String getNextTempName() { + // TODO(ngeoffray): Decide on a convention for temporary variables + // introduced by the compiler. + return "tmp$" + (scopeId != null ? scopeId + "$" : "") + tempIndex++; + } + + /** + * Creates a temporary variable with an unique name in this scope. + * The generated temporary is guaranteed to have an identifier (but not short + * name) that does not clash with any existing variables in the scope. + * Future declarations of variables might however clash with the temporary. + */ + public JsName declareTemporary() { + return declareFreshName(getNextTempName()); + } + + /** + * Gets a name object associated with the specified ident in this scope, + * creating it if necessary.
    + * If the JsName does not exist yet, a new JsName is created with the given + * ident, short name and original name. + * + * @param ident An identifier that is unique within this scope. + * @param shortIdent A "pretty" name that does not have to be unique. + * @throws IllegalArgumentException if ident already exists in this scope but + * the requested short name does not match the existing short name. + */ + public JsName declareName(String ident, String shortIdent) { + return declareName(ident, shortIdent, ident); + } + + /** + * Gets a name object associated with the specified ident in this scope, + * creating it if necessary.
    + * If the JsName does not exist yet, a new JsName is created. The original + * name stored in the JsName is equal to the (unmangled) specified originalName. + * + * @param ident An identifier that is unique within this scope. + * @param shortIdent A "pretty" name that does not have to be unique. + * @param originalName The original name in the source. + * @throws IllegalArgumentException if ident already exists in this scope but + * the requested short name does not match the existing short name, + * or the original name does not match the existing original name. + */ + public JsName declareName(String ident, String shortIdent, String originalName) { + JsName name = findExistingNameNoRecurse(ident); + if (name != null) { + if (!name.getShortIdent().equals(shortIdent) + || !nullableEquals(name.getOriginalName(), originalName)) { + throw new IllegalArgumentException("Requested short name " + shortIdent + + " conflicts with preexisting short name " + name.getShortIdent() + " for identifier " + + ident); + } + return name; + } + return doCreateName(ident, shortIdent, originalName); + } + + boolean nullableEquals(String s1, String s2) { + return (s1 == null) ? (s2 == null) : s1.equals(s2); + } + + /** + * Attempts to find the name object for the specified ident, searching in this + * scope, and if not found, in the parent scopes. + * + * @return null if the identifier has no associated name + */ + public final JsName findExistingName(String ident) { + JsName name = findExistingNameNoRecurse(ident); + if (name == null && parent != null) { + return parent.findExistingName(ident); + } + return name; + } + + /** + * Attempts to find an unobfuscatable name object for the specified ident, + * searching in this scope, and if not found, in the parent scopes. + * + * @return null if the identifier has no associated name + */ + public final JsName findExistingUnobfuscatableName(String ident) { + JsName name = findExistingNameNoRecurse(ident); + if (name != null && name.isObfuscatable()) { + name = null; + } + if (name == null && parent != null) { + return parent.findExistingUnobfuscatableName(ident); + } + return name; + } + + /** + * Returns an iterator for all the names defined by this scope. + */ + public Iterator getAllNames() { + return names.values().iterator(); + } + + /** + * Returns a list of this scope's child scopes. + */ + public final List getChildren() { + return children; + } + + /** + * Returns the parent scope of this scope, or null if this is the + * root scope. + */ + public final JsScope getParent() { + return parent; + } + + /** + * Returns the associated program. + */ + public JsProgram getProgram() { + assert (parent != null) : "Subclasses must override getProgram() if they do not set a parent"; + return parent.getProgram(); + } + + @Override + public final String toString() { + if (parent != null) { + return description + "->" + parent; + } else { + return description; + } + } + + /** + * Creates a new name in this scope. + */ + protected JsName doCreateName(String ident, String shortIdent, String originalName) { + JsName name = new JsName(this, ident, shortIdent, originalName); + names = Maps.putOrdered(names, ident, name); + return name; + } + + /** + * Attempts to find the name object for the specified ident, searching in this + * scope only. + * + * @return null if the identifier has no associated name + */ + protected JsName findExistingNameNoRecurse(String ident) { + return names.get(ident); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsStatement.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsStatement.java new file mode 100644 index 00000000000..82d37ca1185 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsStatement.java @@ -0,0 +1,22 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Abstract base class for JavaScript statement objects. + */ +public abstract class JsStatement extends JsNode { + + protected JsStatement() { + } + + /** + * Returns true if this statement definitely causes an abrupt change in flow + * control. + */ + public boolean unconditionalControlBreak() { + return false; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java new file mode 100644 index 00000000000..77e60c9b89a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript string literal expression. + */ +public final class JsStringLiteral extends JsValueLiteral { + + private final String value; + + // These only get created by JsProgram so that they can be interned. + JsStringLiteral(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean isBooleanFalse() { + return value.length() == 0; + } + + @Override + public boolean isBooleanTrue() { + return value.length() != 0; + } + + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.STRING; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitch.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitch.java new file mode 100644 index 00000000000..5a49624af42 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitch.java @@ -0,0 +1,47 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A JavaScript switch statement. + */ +public class JsSwitch extends JsStatement { + + private final List cases = new ArrayList(); + private JsExpression expr; + + public JsSwitch() { + super(); + } + + public List getCases() { + return cases; + } + + public JsExpression getExpr() { + return expr; + } + + public void setExpr(JsExpression expr) { + this.expr = expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + expr = v.accept(expr); + v.acceptWithInsertRemove(cases); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.SWITCH; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java new file mode 100644 index 00000000000..0a748f60078 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java @@ -0,0 +1,24 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A member/case in a JavaScript switch object. + */ +public abstract class JsSwitchMember extends JsNode { + + protected final List stmts = new ArrayList(); + + protected JsSwitchMember() { + super(); + } + + public List getStmts() { + return stmts; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsThisRef.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsThisRef.java new file mode 100644 index 00000000000..9adb6459488 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsThisRef.java @@ -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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript this reference. + */ +public final class JsThisRef extends JsValueLiteral { + + public JsThisRef() { + super(); + } + + @Override + public boolean isBooleanFalse() { + return false; + } + + @Override + public boolean isBooleanTrue() { + return true; + } + + @Override + public boolean isDefinitelyNotNull() { + /* + * You'd think that you could get a null this via function.call/apply, but + * in fact you can't: they just make this be the window object instead. So + * it really can't ever be null. + */ + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.THIS; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsThrow.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsThrow.java new file mode 100644 index 00000000000..4616418fb6b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsThrow.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript throw statement. + */ +public class JsThrow extends JsStatement { + + private JsExpression expr; + + public JsThrow() { + super(); + } + + public JsThrow(JsExpression expr) { + super(); + this.expr = expr; + } + + public JsExpression getExpr() { + return expr; + } + + public void setExpr(JsExpression expr) { + this.expr = expr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + expr = v.accept(expr); + } + v.endVisit(this, ctx); + } + + @Override + public boolean unconditionalControlBreak() { + return true; + } + + @Override + public NodeKind getKind() { + return NodeKind.THROW; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsTry.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsTry.java new file mode 100644 index 00000000000..61357bcd406 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsTry.java @@ -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. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A JavaScript try statement. + */ +public class JsTry extends JsStatement { + + private final List catches = new ArrayList(); + private JsBlock finallyBlock; + private JsBlock tryBlock; + + public JsTry() { + super(); + } + + public List getCatches() { + return catches; + } + + public JsBlock getFinallyBlock() { + return finallyBlock; + } + + public JsBlock getTryBlock() { + return tryBlock; + } + + public void setFinallyBlock(JsBlock block) { + this.finallyBlock = block; + } + + public void setTryBlock(JsBlock block) { + tryBlock = block; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + tryBlock = v.accept(tryBlock); + v.acceptWithInsertRemove(catches); + if (finallyBlock != null) { + finallyBlock = v.accept(finallyBlock); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.TRY; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java new file mode 100644 index 00000000000..00f103f35c9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript prefix or postfix operation. + */ +public abstract class JsUnaryOperation extends JsExpression { + + private JsExpression arg; + + private final JsUnaryOperator op; + + public JsUnaryOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsUnaryOperation(JsUnaryOperator op, JsExpression arg) { + super(); + this.op = op; + this.arg = arg; + } + + public JsExpression getArg() { + return arg; + } + + public JsUnaryOperator getOperator() { + return op; + } + + @Override + public final boolean hasSideEffects() { + return op.isModifying() || arg.hasSideEffects(); + } + + public void setArg(JsExpression arg) { + this.arg = arg; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (op.isModifying()) { + // The delete operator is practically like an assignment of undefined, so + // for practical purposes we're treating it as an lvalue. + arg = v.acceptLvalue(arg); + } else { + arg = v.accept(arg); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java new file mode 100644 index 00000000000..2b854d8313e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java @@ -0,0 +1,78 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript unary operator. + */ +public enum JsUnaryOperator implements JsOperator { + + /* + * Precedence indices from "JavaScript - The Definitive Guide" 4th Edition + * (page 57) + */ + BIT_NOT("~", 14, PREFIX), DEC("--", 14, POSTFIX | PREFIX), DELETE("delete", 14, PREFIX), INC( + "++", 14, POSTFIX | PREFIX), NEG("-", 14, PREFIX), POS("+", 14, PREFIX), + NOT("!", 14, PREFIX), TYPEOF("typeof", 14, PREFIX), VOID("void", 14, PREFIX); + + private final int mask; + private final int precedence; + private final String symbol; + + private JsUnaryOperator(String symbol, int precedence, int mask) { + this.symbol = symbol; + this.precedence = precedence; + this.mask = mask; + } + + @Override + public int getPrecedence() { + return precedence; + } + + @Override + public String getSymbol() { + return symbol; + } + + @Override + public boolean isKeyword() { + return this == DELETE || this == TYPEOF || this == VOID; + } + + @Override + public boolean isLeftAssociative() { + return (mask & LEFT) != 0; + } + + public boolean isModifying() { + return this == DEC || this == INC || this == DELETE; + } + + @Override + public boolean isPrecedenceLessThan(JsOperator other) { + return precedence < other.getPrecedence(); + } + + @Override + public boolean isValidInfix() { + return (mask & INFIX) != 0; + } + + @Override + public boolean isValidPostfix() { + return (mask & POSTFIX) != 0; + } + + @Override + public boolean isValidPrefix() { + return (mask & PREFIX) != 0; + } + + @Override + public String toString() { + return symbol; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java new file mode 100644 index 00000000000..f23ab5dbce4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java @@ -0,0 +1,24 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript string literal expression. + */ +public abstract class JsValueLiteral extends JsLiteral { + + protected JsValueLiteral() { + } + + @Override + public final boolean hasSideEffects() { + return false; + } + + @Override + public final boolean isLeaf() { + return true; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsVars.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsVars.java new file mode 100644 index 00000000000..7f2f50f3d51 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsVars.java @@ -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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.common.Symbol; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * A JavaScript var statement. + */ +public class JsVars extends JsStatement implements Iterable { + + /** + * A var declared using the JavaScript var statement. + */ + public static class JsVar extends JsNode implements HasName { + + private final JsName name; + private JsExpression initExpr; + + public JsVar(JsName name) { + this.name = name; + } + + public JsVar(JsName name, JsExpression initExpr) { + this.name = name; + this.initExpr = initExpr; + } + + public JsExpression getInitExpr() { + return initExpr; + } + + @Override + public JsName getName() { + return name; + } + + @Override + public Symbol getSymbol() { + return name; + } + + public void setInitExpr(JsExpression initExpr) { + this.initExpr = initExpr; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (initExpr != null) { + initExpr = v.accept(initExpr); + } + } + v.endVisit(this, ctx); + } + + @Override + public JsVar setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + @Override + public NodeKind getKind() { + return NodeKind.VAR; + } + } + + private final List vars = new ArrayList(); + + public JsVars() { + } + + public void add(JsVar var) { + vars.add(var); + } + + public int getNumVars() { + return vars.size(); + } + + public void insert(JsVar var) { + vars.add(var); + } + + public boolean isEmpty() { + return vars.isEmpty(); + } + + // Iterator returns JsVar objects + @Override + public Iterator iterator() { + return vars.iterator(); + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(vars); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.VARS; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitable.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitable.java new file mode 100644 index 00000000000..626648af8ae --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitable.java @@ -0,0 +1,19 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Abstracts the idea that a class can be traversed. + */ +public interface JsVisitable { + + /** + * Causes this object to have the visitor visit itself and its children. + * + * @param visitor the visitor that should traverse this node + * @param ctx the context of an existing traversal + */ + void traverse(JsVisitor visitor, JsContext ctx); +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitor.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitor.java new file mode 100644 index 00000000000..8781300ec13 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsVisitor.java @@ -0,0 +1,433 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; + +import java.util.Iterator; +import java.util.List; + +/** + * Implemented by nodes that will visit child nodes. + */ +@SuppressWarnings("unused") +public class JsVisitor { + + protected static final JsContext LVALUE_CONTEXT = new JsContext() { + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return true; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(JsVisitable node) { + throw new UnsupportedOperationException(); + } + }; + + protected static final JsContext UNMODIFIABLE_CONTEXT = new JsContext() { + + @Override + public boolean canInsert() { + return false; + } + + @Override + public boolean canRemove() { + return false; + } + + @Override + public void insertAfter(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertBefore(JsVisitable node) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLvalue() { + return false; + } + + @Override + public void removeMe() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceMe(JsVisitable node) { + throw new UnsupportedOperationException(); + } + }; + + public final T accept(T node) { + return this.doAccept(node); + } + + public final void acceptList(List collection) { + doAcceptList(collection); + } + + public JsExpression acceptLvalue(JsExpression expr) { + return doAcceptLvalue(expr); + } + + public final void acceptWithInsertRemove(List collection) { + doAcceptWithInsertRemove(collection); + } + + public boolean didChange() { + throw new UnsupportedOperationException(); + } + + public void endVisit(JsArrayAccess x, JsContext ctx) { + } + + public void endVisit(JsArrayLiteral x, JsContext ctx) { + } + + public void endVisit(JsBinaryOperation x, JsContext ctx) { + } + + public void endVisit(JsBlock x, JsContext ctx) { + } + + public void endVisit(JsBooleanLiteral x, JsContext ctx) { + } + + public void endVisit(JsBreak x, JsContext ctx) { + } + + public void endVisit(JsCase x, JsContext ctx) { + } + + public void endVisit(JsCatch x, JsContext ctx) { + } + + public void endVisit(JsConditional x, JsContext ctx) { + } + + public void endVisit(JsContinue x, JsContext ctx) { + } + + public void endVisit(JsDebugger x, JsContext ctx) { + } + + public void endVisit(JsDefault x, JsContext ctx) { + } + + public void endVisit(JsDoWhile x, JsContext ctx) { + } + + public void endVisit(JsEmpty x, JsContext ctx) { + } + + public void endVisit(JsExprStmt x, JsContext ctx) { + } + + public void endVisit(JsFor x, JsContext ctx) { + } + + public void endVisit(JsForIn x, JsContext ctx) { + } + + public void endVisit(JsFunction x, JsContext ctx) { + } + + public void endVisit(JsIf x, JsContext ctx) { + } + + public void endVisit(JsInvocation x, JsContext ctx) { + } + + public void endVisit(JsLabel x, JsContext ctx) { + } + + public void endVisit(JsNameRef x, JsContext ctx) { + } + + public void endVisit(JsNew x, JsContext ctx) { + } + + public void endVisit(JsNullLiteral x, JsContext ctx) { + } + + public void endVisit(JsNumberLiteral x, JsContext ctx) { + } + + public void endVisit(JsObjectLiteral x, JsContext ctx) { + } + + public void endVisit(JsParameter x, JsContext ctx) { + } + + public void endVisit(JsPostfixOperation x, JsContext ctx) { + } + + public void endVisit(JsPrefixOperation x, JsContext ctx) { + } + + public void endVisit(JsProgram x, JsContext ctx) { + } + + public void endVisit(JsProgramFragment x, JsContext ctx) { + } + + public void endVisit(JsPropertyInitializer x, JsContext ctx) { + } + + public void endVisit(JsRegExp x, JsContext ctx) { + } + + public void endVisit(JsReturn x, JsContext ctx) { + } + + public void endVisit(JsStringLiteral x, JsContext ctx) { + } + + public void endVisit(JsSwitch x, JsContext ctx) { + } + + public void endVisit(JsThisRef x, JsContext ctx) { + } + + public void endVisit(JsThrow x, JsContext ctx) { + } + + public void endVisit(JsTry x, JsContext ctx) { + } + + public void endVisit(JsVar x, JsContext ctx) { + } + + public void endVisit(JsVars x, JsContext ctx) { + } + + public void endVisit(JsWhile x, JsContext ctx) { + } + + public boolean visit(JsArrayAccess x, JsContext ctx) { + return true; + } + + public boolean visit(JsArrayLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsBinaryOperation x, JsContext ctx) { + return true; + } + + public boolean visit(JsBlock x, JsContext ctx) { + return true; + } + + public boolean visit(JsBooleanLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsBreak x, JsContext ctx) { + return true; + } + + public boolean visit(JsCase x, JsContext ctx) { + return true; + } + + public boolean visit(JsCatch x, JsContext ctx) { + return true; + } + + public boolean visit(JsConditional x, JsContext ctx) { + return true; + } + + public boolean visit(JsContinue x, JsContext ctx) { + return true; + } + + public boolean visit(JsDebugger x, JsContext ctx) { + return true; + } + + public boolean visit(JsDefault x, JsContext ctx) { + return true; + } + + public boolean visit(JsDoWhile x, JsContext ctx) { + return true; + } + + public boolean visit(JsEmpty x, JsContext ctx) { + return true; + } + + public boolean visit(JsExprStmt x, JsContext ctx) { + return true; + } + + public boolean visit(JsFor x, JsContext ctx) { + return true; + } + + public boolean visit(JsForIn x, JsContext ctx) { + return true; + } + + public boolean visit(JsFunction x, JsContext ctx) { + return true; + } + + public boolean visit(JsIf x, JsContext ctx) { + return true; + } + + public boolean visit(JsInvocation x, JsContext ctx) { + return true; + } + + public boolean visit(JsLabel x, JsContext ctx) { + return true; + } + + public boolean visit(JsNameRef x, JsContext ctx) { + return true; + } + + public boolean visit(JsNew x, JsContext ctx) { + return true; + } + + public boolean visit(JsNullLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsNumberLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsObjectLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsParameter x, JsContext ctx) { + return true; + } + + public boolean visit(JsPostfixOperation x, JsContext ctx) { + return true; + } + + public boolean visit(JsPrefixOperation x, JsContext ctx) { + return true; + } + + public boolean visit(JsProgram x, JsContext ctx) { + return true; + } + + public boolean visit(JsProgramFragment x, JsContext ctx) { + return true; + } + + public boolean visit(JsPropertyInitializer x, JsContext ctx) { + return true; + } + + public boolean visit(JsRegExp x, JsContext ctx) { + return true; + } + + public boolean visit(JsReturn x, JsContext ctx) { + return true; + } + + public boolean visit(JsStringLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsSwitch x, JsContext ctx) { + return true; + } + + public boolean visit(JsThisRef x, JsContext ctx) { + return true; + } + + public boolean visit(JsThrow x, JsContext ctx) { + return true; + } + + public boolean visit(JsTry x, JsContext ctx) { + return true; + } + + public boolean visit(JsVar x, JsContext ctx) { + return true; + } + + public boolean visit(JsVars x, JsContext ctx) { + return true; + } + + public boolean visit(JsWhile x, JsContext ctx) { + return true; + } + + protected T doAccept(T node) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + return node; + } + + protected void doAcceptList(List collection) { + for (T node : collection) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + } + } + + protected JsExpression doAcceptLvalue(JsExpression expr) { + doTraverse(expr, LVALUE_CONTEXT); + return expr; + } + + protected void doAcceptWithInsertRemove(List collection) { + for (T node : collection) { + doTraverse(node, UNMODIFIABLE_CONTEXT); + } + } + + protected void doTraverse(JsVisitable node, JsContext ctx) { + node.traverse(this, ctx); + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/JsWhile.java b/compiler/java/com/google/dart/compiler/backend/js/ast/JsWhile.java new file mode 100644 index 00000000000..c40203bc1f1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/JsWhile.java @@ -0,0 +1,52 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript while statement. + */ +public class JsWhile extends JsStatement { + + private JsStatement body; + private JsExpression condition; + + public JsWhile() { + } + + public JsWhile(JsExpression condition, JsStatement body) { + this.condition = condition; + this.body = body; + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getCondition() { + return condition; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setCondition(JsExpression condition) { + this.condition = condition; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + condition = v.accept(condition); + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.WHILE; + } +} diff --git a/compiler/java/com/google/dart/compiler/backend/js/ast/NodeKind.java b/compiler/java/com/google/dart/compiler/backend/js/ast/NodeKind.java new file mode 100644 index 00000000000..614e4bb3bd9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/backend/js/ast/NodeKind.java @@ -0,0 +1,50 @@ +// 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. + +package com.google.dart.compiler.backend.js.ast; + +public enum NodeKind { + ARRAY_ACCESS, + ARRAY, + BINARY_OP, + BLOCK, + BOOLEAN, + BREAK, + CASE, + CATCH, + CONDITIONAL, + CONTINUE, + DEBUGGER, + DEFAULT, + DO, + EMPTY, + EXPR_STMT, + FOR, + FOR_IN, + FUNCTION, + IF, + INVOKE, + LABEL, + NAME_REF, + NEW, + NULL, + NUMBER, + OBJECT, + PARAMETER, + POSTFIX_OP, + PREFIX_OP, + PROGRAM, + PROGRAM_FRAGMENT, + PROPERTY_INIT, + REGEXP, + RETURN, + STRING, + SWITCH, + THIS, + THROW, + TRY, + VARS, + VAR, + WHILE +} diff --git a/compiler/java/com/google/dart/compiler/common/AbstractNode.java b/compiler/java/com/google/dart/compiler/common/AbstractNode.java new file mode 100644 index 00000000000..b4232327747 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/AbstractNode.java @@ -0,0 +1,82 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.common.base.Preconditions; +import com.google.dart.compiler.Source; + +/** + * Abstract base class for nodes that carry source information. + */ +public class AbstractNode implements SourceInfo, HasSourceInfo { + + // TODO(johnlenz): All this source location data is wasteful. + // Move it into a common object, that can be shared between the ASTs + // or something. + protected Source source = null; + protected int sourceLine = -1; + protected int sourceColumn = -1; + protected int sourceStart = -1; + protected int sourceLength = -1; + + @Override + public Source getSource() { + return source; + } + + @Override + public int getSourceLine() { + return sourceLine; + } + + @Override + public int getSourceColumn() { + return sourceColumn; + } + + @Override + public int getSourceStart() { + return sourceStart; + } + + @Override + public int getSourceLength() { + return sourceLength; + } + + @Override + public SourceInfo getSourceInfo() { + return this; + } + + @Override + public void setSourceInfo(SourceInfo info) { + source = info.getSource(); + sourceStart = info.getSourceStart(); + sourceLength = info.getSourceLength(); + sourceLine = info.getSourceLine(); + sourceColumn = info.getSourceColumn(); + } + + @Override + public final void setSourceLocation( + Source source, int line, int column, int startPosition, int length) { + Preconditions.checkArgument(startPosition != -1 && length >= 0 + || startPosition == -1 && length == 0); + this.source = source; + this.sourceLine = line; + this.sourceColumn = column; + this.sourceStart = startPosition; + this.sourceLength = length; + } + + public final void setSourceRange(int startPosition, int length) { + Preconditions.checkArgument(startPosition != -1 && length >= 0 + || startPosition == -1 && length == 0); + this.sourceStart = startPosition; + this.sourceLength = length; + } + +} diff --git a/compiler/java/com/google/dart/compiler/common/GenerateSourceMap.java b/compiler/java/com/google/dart/compiler/common/GenerateSourceMap.java new file mode 100644 index 00000000000..5435b9a5069 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/GenerateSourceMap.java @@ -0,0 +1,85 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.backend.js.ast.HasName; +import com.google.debugging.sourcemap.FilePosition; +import com.google.debugging.sourcemap.SourceMapFormat; +import com.google.debugging.sourcemap.SourceMapGenerator; +import com.google.debugging.sourcemap.SourceMapGeneratorFactory; +import com.google.debugging.sourcemap.SourceMapSection; + +import java.io.IOException; +import java.util.List; + +/** + * Collects information mapping the generated (compiled) source back to + * its original source for debugging purposes. + * + * @author johnlenz@google.com (John Lenz) + */ +public class GenerateSourceMap { + + private final SourceMapGenerator generator; + + public GenerateSourceMap() { + // Get the source map in the default format. + this.generator = SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3); + } + + /** + * Adds a mapping for the given node. Mappings must be added in order. + * + * @param node The node that the new mapping represents. + * @param startPosition The position on the starting line + * @param endPosition The position on the ending line. + */ + public void addMapping( + HasSourceInfo node, FilePosition startPosition, FilePosition endPosition) { + SourceInfo sourceInfo = node.getSourceInfo(); + + // If the node does not have an associated source file or + // its line number is -1, then the node does not have sufficient + // information for a mapping to be useful. + if (sourceInfo.getSource() == null || sourceInfo.getSourceLine() < 0) { + return; + } + + String sourceFile = sourceInfo.getSource().getName(); + + String originalName = null; + if (node instanceof HasName) { + Symbol symbol = ((HasName)node).getSymbol(); + if (symbol != null) { + originalName = symbol.getOriginalSymbolName(); + } + } else if (node instanceof DartIdentifier) { + // We need a better abstraction, see bug 4188120. + originalName = ((DartIdentifier) node).getTargetName(); + } else if (node instanceof DartPropertyAccess) { + // We need a better abstraction, see bug 4188120. + originalName = ((DartPropertyAccess) node).getPropertyName(); + } + generator.addMapping(sourceFile, originalName, new FilePosition( + sourceInfo.getSourceLine(), sourceInfo.getSourceColumn()), startPosition, endPosition); + } + + public void appendTo(Appendable out, String name) throws IOException { + generator.appendTo(out, name); + } + + /** + * To facilitate incremental compiles, create source map that is built + * piecemeal from other source maps. + * @throws IOException + */ + public void appendIndexMapTo( + Appendable out, String name, List appSections) + throws IOException { + generator.appendIndexMapTo(out, name, appSections); + } +} diff --git a/compiler/java/com/google/dart/compiler/common/HasSourceInfo.java b/compiler/java/com/google/dart/compiler/common/HasSourceInfo.java new file mode 100644 index 00000000000..bda3da94996 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/HasSourceInfo.java @@ -0,0 +1,55 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.dart.compiler.Source; + +/** + * Abstract view of a class that has source info. + */ +public interface HasSourceInfo { + + /** + * Return the source info associated with this object. + */ + SourceInfo getSourceInfo(); + + /** + * Set the source info associated with this object. May only be called once. + * @param info + */ + void setSourceInfo(SourceInfo info); + + /** + * Sets the source range of the original source file where the source fragment + * corresponding to this node was found. + * + *

    + * Each node in the subtree (other than the contrived nodes) carries source + * range(s) information relating back to positions in the given source (the + * given source itself is not remembered with the AST). The source range + * usually begins at the first character of the first token corresponding to + * the node; leading whitespace and comments are not included. The + * source range usually extends through the last character of the last token + * corresponding to the node; trailing whitespace and comments are not + * included. There are a handful of exceptions (including the various body + * declarations). Source ranges nest properly: the source range for a child is + * always within the source range of its parent, and the source ranges of + * sibling nodes never overlap. + * + * @param source the associated source + * @param line the 1-based line index, or -1, if no source + * location is available + * @param column the 1-based column index, or -1, if no source + * location is available + * @param startPosition a 0-based character index, or -1, if no + * source location is available + * @param length a (possibly 0) length, or -1, if no source + * location is available + * @see SourceInfo#getSourceStart() + * @see SourceInfo#getSourceLength() + */ + void setSourceLocation(Source source, int line, int column, int startPosition, int length); +} diff --git a/compiler/java/com/google/dart/compiler/common/HasSymbol.java b/compiler/java/com/google/dart/compiler/common/HasSymbol.java new file mode 100644 index 00000000000..0128679bed1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/HasSymbol.java @@ -0,0 +1,16 @@ +// 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. + +package com.google.dart.compiler.common; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public interface HasSymbol { + /** + * @return Return the original user visible name for a Object represented + * in a source map. + */ + Symbol getSymbol(); +} diff --git a/compiler/java/com/google/dart/compiler/common/Name.java b/compiler/java/com/google/dart/compiler/common/Name.java new file mode 100644 index 00000000000..2ddd51256b4 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/Name.java @@ -0,0 +1,125 @@ +// 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. + +package com.google.dart.compiler.common; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Serializable; +import java.io.Writer; +import java.nio.charset.Charset; + +/** + * Instead of Strings, we use Names to abstract the underlying representation. + * + *

      + *
    • Names are globally unique and use identity for equality.
    • + *
    • Names are interned, so all references to the same name use the same + * bytes.
    • + *
    + * TODO(scottb): use byte[] instead of char[] when the parser is rewritten + * to parse byte[]. + */ +public final class Name implements Serializable { + + /** + * The encoding this class uses when converting between chars and bytes. + */ + public static final Charset CHARSET = Charset.forName("UTF-8"); + + private static final NameFactory factory = new NameFactory(); + + private static final long serialVersionUID = 0L; + + /** + * Return the Name corresponding to the data. An internal reference to + * data is kept for efficiency, do NOT mutate data after calling + * this method. + */ + public static Name of(char[] data) { + return factory.of(data); + } + + /** + * Return the Name corresponding to the data. An internal copy of the data is + * made. + */ + public static Name of(char[] data, int offset, int length) { + return factory.of(data, offset, length); + } + + static int computeHashCode(char[] data, int offset, int length) { + // Effective Java Item 9. + int result = 89; + for (int i = offset, end = offset + length; i < end; ++i) { + result *= 31; + result += data[i]; + } + return result; + } + + final char[] data; + + private final transient int hashCode; + + Name(char[] data, int hashCode) { + this.data = data; + this.hashCode = hashCode; + } + + /** + * Always compares based on identity. + */ + @Override + public boolean equals(Object obj) { + return this == obj; + } + + /** + * Returns the hashCode of the underlying data. + */ + @Override + public int hashCode() { + return hashCode; + } + + /** + * Constructs a String to represent the internal data. + */ + @Override + public String toString() { + return String.valueOf(data); + } + + /** + * Write my data into a {@link OutputStream} using the encoding specified in + * {@link #CHARSET}. + */ + public void writeBytesTo(OutputStream out) throws IOException { + // TODO(scottb): avoid allocating the String. + out.write(new String(data).getBytes(CHARSET)); + } + + /** + * Write my character data into a {@link PrintStream}. + */ + public void writeCharsTo(PrintStream out) { + out.print(data); + } + + /** + * Write my character data into a {@link Writer}. + */ + public void writeCharsTo(Writer writer) throws IOException { + writer.write(data); + } + + /** + * Replace with the canonical instance. + */ + private Object readResolve() { + return Name.of(data); + } +} diff --git a/compiler/java/com/google/dart/compiler/common/NameFactory.java b/compiler/java/com/google/dart/compiler/common/NameFactory.java new file mode 100644 index 00000000000..1edfe141283 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/NameFactory.java @@ -0,0 +1,226 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.MapMaker; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.concurrent.ConcurrentMap; + +/** + * Manages the life cycle, uniqueness, and object identity invariants of + * {@link Name}. + */ +final class NameFactory { + /* + * TODO: does this actually save memory in practice? For each interned Name, + * we use: + * + * 1) The data array (n bytes). + * + * 2) The Name object (2 fields) + * + * 3) The RealKey (1 field + 4 fields from Reference). + * + * 4) ConcurrentMap.Entry (4 fields) + * + * Of course, a Map is really much heavier than we need, all we really need is + * a Set where you can retrieve an object already in the Set. A bare linear + * probe hash table of RealKey would let us get rid of the Entry object. + */ + + /** + * The whole point of this class is to cheaply create a light-weight key that + * doesn't need to make its own copy of the data. This object is constructed + * with equality semantics to {@link RealKey}, for doing cheap map lookups. + */ + private static final class FakeKey { + private final char[] data; + private final int hashCode; + private final int length; + private final int offset; + + public FakeKey(char[] data, int hashCode) { + this(data, 0, data.length, hashCode); + } + + public FakeKey(char[] data, int offset, int length, int hashCode) { + this.data = data; + this.offset = offset; + this.length = length; + this.hashCode = hashCode; + } + + @Override + public boolean equals(Object obj) { + /* + * NOTE: this ONLY WORKS for comparisons to RealKey. But that's all we + * need since we only store real keys in the map. + */ + Name name = ((RealKey) obj).get(); + if (name == null) { + return false; + } + return equalsName(name); + } + + public boolean equalsName(Name name) { + if (this.length != name.data.length) { + return false; + } + int itMine = this.offset; + int itTheirs = 0; + int endMine = this.offset + this.length; + while (itMine < endMine) { + if (this.data[itMine++] != name.data[itTheirs++]) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return "FakeKey(" + String.valueOf(data, offset, length) + ")"; + } + } + + /** + * Adapted from {@link com.google.common.collect.Interners#newWeakInterner()}. + */ + private static final class RealKey extends WeakReference { + /** + * Must store the hashCode locally so we can be removed from + * {@link NameFactory#map} after our referent is cleared. + */ + private final int hashCode; + + public RealKey(Name name, ReferenceQueue queue) { + super(name, queue); + hashCode = name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof FakeKey) { + Name referent = get(); + if (referent == null) { + return false; + } + return ((FakeKey) obj).equalsName(referent); + } + if (obj instanceof RealKey) { + Name referent = get(); + if (referent == null) { + return false; + } + Name otherReferent = ((RealKey) obj).get(); + if (otherReferent == null) { + return false; + } + return Arrays.equals(referent.data, otherReferent.data); + } + return false; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + Name referent = get(); + return "RealKey(" + referent + ")"; + } + } + + private final ConcurrentMap map = new MapMaker().makeMap(); + private final ReferenceQueue queue = new ReferenceQueue(); + + /** + * Return the Name corresponding to the data. An internal reference to + * data may be kept. + */ + public Name of(char[] data) { + cleanUp(); + int hashCode = Name.computeHashCode(data, 0, data.length); + FakeKey fakeKey = new FakeKey(data, hashCode); + Name result = get(fakeKey); + if (result == null) { + result = put(new Name(data, hashCode)); + } + return result; + } + + /** + * Return the Name corresponding to the data. An internal copy of the data is + * made. + */ + public Name of(char[] data, int offset, int length) { + cleanUp(); + int hashCode = Name.computeHashCode(data, offset, length); + FakeKey fakeKey = new FakeKey(data, offset, length, hashCode); + Name result = get(fakeKey); + if (result == null) { + char[] copy = new char[length]; + System.arraycopy(data, offset, copy, 0, length); + result = put(new Name(copy, hashCode)); + } + return result; + } + + @VisibleForTesting + void cleanUp() { + RealKey item; + while ((item = (RealKey) queue.poll()) != null) { + map.remove(item); + } + } + + @VisibleForTesting + WeakReference getRefFor(Name name) { + return map.get(new FakeKey(name.data, name.hashCode())); + } + + @VisibleForTesting + int numEntries() { + return map.size(); + } + + private Name get(FakeKey fakeKey) { + RealKey realKey = map.get(fakeKey); + if (realKey != null) { + return realKey.get(); + } + return null; + } + + private Name put(Name name) { + RealKey realKey = new RealKey(name, queue); + while (true) { + RealKey sneakyRef = map.putIfAbsent(realKey, realKey); + if (sneakyRef == null) { + return name; + } else { + Name canonical = sneakyRef.get(); + if (canonical != null) { + return canonical; + } + } + } + } +} diff --git a/compiler/java/com/google/dart/compiler/common/SourceInfo.java b/compiler/java/com/google/dart/compiler/common/SourceInfo.java new file mode 100644 index 00000000000..261e037f864 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/SourceInfo.java @@ -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. + +package com.google.dart.compiler.common; + +import com.google.dart.compiler.Source; + +import java.io.Serializable; + +/** + * Tracks file and line information for AST nodes. + */ +public interface SourceInfo extends Serializable { + + /** + * The source code provider. + */ + Source getSource(); + + /** + * @return A 1-based line number into the original source file indicating + * where the source fragment begins. + */ + int getSourceLine(); + + /** + * @return A 1-based column number into the original source file indicating + * where the source fragment begins. + */ + int getSourceColumn(); + + /** + * Returns the character index into the original source file indicating + * where the source fragment corresponding to this node begins. + * + *

    + * The parser supplies useful well-defined source ranges to the nodes it creates. + * + * @return the 0-based character index, or -1 + * if no source startPosition information is recorded for this node + * @see #getSourceLength() + * @see HasSourceInfo#setSourceLocation(Source, int, int, int, int) + */ + int getSourceStart(); + + /** + * Returns the length in characters of the original source file indicating + * where the source fragment corresponding to this node ends. + *

    + * The parser supplies useful well-defined source ranges to the nodes it creates. + * + * @return a (possibly 0) length, or 0 + * if no source source position information is recorded for this node + * @see #getSourceStart() + * @see HasSourceInfo#setSourceLocation(Source, int, int, int, int) + */ + int getSourceLength(); +} diff --git a/compiler/java/com/google/dart/compiler/common/SourceMapping.java b/compiler/java/com/google/dart/compiler/common/SourceMapping.java new file mode 100644 index 00000000000..6aee6d77ecc --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/SourceMapping.java @@ -0,0 +1,52 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.debugging.sourcemap.FilePosition; + + +/** + * Maintains a mapping from a given node to the position + * in the source code at which its generated form was + * placed. This position is relative only to the current + * run. + * + * @see GenerateSourceMap + */ +public class SourceMapping { + final HasSourceInfo node; + final FilePosition start; + FilePosition end; + + public SourceMapping(HasSourceInfo node, FilePosition start) { + this.node = node; + this.start = start; + } + + /** + * @return the end + */ + public FilePosition getEnd() { + return end; + } + /** + * @param end the end to set + */ + public void setEnd(FilePosition end) { + this.end = end; + } + /** + * @return the node + */ + public HasSourceInfo getNode() { + return node; + } + /** + * @return the start + */ + public FilePosition getStart() { + return start; + } +} diff --git a/compiler/java/com/google/dart/compiler/common/Symbol.java b/compiler/java/com/google/dart/compiler/common/Symbol.java new file mode 100644 index 00000000000..78c20ad76d8 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/common/Symbol.java @@ -0,0 +1,16 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.dart.compiler.ast.DartNode; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public interface Symbol { + String getOriginalSymbolName(); + + DartNode getNode(); +} diff --git a/compiler/java/com/google/dart/compiler/metrics/CompilerMetrics.java b/compiler/java/com/google/dart/compiler/metrics/CompilerMetrics.java new file mode 100644 index 00000000000..07a715bf822 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/metrics/CompilerMetrics.java @@ -0,0 +1,247 @@ +// 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. + +package com.google.dart.compiler.metrics; + +import java.io.PrintStream; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Collection of compiler metrics. + */ +public final class CompilerMetrics { + // TODO: Consider refactoring this class so each subsystem has it own metrics class. + + private final long milliStartTime; + private long milliEndTime = -1; + + // Parser metrics + private AtomicLong unitsParsed = new AtomicLong(); + private AtomicLong charactersParsed = new AtomicLong(); + private AtomicLong linesParsed = new AtomicLong(); + private AtomicLong charactersParsedExcludingComments = new AtomicLong(); + private AtomicLong linesParsedExcludingComments = new AtomicLong(); + private long nanoParseWallTime = 0; + private AtomicLong nanoTotalParseTime = new AtomicLong(); + + // JavascriptBackend Data + private long totalJsOutputCharCount; + private long nativeLibCharCount; + + // Timing metrics for complete stages + private long updateAndResolveTimeStart = 0L; + private long compileLibrariesTimeStart = 0L; + private long packageAppTimeStart = 0L; + private long updateAndResolveTime = 0L; + private long compileLibrariesTime = 0L; + private long packageAppTime = 0L; + + public CompilerMetrics() { + this.milliStartTime = System.currentTimeMillis(); + } + + public void done() { + if (milliEndTime == -1) { + milliEndTime = System.currentTimeMillis(); + } + } + + public void unitParsed(int charactersParsed, int charactersParsedExcludingComments, + int linesParsed, int linesParsedExcludingComments) { + this.unitsParsed.incrementAndGet(); + this.charactersParsed.addAndGet(charactersParsed); + this.charactersParsedExcludingComments.addAndGet(charactersParsedExcludingComments); + this.linesParsed.addAndGet(linesParsed); + this.linesParsedExcludingComments.addAndGet(linesParsedExcludingComments); + } + + /** + * Writes the metrics to the {@link PrintStream}. + */ + public void write(PrintStream out) { + /* This is mainly for the metrics system. Units should be encoded in + * the label name and end up as the benchmark names. + */ + done(); + out.format("Compile-time-total-ms : %1$.2f%n", getTotalCompilationTime()); + out.format("# Update-and-resolve-time-ms : %d\n", getUpdateAndResolveTime()); + out.format("# Compile-libraries-time-ms : %d\n", getCompileLibrariesTime()); + out.format("# Package-app-time-ms : %d\n", getPackageAppTime()); + out.println("# Compile-time-unit-average-ms : " + getTimeSpentPerUnit()); + out.format("# Parse-wall-time-ms : %1$.2f%n", getParseWallTime()); + out.format("# Parse-time-ms : %1$.2f%n", getParseTime()); + out.println("# Parsed-units : " + getNumUnitsParsed()); + out.println("# Parsed-src-chars : " + getNumCharsParsed()); + out.println("# Parsed-src-lines : " + getNumLinesParsed()); + out.println("# Parsed-code-chars : " + getNumNonCommentChars()); + out.println("# Parsed-code-lines : " + getNumNonCommentLines()); + out.println("# Output-js-chars : " + getJSOutputCharSize()); + double jsNativeLibCharSize = (getJSNativeLibCharSize() == -1) ? 0 : getJSNativeLibCharSize(); + out.println("# Output-js-native-lib-chars : " + jsNativeLibCharSize ); + out.println("# Processed-total-lines-ms : " + getLinesPerMS()); + out.println("# Processed-code-lines-ms : " + getNonCommentLinesPerMS()); + out.println("# Ratio-output-intput-total : " + getRatioOutputToInput()); + out.println("# Ratio-output-intput-code : " + getRatioOutputToInputExcludingComments()); + out.println("# Ratio-parsing-compile-percent : " + getPercentTimeParsing() * 100); + } + + private static double nanoToMillis(long nanoTime) { + return nanoTime / 1000000.0d; + } + + /** + * Records that the application was packaged to JS. + * + * @param totalJsOutputCharSize number of characts of JS output produced + * @param nativeLibCharCount number of characters of JS output consumed by native JS libs or -1 if + * the backend did not record this information + */ + public void packagedJsApplication(long totalJsOutputCharSize, long nativeLibCharCount) { + this.totalJsOutputCharCount = totalJsOutputCharSize; + this.nativeLibCharCount = nativeLibCharCount; + } + + /** + * Accumulate more parsing time. TODO: Once the parser gets cleaned up we should be able to + * integrate this with unit parsed. + */ + public void addParseTimeNano(long nanoTotalParseTime) { + this.nanoTotalParseTime.addAndGet(nanoTotalParseTime); + } + + public void addParseWallTimeNano( long nanoWallParseTime) { + this.nanoParseWallTime = nanoWallParseTime; + } + + /** + * Returns the current thread's CPU time or -1 if this is not supported. + */ + public static long getThreadTime() { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + if (threadMXBean.isThreadCpuTimeSupported()) { + return threadMXBean.getCurrentThreadCpuTime(); + } + + return -1; + } + + public static long getCPUTime() { + return System.currentTimeMillis() * 1000000; + } + + public double getTotalCompilationTime() { + return milliEndTime - milliStartTime; + } + + public double getParseTime() { + return nanoToMillis(nanoTotalParseTime.get()); + } + + public double getParseWallTime() { + return nanoToMillis(nanoParseWallTime); + } + + public double getNumUnitsParsed() { + return unitsParsed.get(); + } + + public double getNumCharsParsed() { + return charactersParsed.get(); + } + + public double getNumLinesParsed() { + return linesParsed.get(); + } + + public double getNumNonCommentChars() { + return charactersParsedExcludingComments.get(); + } + + public double getNumNonCommentLines() { + return linesParsedExcludingComments.get(); + } + + public double getJSOutputCharSize() { + return totalJsOutputCharCount; + } + + public double getJSNativeLibCharSize() { + return nativeLibCharCount; + } + + public double getPercentCharsConsumedByNativeLibraries() { + return (getJSNativeLibCharSize() / getNumCharsParsed()) * 100d; + } + + public double getPercentTimeParsing() { + return getParseTime() / getTotalCompilationTime(); + } + + public double getTimeSpentPerUnit() { + if (getNumUnitsParsed() == 0) { + return 0; + } + return getTotalCompilationTime() / getNumUnitsParsed(); + } + + public double getLinesPerMS() { + return getNumLinesParsed() / getTotalCompilationTime(); + } + + public double getNonCommentLinesPerMS() { + return getNumNonCommentLines() / getTotalCompilationTime(); + } + + public double getRatioOutputToInput() { + if (getNumCharsParsed() == 0) { + return 0; + } + return getJSOutputCharSize() / getNumCharsParsed(); + } + + public double getRatioOutputToInputExcludingComments() { + if (getNumNonCommentChars() == 0) { + return 0; + } + return getJSOutputCharSize() / getNumNonCommentChars(); + } + + public long getUpdateAndResolveTime() { + return updateAndResolveTime; + } + + public long getCompileLibrariesTime() { + return compileLibrariesTime; + } + + public long getPackageAppTime() { + return packageAppTime; + } + + public void startUpdateAndResolveTime() { + updateAndResolveTimeStart = System.currentTimeMillis(); + } + + public void startCompileLibrariesTime() { + compileLibrariesTimeStart = System.currentTimeMillis(); + } + + public void startPackageAppTime() { + packageAppTimeStart = System.currentTimeMillis(); + } + + public void endUpdateAndResolveTime() { + updateAndResolveTime = System.currentTimeMillis() - updateAndResolveTimeStart; + } + + public void endCompileLibrariesTime() { + compileLibrariesTime = System.currentTimeMillis() - compileLibrariesTimeStart; + } + + public void endPackageAppTime() { + packageAppTime = System.currentTimeMillis() - packageAppTimeStart; + } +} diff --git a/compiler/java/com/google/dart/compiler/metrics/DartEventType.java b/compiler/java/com/google/dart/compiler/metrics/DartEventType.java new file mode 100644 index 00000000000..68164fae751 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/metrics/DartEventType.java @@ -0,0 +1,63 @@ +// 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. + +package com.google.dart.compiler.metrics; + +import com.google.dart.compiler.metrics.Tracer.EventType; + +/** + * Dart events for SpeedTracer. + */ +public enum DartEventType implements EventType { + ADD_OUTOFDATE("MistyRose"), + BACKEND_COMPILE("DarkGreen"), + BACKEND_OUTOFDATE("DarkOliveGreen"), + BACKEND_PACKAGE_APP("MediumPurple"), + BUILD_LIB_SCOPES("violet"), + COMPILE("green"), + COMPILE_APP("gray"), + COMPILE_LIBRARIES("brown"), + EXEC_PHASE("blue"), + IMPORT_EMBEDDED_LIBRARIES("purple"), + IS_CLASS_OUT_OF_DATE("Aqua"), + IS_SOURCE_OUTOFDATE("Chartreuse"), + JS_NORMALIZE("DarkMagenta"), + JS_SOURCE_GEN("LightBlue"), + NAMER("MidnightBlue"), + PACKAGE_APP("pink"), + SCANNER("GoldenRod"), + PARSE("red"), + PARSE_API("green"), + PARSE_OUTOFDATE("LightCoral"), + RESOLVE_LIBRARIES("black"), + TRANSLATE_NODE("Gold"), + TRANSLATE_TO_JS("MediumOrchid"), + UPDATE_LIBRARIES("yellow"), + UPDATE_RESOLVE("orange"), + WRITE_METRICS("LightChiffon"), + WRITE_SOURCE_MAP("MediumAquaMarine"), + GEN_AST_INIT("Olive"); + + final String cssColor; + final String name; + + DartEventType(String cssColor) { + this(null, cssColor); + } + + DartEventType(String name, String cssColor) { + this.name = name; + this.cssColor = cssColor; + } + + @Override + public String getColor() { + return cssColor; + } + + @Override + public String getName() { + return name == null ? toString() : name; + } +} diff --git a/compiler/java/com/google/dart/compiler/metrics/JvmMetrics.java b/compiler/java/com/google/dart/compiler/metrics/JvmMetrics.java new file mode 100644 index 00000000000..5123ed66cd8 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/metrics/JvmMetrics.java @@ -0,0 +1,249 @@ +// 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. + +package com.google.dart.compiler.metrics; + +import java.io.PrintStream; +import java.lang.management.CompilationMXBean; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.List; +import java.util.StringTokenizer; + +/** + * A class to report jvm/jmx statistics + */ +public class JvmMetrics { + + private static int TABULAR_COLON_POS = 40; + private static long ONE_KILO_BYTE = 1L << 10L; + private static long ONE_MEGA_BYTE = 1L << 20L; + private static long ONE_GIGA_BYTE = 1L << 30L; + + public static void maybeWriteJvmMetrics(PrintStream out, String options) { + if (options == null) { + return; + } + + boolean verboseMode = false; + boolean prettyMode = false; + StringTokenizer st = new StringTokenizer(options,":"); + // options are grouped in order 'detail:format:types' + if (st.hasMoreTokens()) { + String mode = st.nextToken(); + if (mode.equalsIgnoreCase("verbose")) { + verboseMode = true; + } + } + + if (st.hasMoreTokens()) { + String mode = st.nextToken(); + if (mode.equalsIgnoreCase("pretty")) { + prettyMode = true; + } + } + + if (st.hasMoreTokens()) { + while (st.hasMoreTokens()) { + String types = st.nextToken(); + StringTokenizer typeSt = new StringTokenizer(types,","); + while (typeSt.hasMoreElements()) { + String type = typeSt.nextToken(); + writeMetrics(out, type, verboseMode, prettyMode); + } + } + } else { + // the default + writeMetrics(out, "all", verboseMode, prettyMode); + } + } + + private static void writeMetrics(PrintStream out, String type, boolean verbose, boolean pretty) { + + if (type.equals("gc") || type.equalsIgnoreCase("all")) { + writeGarbageCollectionStats(out, verbose, pretty); + } + if (type.equals("mem") || type.equalsIgnoreCase("all")) { + writeMemoryMetrics(out, verbose, pretty); + } + if (type.equals("jit") || type.equalsIgnoreCase("all")) { + writeJitMetrics(out, verbose, pretty); + } + } + + private static void writeJitMetrics(PrintStream out, boolean verbose, boolean pretty) { + + CompilationMXBean cBean = ManagementFactory.getCompilationMXBean(); + + String name; + if (verbose) { + name = cBean.getName(); + } else { + name = "total"; + } + + if (pretty) { + out.println("\nJIT Stats"); + out.println(String.format("\t%s jit time: %d ms", name, cBean.getTotalCompilationTime())); + } else { + out.println(normalizeTabularColonPos(String.format("%s-jit-time-ms : %d", normalizeName(name), + cBean.getTotalCompilationTime()))); + } + } + + private static void writeOverallMemoryUsage(PrintStream out, MemoryUsage usage, + String prefix, boolean pretty) { + if (pretty) { + out.format("\t%s\n", prefix); + out.format("\t\tavailable : %s\n", formatBytes(usage.getMax())); + out.format("\t\tcurrent : %s\n", formatBytes(usage.getUsed())); + } else { + prefix = normalizeName(prefix); + out.println(normalizeTabularColonPos(String.format(prefix + "-available-bytes : %d", + usage.getMax()))); + out.println(normalizeTabularColonPos(String.format(prefix + "-current-bytes : %d", + usage.getUsed()))); + } + } + + private static void writePoolMemoryUsage(PrintStream out, MemoryUsage usage, + MemoryUsage peakUsage, String prefix, boolean pretty) { + if (pretty) { + out.format("\t\tavailable : %s\n", formatBytes(usage.getMax())); + out.format("\t\tpeak : %s\n", formatBytes(peakUsage.getUsed())); + out.format("\t\tcurrent : %s\n", formatBytes(usage.getUsed())); + } else { + out.println(normalizeTabularColonPos(String.format(prefix + "-available-bytes : %d", + usage.getMax()))); + out.println(normalizeTabularColonPos(String.format(prefix + "-peak-bytes : %d", + peakUsage.getUsed()))); + out.println(normalizeTabularColonPos(String.format(prefix + "-current-bytes : %d", + usage.getUsed()))); + } + } + + private static void writeMemoryMetrics(PrintStream out, boolean verbose, boolean pretty) { + if (pretty) { + out.println("\nMemory usage"); + } + + // only show overall stats in verbose mode + if (verbose) { + MemoryMXBean overallMemBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage usage = overallMemBean.getHeapMemoryUsage(); + writeOverallMemoryUsage(out, usage, "Heap", pretty); + + usage = overallMemBean.getNonHeapMemoryUsage(); + writeOverallMemoryUsage(out, usage, "Non-heap", pretty); + } + + if (verbose) { + List mpBeans = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean mpBean : mpBeans) { + MemoryUsage currentUsage = mpBean.getUsage(); + MemoryUsage peakUsage = mpBean.getPeakUsage(); + if (pretty) { + out.println("\tPool " + mpBean.getName()); + writePoolMemoryUsage(out, currentUsage, peakUsage, null, true); + } else { + writePoolMemoryUsage(out, currentUsage, peakUsage, + "mem-pool-" + normalizeName(mpBean.getName()), false); + } + } + } else { + long available = 0; + long current = 0; + long peak = 0; + List mpBeans = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean mpBean : mpBeans) { + MemoryUsage currentUsage = mpBean.getUsage(); + available += currentUsage.getMax(); + current += currentUsage.getUsed(); + MemoryUsage peakUsage = mpBean.getPeakUsage(); + peak += peakUsage.getUsed(); + } + MemoryUsage summaryUsage = new MemoryUsage(0, current, current, available); + MemoryUsage summaryPeakUsage = new MemoryUsage(0, peak, peak, peak); + if (pretty) { + out.format("\tAggregate of %d memory pools\n", mpBeans.size()); + writePoolMemoryUsage(out, summaryUsage, summaryPeakUsage, null, true); + } else { + writePoolMemoryUsage(out, summaryUsage, summaryPeakUsage, + "mem", false); + } + } + } + + private static void writeGarbageCollectionStats(PrintStream out, boolean verbose, boolean pretty) { + List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); + + if (verbose) { + if (pretty) { + out.println("\nGarbage collection stats"); + for (GarbageCollectorMXBean gcBean : gcBeans) { + out.println("\tCollector " + gcBean.getName()); + out.format("\t\tcollection count : %d\n", gcBean.getCollectionCount()); + out.format("\t\tcollection time : %d ms\n", gcBean.getCollectionTime()); + } + } else { + for (GarbageCollectorMXBean gcBean : gcBeans) { + String name = normalizeName(gcBean.getName()); + out.println(normalizeTabularColonPos(String.format("gc-" + name + "-collection-count : %d", + gcBean.getCollectionCount()))); + out.println(normalizeTabularColonPos(String.format("gc-" + name + "-collection-time-ms : %d", + gcBean.getCollectionTime()))); + } + } + } else { + long collectionCount = 0; + long collectionTime = 0; + int collectorCount = gcBeans.size(); + for (GarbageCollectorMXBean gcBean : gcBeans) { + collectionCount += gcBean.getCollectionCount(); + collectionTime += gcBean.getCollectionTime(); + } + if (pretty) { + out.println("\nGarbage collection stats"); + out.format("\tAggregate of %d collectors\n", collectorCount); + out.format("\t\tcollection count : %d\n", collectionCount); + out.format("\t\tcollection time : %d ms\n", collectionTime); + } else { + String name = normalizeName("aggregate"); + out.println(normalizeTabularColonPos(String.format("gc-" + name + "-collection-count : %d", + collectionCount))); + out.println(normalizeTabularColonPos(String.format("gc-" + name + "-collection-time-ms : %d", + collectionTime))); + } + } + } + + private static String normalizeName(String name) { + return name.replace(" ","_").toLowerCase(); + } + + private static String normalizeTabularColonPos(String string) { + StringBuilder sb = new StringBuilder(string); + int index = sb.indexOf(":"); + for (;index < TABULAR_COLON_POS;++index) { + sb.insert(index, ' '); + } + return sb.toString(); + } + + private static String formatBytes(long numBytes) { + if (numBytes < ONE_KILO_BYTE) { + return String.format("%d B", numBytes); + } else if (numBytes < ONE_MEGA_BYTE) { + return String.format("%d KB", numBytes / ONE_KILO_BYTE); + } else if (numBytes < ONE_GIGA_BYTE) { + return String.format("%d MB", numBytes / ONE_MEGA_BYTE); + } else { + return String.format("%d GB", numBytes / ONE_GIGA_BYTE); + } + } + +} diff --git a/compiler/java/com/google/dart/compiler/metrics/SpeedTracerEventType.java b/compiler/java/com/google/dart/compiler/metrics/SpeedTracerEventType.java new file mode 100644 index 00000000000..6c84f1b7c7f --- /dev/null +++ b/compiler/java/com/google/dart/compiler/metrics/SpeedTracerEventType.java @@ -0,0 +1,33 @@ +// 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. + +package com.google.dart.compiler.metrics; + +import com.google.dart.compiler.metrics.Tracer.EventType; + +/** + * Represents a type of event whose performance is tracked while running. + */ +public enum SpeedTracerEventType implements EventType { + GC("Garbage Collection", "Plum"), + OVERHEAD("Speedtracer Overhead","Black"); + + final String cssColor; + final String name; + + SpeedTracerEventType(String name, String cssColor) { + this.name = name; + this.cssColor = cssColor; + } + + @Override + public String getColor() { + return cssColor; + } + + @Override + public String getName() { + return name; + } +} diff --git a/compiler/java/com/google/dart/compiler/metrics/Tracer.java b/compiler/java/com/google/dart/compiler/metrics/Tracer.java new file mode 100644 index 00000000000..9103026b0b7 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/metrics/Tracer.java @@ -0,0 +1,959 @@ +// 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. + +package com.google.dart.compiler.metrics; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONArray; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * Logs performance metrics for internal development purposes. The output is + * formatted so it can be opened directly in the SpeedTracer Chrome extension. + * This class formats events using SpeedTracer's custom event feature. The html + * file output can be viewed by using Chrome to open the file on a Chrome + * browser that has the SpeedTracer extension installed. + * + *

    + * Enable logging by setting the system property {@code dart.speedtracerlog} to + * the output file path. + *

    + * + * NB: This class has been copied almost verbatim from the gwt source tree + */ +public final class Tracer { + + // Log file name (logging is enabled if this is non-null) + private static final String logFile = System.getProperty("dart.speedtracerlog"); + + // Allow a system property to override the default output format + private static final String defaultFormatString = System.getProperty("dart.speedtracerformat"); + + // Use cumulative multi-threaded process cpu time instead of wall time + private static final boolean logProcessCpuTime = + getBooleanProperty("dart.speedtracer.logProcessCpuTime"); + + // Use per thread cpu time instead of wall time. If logProcessCpuTime is set, + // then this can remain false - we only need one or the other. + private static final boolean logThreadCpuTime = + getBooleanProperty("dart.speedtracer.logThreadCpuTime"); + + // Turn on logging summarizing gc time during an event + private static final boolean logGcTime = getBooleanProperty("dart.speedtracer.logGcTime"); + + // Turn on logging estimating overhead used for speedtracer logging. + private static final boolean logOverheadTime = + getBooleanProperty("dart.speedtracer.logOverheadTime"); + + static { + // verify configuration + if (logProcessCpuTime && logThreadCpuTime) { + throw new RuntimeException("System properties are misconfigured: " + + "Specify one or the other of 'dart.speedtracer.logProcessCpuTime' " + + "or 'dart.speedtracer.logThreadCpuTime', not both."); + } + } + + /** + * Represents a node in a tree of SpeedTracer events. + */ + public class TraceEvent { + protected final EventType type; + List children; + List data; + + long elapsedDurationNanos; + long elapsedStartTimeNanos; + + long processCpuDurationNanos; + long processCpuStartTimeNanos; + + long threadCpuDurationNanos; + long threadCpuStartTimeNanos; + + TraceEvent() { + if (enabled) { + threadCpuTimeKeeper.resetTimeBase(); + recordStartTime(); + this.data = new ArrayList(); + this.children = new ArrayList(); + } else { + this.processCpuStartTimeNanos = 0L; + this.threadCpuStartTimeNanos = 0L; + this.elapsedStartTimeNanos = 0L; + this.data = null; + this.children = null; + } + this.type = null; + } + + TraceEvent(TraceEvent parent, EventType type, String... data) { + + if (parent != null) { + parent.children.add(this); + } + this.type = type; + assert (data.length % 2 == 0); + recordStartTime(); + this.data = new ArrayList(); + this.data.addAll(Arrays.asList(data)); + this.children = new ArrayList(); + } + + /** + * @param data key/value pairs to add to JSON object. + */ + public void addData(String... data) { + if (data != null) { + assert (data.length % 2 == 0); + this.data.addAll(Arrays.asList(data)); + } + } + + /** + * Signals the end of the current event. + */ + public void end(String... data) { + endImpl(this, data); + } + + /** + * Returns the event duration, in nanoseconds, for the log file. Depending + * on system properties, this will measured in elapsed time, process CPU + * time, or thread CPU time. + */ + public long getDurationNanos() { + return logProcessCpuTime ? processCpuDurationNanos : (logThreadCpuTime + ? threadCpuDurationNanos : elapsedDurationNanos); + } + + public long getElapsedDurationNanos() { + return this.elapsedDurationNanos; + } + + public long getElapsedStartTimeNanos() { + return this.elapsedStartTimeNanos; + } + + /** + * Returns the event start time, normalized in nanoseconds, for the log + * file. Depending on system properties, this will be normalized based on + * elapsed time, process CPU time, or thread CPU time. + */ + public long getStartTimeNanos() { + return logProcessCpuTime ? processCpuStartTimeNanos : (logThreadCpuTime + ? threadCpuStartTimeNanos : elapsedStartTimeNanos); + } + + public EventType getType() { + return type; + } + + @Override + public String toString() { + return type.getName(); + } + + /** + * Extends the durations of the current event by the durations of the + * specified event. + */ + void extendDuration(TraceEvent refEvent) { + elapsedDurationNanos += refEvent.elapsedDurationNanos; + processCpuDurationNanos += refEvent.processCpuDurationNanos; + threadCpuDurationNanos += refEvent.threadCpuDurationNanos; + } + + /** + * Sets the start time of this event to start immediately after the + * specified event ends. + */ + void setStartsAfter(TraceEvent refEvent) { + elapsedStartTimeNanos = refEvent.elapsedStartTimeNanos + refEvent.elapsedDurationNanos; + processCpuStartTimeNanos = + refEvent.processCpuStartTimeNanos + refEvent.processCpuDurationNanos; + threadCpuStartTimeNanos = refEvent.threadCpuStartTimeNanos + refEvent.threadCpuDurationNanos; + } + + JSONObject toJson() throws JSONException { + JSONObject json = new JSONObject(); + json.put("type", -2); + json.put("typeName", type.getName()); + json.put("color", type.getColor()); + double startMs = convertToMilliseconds(getStartTimeNanos()); + json.put("time", startMs); + double durationMs = convertToMilliseconds(getDurationNanos()); + json.put("duration", durationMs); + + JSONObject jsonData = new JSONObject(); + for (int i = 0; i < data.size(); i += 2) { + jsonData.put(data.get(i), data.get(i + 1)); + } + json.put("data", jsonData); + + JSONArray jsonChildren = new JSONArray(); + for (TraceEvent child : children) { + jsonChildren.put(child.toJson()); + } + json.put("children", jsonChildren); + + return json; + } + + /** + * Records the duration of this event based on the current time and the + * event's recorded start time. + */ + void updateDuration() { + long elapsedEndTimeNanos = elapsedTimeKeeper.normalizedTimeNanos(); + assert (elapsedEndTimeNanos >= elapsedStartTimeNanos); + elapsedDurationNanos = elapsedEndTimeNanos - elapsedStartTimeNanos; + + // don't bother making expensive time keeping method calls unless + // necessary + if (logProcessCpuTime) { + long processCpuEndTimeNanos = processCpuTimeKeeper.normalizedTimeNanos(); + assert (processCpuEndTimeNanos >= processCpuStartTimeNanos); + processCpuDurationNanos = processCpuEndTimeNanos - processCpuStartTimeNanos; + } else if (logThreadCpuTime) { + long threadCpuEndTimeNanos = threadCpuTimeKeeper.normalizedTimeNanos(); + assert (threadCpuEndTimeNanos >= threadCpuStartTimeNanos); + threadCpuDurationNanos = threadCpuEndTimeNanos - threadCpuStartTimeNanos; + } + } + + /** + * Marks the start time for this event. Three different time measurements + * are used: + *
      + *
    1. Elapsed (wall-clock) time
    2. + *
    3. Process CPU time
    4. + *
    5. Thread CPU time
    6. + *
    + */ + private void recordStartTime() { + elapsedStartTimeNanos = elapsedTimeKeeper.normalizedTimeNanos(); + + // don't bother making expensive time keeping method calls unless + // necessary + if (logProcessCpuTime) { + processCpuStartTimeNanos = processCpuTimeKeeper.normalizedTimeNanos(); + } else if (logThreadCpuTime) { + threadCpuStartTimeNanos = threadCpuTimeKeeper.normalizedTimeNanos(); + } + } + } + + /** + * Enumerated types for logging events implement this interface. + */ + public interface EventType { + String getColor(); + + String getName(); + } + + static enum Format { + /** + * Standard SpeedTracer log that includes JSON wrapped in HTML that will + * launch a SpeedTracer monitor session. + */ + HTML, + + /** + * Only the JSON data without any HTML wrappers. + */ + RAW + } + + /** + * A dummy implementation to do nothing if logging has not been turned on. + */ + private class DummyEvent extends TraceEvent { + @Override + public void addData(String... data) { + // do nothing + } + + @Override + public void end(String... data) { + // do nothing + } + + @Override + public String toString() { + return "Dummy"; + } + } + + /** + * Provides functionality specific to garbage collection events. + */ + private class GcEvent extends TraceEvent { + private TraceEvent refEvent; + + /** + * Constructs an event that represents garbage collection metrics. + * + * @param refEvent the event during which the garbage collections took place + * @param gcType the garbage collector type + * @param collectionCount the total number of collections for this garbage + * collector type + * @param durationNanos the total elapsed time spent in garbage collection + * during the span of {@code refEvent} + */ + GcEvent(TraceEvent refEvent, String gcType, long collectionCount, long durationNanos) { + super(null, SpeedTracerEventType.GC, "Collector Type", gcType, "Cumulative Collection Count", + Long.toString(collectionCount)); + + this.refEvent = refEvent; + // GarbageCollectorMXBean can only provide elapsed time, so that's all we + // record + this.elapsedDurationNanos = durationNanos; + } + + /** + * Returns elapsed duration since that is the only duration we can measure + * for garbage collection events. + */ + @Override + public long getDurationNanos() { + return getElapsedDurationNanos(); + } + + /** + * Returns a start time so that this event ends with its {@code refEvent}. + */ + @Override + public long getElapsedStartTimeNanos() { + return refEvent.getElapsedStartTimeNanos() + refEvent.getElapsedDurationNanos() + - getElapsedDurationNanos(); + } + + /** + * Returns a start time so that this event ends with its {@code refEvent}. + */ + @Override + public long getStartTimeNanos() { + return refEvent.getStartTimeNanos() + refEvent.getDurationNanos() - getDurationNanos(); + } + } + + /** + * Time keeper which uses wall time. + */ + private class ElapsedNormalizedTimeKeeper { + + private final long zeroTimeMillis; + + public ElapsedNormalizedTimeKeeper() { + zeroTimeMillis = System.currentTimeMillis(); + } + + public long normalizedTimeNanos() { + return (System.currentTimeMillis() - zeroTimeMillis) * 1000000L; + } + + public long zeroTimeMillis() { + return zeroTimeMillis; + } + } + + /** + * Time keeper which uses process cpu time. This can be greater than wall + * time, since it is cumulative over the multiple threads of a process. + */ + private class ProcessNormalizedTimeKeeper { + private final OperatingSystemMXBean osMXBean; + private final Method getProcessCpuTimeMethod; + private final long zeroTimeNanos; + private final long zeroTimeMillis; + + public ProcessNormalizedTimeKeeper() { + try { + osMXBean = ManagementFactory.getOperatingSystemMXBean(); + /* + * Find this method by reflection, since it's part of the Sun + * implementation for OperatingSystemMXBean, and we can't always assume + * that com.sun.management.OperatingSystemMXBean will be available. + */ + getProcessCpuTimeMethod = osMXBean.getClass().getMethod("getProcessCpuTime"); + getProcessCpuTimeMethod.setAccessible(true); + zeroTimeNanos = (Long) getProcessCpuTimeMethod.invoke(osMXBean); + zeroTimeMillis = (long) convertToMilliseconds(zeroTimeNanos); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public long normalizedTimeNanos() { + try { + return (Long) getProcessCpuTimeMethod.invoke(osMXBean) - zeroTimeNanos; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public long zeroTimeMillis() { + return zeroTimeMillis; + } + } + + /** + * Time keeper which uses per thread cpu time. It is assumed that individual + * events logged will be single threaded, and that top-level events will call + * {@link #resetTimeBase()} prior to logging time. The resettable time base is + * needed since each individual thread starts its timing at 0, regardless of + * when the thread is created. So we reset the time base at the beginning of + * an event, so that we can generate a chronologically representative output, + * although the relation to wall time is actually compressed within a logged + * event (thread cpu time does not include wait time, etc.). + */ + private class ThreadNormalizedTimeKeeper { + + private final ThreadMXBean threadMXBean; + private final ThreadLocal resettableTimeBase = new ThreadLocal(); + private final long zeroTimeNanos; + private final long zeroTimeMillis; + + public ThreadNormalizedTimeKeeper() { + threadMXBean = ManagementFactory.getThreadMXBean(); + if (!threadMXBean.isCurrentThreadCpuTimeSupported()) { + throw new RuntimeException("Current thread cpu time not supported"); + } + zeroTimeNanos = System.nanoTime(); + zeroTimeMillis = (long) convertToMilliseconds(zeroTimeNanos); + } + + public long normalizedTimeNanos() { + return threadMXBean.getCurrentThreadCpuTime() + resettableTimeBase.get(); + } + + public void resetTimeBase() { + /* + * Since all threads start individually at time 0L, we use this to offset + * each event's time so we can generate chronological output. + */ + resettableTimeBase.set(System.nanoTime() - zeroTimeNanos + - threadMXBean.getCurrentThreadCpuTime()); + } + + public long zeroTimeMillis() { + return zeroTimeMillis; + } + } + + /** + * Initializes the singleton on demand. + */ + private static class LazySpeedTracerLoggerHolder { + public static Tracer singleton = new Tracer(); + } + + /** + * Thread that converts log requests to JSON in the background. + */ + private class LogWriterThread extends Thread { + private static final int FLUSH_TIMER_MSECS = 10000; + private final String fileName; + private final BlockingQueue threadEventQueue; + private final Writer writer; + + public LogWriterThread(Writer writer, String fileName, final BlockingQueue eventQueue) { + super(); + this.writer = writer; + this.fileName = fileName; + this.threadEventQueue = eventQueue; + } + + @Override + public void run() { + long nextFlush = System.currentTimeMillis() + FLUSH_TIMER_MSECS; + try { + while (true) { + TraceEvent event = + threadEventQueue.poll(nextFlush - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + if (event == null) { + // ignore. + } else if (event == shutDownSentinel) { + break; + } else if (event == flushSentinel) { + writer.flush(); + flushLatch.countDown(); + } else { + JSONObject json = event.toJson(); + json.write(writer); + writer.write('\n'); + } + if (System.currentTimeMillis() >= nextFlush) { + writer.flush(); + nextFlush = System.currentTimeMillis() + FLUSH_TIMER_MSECS; + } + } + // All queued events have been written. + if (outputFormat.equals(Format.HTML)) { + writer.write("\n"); + } + writer.close(); + } catch (InterruptedException ignored) { + } catch (IOException e) { + System.err.println("Unable to write to dart.speedtracerlog '" + + (fileName == null ? "" : fileName) + "'"); + e.printStackTrace(); + } catch (JSONException e) { + // TODO(jat): Auto-generated catch block + e.printStackTrace(); + } finally { + shutDownLatch.countDown(); + } + } + } + + /** + * Records a LOG_MESSAGE type of SpeedTracer event. + */ + private class MarkTimelineEvent extends TraceEvent { + public MarkTimelineEvent(TraceEvent parent) { + super(); + if (parent != null) { + parent.children.add(this); + } + } + + @Override + JSONObject toJson() throws JSONException { + JSONObject json = new JSONObject(); + json.put("type", 11); + double startMs = convertToMilliseconds(getStartTimeNanos()); + json.put("time", startMs); + json.put("duration", 0.0); + JSONObject jsonData = new JSONObject(); + for (int i = 0; i < data.size(); i += 2) { + jsonData.put(data.get(i), data.get(i + 1)); + } + json.put("data", jsonData); + return json; + } + } + + /** + * Annotate the current event on the top of the stack with more information. + * The method expects key, value pairs, so there must be an even number of + * parameters. + * + * @param data JSON property, value pair to add to current event. + */ + public static void addData(String... data) { + Tracer.get().addDataImpl(data); + } + + /** + * Create a new global instance. Force the zero time to be recorded and the + * log to be opened if the default logging is turned on with the + * -Ddart.speedtracerlog VM property. + * + * This method is only intended to be called once. + */ + public static void init() { + get(); + } + + /** + * Returns true if the trace output file is configured. This is intended to be + * the quickest possible check, statically determined. + */ + public static boolean canTrace() { + return logFile != null; + } + + /** + * Adds a LOG_MESSAGE SpeedTracer event to the log. This represents a single + * point in time and has a special representation in the SpeedTracer UI. + */ + public static void markTimeline(String message) { + Tracer.get().markTimelineImpl(message); + } + + /** + * Signals that a new event has started. You must end each event for each + * corresponding call to {@code start}. You may nest timing calls. + * + * @param type the type of event + * @param data a set of key-value pairs (each key is followed by its value) + * that contain additional information about the event + * @return an Event object to be ended by the caller + */ + public static TraceEvent start(EventType type, String... data) { + return Tracer.get().startImpl(type, data); + } + + private static double convertToMilliseconds(long nanos) { + return nanos / 1000000.0d; + } + + /** + * Convenience method for ending event, which might possibly be null. + */ + public static void end(TraceEvent event, String... data) { + if (event != null) { + event.end(data); + } + } + + /** + * For accessing the logger as a singleton, you can retrieve the global + * instance. It is prudent, but not necessary to first initialize the + * singleton with a call to {@link #init()} to set the base time. + * + * @return the current global {@link Tracer} instance. + */ + private static Tracer get() { + return LazySpeedTracerLoggerHolder.singleton; + } + + private static boolean getBooleanProperty(String propName) { + try { + return System.getProperty(propName) != null; + } catch (RuntimeException ruEx) { + return false; + } + } + + private final boolean enabled; + + private final DummyEvent dummyEvent = new DummyEvent(); + + private BlockingQueue eventsToWrite; + + private final boolean fileLoggingEnabled; + + private CountDownLatch flushLatch; + + private TraceEvent flushSentinel; + + private Format outputFormat; + + private ThreadLocal> pendingEvents; + + private CountDownLatch shutDownLatch; + + private TraceEvent shutDownSentinel; + + private List gcMXBeans; + + private Map lastGcTimes; + + private final ElapsedNormalizedTimeKeeper elapsedTimeKeeper; + + private final ProcessNormalizedTimeKeeper processCpuTimeKeeper; + + private final ThreadNormalizedTimeKeeper threadCpuTimeKeeper; + + /** + * Constructor intended for unit testing. + * + * @param writer alternative {@link Writer} to send speed tracer output. + */ + Tracer(Writer writer, Format format) { + enabled = true; + fileLoggingEnabled = true; + outputFormat = format; + eventsToWrite = openLogWriter(writer, ""); + pendingEvents = initPendingEvents(); + elapsedTimeKeeper = new ElapsedNormalizedTimeKeeper(); + processCpuTimeKeeper = new ProcessNormalizedTimeKeeper(); + threadCpuTimeKeeper = new ThreadNormalizedTimeKeeper(); + shutDownSentinel = new DummyEvent(); + flushSentinel = new DummyEvent(); + shutDownLatch = new CountDownLatch(1); + } + + private Tracer() { + fileLoggingEnabled = logFile != null; + enabled = fileLoggingEnabled; + + if (enabled) { + elapsedTimeKeeper = new ElapsedNormalizedTimeKeeper(); + processCpuTimeKeeper = new ProcessNormalizedTimeKeeper(); + threadCpuTimeKeeper = new ThreadNormalizedTimeKeeper(); + + if (fileLoggingEnabled) { + // Allow a system property to override the default output format + Format format = Format.HTML; + if (defaultFormatString != null) { + for (Format value : Format.values()) { + if (value.name().toLowerCase().equals(defaultFormatString.toLowerCase())) { + format = value; + break; + } + } + } + outputFormat = format; + eventsToWrite = openDefaultLogWriter(); + + shutDownSentinel = new TraceEvent(); + flushSentinel = new TraceEvent(); + shutDownLatch = new CountDownLatch(1); + } + + if (logGcTime) { + gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans(); + lastGcTimes = new ConcurrentHashMap(); + } + + pendingEvents = initPendingEvents(); + } else { + elapsedTimeKeeper = null; + processCpuTimeKeeper = null; + threadCpuTimeKeeper = null; + } + } + + public void addDataImpl(String... data) { + Stack threadPendingEvents = pendingEvents.get(); + if (threadPendingEvents.isEmpty()) { + throw new IllegalStateException("Tried to add data to an event that never started!"); + } + + TraceEvent currentEvent = threadPendingEvents.peek(); + currentEvent.addData(data); + } + + public void markTimelineImpl(String message) { + Stack threadPendingEvents = pendingEvents.get(); + TraceEvent parent = null; + if (!threadPendingEvents.isEmpty()) { + parent = threadPendingEvents.peek(); + } + TraceEvent newEvent = new MarkTimelineEvent(parent); + threadPendingEvents.push(newEvent); + newEvent.end("message", message); + } + + void addGcEvents(TraceEvent refEvent) { + // we're not sending GC events to the dartboard, so we only record them + // to file + if (!fileLoggingEnabled) { + return; + } + + for (GarbageCollectorMXBean gcMXBean : gcMXBeans) { + String gcName = gcMXBean.getName(); + Long lastGcTime = lastGcTimes.get(gcName); + long currGcTime = gcMXBean.getCollectionTime(); + if (lastGcTime == null) { + lastGcTime = 0L; + } + if (currGcTime > lastGcTime) { + // create a new event + long gcDurationNanos = (currGcTime - lastGcTime) * 1000000L; + TraceEvent gcEvent = + new GcEvent(refEvent, gcName, gcMXBean.getCollectionCount(), gcDurationNanos); + + eventsToWrite.add(gcEvent); + lastGcTimes.put(gcName, currGcTime); + } + } + } + + void addOverheadEvent(TraceEvent refEvent) { + TraceEvent overheadEvent = new TraceEvent(refEvent, SpeedTracerEventType.OVERHEAD); + // measure the time between the end of refEvent and now + overheadEvent.setStartsAfter(refEvent); + overheadEvent.updateDuration(); + + refEvent.extendDuration(overheadEvent); + } + + void endImpl(TraceEvent event, String... data) { + if (!enabled) { + return; + } + + if (data.length % 2 == 1) { + throw new IllegalArgumentException("Unmatched data argument"); + } + + Stack threadPendingEvents = pendingEvents.get(); + if (threadPendingEvents.isEmpty()) { + throw new IllegalStateException("Tried to end an event that never started!"); + } + TraceEvent currentEvent = threadPendingEvents.pop(); + currentEvent.updateDuration(); + + while (currentEvent != event && !threadPendingEvents.isEmpty()) { + // Missed a closing end for one or more frames! Try to sync back up. + currentEvent.addData("Missed", + "This event was closed without an explicit call to Event.end()"); + currentEvent = threadPendingEvents.pop(); + currentEvent.updateDuration(); + } + + if (threadPendingEvents.isEmpty() && currentEvent != event) { + currentEvent.addData("Missed", "Fell off the end of the threadPending events"); + } + + if (logGcTime) { + addGcEvents(currentEvent); + } + + currentEvent.addData(data); + + if (logOverheadTime) { + addOverheadEvent(currentEvent); + } + + if (threadPendingEvents.isEmpty()) { + if (fileLoggingEnabled) { + eventsToWrite.add(currentEvent); + } + } + } + + /** + * Notifies the background thread to finish processing all data in the queue. + * Blocks the current thread until the data is flushed in the Log Writer + * thread. + */ + void flush() { + if (!fileLoggingEnabled) { + return; + } + + try { + // Wait for the other thread to drain the queue. + flushLatch = new CountDownLatch(1); + eventsToWrite.add(flushSentinel); + flushLatch.await(); + } catch (InterruptedException e) { + // Ignored + } + } + + TraceEvent startImpl(EventType type, String... data) { + if (!enabled) { + return dummyEvent; + } + + if (data.length % 2 == 1) { + throw new IllegalArgumentException("Unmatched data argument"); + } + + Stack threadPendingEvents = pendingEvents.get(); + TraceEvent parent = null; + if (!threadPendingEvents.isEmpty()) { + parent = threadPendingEvents.peek(); + } else { + // reset the thread CPU time base for top-level events (so events can be + // properly sequenced chronologically) + threadCpuTimeKeeper.resetTimeBase(); + } + + TraceEvent newEvent = new TraceEvent(parent, type, data); + // Add a field to the top level event in order to track the base time + // so we can re-normalize the data + if (threadPendingEvents.size() == 0) { + long baseTime = + logProcessCpuTime ? processCpuTimeKeeper.zeroTimeMillis() : (logThreadCpuTime + ? threadCpuTimeKeeper.zeroTimeMillis() : elapsedTimeKeeper.zeroTimeMillis()); + newEvent.addData("baseTime", "" + baseTime); + } + threadPendingEvents.push(newEvent); + return newEvent; + } + + private ThreadLocal> initPendingEvents() { + return new ThreadLocal>() { + @Override + protected Stack initialValue() { + return new Stack(); + } + }; + } + + private BlockingQueue openDefaultLogWriter() { + Writer writer = null; + if (enabled) { + try { + writer = new BufferedWriter(new FileWriter(logFile)); + return openLogWriter(writer, logFile); + } catch (IOException e) { + System.err.println("Unable to open dart.speedtracerlog '" + logFile + "'"); + e.printStackTrace(); + } + } + return null; + } + + private BlockingQueue openLogWriter(final Writer writer, final String fileName) { + try { + if (outputFormat.equals(Format.HTML)) { + writer.write("" + + "" + + "

    Performance dump from GWT

    " + + "

    " + + "(You must install the SpeedTracer extension to open this file)

    " + + "
    \n"); + } + } catch (IOException e) { + System.err.println("Unable to write to dart.speedtracerlog '" + + (fileName == null ? "" : fileName) + "'"); + e.printStackTrace(); + return null; + } + + final BlockingQueue eventQueue = new LinkedBlockingQueue(); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + try { + // Wait for the other thread to drain the queue. + eventQueue.add(shutDownSentinel); + shutDownLatch.await(); + } catch (InterruptedException e) { + // Ignored + } + } + }); + + // Background thread to write SpeedTracer events to log + Thread logWriterWorker = new LogWriterThread(writer, fileName, eventQueue); + + // Lower than normal priority. + logWriterWorker.setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2); + + /* + * This thread must be daemon, otherwise shutdown hooks would never begin to + * run, and an app wouldn't finish. + */ + logWriterWorker.setDaemon(true); + logWriterWorker.setName("SpeedTracerLogger writer"); + logWriterWorker.start(); + return eventQueue; + } +} diff --git a/compiler/java/com/google/dart/compiler/parser/AbstractParser.java b/compiler/java/com/google/dart/compiler/parser/AbstractParser.java new file mode 100644 index 00000000000..4a7bf1268b1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/AbstractParser.java @@ -0,0 +1,136 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.parser.DartScanner.Location; + +/** + * Abstract base class for sharing common utility methods between implementation + * classes, like {@link DartParser}. + */ +abstract class AbstractParser { + + protected final ParserContext ctx; + private int lastErrorPosition = Integer.MIN_VALUE; + + protected AbstractParser(ParserContext ctx) { + this.ctx = ctx; + } + + protected boolean EOS() { + return match(Token.EOS) || match(Token.ILLEGAL); + } + + protected boolean expect(Token expectedToken) { + if (!optional(expectedToken)) { + /* + * Save the current token, then advance to make sure that we have the + * right position. + */ + Token actualToken = peek(0); + ctx.advance(); + reportUnexpectedToken(position(), expectedToken, actualToken); + return false; + } + return true; + } + + protected String getPeekTokenValue(int n) { + assert (n >= 0); + String value = ctx.peekTokenString(n); + return value; + } + + protected boolean match(Token token) { + return peek(0) == token; + } + + protected Token next() { + ctx.advance(); + return ctx.getCurrentToken(); + } + + protected boolean optionalPseudoKeyword(String keyword) { + if (!peekPseudoKeyword(0, keyword)) { + return false; + } + next(); + return true; + } + + protected boolean optional(Token token) { + if (peek(0) != token) { + return false; + } + next(); + return true; + } + + protected Token peek(int n) { + return ctx.peek(n); + } + + protected boolean peekPseudoKeyword(int n, String keyword) { + return (peek(n) == Token.IDENTIFIER) && keyword.equals(getPeekTokenValue(n)); + } + + protected DartScanner.Position position() { + DartScanner.Location tokenLocation = ctx.getTokenLocation(); + return tokenLocation != null ? tokenLocation.getBegin() : new DartScanner.Position(0, 1, 1); + } + + /** + * Report a syntax error, unless an error has already been reported at the given or a later + * position. + */ + protected void reportError(DartScanner.Position position, ErrorCode errorCode, + Object... arguments) { + DartCompilationError dartError = new DartCompilationError(ctx.getTokenLocation(), errorCode, + arguments); + if (dartError.getStartPosition() <= lastErrorPosition) { + return; + } + lastErrorPosition = position.getPos(); + dartError.setSource(ctx.getSource()); + ctx.error(dartError); + } + + protected void reportWarning(DartNode node, ErrorCode errorCode, Object... arguments) { + DartCompilationError dartError = new DartCompilationError(node, errorCode, arguments); + dartError.setSource(ctx.getSource()); + ctx.warning(dartError); + } + + protected void reportUnexpectedToken(DartScanner.Position position, Token expected, + Token actual) { + if (expected == Token.EOS) { + reportError(position, DartCompilerErrorCode.EXPECTED_EOS, actual); + } else if (expected == null) { + reportError(position, DartCompilerErrorCode.UNEXPECTED_TOKEN, actual); + } else { + reportError(position, DartCompilerErrorCode.EXPECTED_TOKEN, actual, expected); + } + } + + protected void setPeek(int n, Token token) { + assert n == 0; // so far, n is always zero + ctx.replaceNextToken(token); + } + + protected boolean consume(Token token) { + boolean result = (peek(0) == token); + assert (result); + next(); + return result; + } + + public void error(Location location, DartCompilerErrorCode code, Object... arguments) { + ctx.error(new DartCompilationError(location, code, arguments)); + } +} diff --git a/compiler/java/com/google/dart/compiler/parser/CommentPreservingParser.java b/compiler/java/com/google/dart/compiler/parser/CommentPreservingParser.java new file mode 100644 index 00000000000..3164c423cd6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/CommentPreservingParser.java @@ -0,0 +1,151 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.ast.DartComment; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.util.DartSourceString; + +import java.util.ArrayList; +import java.util.List; + +/** + * A parser for Dart that records comment positions. + */ +public class CommentPreservingParser extends DartParser { + + private static class CommentParserContext extends DartScannerParserContext { + + private List commentLocs; + private String source; + + CommentParserContext(Source source, String code, + DartCompilerListener listener) { + super(source, code, listener); + this.source = code; + } + + CommentParserContext(Source source, String code, + DartCompilerListener listener, CompilerMetrics metrics) { + super(source, code, listener, metrics); + this.source = code; + } + + List getCommentLocs() { + return commentLocs; + } + + @Override + protected DartScanner createScanner(String sourceCode) { + commentLocs = new ArrayList(); + return this.new CommentScanner(sourceCode); + } + + private class CommentScanner extends DartScanner { + + CommentScanner(String sourceCode) { + super(sourceCode); + } + + @Override + protected void recordCommentLocation(int start, int stop, int line, int col) { + int size = commentLocs.size(); + if (size > 0) { + // the parser may re-scan lookahead tokens + // fortunately, comments are always scanned as comments + int[] loc = commentLocs.get(size - 1); + if (start <= loc[0] && stop <= loc[1]) { + return; + } + } + commentLocs.add(new int[]{start, stop, line, col}); + } + } + } + + /** + * Create a parsing context for the comment-recording parser. + */ + public static CommentParserContext createContext(Source source, String code, + DartCompilerListener listener) { + return new CommentParserContext(source, code, listener); + } + + /** + * Create a parsing context for the comment-recording parser. + */ + public static CommentParserContext createContext(Source source, String code, + DartCompilerListener listener, CompilerMetrics metrics) { + return new CommentParserContext(source, code, listener, metrics); + } + + private CommentParserContext context; + private boolean onlyDartDoc; + + /** + * Create a parser on the given code that records comment locations. + */ + public CommentPreservingParser(String code) { + this(code, null, false); + } + + /** + * Create a parser on the given code that records some comment + * locations. If onlyDartDoc is true then only + * DartDoc comments will be recorded, otherwise all comments will be recorded. + * The given listener will be used to inform clients of errors. + */ + public CommentPreservingParser(String code, DartCompilerListener listener, + boolean onlyDartDoc) { + this(createContext(null, code, listener), onlyDartDoc); + } + + /** + * Create a parser with the given parsing context context. + * If onlyDartDoc is true then only + * DartDoc comments will be recorded, otherwise all comments will be recorded. + */ + public CommentPreservingParser(ParserContext context, + boolean onlyDartDoc) { + super(context, onlyDartDoc); + this.context = (CommentParserContext) context; + this.onlyDartDoc = onlyDartDoc; + } + + @Override + public DartUnit parseUnit(DartSource input) { + DartUnit unit = super.parseUnit(input); + String sourceString = context.source; + Source source = new DartSourceString(null, sourceString); + for (int[] loc : context.getCommentLocs()) { + DartComment.Style style = getCommentStyle(sourceString, loc[0]); + if (!onlyDartDoc || style == DartComment.Style.DART_DOC) { + unit.addComment(new DartComment(source, loc[0], loc[1] - loc[0], loc[2], loc[3], style)); + } + } + return unit; + } + + /** + * Return the style of the comment in the given string. + * + * @param sourceString the source containing the comment + * @param commentStart the location of the comment in the source + * + * @return the style of the comment in the given string + */ + private DartComment.Style getCommentStyle(String sourceString, int commentStart) { + if (sourceString.charAt(commentStart + 1) == '/') { + return DartComment.Style.END_OF_LINE; + } else if (sourceString.charAt(commentStart + 2) == '*') { + return DartComment.Style.DART_DOC; + } + return DartComment.Style.BLOCK; + } +} diff --git a/compiler/java/com/google/dart/compiler/parser/CompletionHooksParserBase.java b/compiler/java/com/google/dart/compiler/parser/CompletionHooksParserBase.java new file mode 100644 index 00000000000..43f94d8416c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/CompletionHooksParserBase.java @@ -0,0 +1,449 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.common.HasSourceInfo; + +/** + * This class exists to enforce constraints on begin calls so code + * completion works. + *

    + * In particular, it prevents {@link #begin()} from being called directly, + * ensuring that callers must use appropriate {@code beginFoo} methods. + *

    + * These hooks serve two purposes: + *

      + *
    1. remember start positions to set source location information on AST + * nodes + *
    2. provide an event mechanism that is useful for an IDE operating on code + * being edited - for example, for error recovery or code completion + *
    + *

    + * Every call to {@code beginFoo} must be balanced with exactly one call + * to either {@link #rollback()} or {@link #done(Object)}. Between those + * calls, there may be an arbitrary number of calls to + * {@link #doneWithoutConsuming(Object)} to set AST node positions based on + * the current position on the stack. + */ +public abstract class CompletionHooksParserBase extends AbstractParser { + + /* + * Guards the parser from infinite loops and recursion. + * THIS CLASS IS FOR DEBUG/INTERNAL USE ONLY. + * TODO (fabiomfv) - remove before release. + */ + private class TerminationGuard { + + /* + * Loosely, determines the maximum number of non-terminals 'visited' without + * advancing on input. It does not need to be a precise number, just to have + * an upper bound on the 'space' the parser can consume before declaring + * it is not making progress. + */ + private static final int THRESHOLD = 100; + + private int maxPositionRange = Integer.MIN_VALUE; + private int minPositionRange = Integer.MAX_VALUE; + private int threshold = THRESHOLD; + + /* + * Guard against parser termination bugs. Called from begin(). + * If the parser does not consume tokens it is an indication that it is not + * making progress. Look at the stack in the exception for hints of + * productions at fault. Called from begin() + */ + public boolean assertProgress() { + int currentPosition = position().getPos(); + if (currentPosition > maxPositionRange) { + minPositionRange = maxPositionRange; + maxPositionRange = currentPosition; + threshold = THRESHOLD; + } else if (currentPosition < minPositionRange) { + minPositionRange = currentPosition; + threshold = THRESHOLD; + } + if (threshold-- <= 0) { + StringBuilder sb = new StringBuilder(); + sb.append("Parser failed to make progress after many tries. File a " + + "bug and attach this callstack and error output.\n"); + sb.append("Scanner State: "); + sb.append(ctx.toString()); + sb.append("\n"); + sb.append("Input range ["); + sb.append(minPositionRange); + sb.append(","); + sb.append(maxPositionRange); + sb.append("]\n"); + throw new AssertionError(sb.toString()); + } + return true; + } + } + + /** + * Guards against termination bugs. For debugging purposes only. + * See {@link TerminationGuard} for details. + */ + private TerminationGuard guard = new TerminationGuard(); + + /** + * Set the context the parser will use. + * + * @param ctx the {@link ParserContext} to use + */ + public CompletionHooksParserBase(ParserContext ctx) { + super(ctx); + } + + protected void beginArrayLiteral() { + begin(); + } + + protected void beginBinaryExpression() { + begin(); + } + + protected void beginBlock() { + begin(); + } + + protected void beginBreakStatement() { + begin(); + } + + protected void beginCatchClause() { + begin(); + } + + protected void beginCatchParameter() { + begin(); + } + + protected void beginClassBody() { + begin(); + } + + protected void beginClassMember() { + begin(); + } + + protected void beginCompilationUnit() { + begin(); + } + + protected void beginConditionalExpression() { + begin(); + } + + protected void beginConstExpression() { + begin(); + } + + protected void beginConstructor() { + begin(); + } + + protected void beginContinueStatement() { + begin(); + } + + protected void beginDoStatement() { + begin(); + } + + protected void beginEmptyStatement() { + begin(); + } + + protected void beginEntryPoint() { + begin(); + } + + protected void beginExpression() { + begin(); + } + + protected void beginExpressionList() { + begin(); + } + + protected void beginExpressionStatement() { + begin(); + } + + protected void beginFieldInitializerOrRedirectedConstructor() { + begin(); + } + + protected void beginFinalDeclaration() { + begin(); + } + + protected void beginForInitialization() { + begin(); + } + + protected void beginFormalParameter() { + begin(); + } + + protected void beginFormalParameterList() { + begin(); + } + + protected void beginForStatement() { + begin(); + } + + protected void beginFunctionDeclaration() { + begin(); + } + + protected void beginFunctionLiteral() { + begin(); + } + + protected void beginFunctionStatementBody() { + begin(); + } + + protected void beginFunctionTypeInterface() { + begin(); + } + + protected void beginIdentifier() { + begin(); + } + + protected void beginIfStatement() { + begin(); + } + + protected void beginImportDirective() { + begin(); + } + + protected void beginInitializer() { + begin(); + } + + protected void beginTypeExpression() { + begin(); + } + + protected void beginLabel() { + begin(); + } + + protected void beginLibraryDirective() { + begin(); + } + + protected void beginLiteral() { + begin(); + } + + protected void beginMapLiteral() { + begin(); + } + + protected void beginMapLiteralEntry() { + begin(); + } + + protected void beginMethodName() { + begin(); + } + + protected void beginNativeBody() { + begin(); + } + + protected void beginNativeDirective() { + begin(); + } + + protected void beginNewExpression() { + begin(); + } + + protected void beginOperatorName() { + begin(); + } + + protected void beginParameter() { + begin(); + } + + protected void beginParameterName() { + begin(); + } + + protected void beginParenthesizedExpression() { + begin(); + } + + protected void beginPostfixExpression() { + begin(); + } + + protected void beginQualifiedIdentifier() { + begin(); + } + + protected void beginResourceDirective() { + begin(); + } + + protected void beginReturnStatement() { + begin(); + } + + protected void beginReturnType() { + begin(); + } + + protected void beginSelectorExpression() { + begin(); + } + + protected void beginSourceDirective() { + begin(); + } + + protected void beginSpreadExpression() { + begin(); + } + + protected void beginStringInterpolation() { + begin(); + } + + protected void beginStringSegment() { + begin(); + } + + protected void beginSuperExpression() { + begin(); + } + + protected void beginSuperInitializer() { + begin(); + } + + protected void beginSwitchMember() { + begin(); + } + + protected void beginSwitchStatement() { + begin(); + } + + protected void beginThisExpression() { + begin(); + } + + protected void beginThrowStatement() { + begin(); + } + + protected void beginTopLevelElement() { + begin(); + } + + protected void beginTryStatement() { + begin(); + } + + protected void beginTypeAnnotation() { + begin(); + } + + protected void beginTypeArguments() { + begin(); + } + + protected void beginTypeFunctionOrVariable() { + begin(); + } + + protected void beginTypeParameter() { + begin(); + } + + protected void beginUnaryExpression() { + begin(); + } + + protected void beginVarDeclaration() { + begin(); + } + + protected void beginVariableDeclaration() { + begin(); + } + + protected void beginWhileStatement() { + begin(); + } + + /** + * Terminates a grammatical structure, saving the source location in the + * supplied AST node. + * + * @param type of the AST node + * @param result the AST node to return, if any - if it implements + * {@link HasSourceInfo}, the source location is set based on the + * current position and the start of this grammatical structure + * @return the supplied AST node (may be null) + */ + protected T done(T result) { + return ctx.done(result); + } + + /** + * Saves the current source location in the supplied AST node, used for + * subcomponents of the AST. This may only be called within an active + * {@link #begin()} call, which must still be terminated with either + * {@link #done(Object)} or {@link #rollback()}. + * + * @param type of the AST node + * @param result the AST node to return - if it implements + * {@link HasSourceInfo}, the source location is set based on the + * current position and the start of this grammatical structure + * @return the supplied AST node + */ + protected T doneWithoutConsuming(T result) { + return ctx.doneWithoutConsuming(result); + } + + /** + * Terminates an attempt to parse a grammatical structure, rolling back to the + * state as of the previous {@link #begin()} call and removing the saved + * state. + */ + protected void rollback() { + ctx.rollback(); + } + + /** + * This should only be called when the parser is looking ahead to decide how + * to parse something, and this will always be rolled back. + */ + protected void startLookahead() { + begin(); + } + + /** + * Begin a grammatical structure, saving the current location to later set in + * an AST node. This may be followed by zero or more + * {@link #doneWithoutConsuming(Object)} calls, and is terminated by exactly + * one {@link #done(Object)} or {@link #rollback()} call. + */ + private void begin() { + assert guard.assertProgress(); + ctx.begin(); + } +} diff --git a/compiler/java/com/google/dart/compiler/parser/DartParser.java b/compiler/java/com/google/dart/compiler/parser/DartParser.java new file mode 100644 index 00000000000..de17342326d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/DartParser.java @@ -0,0 +1,3520 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.io.CharStreams; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartAssertion; +import com.google.dart.compiler.ast.DartBinaryExpression; +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.DartConditional; +import com.google.dart.compiler.ast.DartContinueStatement; +import com.google.dart.compiler.ast.DartDeclaration; +import com.google.dart.compiler.ast.DartDefault; +import com.google.dart.compiler.ast.DartDoWhileStatement; +import com.google.dart.compiler.ast.DartDoubleLiteral; +import com.google.dart.compiler.ast.DartEmptyStatement; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIfStatement; +import com.google.dart.compiler.ast.DartImportDirective; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartInvocation; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartLibraryDirective; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMapLiteralEntry; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNamedExpression; +import com.google.dart.compiler.ast.DartNativeBlock; +import com.google.dart.compiler.ast.DartNativeDirective; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNullLiteral; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartParameterizedNode; +import com.google.dart.compiler.ast.DartParenthesizedExpression; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; +import com.google.dart.compiler.ast.DartResourceDirective; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartSourceDirective; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartStringInterpolation; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartSuperConstructorInvocation; +import com.google.dart.compiler.ast.DartSuperExpression; +import com.google.dart.compiler.ast.DartSwitchMember; +import com.google.dart.compiler.ast.DartSwitchStatement; +import com.google.dart.compiler.ast.DartSyntheticErrorExpression; +import com.google.dart.compiler.ast.DartSyntheticErrorStatement; +import com.google.dart.compiler.ast.DartThisExpression; +import com.google.dart.compiler.ast.DartThrowStatement; +import com.google.dart.compiler.ast.DartTryStatement; +import com.google.dart.compiler.ast.DartTypeExpression; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartUnqualifiedInvocation; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.DartVariableStatement; +import com.google.dart.compiler.ast.DartWhileStatement; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.parser.DartScanner.Location; +import com.google.dart.compiler.util.Lists; + +import java.io.IOException; +import java.io.Reader; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The Dart parser. Parses a single compilation unit and produces a {@link DartUnit}. + * The grammar rules are taken from Dart.g revision 557. + */ +public class DartParser extends CompletionHooksParserBase { + + private Set errorHistory = new HashSet(); + private Set prefixes; + private boolean isDietParse; + private boolean isParsingInterface; + + /** + * Determines the maximum number of errors before terminating the parser. See + * {@link #reportError(com.google.dart.compiler.parser.DartScanner.Position, ErrorCode, + * Object...)}. + */ + final int MAX_DEFAULT_ERRORS = 100; + + // Pseudo-keywords that should also be valid identifiers. + private static final String ABSTRACT_KEYWORD = "abstract"; + private static final String ASSERT_KEYWORD = "assert"; + private static final String CLASS_KEYWORD = "class"; + private static final String EXTENDS_KEYWORD = "extends"; + private static final String FACTORY_KEYWORD = "factory"; + private static final String GETTER_KEYWORD = "get"; + private static final String IMPLEMENTS_KEYWORD = "implements"; + private static final String INTERFACE_KEYWORD = "interface"; + private static final String NATIVE_KEYWORD = "native"; + private static final String NEGATE_KEYWORD = "negate"; + private static final String OPERATOR_KEYWORD = "operator"; + private static final String PREFIX_KEYWORD = "prefix"; + private static final String SETTER_KEYWORD = "set"; + private static final String STATIC_KEYWORD = "static"; + private static final String TYPEDEF_KEYWORD = "typedef"; + + public static final String[] PSEUDO_KEYWORDS = { + ABSTRACT_KEYWORD, + ASSERT_KEYWORD, + CLASS_KEYWORD, + EXTENDS_KEYWORD, + FACTORY_KEYWORD, + GETTER_KEYWORD, + IMPLEMENTS_KEYWORD, + INTERFACE_KEYWORD, + NEGATE_KEYWORD, + NATIVE_KEYWORD, + OPERATOR_KEYWORD, + PREFIX_KEYWORD, + SETTER_KEYWORD, + STATIC_KEYWORD, + TYPEDEF_KEYWORD + }; + + public DartParser(Source source, + String sourceCode, + DartCompilerListener listener) { + this(new DartScannerParserContext(source, sourceCode, listener)); + } + + public DartParser(ParserContext ctx) { + this(ctx, false); + } + + public DartParser(ParserContext ctx, Set prefixes) { + this(ctx, false, prefixes); + } + + public DartParser(ParserContext ctx, boolean isDietParse) { + this(ctx, isDietParse, Collections.emptySet()); + } + + public DartParser(ParserContext ctx, boolean isDietParse, Set prefixes) { + super(ctx); + this.isDietParse = isDietParse; + this.prefixes = prefixes; + } + + private DartParser(Source source, DartCompilerListener listener) throws IOException { + this(source, source.getSourceReader(), listener); + } + + private DartParser(Source source, + Reader sourceReader, + DartCompilerListener listener) throws IOException { + this(new DartScannerParserContext(source, read(sourceReader), listener)); + } + + public static DartParser getSourceParser(Source source, DartCompilerListener listener) + throws IOException { + return new DartParser(source, listener); + } + + private static String read(Reader reader) throws IOException { + try { + return CharStreams.toString(reader); + } finally { + reader.close(); + } + } + + /** + * A flag indicating whether function expressions are allowed. See + * {@link #setAllowFunctionExpression(boolean)}. + */ + private boolean allowFunctionExpression = true; + + /** + * Set the {@link #allowFunctionExpression} flag indicating whether function expressions are + * allowed, returning the old value. This is required to avoid ambiguity in a few places in the + * grammar. + * + * @param allow true if function expressions are allowed, false if not + * @return previous value of the flag, which should be restored + */ + private boolean setAllowFunctionExpression(boolean allow) { + boolean old = allowFunctionExpression; + allowFunctionExpression = allow; + return old; + } + + /** + *

    +   * compilationUnit
    +   *     : libraryDeclaration? topLevelDefinition* EOF
    +   *     ;
    +   *
    +   * libraryDeclaration
    +   *     : libraryDirective? importDirective* sourceDirective* resourceDirective* nativeDirective*
    +   *
    +   * topLevelDefinition
    +   *     : classDefinition
    +   *     | interfaceDefinition
    +   *     | functionTypeAlias
    +   *     | methodOrConstructorDeclaration functionStatementBody
    +   *     | type? getOrSet identifier formalParameterList functionStatementBody
    +   *     | CONST type? staticConstDeclarationList ';'
    +   *     | variableDeclaration ';'
    +   *     ;
    +   * 
    + */ + public DartUnit parseUnit(DartSource source) { + beginCompilationUnit(); + DartUnit unit = new DartUnit(source); + + // parse any directives at the beginning of the source + parseDirectives(unit); + + while (!EOS()) { + DartNode node = null; + beginTopLevelElement(); + isParsingInterface = false; + if (optionalPseudoKeyword(CLASS_KEYWORD)) { + node = done(parseClass()); + } else if (optionalPseudoKeyword(INTERFACE_KEYWORD)) { + isParsingInterface = true; + node = done(parseClass()); + } else if (optionalPseudoKeyword(TYPEDEF_KEYWORD)) { + node = done(parseFunctionTypeAlias()); + } else { + node = done(parseFieldOrMethod(false)); + } + if (node != null) { + unit.addTopLevelNode(node); + } + } + expect(Token.EOS); + return done(unit); + } + + /** + * A version of the parser which only parses the directives of a library. + * + * TODO(jbrosenberg): consider parsing the whole file here, in order to avoid + * duplicate work. Probably requires removing use of LibraryUnit's, etc. + * Also, this minimal parse does have benefit in the incremental compilation + * case. + */ + public LibraryUnit preProcessLibraryDirectives(LibrarySource source) { + beginCompilationUnit(); + LibraryUnit libUnit = new LibraryUnit(source); + if (peek(0) == Token.LIBRARY) { + beginLibraryDirective(); + DartLibraryDirective libDirective = done(parseLibraryDirective()); + libUnit.setName(libDirective.getName().getValue()); + } + while (peek(0) == Token.IMPORT) { + beginImportDirective(); + DartImportDirective importDirective = done(parseImportDirective()); + LibraryNode importPath; + if (importDirective.getPrefix() != null) { + importPath = + new LibraryNode(importDirective.getLibraryUri().getValue(), importDirective.getPrefix() + .getValue()); + } else { + importPath = new LibraryNode(importDirective.getLibraryUri().getValue()); + } + libUnit.addImportPath(importPath); + } + while (peek(0) == Token.SOURCE) { + beginSourceDirective(); + DartSourceDirective sourceDirective = done(parseSourceDirective()); + LibraryNode sourcePath = new LibraryNode(sourceDirective.getSourceUri().getValue()); + libUnit.addSourcePath(sourcePath); + } + while (peek(0) == Token.RESOURCE) { + beginResourceDirective(); + DartResourceDirective resourceDirective = done(parseResourceDirective()); + LibraryNode resourcePath = new LibraryNode(resourceDirective.getResourceUri().getValue()); + libUnit.addResourcePath(resourcePath); + } + while (peek(0) == Token.NATIVE) { + beginNativeDirective(); + DartNativeDirective nativeDirective = done(parseNativeDirective()); + LibraryNode nativePath = new LibraryNode(nativeDirective.getNativeUri().getValue()); + libUnit.addNativePath(nativePath); + } + + // add ourselves to the list of sources, so inline dart code will be parsed + libUnit.addSourcePath(libUnit.getSelfSourcePath()); + return done(libUnit); + } + + private void parseDirectives(DartUnit unit) { + if (peek(0) == Token.LIBRARY) { + beginLibraryDirective(); + unit.addDirective(done(parseLibraryDirective())); + } + while (peek(0) == Token.IMPORT) { + beginImportDirective(); + unit.addDirective(done(parseImportDirective())); + } + while (peek(0) == Token.SOURCE) { + beginSourceDirective(); + unit.addDirective(done(parseSourceDirective())); + } + while (peek(0) == Token.RESOURCE) { + beginResourceDirective(); + unit.addDirective(done(parseResourceDirective())); + } + while (peek(0) == Token.NATIVE) { + beginResourceDirective(); + unit.addDirective(done(parseNativeDirective())); + } + } + + private DartLibraryDirective parseLibraryDirective() { + expect(Token.LIBRARY); + expect(Token.LPAREN); + beginLiteral(); + expect(Token.STRING); + DartStringLiteral libname = done(DartStringLiteral.get(ctx.getTokenString())); + expectCloseParen(); + expect(Token.SEMICOLON); + return new DartLibraryDirective(libname); + } + + private DartImportDirective parseImportDirective() { + expect(Token.IMPORT); + expect(Token.LPAREN); + beginLiteral(); + expect(Token.STRING); + DartStringLiteral libUri = done(DartStringLiteral.get(ctx.getTokenString())); + DartStringLiteral prefix = null; + if (optional(Token.COMMA)) { + if (!optionalPseudoKeyword(PREFIX_KEYWORD)) { + reportError(position(), DartCompilerErrorCode.EXPECTED_PREFIX_KEYWORD); + } + expect(Token.COLON); + beginLiteral(); + expect(Token.STRING); + prefix = done(DartStringLiteral.get(ctx.getTokenString())); + } + expectCloseParen(); + expect(Token.SEMICOLON); + return new DartImportDirective(libUri, prefix); + } + + private DartSourceDirective parseSourceDirective() { + expect(Token.SOURCE); + expect(Token.LPAREN); + beginLiteral(); + expect(Token.STRING); + DartStringLiteral sourceUri = done(DartStringLiteral.get(ctx.getTokenString())); + expectCloseParen(); + expect(Token.SEMICOLON); + return new DartSourceDirective(sourceUri); + } + + private DartResourceDirective parseResourceDirective() { + expect(Token.RESOURCE); + expect(Token.LPAREN); + beginLiteral(); + expect(Token.STRING); + DartStringLiteral resourceUri = done(DartStringLiteral.get(ctx.getTokenString())); + expectCloseParen(); + expect(Token.SEMICOLON); + return new DartResourceDirective(resourceUri); + } + + private DartNativeDirective parseNativeDirective() { + expect(Token.NATIVE); + expect(Token.LPAREN); + beginLiteral(); + expect(Token.STRING); + DartStringLiteral nativeUri = done(DartStringLiteral.get(ctx.getTokenString())); + expect(Token.RPAREN); + expect(Token.SEMICOLON); + return new DartNativeDirective(nativeUri); + } + + /** + *
    +   * typeParameter
    +   *     : identifier (EXTENDS type)?
    +   *     ;
    +   *
    +   * typeParameters
    +   *     : '<' typeParameter (',' typeParameter)* '>'
    +   *     ;
    +   * 
    + */ + private List parseTypeParameters() { + List types = new ArrayList(); + expect(Token.LT); + do { + beginTypeParameter(); + DartIdentifier name = parseIdentifier(); + DartTypeNode bound = null; + if (optionalPseudoKeyword(EXTENDS_KEYWORD)) { + bound = parseTypeAnnotation(); + } + types.add(done(new DartTypeParameter(name, bound))); + } while (optional(Token.COMMA)); + expect(Token.GT); + return types; + } + + private List parseTypeParametersOpt() { + return (peek(0) == Token.LT) + ? parseTypeParameters() + : Collections.emptyList(); + } + + /** + *
    +   * classDefinition
    +   *     : CLASS identifier typeParameters? superclass? interfaces?
    +   *       '{' classMemberDefinition* '}'
    +   *     ;
    +   *
    +   * superclass
    +   *     : EXTENDS type
    +   *     ;
    +   *
    +   * interfaces
    +   *     : IMPLEMENTS typeList
    +   *     ;
    +   *
    +   * superinterfaces
    +   *     : EXTENDS typeList
    +   *     ;
    +   *
    +   * classMemberDefinition
    +   *     : declaration ';'
    +   *     | methodDeclaration blockOrNative
    +   *
    +   * interfaceDefinition
    +   *     : INTERFACE identifier typeParameters? superinterfaces?
    +   *       (DEFAULT type)? '{' (interfaceMemberDefinition)* '}'
    +   *     ;
    +   * 
    + */ + private DartDeclaration parseClass() { + beginClassBody(); + + DartIdentifier name = parseIdentifier(); + List typeParameters = parseTypeParametersOpt(); + + // Parse the extends and implements clauses. + DartTypeNode superType = null; + List interfaces = null; + if (isParsingInterface) { + if (optionalPseudoKeyword(EXTENDS_KEYWORD)) { + interfaces = parseTypeAnnotationList(); + } + } else { + if (optionalPseudoKeyword(EXTENDS_KEYWORD)) { + superType = parseTypeAnnotation(); + } + if (optionalPseudoKeyword(IMPLEMENTS_KEYWORD)) { + interfaces = parseTypeAnnotationList(); + } + } + + // Deal with factory clause for interfaces. + DartTypeNode factory = null; + if (isParsingInterface && optionalPseudoKeyword(FACTORY_KEYWORD)) { + factory = parseTypeAnnotation(); + } + + // Deal with native clause for classes. + DartStringLiteral nativeName = null; + if (!isParsingInterface && optionalPseudoKeyword(NATIVE_KEYWORD)) { + beginLiteral(); + expect(Token.STRING); + nativeName = done(DartStringLiteral.get(ctx.getTokenString())); + if (superType != null) { + reportError(position(), DartCompilerErrorCode.EXTENDED_NATIVE_CLASS); + } + } + + // Parse the members. + expect(Token.LBRACE); + List members = new ArrayList(); + while (!match(Token.RBRACE) && !EOS()) { + DartNode member = parseFieldOrMethod(true); + if (member != null) { + members.add(member); + } + } + expectCloseBrace(); + + if (isParsingInterface) { + return done(new DartClass(name, superType, interfaces, members, typeParameters, factory)); + } else { + return done(new DartClass(name, nativeName, superType, interfaces, members, typeParameters)); + } + } + + private List parseTypeAnnotationList() { + List result = new ArrayList(); + do { + result.add(parseTypeAnnotation()); + } while (optional(Token.COMMA)); + return result; + } + + /** + * Look ahead to detect if we are seeing ident [ TypeParameters ] "(". + * We need this lookahead to distinguish between the optional return type + * and the alias name of a function type alias. + * Token position remains unchanged. + * + * @return true if the next tokens should be parsed as a type + */ + private boolean isFunctionTypeAliasName() { + startLookahead(); + try { + if (peek(0) == Token.IDENTIFIER && peek(1) == Token.LPAREN) { + return true; + } + if (peek(0) == Token.IDENTIFIER && peek(1) == Token.LT) { + consume(Token.IDENTIFIER); + // isTypeParameter leaves the position advanced if it matches + if (isTypeParameter() && peek(0) == Token.LPAREN) { + return true; + } + } + return false; + } finally { + rollback(); + } + } + + /** + * Returns true if the current and next tokens can be parsed as type + * parameters. Current token position is not saved and restored. + */ + private boolean isTypeParameter() { + if (peek(0) == Token.LT) { + // We are possibly looking at type parameters. Find closing ">". + consume(Token.LT); + int nestingLevel = 1; + while (nestingLevel > 0) { + switch (peek(0)) { + case LT: + nestingLevel++; + break; + case GT: + nestingLevel--; + break; + case SAR: // >> + nestingLevel -= 2; + break; + case SHR: // >>> + nestingLevel -= 3; + break; + case COMMA: + case IDENTIFIER: + break; + default: + // We are looking at something other than type parameters. + return false; + } + next(); + if (nestingLevel < 0) { + return false; + } + } + } + return true; + } + + /** + *
    +   * functionTypeAlias
    +   *     : TYPEDEF functionPrefix typeParameters?
    +   *       formalParameterList ';'
    +   *
    +   * functionPrefix
    +   *     : returnType? identifier
    +   * 
    + */ + private DartFunctionTypeAlias parseFunctionTypeAlias() { + beginFunctionTypeInterface(); + + DartTypeNode returnType = null; + if (peek(0) == Token.VOID) { + returnType = parseVoidType(); + } else if (!isFunctionTypeAliasName()) { + returnType = parseTypeAnnotation(); + } + + DartIdentifier name = parseIdentifier(); + List typeParameters = parseTypeParametersOpt(); + List params = parseFormalParameterList(); + expect(Token.SEMICOLON); + + return done(new DartFunctionTypeAlias(name, returnType, params, typeParameters)); + } + + /** + * Parse a field or method, which may be inside a class or at the top level. + * + *
    +   * // This rule is organized in a way that may not be most readable, but
    +   * // gives the best error messages.
    +   * classMemberDefinition
    +   *     : declaration ';'
    +   *     | methodDeclaration bodyOrNative
    +   *     ;
    +   *
    +   * // Note: this syntax is not official, but used in dart_interpreter. It
    +   * // is unlikely that Dart will support numbered natives.
    +   * bodyOrNative
    +   *     : error=NATIVE (':' (STRING | RATIONAL_NUMBER))? ';'
    +   *       { legacy($error, "native not supported (yet)"); }
    +   *     | functionStatementBody
    +   *     ;
    +   *
    +   * // A method, operator, or constructor (which all should be followed by
    +   * // a function body).
    +   * methodDeclaration
    +   *     : factoryConstructorDeclaration
    +   *     | STATIC methodOrConstructorDeclaration
    +   *     | specialSignatureDefinition
    +   *     | methodOrConstructorDeclaration initializers?
    +   *     | namedConstructorDeclaration initializers?
    +   *     ;
    +   *
    +   *
    +   * // An abstract method/operator, a field, or const constructor (which
    +   * // all should be followed by a semicolon).
    +   * declaration
    +   *     : constantConstructorDeclaration initializers?
    +   *     | ABSTRACT specialSignatureDefinition
    +   *     | ABSTRACT methodOrConstructorDeclaration
    +   *     | STATIC CONST type? staticConstDeclarationList
    +   *     | STATIC? variableDeclaration
    +   *     ;
    +   *
    +   * interfaceMemberDefinition
    +   *     : STATIC CONST type? initializedIdentifierList ';'
    +   *     | methodOrConstructorDeclaration ';'
    +   *     | constantConstructorDeclaration ';'
    +   *     | namedConstructorDeclaration ';'
    +   *     | specialSignatureDefinition ';'
    +   *     | variableDeclaration ';'
    +   *     ;
    +   *
    +   * variableDeclaration
    +   *     : constVarOrType identifierList
    +   *     ;
    +   *
    +   * methodOrConstructorDeclaration
    +   *     : typeOrFunction? identifier formalParameterList
    +   *     ;
    +   *
    +   * factoryConstructorDeclaration
    +   *     : FACTORY qualified ('.' identifier)? formalParameterList
    +   *     ;
    +   *
    +   * namedConstructorDeclaration
    +   *     : identifier '.' identifier formalParameterList
    +   *     ;
    +   *
    +   * constructorDeclaration
    +   *     : identifier formalParameterList
    +   *     | namedConstructorDeclaration
    +   *     ;
    +   *
    +   * constantConstructorDeclaration
    +   *     : CONST qualified formalParameterList
    +   *     ;
    +   *
    +   * specialSignatureDefinition
    +   *     : STATIC? type? getOrSet identifier formalParameterList
    +   *     | type? OPERATOR operator formalParameterList
    +   *     ;
    +   *
    +   * getOrSet
    +   *     : GET
    +   *     | SET
    +   *     ;
    +   *
    +   * operator
    +   *     : unaryOperator
    +   *     | binaryOperator
    +   *     | '[' ']' { "[]".equals($text) }?
    +   *     | '[' ']' '=' { "[]=".equals($text) }?
    +   *     | NEGATE
    +   *     ;
    +   * 
    + * + * @param allowStatic true if the static modifier is allowed + * @return a {@link DartNode} representing the grammar fragment above + */ + private DartNode parseFieldOrMethod(boolean allowStatic) { + beginClassMember(); + Modifiers modifiers = Modifiers.NONE; + if (optionalPseudoKeyword(STATIC_KEYWORD)) { + if (!allowStatic) { + reportError(position(), DartCompilerErrorCode.TOP_LEVEL_IS_STATIC); + } else { + if (isParsingInterface + && (peek(0) != Token.FINAL)) { + reportError(position(), DartCompilerErrorCode.NON_FINAL_STATIC_MEMBER_IN_INTERFACE); + } + modifiers = modifiers.makeStatic(); + } + } else if (optionalPseudoKeyword(ABSTRACT_KEYWORD)) { + if (isParsingInterface) { + reportError(position(), DartCompilerErrorCode.ABSTRACT_MEMBER_IN_INTERFACE); + } + modifiers = modifiers.makeAbstract(); + } else if (optionalPseudoKeyword(FACTORY_KEYWORD)) { + if (isParsingInterface) { + reportError(position(), DartCompilerErrorCode.FACTORY_MEMBER_IN_INTERFACE); + } + modifiers = modifiers.makeFactory(); + } + + if (match(Token.VAR) || match(Token.FINAL)) { + if (modifiers.isAbstract()) { + reportError(position(), DartCompilerErrorCode.DISALLOWED_ABSTRACT_KEYWORD); + } else if (modifiers.isFactory()) { + reportError(position(), DartCompilerErrorCode.DISALLOWED_FACTORY_KEYWORD); + } + } + + if (modifiers.isFactory()) { + return done(parseFactory(modifiers)); + } + + final DartNode member; + + switch (peek(0)) { + case VAR: { + consume(Token.VAR); + member = parseFieldDeclaration(modifiers, null); + expectStatmentTerminator(); + break; + } + + case CONST: { + consume(Token.CONST); + modifiers = modifiers.makeConstant(); + member = done(parseMethod(modifiers, null)); + break; + } + + case FINAL: { + consume(Token.FINAL); + modifiers = modifiers.makeFinal(); + DartTypeNode type = null; + if (peek(1) != Token.COMMA + && peek(1) != Token.ASSIGN + && peek(1) != Token.SEMICOLON) { + type = parseTypeAnnotation(); + } + member = parseFieldDeclaration(modifiers, type); + expectStatmentTerminator(); + break; + } + + case VOID: + case IDENTIFIER: { + // Check to see if it's a method/ctor. + if (peek(1) == Token.LPAREN + || peek(1) == Token.PERIOD + || peekPseudoKeyword(0, OPERATOR_KEYWORD) + || peekPseudoKeyword(0, GETTER_KEYWORD) + || peekPseudoKeyword(0, SETTER_KEYWORD)) { + member = parseMethodOrAccessor(modifiers, null); + break; + } + + // The next token must be a type specification: either a method or field. + boolean isVoidType = peek(0) == Token.VOID; + DartTypeNode type = isVoidType ? parseVoidType() : parseTypeAnnotation(); + if (peek(1) == Token.SEMICOLON + || peek(1) == Token.COMMA + || peek(1) == Token.ASSIGN) { + if (modifiers.isAbstract()) { + reportError(position(), DartCompilerErrorCode.INVALID_FIELD_DECLARATION); + } + member = parseFieldDeclaration(modifiers, type); + if (isVoidType) { + reportError(type, DartCompilerErrorCode.VOID_FIELD); + } + expectStatmentTerminator(); + } else { + member = parseMethodOrAccessor(modifiers, type); + } + break; + } + + default: { + done(null); + reportUnexpectedToken(position(), null, next()); + member = null; + break; + } + } + return member; + } + + /** + *
    +   * factoryConstructorDeclaration
    +   *     : FACTORY qualified ('.' identifier)? formalParameterList
    +   *     ;
    +   * 
    + */ + private DartMethodDefinition parseFactory(Modifiers modifiers) { + beginMethodName(); + DartExpression name = parseQualified(); + List typeParameters = parseTypeParametersOpt(); + if (!typeParameters.isEmpty()) { + name = doneWithoutConsuming(new DartParameterizedNode(name, typeParameters)); + } + if (optional(Token.PERIOD)) { + name = doneWithoutConsuming(new DartPropertyAccess(name, parseIdentifier())); + } + done(name); + List formals = parseFormalParameterList(); + DartFunction function; + if (peekPseudoKeyword(0, NATIVE_KEYWORD)) { + modifiers = modifiers.makeNative(); + function = new DartFunction(formals, parseNativeBlock(modifiers), null); + } else { + function = new DartFunction(formals, parseBlock(), null); + } + doneWithoutConsuming(function); + return DartMethodDefinition.create(name, function, modifiers, null, typeParameters); + } + + private DartIdentifier parseVoidIdentifier() { + beginIdentifier(); + expect(Token.VOID); + return done(new DartIdentifier(Token.VOID.getSyntax())); + } + + private DartTypeNode parseVoidType() { + beginTypeAnnotation(); + return done(new DartTypeNode(parseVoidIdentifier())); + } + + private DartMethodDefinition parseMethod(Modifiers modifiers, DartTypeNode returnType) { + DartExpression name = null; + + if (modifiers.isFactory()) { + if (modifiers.isAbstract()) { + reportError(position(), DartCompilerErrorCode.FACTORY_CANNOT_BE_ABSTRACT); + } + if (modifiers.isStatic()) { + reportError(position(), DartCompilerErrorCode.FACTORY_CANNOT_BE_STATIC); + } + } + + int arity = -1; + if (optionalPseudoKeyword(OPERATOR_KEYWORD)) { + // Overloaded operator. + if (modifiers.isStatic()) { + reportError(position(), DartCompilerErrorCode.OPERATOR_CANNOT_BE_STATIC); + } + modifiers = modifiers.makeOperator(); + + beginOperatorName(); + Token operation = next(); + if (operation.isUserDefinableOperator()) { + name = done(new DartIdentifier(operation.getSyntax())); + if (operation == Token.ASSIGN_INDEX) { + arity = 2; + } else if (operation.isBinaryOperator()) { + arity = 1; + } else if (operation == Token.INDEX) { + arity = 1; + } else { + assert operation.isUnaryOperator(); + arity = 0; + } + } else if (operation == Token.IDENTIFIER + && ctx.getTokenString().equals(NEGATE_KEYWORD)) { + name = done(new DartIdentifier(NEGATE_KEYWORD)); + arity = 0; + } else { + reportUnexpectedToken(position(), Token.COMMENT, operation); + done(null); + } + } else { + beginMethodName(); + // Check for getters and setters. + if (optionalPseudoKeyword(GETTER_KEYWORD)) { + name = parseIdentifier(); + modifiers = modifiers.makeGetter(); + arity = 0; + } else if (optionalPseudoKeyword(SETTER_KEYWORD)) { + name = parseIdentifier(); + modifiers = modifiers.makeSetter(); + arity = 1; + } else { + // Normal method or property. + name = parseIdentifier(); + } + + // Check for named constructor. + if (optional(Token.PERIOD)) { + name = doneWithoutConsuming(new DartPropertyAccess(name, parseIdentifier())); + + if (optional(Token.PERIOD)) { + name = doneWithoutConsuming(new DartPropertyAccess(name, parseIdentifier())); + } + } + done(null); + } + + // Parse the argument definitions. + List arguments = parseFormalParameterList(); + + if (arity != -1 && arguments.size() != arity) { + reportError(position(), DartCompilerErrorCode.ILLEGAL_NUMBER_OF_ARGUMENTS); + } + + // Parse initializer expressions for constructors. + List initializers = new ArrayList(); + if (match(Token.COLON) && !(isParsingInterface || modifiers.isFactory())) { + boolean isRedirectedConstructor = parseInitializers(initializers); + if (isRedirectedConstructor) { + modifiers = modifiers.makeRedirectedConstructor(); + } + } + + // Parse the body. + DartBlock body = null; + if (!optional(Token.SEMICOLON)) { + if (peekPseudoKeyword(0, NATIVE_KEYWORD)) { + modifiers = modifiers.makeNative(); + body = parseNativeBlock(modifiers); + } else { + body = parseFunctionStatementBody(true); + } + } + + DartFunction function = doneWithoutConsuming(new DartFunction(arguments, body, returnType)); + return DartMethodDefinition.create(name, function, modifiers, initializers, null); + } + + private DartBlock parseNativeBlock(Modifiers modifiers) { + beginNativeBody(); + if (!optionalPseudoKeyword(NATIVE_KEYWORD)) { + throw new AssertionError(); + } + if (optional(Token.SEMICOLON)) { + return done(new DartNativeBlock()); + } else { + if (!modifiers.isStatic()) { + reportError(position(), DartCompilerErrorCode.EXPORTED_FUNCTIONS_MUST_BE_STATIC); + } + return done(parseFunctionStatementBody(true)); + } + } + + private DartNode parseMethodOrAccessor(Modifiers modifiers, DartTypeNode returnType) { + DartMethodDefinition method = done(parseMethod(modifiers, returnType)); + if (method.getModifiers().isGetter() || method.getModifiers().isSetter()) { + DartField field = new DartField((DartIdentifier) method.getName(), + method.getModifiers().makeAbstractField(), method, null); + field.setSourceInfo(method); + DartFieldDefinition fieldDefinition = + new DartFieldDefinition(null, Lists.create(field)); + fieldDefinition.setSourceInfo(field); + return fieldDefinition; + } + return method; + } + + /** + *
    +   * fieldInitializer
    +   *     : (THIS '.')? identifier '=' conditionalExpression
    +   *     | THIS ('.' identifier)? arguments
    +   *     ;
    +   * 
    + * + * @return true if initializer is a redirected constructor, false otherwise. + */ + private boolean parseFieldInitializersOrRedirectedConstructor(List inits) { + do { + beginFieldInitializerOrRedirectedConstructor(); + boolean hasThisPrefix = optional(Token.THIS); + if (hasThisPrefix) { + if (match(Token.LPAREN)) { + return parseRedirectedConstructorInvocation(null, inits); + } + expect(Token.PERIOD); + } + DartIdentifier name = parseIdentifier(); + if (hasThisPrefix && match(Token.LPAREN)) { + return parseRedirectedConstructorInvocation(name, inits); + } else { + expect(Token.ASSIGN); + boolean save = setAllowFunctionExpression(false); + DartExpression initExpr = parseExpression(); + setAllowFunctionExpression(save); + inits.add(done(new DartInitializer(name, initExpr))); + } + } while (optional(Token.COMMA)); + return false; + } + + private boolean parseRedirectedConstructorInvocation(DartIdentifier name, + List inits) { + if (inits.isEmpty()) { + DartInvocation call = + doneWithoutConsuming(new DartRedirectConstructorInvocation(name, parseArguments())); + inits.add(done(new DartInitializer(null, call))); + return true; + } else { + reportUnexpectedToken(position(), Token.ASSIGN, Token.LPAREN); + } + return false; + } + + /** + *
    +   * initializers : ':' superCallOrFirstFieldInitializer (',' fieldInitializer)*
    +   *              | THIS ('.' identifier) formalParameterList ;
    +   *
    +   * fieldInitializer : (THIS '.')? identifier '=' conditionalExpression ;
    +   *
    +   * superCallOrFirstFieldInitializer : SUPER arguments | SUPER '.' identifier
    +   * arguments | fieldInitializer ;
    +   * 
    +   *
    +   * @return true if initializer is a redirect constructor, false otherwise.
    +   */
    +  private boolean parseInitializers(List initializers) {
    +    expect(Token.COLON);
    +    boolean callSuper = false;
    +    if (match(Token.SUPER)) {
    +      beginInitializer();
    +      beginSuperInitializer();
    +      expect(Token.SUPER);
    +      callSuper = true;
    +      DartIdentifier constructor = null;
    +      if (optional(Token.PERIOD)) {
    +        // Calling a super named constructor.
    +        constructor = parseIdentifier();
    +      }
    +      DartSuperConstructorInvocation call =
    +          done(new DartSuperConstructorInvocation(constructor, parseArguments()));
    +      initializers.add(done(new DartInitializer(null, call)));
    +    }
    +    if (!callSuper || optional(Token.COMMA)) {
    +      return parseFieldInitializersOrRedirectedConstructor(initializers);
    +    }
    +    return false;
    +  }
    +
    +  /**
    +   * 
    +   * variableDeclaration
    +   *    : constVarOrType identifierList
    +   *    ;
    +   * identifierList
    +   *    : identifier (',' identifier)*
    +   *    ;
    +   *
    +   * staticConstDeclarationList
    +   *    : staticConstDeclaration (',' staticConstDeclaration)*
    +   *    ;
    +   *
    +   * staticConstDeclaration
    +   *    : identifier '=' constantExpression
    +   *    ;
    +   *
    +   * // The compile-time expression production is used to mark certain expressions
    +   * // as only being allowed to hold a compile-time constant. The grammar cannot
    +   * // express these restrictions, so this will have to be enforced by a separate
    +   * // analysis phase.
    +   * constantExpression
    +   *    : expression
    +   *    ;
    +   * 
    + */ + private DartFieldDefinition parseFieldDeclaration(Modifiers modifiers, DartTypeNode type) { + if (isParsingInterface) { + modifiers = modifiers.makeFinal(); + } + List fields = new ArrayList(); + do { + beginVariableDeclaration(); + DartIdentifier name = parseIdentifier(); + DartExpression value = null; + if (optional(Token.ASSIGN)) { + value = parseExpression(); + } + fields.add(done(new DartField(name, modifiers, null, value))); + } while (optional(Token.COMMA)); + return done(new DartFieldDefinition(type, fields)); + } + + /** + *
    +   * formalParameterList
    +   *     : '(' restFormalParameter? ')'
    +   *     | '(' namedFormalParameters ')'
    +   *     | '(' legacyNormalFormalParameter normalFormalParameterTail? ')'
    +   *     ;
    +   *
    +   * normalFormalParameterTail
    +   *     : ',' namedFormalParameters
    +   *     | ',' restFormalParameter
    +   *     | ',' legacyNormalFormalParameter normalFormalParameterTail?
    +   *     ;
    +   * 
    + */ + private List parseFormalParameterList() { + beginFormalParameterList(); + List params = new ArrayList(); + expect(Token.LPAREN); + boolean done = optional(Token.RPAREN); + boolean hasDefaultParameter = false; + boolean hasNamed = false; + while (!done) { + if (!hasNamed && optional(Token.LBRACK)) { + hasNamed = true; + } + + DartParameter param = parseFormalParameter(hasNamed); + params.add(param); + + if (param.getModifiers().isVariadic()) { + if (hasNamed) { + // Cannot mix named and variadic parameters. + // TODO(jgw): Enable this error as soon as we remove support for optional unnamed + // parameters. It currently breaks incremental compilation for methods of the form: + // method(x = 0, ...y); + // Because that gets normalized to: + // method([x = 0, ...y]); + // Which is illegal. + // + // reportError(position(), DartCompilerErrorCode.NAMED_AND_VARIADIC_PARAMETERS); + expect(Token.RBRACK); + } + + // Variadic must be the last parameter + expectCloseParen(); + done = true; + } else { + done = optional(Token.RBRACK); + if (done) { + expectCloseParen(); + } else { + done = optional(Token.RPAREN); + } + + if (hasDefaultParameter) { + if (!hasNamed) { + // TODO(jgw): Add this check, and remove the one below, when we remove default + // positional parameters. + // reportError(position(), DartCompilerErrorCode.DEFAULT_POSITIONAL_PARAMETER); + if (param.getDefaultExpr() == null) { + reportError(position(), DartCompilerErrorCode.DEFAULT_PARAMETER_BEFORE_NORMAL_PARAMETER); + } + } + } else { + hasDefaultParameter = (param.getDefaultExpr() != null); + } + + if (!done) { + // Ensure termination if token is anything other than COMMA. + done = !expect(Token.COMMA); + } + } + } + + return done(params); + } + + /** + *
    +   * normalFormalParameter
    +   *     : functionDeclaration
    +   *     | fieldFormalParameter
    +   *     | simpleFormalParameter
    +   *     ;
    +   *
    +   * namedFormalParameters
    +   *     : '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
    +   *     ;
    +   *
    +   * defaultFormalParameter
    +   *     : normalFormalParameter ('=' constantExpression)?
    +   *     ;
    +   *
    +   * restFormalParameter
    +   *     : finalVarOrType? '...' identifier
    +   *     ;
    +   * 
    + */ + private DartParameter parseFormalParameter(boolean isNamed) { + beginFormalParameter(); + DartExpression paramName = null; + DartTypeNode type = null; + DartExpression initExpr = null; + List functionParams = null; + boolean hasVar = false; + Modifiers modifiers = Modifiers.NONE; + + if (isNamed) { + modifiers = modifiers.makeNamed(); + } + + if (optional(Token.FINAL)) { + modifiers = modifiers.makeFinal(); + } else if (optional(Token.VAR)) { + hasVar = true; + } + + boolean isVoidType = false; + if (!hasVar) { + isVoidType = (peek(0) == Token.VOID); + if (isVoidType) { + type = parseVoidType(); + } else if ((peek(0) != Token.ELLIPSIS) + && (peek(1) != Token.COMMA) + && (peek(1) != Token.RPAREN) + && (peek(1) != Token.RBRACK) + && (peek(1) != Token.ASSIGN) + && (peek(1) != Token.LPAREN) + && (peek(0) != Token.THIS)) { + // Must be a type specification. + type = parseTypeAnnotation(); + } + } + + if (optional(Token.ELLIPSIS)) { + modifiers = modifiers.makeVariadic(); + } + + paramName = parseParameterName(); + + if (peek(0) == Token.LPAREN) { + // Function parameter. + if (modifiers.isFinal()) { + reportError(position(), DartCompilerErrorCode.FUNCTION_TYPED_PARAMETER_IS_FINAL); + } + if (hasVar) { + reportError(position(), DartCompilerErrorCode.FUNCTION_TYPED_PARAMETER_IS_VAR); + } + if (modifiers.isVariadic()) { + reportError(position(), DartCompilerErrorCode.FUNCTION_TYPED_PARAMETER_IS_VARIADIC); + } + functionParams = parseFormalParameterList(); + } else { + // Not a function parameter. + if (isVoidType) { + reportError(type, DartCompilerErrorCode.VOID_PARAMETER); + } + } + + // Look for an initialization expression + switch (peek(0)) { + case COMMA: + case RPAREN: + case RBRACK: + // It is a simple parameter. + break; + + case ASSIGN: + // Default parameter. + if (modifiers.isVariadic()) { + reportError(position(), DartCompilerErrorCode.VARIADIC_PARAMETER_HAS_INITIALIZER); + } + + // TODO(jgw): This makes legacy default parameters implicitly named, which will ease the + // transition. Remove this as soon as positional default parameters are removed. + modifiers = modifiers.makeNamed(); + + consume(Token.ASSIGN); + initExpr = parseExpression(); + + break; + + default: + reportUnexpectedToken(position(), null, peek(0)); + break; + } + + return done(new DartParameter(paramName, type, functionParams, initExpr, modifiers)); + } + + /** + *
    +   * simpleFormalParameter
    +   *     : declaredIdentifier
    +   *     | identifier
    +   *     ;
    +   *
    +   * fieldFormalParameter
    +   *    : finalVarOrType? THIS '.' identifier
    +   *    ;
    +   * 
    + */ + private DartExpression parseParameterName() { + beginParameterName(); + if (optional(Token.THIS)) { + beginThisExpression(); + expect(Token.PERIOD); + return done(new DartPropertyAccess(done(DartThisExpression.get()), parseIdentifier())); + } + return done(parseIdentifier()); + } + + /** + * Parse an expression. + * + *
    +   * expression
    +   *     : assignableExpression assignmentOperator expression
    +   *     | conditionalExpression
    +   *     ;
    +   *
    +   * assignableExpression
    +   *     : primary (arguments* assignableSelector)+
    +   *     | SUPER assignableSelector
    +   *     | identifier
    +   *     ;
    +   * 
    + * + * @return an expression matching the {@code expression} production above + */ + @VisibleForTesting + public DartExpression parseExpression() { + beginExpression(); + DartExpression result = parseConditionalExpression(); + Token token = peek(0); + if (token.isAssignmentOperator()) { + ensureAssignable(result); + consume(token); + result = done(new DartBinaryExpression(token, result, parseExpression())); + } else { + done(null); + } + return result; + } + + /** + * expressionList + * : expression (',' expression)* + * ; + */ + private DartExpression parseExpressionList() { + beginExpressionList(); + DartExpression result = parseExpression(); + while (optional(Token.COMMA)) { + result = new DartBinaryExpression(Token.COMMA, result, parseExpression()); + if (match(Token.COMMA)) { + result = doneWithoutConsuming(result); + } + } + return done(result); + } + + + /** + * Parse a binary expression. + * + *
    +   * logicalOrExpression
    +   *     : logicalAndExpression ('||' logicalAndExpression)*
    +   *     ;
    +   *
    +   * logicalAndExpression
    +   *     : bitwiseOrExpression ('&&' bitwiseOrExpression)*
    +   *     ;
    +   *
    +   * bitwiseOrExpression
    +   *     : bitwiseXorExpression ('|' bitwiseXorExpression)*
    +   *     ;
    +   *
    +   * bitwiseXorExpression
    +   *     : bitwiseAndExpression ('^' bitwiseAndExpression)*
    +   *     ;
    +   *
    +   * bitwiseAndExpression
    +   *     : equalityExpression ('&' equalityExpression)*
    +   *     ;
    +   *
    +   * equalityExpression
    +   *     : relationalExpression (equalityOperator relationalExpression)?
    +   *     ;
    +   *
    +   * relationalExpression
    +   *     : shiftExpression (isOperator type | relationalOperator shiftExpression)?
    +   *     ;
    +   *
    +   * shiftExpression
    +   *     : additiveExpression (shiftOperator additiveExpression)*
    +   *     ;
    +   *
    +   * additiveExpression
    +   *     : multiplicativeExpression (additiveOperator multiplicativeExpression)*
    +   *     ;
    +   *
    +   * multiplicativeExpression
    +   *     : unaryExpression (multiplicativeOperator unaryExpression)*
    +   *     ;
    +   * 
    + * + * @return an expression matching one of the productions above + */ + private DartExpression parseBinaryExpression(int precedence) { + assert (precedence >= 4); + beginBinaryExpression(); + DartExpression result = parseUnaryExpression(); + for (int level = peek(0).getPrecedence(); level >= precedence; level--) { + while (peek(0).getPrecedence() == level) { + Token token = next(); + DartExpression right; + if (token == Token.IS) { + beginTypeExpression(); + if (optional(Token.NOT)) { + beginTypeExpression(); + DartTypeExpression typeExpression = done(new DartTypeExpression(parseTypeAnnotation())); + right = done(new DartUnaryExpression(Token.NOT, typeExpression, true)); + } else { + right = done(new DartTypeExpression(parseTypeAnnotation())); + } + } else { + right = parseBinaryExpression(level + 1); + } + result = doneWithoutConsuming(new DartBinaryExpression(token, result, right)); + if ((token == Token.IS) + || token.isRelationalOperator() + || token.isEqualityOperator()) { + // The operations cannot be chained. + if (match(token)) { + reportError(position(), DartCompilerErrorCode.INVALID_OPERATOR_CHAINING, + token.toString().toLowerCase()); + } + break; + } + } + } + done(null); + return result; + } + + /** + * Parse the arguments passed to a function or method invocation. + * + *
    +   * arguments
    +   *    : '(' argumentList? ')'
    +   *    ;
    +   *
    +   * argumentList
    +   *    : expression (',' expression)* (',' spreadArgument)?
    +   *    | spreadArgument
    +   *    ;
    +   *
    +   * spreadArgument
    +   *    : '...' expression
    +   *    ;
    +   * 
    + * + * @return a list of expressions containing the arguments to be passed + */ + private List parseArguments() { + List arguments = new ArrayList(); + expect(Token.LPAREN); + // SEMICOLON is for error recovery + while (!match(Token.RPAREN) && !match(Token.EOS) && !match(Token.SEMICOLON)) { + beginParameter(); + DartExpression expression; + if (peek(1) == Token.COLON) { + DartIdentifier name = parseIdentifier(); + expect(Token.COLON); + expression = new DartNamedExpression(name, parseExpression()); + } else { + expression = parseExpression(); + } + arguments.add(done(expression)); + switch(peek(0)) { + case COMMA: + consume(Token.COMMA); + break; + case RPAREN: + break; + default: + // Make sure that the parser's state is advanced. + Token actual = peek(0); + ctx.advance(); + reportError(ctx.getTokenLocation().getEnd(), + DartCompilerErrorCode.EXPECTED_COMMA_OR_RIGHT_PAREN, actual); + break; + } + } + expectCloseParen(); + return arguments; + } + + /** + * Parse a conditional expression. + * + *
    +   * conditionalExpression
    +   *     : logicalOrExpression ('?' expression ':' expression)?
    +   *     ;
    +   * 
    + * + * @return an expression matching the {@code conditionalExpression} production + */ + private DartExpression parseConditionalExpression() { + beginConditionalExpression(); + DartExpression result = parseBinaryExpression(4); + if (peek(0) != Token.CONDITIONAL) { + return done(result); + } + consume(Token.CONDITIONAL); + DartExpression yes = parseExpression(); + expect(Token.COLON); + DartExpression no = parseExpression(); + return done(new DartConditional(result, yes, no)); + } + + private DartExpression parseString() { + switch(peek(0)) { + case STRING: + return parseLiteral(); + case STRING_SEGMENT: + case STRING_EMBED_EXP_START: + return parseStringInterpolation(); + default: + DartExpression expression = parseExpression(); + reportError(position(), DartCompilerErrorCode.EXPECTED_STRING_LITERAL); + return expression; + } + } + + /** + * Parse any literal that is not a function literal (those have already been + * handled before this method is called, so we don't need to handle them + * here). + * + *
    +   * nonFunctionLiteral
    +   *   : NULL
    +   *   | TRUE
    +   *   | FALSE
    +   *   | HEX_NUMBER
    +   *   | RATIONAL_NUMBER
    +   *   | DOUBLE_NUMBER
    +   *   | STRING
    +   *   | mapLiteral
    +   *   | arrayLiteral
    +   *   ;
    +   * 
    + * + * @return an expression matching the {@code literal} production above + */ + private DartExpression parseLiteral() { + beginLiteral(); + switch (peek(0)) { + case NULL_LITERAL: { + consume(Token.NULL_LITERAL); + return done(DartNullLiteral.get()); + } + + case TRUE_LITERAL: { + consume(Token.TRUE_LITERAL); + return done(DartBooleanLiteral.get(true)); + } + + case FALSE_LITERAL: { + consume(Token.FALSE_LITERAL); + return done(DartBooleanLiteral.get(false)); + } + + case INTEGER_LITERAL: { + consume(Token.INTEGER_LITERAL); + String number = ctx.getTokenString(); + return done(DartIntegerLiteral.get(new BigInteger(number))); + } + + case DOUBLE_LITERAL: { + consume(Token.DOUBLE_LITERAL); + String number = ctx.getTokenString(); + return done(DartDoubleLiteral.get(Double.parseDouble(number))); + } + + case HEX_LITERAL: { + consume(Token.HEX_LITERAL); + String number = ctx.getTokenString(); + return done(DartIntegerLiteral.get(new BigInteger(number, 16))); + } + + case STRING: { + consume(Token.STRING); + return done(DartStringLiteral.get(ctx.getTokenString())); + } + + case LBRACE: { + return done(parseMapLiteral(false, null)); + } + + case INDEX: { + expect(peek(0)); + return done(new DartArrayLiteral(false, null, new ArrayList())); + } + + case LBRACK: { + return done(parseArrayLiteral(false, null)); + } + + case VOID: + // For better error recovery / code completion in the IDE, treat "void" as an identifier + // here and let it get reported as a resolution error. + case IDENTIFIER: { + return done(parseIdentifier()); + } + + case SEMICOLON: { + // this is separate from the default case for better error recovery, + // leaving the semicolon for the caller to use for a statement boundary + + // we have to advance to get the proper position, but we want to leave + // the semicolon + startLookahead(); + next(); + reportUnexpectedToken(position(), null, Token.SEMICOLON); + rollback(); + return done(new DartSyntheticErrorExpression("")); + } + + default: { + Token unexpected = peek(0); + String unexpectedString = ctx.getTokenString(); + if (unexpectedString == null && unexpected != Token.EOS) { + unexpectedString = unexpected.getSyntax(); + } + next(); + reportUnexpectedToken(position(), null, unexpected); + StringBuilder tokenStr = new StringBuilder(); + if (unexpectedString != null) { + tokenStr.append(unexpectedString); + } + // TODO(jat): should we eat additional tokens here for error recovery? + return done(new DartSyntheticErrorExpression(tokenStr.toString())); + } + } + } + + /** + *
    +   * mapLiteral
    +   *     : '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
    +   *     ;
    +   * 
    + */ + private DartExpression parseMapLiteral(boolean isConst, List typeArguments) { + beginMapLiteral(); + expect(Token.LBRACE); + boolean save = setAllowFunctionExpression(true); + List entries = new ArrayList(); + + while (!match(Token.RBRACE) && !match(Token.EOS)) { + DartMapLiteralEntry entry = parseMapLiteralEntry(); + if (entry != null) { + entries.add(entry); + } + switch(peek(0)) { + case COMMA: + consume(Token.COMMA); + break; + case RBRACE: + break; + default: + if (entry == null) { + // Ensure the parser makes progress. + ctx.advance(); + } + reportError(position(), DartCompilerErrorCode.EXPECTED_COMMA_OR_RIGHT_BRACE); + break; + } + } + + expectCloseBrace(); + setAllowFunctionExpression(save); + return done(new DartMapLiteral(isConst, typeArguments, entries)); + } + + /** + * mapLiteralEntry + * : STRING ':' expression + * ; + */ + private DartMapLiteralEntry parseMapLiteralEntry() { + beginMapLiteralEntry(); + // Parse the key. + DartExpression keyExpr = parseString(); + if (keyExpr == null) { + return done(null); + } + // Parse the value. + DartExpression value; + if (expect(Token.COLON)) { + value = parseExpression(); + } else { + value = doneWithoutConsuming(DartNullLiteral.get()); + } + return done(new DartMapLiteralEntry(keyExpr, value)); + } + + /** + * // The array literal syntax doesn't allow elided elements, unlike + * // in ECMAScript. + * + *
    +   * arrayLiteral
    +   *     : '[' expressionList? ']'
    +   *     ;
    +   * 
    + */ + private DartExpression parseArrayLiteral(boolean isConst, List typeArguments) { + beginArrayLiteral(); + expect(Token.LBRACK); + boolean save = setAllowFunctionExpression(true); + List exprs = new ArrayList(); + while (!match(Token.RBRACK) && !EOS()) { + exprs.add(parseExpression()); + if (!optional(Token.COMMA)) { + break; + } + } + expect(Token.RBRACK); + setAllowFunctionExpression(save); + return done(new DartArrayLiteral(isConst, typeArguments, exprs)); + } + + /** + * Parse a postfix expression. + * + *
    +   * postfixExpression
    +   *     | assignableExpression postfixOperator
    +   *     : primary selector*
    +   *     ;
    +   * 
    + * + * @return an expression matching the {@code postfixExpression} production above + */ + private DartExpression parsePostfixExpression() { + beginPostfixExpression(); + DartExpression receiver = doneWithoutConsuming(parsePrimaryExpression()); + DartExpression result = receiver; + do { + receiver = result; + result = doneWithoutConsuming(parseSelectorExpression(receiver)); + } while (receiver != result); + + Token token = peek(0); + if (token.isCountOperator()) { + ensureAssignable(result); + consume(token); + result = doneWithoutConsuming(new DartUnaryExpression(token, result, false)); + } + + return done(result); + } + + /** + *
    +   * typeParameters? (arrayLiteral | mapLiteral)
    +   * 
    + * + * @param isConst true if a CONST expression + * + */ + private DartExpression tryParseTypedCompoundLiteral(boolean isConst) { + beginLiteral(); + List typeArguments = parseTypeArgumentsOpt(); + switch (peek(0)) { + case INDEX: + beginArrayLiteral(); + consume(Token.INDEX); + return done(done(new DartArrayLiteral(isConst, null, new ArrayList()))); + case LBRACK: + return done(parseArrayLiteral(isConst, typeArguments)); + case LBRACE: + return done(parseMapLiteral(isConst, typeArguments)); + default: + if (typeArguments != null) { + rollback(); + return null; + } + + } + // Doesn't look like a typed compound literal and no tokens consumed. + return done(null); + } + + /** + *
    +   * string-interpolation
    +   *   : (STRING_SEGMENT? embedded-exp?)* STRING_LAST_SEGMENT
    +   *
    +   * embedded-exp
    +   *   : STRING_EMBED_EXP_START expression STRING_EMBED_EXP_END
    +   * 
    + */ + private DartExpression parseStringInterpolation() { + // TODO(sigmund): generalize to parse string templates as well. + if (peek(0) == Token.STRING_LAST_SEGMENT) { + throw new InternalCompilerException("Invariant broken"); + } + beginStringInterpolation(); + List strings = new ArrayList(); + List expressions = new ArrayList(); + + boolean inString = true; + while (inString) { // Iterate until we find the last string segment. + switch (peek(0)) { + case STRING_SEGMENT: { + assert strings.size() == expressions.size() : "Invariant broken"; + beginStringSegment(); + consume(Token.STRING_SEGMENT); + strings.add(done(DartStringLiteral.get(ctx.getTokenString()))); + break; + } + case STRING_LAST_SEGMENT: { + assert strings.size() == expressions.size() : "Invariant broken"; + beginStringSegment(); + consume(Token.STRING_LAST_SEGMENT); + strings.add(done(DartStringLiteral.get(ctx.getTokenString()))); + inString = false; + break; + } + case STRING_EMBED_EXP_START: { + consume(Token.STRING_EMBED_EXP_START); + if (strings.size() == expressions.size()) { + // Ensure that strings and expressions are alternating, add empty + // strings if we see 2 consecutive expressions. + beginStringSegment(); + strings.add(done(DartStringLiteral.get(""))); + } + /* + * We check for ILLEGAL specifically here to give nicer error + * messages, and because the scanner doesn't generate a + * STRING_EMBED_EXP_END to match the START in the case of an ILLEGAL + * token. + */ + if (peek(0) == Token.ILLEGAL) { + reportError(position(), DartCompilerErrorCode.UNEXPECTED_TOKEN_IN_STRING_INTERPOLATION, + next()); + expressions.add(new DartSyntheticErrorExpression(ctx.getTokenString())); + break; + } else { + DartExpression expr = parseExpression(); + expressions.add(expr); + } + if (!expect(Token.STRING_EMBED_EXP_END)) { + return done(new DartSyntheticErrorExpression()); + } + break; + } + case EOS: { + reportError(position(), DartCompilerErrorCode.INCOMPLETE_STRING_LITERAL); + return done(null); + } + default: { + reportError(position(), DartCompilerErrorCode.UNEXPECTED_TOKEN_IN_STRING_INTERPOLATION, + next()); + break; + } + } + } + assert (strings.size() == expressions.size() + 1) : "Invariant broken"; + return done(new DartStringInterpolation(strings, expressions)); + } + + /** + * Parse a return type, giving an error if the . + * + * @return a return type or null if the current text is not a return type + */ + private DartTypeNode parseReturnType() { + if (peek(0) == Token.VOID) { + return parseVoidType(); + } else { + return parseTypeAnnotation(); + } + } + + /** + * Check if the current text could be a return type, and advance past it if so. The current + * position is unchanged if it is not a return type. + * + * NOTE: if the grammar is changed for what constitutes an acceptable return type, this method + * must be updated to match {@link #parseReturnType()}/etc. + * + * @return true if current text could be a return type, false otherwise + */ + private boolean isReturnType() { + beginReturnType(); + if (optional(Token.VOID)) { + done(null); + return true; + } + if (!optional(Token.IDENTIFIER)) { + rollback(); + return false; + } + // handle prefixed identifiers + if (optional(Token.PERIOD)) { + if (!optional(Token.IDENTIFIER)) { + rollback(); + return false; + } + } + // skip over type arguments if they are present + if (optional(Token.LT)) { + int count = 1; + while (count > 0) { + switch (next()) { + case EOS: + rollback(); + return false; + case LT: + count++; + break; + case GT: + count--; + break; + case SHL: + count += 2; + break; + case SHR: // >>> + count -= 3; + break; + case SAR: // >> + count -= 2; + break; + case COMMA: + case IDENTIFIER: + // extends is a pseudokeyword, so shows up as IDENTIFIER + break; + default: + rollback(); + return false; + } + } + if (count < 0) { + // if we had too many > (which can only be >> or >>>), can't be a return type + rollback(); + return false; + } + } + done(null); + return true; + } + + /** + * Checks to see if the current text looks like a function expression: + * + *
    +   *   FUNCTION name? ( args ) < => | { >
    +   *   returnType name? ( args ) < => | { >
    +   *   name? ( args ) < => | { >
    +   * 
    + * + * The current position is unchanged on return. + * + * NOTE: if the grammar for function expressions changes, this method must be + * adapted to match the actual parsing code. It is acceptable for this method + * to return true when the source text does not actually represent a function + * expression (which would result in error messages assuming it was a function + * expression, but it must not do so when the source text would be correct if + * parsed as a non-function expression. + * + * @return true if the current text looks like a function expression, false + * otherwise + */ + @VisibleForTesting + boolean looksLikeFunctionExpression() { + if (!allowFunctionExpression) { + return false; + } + return looksLikeFunctionDeclarationOrExpression(); + } + + /** + * Check to see if the following tokens could be a function expression, and if so try and parse + * it as one. + * + * @return a function expression if found, or null (with no tokens consumed) if not + */ + private DartExpression parseFunctionExpressionWithReturnType() { + beginFunctionLiteral(); + DartIdentifier[] namePtr = new DartIdentifier[1]; + DartFunction function = parseFunctionDeclarationOrExpression(namePtr, false); + if (function == null) { + rollback(); + return null; + } + return done(new DartFunctionExpression(namePtr[0], doneWithoutConsuming(function), false)); + } + + /** + * Parse a function declaration or expression, including the body. + *
    +   *     ... | functionDeclaration functionBody
    +   *
    +   * functionDeclaration
    +   *    : returnType? identifier formalParameterList
    +   *    ;
    +   *
    +   * functionExpression
    +   *    : (returnType? identifier)? formalParameterList functionExpressionBody
    +   *    ;
    +   *
    +   * functionBody
    +   *    : '=>' expression ';'
    +   *    | block
    +   *    ;
    +   *
    +   * functionExpressionBody
    +   *    : '=>' expression
    +   *    | block
    +   *    ;
    +   * 
    + * + * @param namePtr out parameter - parsed function name stored in namePtr[0] + * @param isDeclaration true if this is a declaration (ie, a name is required and a trailing + * semicolon is needed for arrow syntax + * @return a {@link DartFunction} containing the body of the function, or null + * if the next tokens cannot be parsed as a function declaration or expression + */ + private DartFunction parseFunctionDeclarationOrExpression(DartIdentifier[] namePtr, + boolean isDeclaration) { + DartTypeNode returnType = null; + namePtr[0] = null; + switch (peek(0)) { + case LPAREN: + // no type or name, just the formal parameter list + break; + case IDENTIFIER: + if (peek(1) == Token.LPAREN) { + // if there is only one identifier, it must be the name + namePtr[0] = parseIdentifier(); + break; + } + //$FALL-THROUGH$ + case VOID: + returnType = parseReturnType(); + if (peek(0) == Token.IDENTIFIER) { + namePtr[0] = parseIdentifier(); + } + break; + default: + return null; + } + List params = parseFormalParameterList(); + DartBlock body = parseFunctionStatementBody(isDeclaration); + DartFunction function = new DartFunction(params, body, returnType); + if (isDeclaration && namePtr[0] == null) { + reportError(function, DartCompilerErrorCode.MISSING_FUNCTION_NAME); + } + return function; + } + + /** + * Parse a primary expression. + * + *
    +   * primary
    +   *   : THIS
    +   *   | SUPER assignableSelector
    +   *   | literal
    +   *   | identifier
    +   *   | NEW type ('.' identifier)? arguments
    +   *   | typeArguments? (arrayLiteral | mapLiteral)
    +   *   | CONST typeArguments? (arrayLiteral | mapLiteral)
    +   *   | CONST typeArguments? (arrayLiteral | mapLiteral)
    +   *   | CONST type ('.' identifier)? arguments
    +   *   | '(' expression ')'
    +   *   | string-interpolation
    +   *   | functionExpression
    +   *   ;
    +   * 
    + * + * @return an expression matching the {@code primary} production above + */ + private DartExpression parsePrimaryExpression() { + if (looksLikeFunctionExpression()) { + return parseFunctionExpressionWithReturnType(); + } + switch (peek(0)) { + case THIS: { + beginThisExpression(); + consume(Token.THIS); + return done(DartThisExpression.get()); + } + + case SUPER: { + beginSuperExpression(); + consume(Token.SUPER); + return done(parseAssignableSelector(doneWithoutConsuming(DartSuperExpression.get()))); + } + + case NEW: { + beginNewExpression(); // DartNewExpression + consume(Token.NEW); + return done(parseConstructor(false)); + } + + case CONST: { + beginConstExpression(); + consume(Token.CONST); + + DartExpression literal = tryParseTypedCompoundLiteral(true); + if (literal != null) { + return done(literal); + } + return done(parseConstructor(true)); + } + + case LPAREN: { + beginParenthesizedExpression(); + consume(Token.LPAREN); + beginExpression(); + // inside parens, function blocks are allowed again + boolean save = setAllowFunctionExpression(true); + DartExpression expression = done(parseExpression()); + setAllowFunctionExpression(save); + expectCloseParen(); + return done(new DartParenthesizedExpression(expression)); + } + + case LT: { + beginLiteral(); + DartExpression literal = tryParseTypedCompoundLiteral(false); + if (literal == null) { + reportError(position(), DartCompilerErrorCode.EXPECTED_ARRAY_OR_MAP_LITERAL); + } + return done(literal); + } + + case STRING_SEGMENT: + case STRING_LAST_SEGMENT: + case STRING_EMBED_EXP_START: { + return parseStringInterpolation(); + } + + default: { + return parseLiteral(); + } + } + } + + private DartExpression parseConstructor(boolean isConst) { + List parts = new ArrayList(); + beginConstructor(); + do { + parts.add(new DartTypeNode(parseIdentifier(), parseTypeArgumentsOpt())); + } while (optional(Token.PERIOD)); + assert parts.size() > 0; + + DartNode constructor; + switch (parts.size()) { + case 1: + constructor = doneWithoutConsuming(parts.get(0)); + break; + + case 2: { + // This case is ambiguous. It can either be prefix.Type or + // Type.namedConstructor. + boolean hasPrefix = false; + DartTypeNode part1 = parts.get(0); + DartTypeNode part2 = parts.get(1); + if (prefixes.contains(((DartIdentifier) part1.getIdentifier()).getTargetName())) { + hasPrefix = true; + } + if (!part2.getTypeArguments().isEmpty()) { + // If the second part has type arguments, the first part must be a prefix. + // If it isn't a prefix, the resolver will complain. + hasPrefix = true; + } + if (hasPrefix) { + constructor = doneWithoutConsuming(toPrefixedType(parts)); + } else { + // Named constructor. + DartIdentifier identifier = ensureIdentifier(part2); + constructor = doneWithoutConsuming(new DartPropertyAccess(doneWithoutConsuming(part1), + identifier)); + } + break; + } + default: { + // This case is unambiguous. It must be prefix.Type.namedConstructor. + if (parts.size() > 3) { + reportError(parts.get(3), DartCompilerErrorCode.EXPECTED_LEFT_PAREN); + } + DartTypeNode typeNode = doneWithoutConsuming(toPrefixedType(parts)); + DartIdentifier identifier = ensureIdentifier(parts.get(2)); + constructor = doneWithoutConsuming(new DartPropertyAccess(typeNode, identifier)); + break; + } + } + + return done(new DartNewExpression(constructor, parseArguments(), isConst)); + } + + private DartIdentifier ensureIdentifier(DartTypeNode node) { + List typeArguments = node.getTypeArguments(); + if (!typeArguments.isEmpty()) { + reportError(typeArguments.get(0), DartCompilerErrorCode.UNEXPECTED_TYPE_ARGUMENT); + } + return (DartIdentifier) node.getIdentifier(); + } + + private DartTypeNode toPrefixedType(List parts) { + DartIdentifier part1 = ensureIdentifier(parts.get(0)); + DartTypeNode part2 = parts.get(1); + DartIdentifier identifier = (DartIdentifier) part2.getIdentifier(); + DartPropertyAccess access = doneWithoutConsuming(new DartPropertyAccess(part1, identifier)); + return new DartTypeNode(access, part2.getTypeArguments()); + } + + /** + * Parse a selector expression. + * + *
    +   * selector
    +   *    : assignableSelector
    +   *    | arguments
    +   *    ;
    +   * 
    + * + * @return an expression matching the {@code selector} production above + */ + private DartExpression parseSelectorExpression(DartExpression receiver) { + DartExpression expression = tryParseAssignableSelector(receiver); + if (expression != null) { + return expression; + } + + if (peek(0) == Token.LPAREN) { + beginSelectorExpression(); + boolean save = setAllowFunctionExpression(true); + List args = parseArguments(); + setAllowFunctionExpression(save); + if (receiver instanceof DartIdentifier) { + return(done(new DartUnqualifiedInvocation((DartIdentifier) receiver, args))); + } else { + return(done(new DartFunctionObjectInvocation(receiver, args))); + } + } + + return receiver; + } + + /** + *
    +   * assignableSelector
    +   *    : '[' expression ']'
    +   *    | '.' identifier
    +   *    ;
    +   * 
    + */ + private DartExpression tryParseAssignableSelector(DartExpression receiver) { + switch (peek(0)) { + case PERIOD: + consume(Token.PERIOD); + switch (peek(0)) { + case SEMICOLON: + case RBRACE: + reportError(position(), DartCompilerErrorCode.EXPECTED_IDENTIFIER); + DartIdentifier error = doneWithoutConsuming(new DartIdentifier("")); + return doneWithoutConsuming(new DartPropertyAccess(receiver, error)); + } + DartIdentifier name = parseIdentifier(); + if (peek(0) == Token.LPAREN) { + boolean save = setAllowFunctionExpression(true); + DartMethodInvocation expr = doneWithoutConsuming(new DartMethodInvocation(receiver, name, + parseArguments())); + setAllowFunctionExpression(save); + return expr; + } else { + return doneWithoutConsuming(new DartPropertyAccess(receiver, name)); + } + + case LBRACK: + consume(Token.LBRACK); + DartExpression key = parseExpression(); + expect(Token.RBRACK); + return doneWithoutConsuming(new DartArrayAccess(receiver, key)); + + default: + return null; + } + } + + private DartExpression parseAssignableSelector(DartExpression receiver) { + DartExpression expression = tryParseAssignableSelector(receiver); + if (expression == null) { + reportError(position(), DartCompilerErrorCode.EXPECTED_PERIOD_OR_LEFT_BRACKET); + } + return expression; + } + + /** + *
    +   * block
    +   *     : '{' statements deadCode* '}'
    +   *     ;
    +   *
    +   * statements
    +   *     : statement*
    +   *     ;
    +   *
    +   * deadCode
    +   *     : (normalCompletingStatement | abruptCompletingStatement)
    +   *     ;
    +   * 
    + */ + private DartBlock parseBlock() { + if (isDietParse) { + expect(Token.LBRACE); + DartBlock emptyBlock = new DartBlock(new ArrayList()); + int nesting = 1; + while (nesting > 0) { + Token token = next(); + switch (token) { + case LBRACE: + ++nesting; + break; + case RBRACE: + --nesting; + break; + case EOS: + return emptyBlock; + } + } + // Return an empty block so we don't generate unparseable code. + return emptyBlock; + } else { + beginBlock(); + List statements = new ArrayList(); + expect(Token.LBRACE); + while (!match(Token.RBRACE) && !EOS()) { + DartStatement newStatement = parseStatement(); + if (newStatement == null) { + break; + } + statements.add(newStatement); + } + expectCloseBrace(); + return done(new DartBlock(statements)); + } + } + + /** + * Parse a function statement body. + * + *
    +   * functionStatementBody
    +   *    : '=>' expression ';'
    +   *    | block
    +   * 
    + * + * @param requireSemicolonForArrow true if a semicolon is required after an arrow expression + * @return {@link DartBlock} instance containing function body + */ + private DartBlock parseFunctionStatementBody(boolean requireSemicolonForArrow) { + if (isDietParse) { + expect(Token.LBRACE); + DartBlock emptyBlock = new DartBlock(new ArrayList()); + int nesting = 1; + while (nesting > 0) { + Token token = next(); + switch (token) { + case LBRACE: + ++nesting; + break; + case RBRACE: + --nesting; + break; + case EOS: + return emptyBlock; + } + } + // Return an empty block so we don't generate unparseable code. + return emptyBlock; + } else { + beginFunctionStatementBody(); + if (optional(Token.ARROW)) { + DartExpression expr = parseExpression(); + if (requireSemicolonForArrow) { + expect(Token.SEMICOLON); + } + return done(makeReturnBlock(expr)); + } else { + return done(parseBlock()); + } + } + } + + /** + * Create a block containing a single return statement. + * + * @param returnVal return value expression + * @return block containing a single return statement + */ + private DartBlock makeReturnBlock(DartExpression returnVal) { + // TODO(jat): consider making a different AST node to represent this + List statements = new ArrayList(); + statements.add(new DartReturnStatement(returnVal)); + return new DartBlock(statements); + } + + /** + *
    +   * initializedVariableDeclaration
    +   *     : constVarOrType initializedIdentifierList
    +   *     ;
    +   *
    +   * initializedIdentifierList
    +   *     : initializedIdentifier (',' initializedIdentifier)*
    +   *     ;
    +   *
    +   * initializedIdentifier
    +   *     : IDENTIFIER ('=' assignmentExpression)?
    +   *     ;
    +   *  
    + */ + private List parseInitializedVariableList() { + List idents = new ArrayList(); + do { + beginVariableDeclaration(); + DartIdentifier name = parseIdentifier(); + DartExpression value = null; + if (isParsingInterface) { + expect(Token.ASSIGN); + value = parseExpression(); + } else if (optional(Token.ASSIGN)) { + value = parseExpression(); + } + idents.add(done(new DartVariable(name, value))); + } while (optional(Token.COMMA)); + + return idents; + } + + /** + *
    +   * abruptCompletingStatement
    +   *     : BREAK identifier? ';'
    +   *     | CONTINUE identifier? ';'
    +   *     | RETURN expression? ';'
    +   *     | THROW expression? ';'
    +   *     ;
    +   *  
    + */ + private DartBreakStatement parseBreakStatement() { + beginBreakStatement(); + expect(Token.BREAK); + DartIdentifier label = null; + if (match(Token.IDENTIFIER)) { + label = parseIdentifier(); + } + expectStatmentTerminator(); + return done(new DartBreakStatement(label)); + } + + private DartContinueStatement parseContinueStatement() { + beginContinueStatement(); + expect(Token.CONTINUE); + DartIdentifier label = null; + if (peek(0) == Token.IDENTIFIER) { + label = parseIdentifier(); + } + expectStatmentTerminator(); + return done(new DartContinueStatement(label)); + } + + private DartReturnStatement parseReturnStatement() { + beginReturnStatement(); + expect(Token.RETURN); + DartExpression value = null; + if (peek(0) != Token.SEMICOLON) { + value = parseExpression(); + } + expectStatmentTerminator(); + return done(new DartReturnStatement(value)); + } + + private DartThrowStatement parseThrowStatement() { + beginThrowStatement(); + expect(Token.THROW); + DartExpression exception = null; + if (peek(0) != Token.SEMICOLON) { + exception = parseExpression(); + } + expectStatmentTerminator(); + return done(new DartThrowStatement(exception)); + } + + /** + *
    +   * statement
    +   *     : label* nonLabelledStatement
    +   *     ;
    +   *
    +   * label
    +   *     : identifier ':'
    +   *     ;
    +   * 
    + * + * @return a {@link DartStatement} + */ + @VisibleForTesting + public DartStatement parseStatement() { + if (peek(0) == Token.IDENTIFIER && peek(1) == Token.COLON) { + beginLabel(); + DartIdentifier label = parseIdentifier(); + expect(Token.COLON); + DartStatement statement = parseNonLabelledStatement(); + return done(new DartLabel(label, statement)); + } + return parseNonLabelledStatement(); + } + + /** + *
    +   * normalCompletingStatement
    +   *     : functionStatement
    +   *     | initializedVariableDeclaration ';'
    +   *     | simpleStatement
    +   *     ;
    +   *
    +   * functionStatement
    +   *     : typeOrFunction identifier formalParameterList block
    +   *     ;
    +   *     ;
    +   *
    +   * simpleStatement
    +   *     : ('{')=> block // Guard to break tie with map literal.
    +   *     | expression? ';'
    +   *     | tryStatement
    +   *     | ASSERT '(' conditionalExpression ')' ';'
    +   *     | abruptCompletingStatement
    +   *     ;
    +   * 
    + */ + private DartStatement parseNonLabelledStatement() { + if (looksLikeFunctionDeclarationOrExpression()) { + return parseFunctionDeclaration(); + } + switch (peek(0)) { + case IF: + return parseIfStatement(); + + case SWITCH: + return parseSwitchStatement(); + + case WHILE: + return parseWhileStatement(); + + case DO: + return parseDoWhileStatement(); + + case FOR: + return parseForStatement(); + + case VAR: { + beginVarDeclaration(); + consume(Token.VAR); + List vars = parseInitializedVariableList(); + expectStatmentTerminator(); + return done(new DartVariableStatement(vars, null)); + } + + case FINAL: { + beginFinalDeclaration(); + consume(peek(0)); + DartTypeNode type = null; + if (peek(1) == Token.IDENTIFIER || peek(1) == Token.LT) { + // We know we have a type. + type = parseTypeAnnotation(); + } + List vars = parseInitializedVariableList(); + expectStatmentTerminator(); + return done(new DartVariableStatement(vars, type, Modifiers.NONE.makeFinal())); + } + + case LBRACE: + return parseBlock(); + + case CONTINUE: + return parseContinueStatement(); + + case BREAK: + return parseBreakStatement(); + + case RETURN: + return parseReturnStatement(); + + case THROW: + return parseThrowStatement(); + + case TRY: + return parseTryStatement(); + + case SEMICOLON: + beginEmptyStatement(); + consume(Token.SEMICOLON); + return done(new DartEmptyStatement()); + + // Things that we know can't be valid statements get a synthetic error statement + case RBRACE: + // no need to create a separate parser event as the AST node is enough + beginEmptyStatement(); + // TODO(jat): other tokens that should be caught here? + return done(parseErrorStatement()); + + case IDENTIFIER: + // we have already eliminated function declarations earlier, so just need to check for + // variable declarations here. + if (peek(1) == Token.LT || peek(1) == Token.IDENTIFIER) { + beginTypeFunctionOrVariable(); + DartTypeNode type = tryTypeAnnotation(); + if (type != null) { + List vars = parseInitializedVariableList(); + expect(Token.SEMICOLON); + return done(new DartVariableStatement(vars, type)); + } else { + rollback(); + } + } + //$FALL-THROUGH$ + + default: + return parseExpressionStatement(); + } + } + + /** + * Check if succeeding tokens look like a function declaration - the parser state is unchanged + * upon return. + * + * See {@link #parseFunctionDeclaration()}. + * + * @return true if the following tokens should be parsed as a function definition + */ + private boolean looksLikeFunctionDeclarationOrExpression() { + startLookahead(); + try { + if (peek(0) == Token.IDENTIFIER && peek(1) == Token.LPAREN) { + // just a name, no return type + consume(Token.IDENTIFIER); + } else if (isReturnType()) { + if (!optional(Token.IDENTIFIER)) { + // return types must be followed by a function name + return false; + } + } + // start of parameter list + if (!optional(Token.LPAREN)) { + return false; + } + // find matching parenthesis + int count = 1; + while (count != 0) { + switch (next()) { + case EOS: + return false; + case LPAREN: + count++; + break; + case RPAREN: + count--; + break; + } + } + return (peek(0) == Token.ARROW || peek(0) == Token.LBRACE); + } finally { + rollback(); + } + } + + /** + * Parse a function declaration. + * + *
    +   * nonLabelledStatement : ...
    +   *     | functionDeclaration functionBody
    +   *
    +   * functionDeclaration
    +   *    : FUNCTION identifier formalParameterList
    +   *      { legacy($start, "deprecated 'function' keyword"); }
    +   *    | returnType error=FUNCTION identifier? formalParameterList
    +   *      { legacy($error, "deprecated 'function' keyword"); }
    +   *    | returnType? identifier formalParameterList
    +   *    ;
    +   * 
    + * + * @return a {@link DartStatement} representing the function declaration + */ + private DartStatement parseFunctionDeclaration() { + beginFunctionDeclaration(); + DartIdentifier[] namePtr = new DartIdentifier[1]; + DartFunction function = parseFunctionDeclarationOrExpression(namePtr, true); + return done(new DartExprStmt(doneWithoutConsuming(new DartFunctionExpression(namePtr[0], + doneWithoutConsuming(function), true)))); + } + + private DartStatement parseExpressionStatement() { + beginExpressionStatement(); + if (peek(1) == Token.LPAREN && optionalPseudoKeyword(ASSERT_KEYWORD)) { + consume(Token.LPAREN); + DartExpression expression = parseConditionalExpression(); + DartExpression message = null; + if (optional(Token.COMMA)) { + message = parseConditionalExpression(); + } + expectCloseParen(); + expectStatmentTerminator(); + return done(new DartAssertion(expression, message)); + } + DartExpression expression = parseExpression(); + expectStatmentTerminator(); + return done(new DartExprStmt(expression)); + } + + /** + * Expect a close paren, reporting an error and consuming tokens until a + * plausible continuation is found if it isn't present. + */ + private void expectCloseParen() { + int parenCount = 1; + switch (peek(0)) { + case RPAREN: + expect(Token.RPAREN); + return; + + case EOS: + case LBRACE: + case SEMICOLON: + reportErrorWithoutAdvancing(DartCompilerErrorCode.UNEXPECTED_TOKEN); + return; + + case LPAREN: + ++parenCount; + //$FALL-THROUGH$ + default: + reportErrorWithoutAdvancing(DartCompilerErrorCode.UNEXPECTED_TOKEN); + break; + } + + // eat tokens until we get a close paren or a plausible terminator (which + // is not consumed) + while (parenCount > 0) { + switch (peek(0)) { + case RPAREN: + expect(Token.RPAREN); + --parenCount; + break; + + case LPAREN: + expect(Token.LPAREN); + ++parenCount; + break; + + case LBRACE: + case SEMICOLON: + return; + + default: + next(); + break; + } + } + } + + /** + * Expect a close brace, reporting an error and consuming tokens until a + * plausible continuation is found if it isn't present. + */ + private void expectCloseBrace() { + int braceCount = 1; + switch (peek(0)) { + case RBRACE: + expect(Token.RBRACE); + return; + + case EOS: + case SEMICOLON: + reportErrorWithoutAdvancing(DartCompilerErrorCode.UNEXPECTED_TOKEN); + return; + + case LBRACE: + ++braceCount; + //$FALL-THROUGH$ + default: + reportErrorWithoutAdvancing(DartCompilerErrorCode.UNEXPECTED_TOKEN); + break; + } + + // eat tokens until we get a matching close brace or end of stream + while (braceCount > 0) { + switch (next()) { + case RBRACE: + braceCount--; + break; + + case LBRACE: + braceCount++; + break; + + case EOS: + return; + } + } + } + + /** + * Collect plausible statement tokens and return a synthetic error statement + * containing them. + *

    + * Note that this is a crude heuristic that needs to be improved for better + * error recovery. + * + * @return a {@link DartSyntheticErrorStatement} + */ + private DartStatement parseErrorStatement() { + StringBuilder buf = new StringBuilder(); + boolean done = false; + int braceCount = 1; + while (!done) { + buf.append(getPeekTokenValue(0)); + next(); + switch (peek(0)) { + case RBRACE: + if (--braceCount == 0) { + done = true; + } + break; + case LBRACE: + braceCount++; + break; + case EOS: + case SEMICOLON: + done = true; + break; + } + } + return new DartSyntheticErrorStatement(buf.toString()); + } + + + /** + * Look for a statement terminator, giving error messages and consuming tokens + * for error recovery. + */ + protected void expectStatmentTerminator() { + Token token = peek(0); + int braceCount = 1; + switch (token) { + case SEMICOLON: + expect(Token.SEMICOLON); + return; + + case EOS: + case RBRACE: + reportErrorWithoutAdvancing(DartCompilerErrorCode.EXPECTED_SEMICOLON); + return; + + case LBRACE: + ++braceCount; + //$FALL-THROUGH$ + default: + // give error message + expect(Token.SEMICOLON); + break; + } + + while (true) { + switch (peek(0)) { + case EOS: + return; + + case SEMICOLON: + if (braceCount < 2) { + // if we have seen open braces while skipping, keep looking + return; + } + next(); + break; + + case RBRACE: + if (--braceCount == 0) { + return; + } + break; + + case LBRACE: + ++braceCount; + //$FALL-THROUGH$ + default: + next(); + break; + } + } + } + + /** + * Report an error without advancing past the next token. + * + * @param errCode the error code to report, which may take a string parameter + * containing the actual token found + */ + private void reportErrorWithoutAdvancing(DartCompilerErrorCode errCode) { + startLookahead(); + Token actual = peek(0); + next(); + reportError(position(), errCode, actual); + rollback(); + } + + /** + *

    +   * iterationStatement
    +   *     : WHILE '(' expression ')' statement
    +   *     | DO statement WHILE '(' expression ')' ';'
    +   *     | FOR '(' forLoopParts ')' statement
    +   *     ;
    +   *  
    + */ + private DartWhileStatement parseWhileStatement() { + beginWhileStatement(); + expect(Token.WHILE); + expect(Token.LPAREN); + DartExpression condition = parseExpression(); + expectCloseParen(); + DartStatement body = parseStatement(); + return done(new DartWhileStatement(condition, body)); + } + + /** + *
    +   * iterationStatement
    +   *     : WHILE '(' expression ')' statement
    +   *     | DO statement WHILE '(' expression ')' ';'
    +   *     | FOR '(' forLoopParts ')' statement
    +   *     ;
    +   *  
    + */ + private DartDoWhileStatement parseDoWhileStatement() { + beginDoStatement(); + expect(Token.DO); + DartStatement body = parseStatement(); + expect(Token.WHILE); + expect(Token.LPAREN); + DartExpression condition = parseExpression(); + expectCloseParen(); + expectStatmentTerminator(); + return done(new DartDoWhileStatement(condition, body)); + } + + /** + *
    +   * iterationStatement
    +   *     : WHILE '(' expression ')' statement
    +   *     | DO statement WHILE '(' expression ')' ';'
    +   *     | FOR '(' forLoopParts ')' statement
    +   *     ;
    +   *
    +   * forLoopParts
    +   *     : forInitializerStatement expression? ';' expressionList?
    +   *     | constVarOrType? identifier IN expression
    +   *     ;
    +   *
    +   * forInitializerStatement
    +   *     : initializedVariableDeclaration ';'
    +   *     | expression? ';'
    +   *     ;
    +   * 
    + */ + private DartStatement parseForStatement() { + beginForStatement(); + expect(Token.FOR); + expect(Token.LPAREN); + + // Setup + DartStatement setup = null; + if (peek(0) != Token.SEMICOLON) { + // Found a setup expression/statement + beginForInitialization(); + Modifiers modifiers = Modifiers.NONE; + if (optional(Token.VAR)) { + setup = done(new DartVariableStatement(parseInitializedVariableList(), null, modifiers)); + } else { + if (optional(Token.FINAL)) { + modifiers = modifiers.makeFinal(); + } + DartTypeNode type = (peek(1) == Token.IDENTIFIER || peek(1) == Token.LT) + ? tryTypeAnnotation() : null; + if (modifiers.isFinal() || type != null) { + setup = done(new DartVariableStatement(parseInitializedVariableList(), type, modifiers)); + } else { + setup = done(new DartExprStmt(parseExpression())); + } + } + } + + if (optional(Token.IN)) { + if (setup instanceof DartVariableStatement) { + DartVariableStatement variableStatement = (DartVariableStatement) setup; + List variables = variableStatement.getVariables(); + if (variables.size() != 1) { + reportError(variables.get(1), DartCompilerErrorCode.FOR_IN_WITH_MULTIPLE_VARIABLES); + } + DartExpression initializer = variables.get(0).getValue(); + if (initializer != null) { + reportError(initializer, DartCompilerErrorCode.FOR_IN_WITH_VARIABLE_INITIALIZER); + } + } else { + DartExpression expression = ((DartExprStmt) setup).getExpression(); + if (!(expression instanceof DartIdentifier)) { + reportError(setup, DartCompilerErrorCode.FOR_IN_WITH_COMPLEX_VARIABLE); + } + } + + DartExpression iterable = parseExpression(); + expectCloseParen(); + + DartStatement body = parseStatement(); + return done(new DartForInStatement(setup, iterable, body)); + + } else if (optional(Token.SEMICOLON)) { + + // Condition + DartExpression condition = null; + if (peek(0) != Token.SEMICOLON) { + condition = parseExpression(); + } + expect(Token.SEMICOLON); + + // Next + DartExpression next = null; + if (peek(0) != Token.RPAREN) { + next = parseExpressionList(); + } + expectCloseParen(); + + DartStatement body = parseStatement(); + return done(new DartForStatement(setup, condition, next, body)); + } else { + reportUnexpectedToken(position(), null, peek(0)); + return done(parseErrorStatement()); + } + } + + /** + *
    +   * selectionStatement
    +   *    : IF '(' expression ')' statement ((ELSE)=> ELSE statement)?
    +   *    | SWITCH '(' expression ')' '{' switchCase* defaultCase? '}'
    +   *    ;
    +   * 
    + */ + private DartIfStatement parseIfStatement() { + beginIfStatement(); + expect(Token.IF); + expect(Token.LPAREN); + DartExpression condition = parseExpression(); + expectCloseParen(); + DartStatement yes = parseStatement(); + DartStatement no = null; + if (optional(Token.ELSE)) { + no = parseStatement(); + } + return done(new DartIfStatement(condition, yes, no)); + } + + /** + *
    +   * caseStatements
    +   *    : normalCompletingStatement* abruptCompletingStatement
    +   *    ;
    +   * 
    + */ + private List parseCaseStatements() { + List statements = new ArrayList(); + DartStatement statement = null; + while (true) { + switch (peek(0)) { + case CASE: + case DEFAULT: + case RBRACE: + case EOS: + return statements; + default: + if ((statement = parseStatement()) == null) { + return statements; + } + statements.add(statement); + if (statement.isAbruptCompletingStatement()) { + /* + * TODO(jat): is this correct? It seems like we would get better + * error messages if we parsed dead code as part of this case block + * and gave an error for unreachable code + */ + return statements; + } + } + } + } + + /** + *
    +   * switchCase
    +   *    : label? (CASE expression ':')+ caseStatements
    +   *    ;
    +   * 
    + */ + private DartSwitchMember parseCaseMember(DartLabel label) { + // The begin() associated with the done() in this method is in the method + // parseSwitchStatement(), called by beginSwitchMember(). + expect(Token.CASE); + DartExpression caseExpr = parseExpression(); + expect(Token.COLON); + return done(new DartCase(caseExpr, label, parseCaseStatements())); + } + + /** + *
    +   * defaultCase
    +   *    : label? (CASE expression ':')* DEFAULT ':' caseStatements
    +   *    ;
    +   * 
    + */ + private DartSwitchMember parseDefaultMember(DartLabel label) { + // The begin() associated with the done() in this method is in the method + // parseSwitchStatement(), called by beginSwitchMember(). + expect(Token.DEFAULT); + expect(Token.COLON); + return done(new DartDefault(label, parseCaseStatements())); + } + + + /** + *
    +   * selectionStatement
    +   *    : IF '(' expression ')' statement ((ELSE)=> ELSE statement)?
    +   *    | SWITCH '(' expression ')' '{' switchCase* defaultCase? '}'
    +   *    ;
    +   * 
    + */ + private DartStatement parseSwitchStatement() { + beginSwitchStatement(); + expect(Token.SWITCH); + + expect(Token.LPAREN); + DartExpression expr = parseExpression(); + expectCloseParen(); + + List members = new ArrayList(); + expect(Token.LBRACE); + + boolean done = optional(Token.RBRACE); + while (!done) { + DartLabel label = null; + beginSwitchMember(); // switch member + if (peek(0) == Token.IDENTIFIER) { + beginLabel(); + DartIdentifier identifier = parseIdentifier(); + expect(Token.COLON); + label = done(new DartLabel(identifier, null)); + } + + if (peek(0) == Token.CASE) { + members.add(parseCaseMember(label)); + } else if (optional(Token.RBRACE)) { + if (label != null) { + reportError(position(), DartCompilerErrorCode.EXPECTED_CASE_OR_DEFAULT); + } + done = true; + done(null); + } else { + if (peek(0) != Token.EOS) { + members.add(parseDefaultMember(label)); + } + expectCloseBrace(); + done = true; // Ensure termination. + } + } + return done(new DartSwitchStatement(expr, members)); + } + + /** + *
    +   * catchParameter
    +   *    : FINAL type? identifier
    +   *    | VAR identifier
    +   *    | type identifier
    +   *    ;
    +   *  
    + */ + private DartParameter parseCatchParameter() { + beginCatchParameter(); + DartTypeNode type = null; + Modifiers modifiers = Modifiers.NONE; + boolean isDeclared = false; + if (optional(Token.VAR)) { + isDeclared = true; + } else { + if (optional(Token.FINAL)) { + modifiers = modifiers.makeFinal(); + isDeclared = true; + } + if (peek(1) != Token.COMMA && peek(1) != Token.RPAREN) { + type = parseTypeAnnotation(); + isDeclared = true; + } + } + DartIdentifier name = parseIdentifier(); + if (!isDeclared) { + reportError(name, DartCompilerErrorCode.EXPECTED_VAR_FINAL_OR_TYPE); + } + return done(new DartParameter(name, type, null, null, modifiers)); + } + + /** + *
    +   * tryStatement
    +   *     : TRY block (catchPart+ finallyPart? | finallyPart)
    +   *     ;
    +   *
    +   * catchPart
    +   *     : CATCH '(' declaredIdentifier (',' declaredIdentifier)? ')' block
    +   *     ;
    +   *
    +   * finallyPart
    +   *     : FINALLY block
    +   *     ;
    +   * 
    + */ + private DartTryStatement parseTryStatement() { + beginTryStatement(); + // Try. + expect(Token.TRY); + DartBlock tryBlock = parseBlock(); + + List catches = new ArrayList(); + while (optional(Token.CATCH)) { + beginCatchClause(); + expect(Token.LPAREN); + DartParameter exception = parseCatchParameter(); + DartParameter stackTrace = null; + if (optional(Token.COMMA)) { + stackTrace = parseCatchParameter(); + } + expectCloseParen(); + DartBlock block = parseBlock(); + catches.add(done(new DartCatchBlock(block, exception, stackTrace))); + } + + // Finally. + DartBlock finallyBlock = null; + if (optional(Token.FINALLY)) { + finallyBlock = parseBlock(); + } + + if ( catches.size() == 0 && finallyBlock == null) { + reportError(new DartCompilationError(tryBlock.getSource(), new Location(position()), + DartCompilerErrorCode.CATCH_OR_FINALLY_EXPECTED)); + } + + return done(new DartTryStatement(tryBlock, catches, finallyBlock)); + } + + /** + *
    +   * unaryExpression
    +   *     : postfixExpression
    +   *     | prefixOperator unaryExpression
    +   *     | incrementOperator assignableExpression
    +   *     ;
    +   *
    +   *  @return an expression or null if noFail is true and the next tokens could not be parsed as an
    +   *      expression, leaving the state unchanged.
    +   *  
    + */ + private DartExpression parseUnaryExpression() { + // A '+' prefix does not have any effect. + optional(Token.ADD); + Token token = peek(0); + if (token.isUnaryOperator() || token == Token.SUB) { + beginUnaryExpression(); + consume(token); + DartExpression unary = parseUnaryExpression(); + if (token.isCountOperator()) { + ensureAssignable(unary); + } + return done(new DartUnaryExpression(token, unary, true)); + } else { + return parsePostfixExpression(); + } + } + + /** + *
    +   * type
    +   *     : qualified typeArguments?
    +   *     ;
    +   * 
    + */ + private DartTypeNode parseTypeAnnotation() { + beginTypeAnnotation(); + return done(new DartTypeNode(parseQualified(), parseTypeArgumentsOpt())); + } + + /** + *
    +   * typeArguments
    +   *     : '<' typeList '>'
    +   *     ;
    +   *
    +   * typeList
    +   *     : type (',' type)*
    +   *     ;
    +   * 
    + */ + private List parseTypeArguments() { + consume(Token.LT); + List arguments = new ArrayList(); + do { + arguments.add(parseTypeAnnotation()); + } while (optional(Token.COMMA)); + if (!tryParameterizedTypeEnd()) { + expect(Token.GT); + } + return arguments; + } + + /** + *
    +   * typeArguments?
    +   * 
    + */ + private List parseTypeArgumentsOpt() { + return (peek(0) == Token.LT) + ? parseTypeArguments() + : Collections.emptyList(); + } + + /** + *
    +   * qualified
    +   *     : identifier ('.' identifier)?
    +   *     ;
    +   * 
    + */ + private DartExpression parseQualified() { + beginQualifiedIdentifier(); + DartExpression qualified = parseIdentifier(); + if (optional(Token.PERIOD)) { + // The previous identifier was a prefix. + qualified = new DartPropertyAccess(qualified, parseIdentifier()); + } + return done(qualified); + } + + private boolean tryParameterizedTypeEnd() { + switch (peek(0)) { + case GT: + consume(Token.GT); + return true; + case SAR: + setPeek(0, Token.GT); + return true; + case SHR: + setPeek(0, Token.SAR); + return true; + default: + return false; + } + } + + private DartTypeNode tryTypeAnnotation() { + if (peek(0) != Token.IDENTIFIER) { + return null; + } + List typeArguments = new ArrayList(); + beginTypeAnnotation(); // to allow roll-back in case we're not at a type + + DartNode qualified = parseQualified(); + + if (optional(Token.LT)) { + if (peek(0) != Token.IDENTIFIER) { + rollback(); + return null; + } + beginTypeArguments(); + DartNode qualified2 = parseQualified(); + DartTypeNode argument; + switch (peek(0)) { + case LT: + // qualified < qualified2 < + argument = done(new DartTypeNode(qualified2, parseTypeArguments())); + break; + + case GT: + case SAR: + case SHR: + // qualified < qualified2 > + case COMMA: + // qualified < qualified2 , + argument = done(new DartTypeNode(qualified2, Collections.emptyList())); + break; + + default: + done(null); + rollback(); + return null; + } + typeArguments.add(argument); + + while (optional(Token.COMMA)) { + typeArguments.add(parseTypeAnnotation()); + } + if (!tryParameterizedTypeEnd()) { + expect(Token.GT); + } + } + + return done(new DartTypeNode(qualified, typeArguments)); + } + + private DartIdentifier parseIdentifier() { + beginIdentifier(); + expect(Token.IDENTIFIER); + String token = ctx.getTokenString(); + return done(new DartIdentifier(token != null ? token : "")); + } + + public DartExpression parseEntryPoint() { + beginEntryPoint(); + DartExpression entry = parseIdentifier(); + while (!EOS()) { + expect(Token.PERIOD); + entry = doneWithoutConsuming(new DartPropertyAccess(entry, parseIdentifier())); + } + return done(entry); + } + + private void ensureAssignable(DartExpression expression) { + if (expression != null && !expression.isAssignable()) { + reportError(position(), DartCompilerErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE); + } + } + + private void reportError(DartCompilationError dartError) { + if ((errorHistory.size() > MAX_DEFAULT_ERRORS) || + errorHistory.contains(dartError.hashCode())) { + // Force parser termination if one of the conditions are observed: + // 1) We have already reported the same error at the same location; This + // is an indication that the parser is not making progress. + // 2) If we reached the absolute maximum number of errors. + // TODO (fabiomfv) consider throwing AssertionError to terminate parsing. + } else { + ctx.error(dartError); + errorHistory.add(dartError.hashCode()); + } + } + + private void reportError(DartNode node, ErrorCode errorCode, Object... arguments) { + reportError(new DartCompilationError(node, errorCode, arguments)); + } +} diff --git a/compiler/java/com/google/dart/compiler/parser/DartScanner.java b/compiler/java/com/google/dart/compiler/parser/DartScanner.java new file mode 100644 index 00000000000..1a128805824 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/DartScanner.java @@ -0,0 +1,1388 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.metrics.DartEventType; +import com.google.dart.compiler.metrics.Tracer; +import com.google.dart.compiler.metrics.Tracer.TraceEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * The Dart scanner. Should normally be used only by {@link DartParser}. + */ +public class DartScanner { + + /** + * Represents a position in a source file, including absolute character position, + * line, and column. + */ + public static class Position implements Cloneable { + private int pos; + private int line; + private int col; + + public Position(int pos, int line, int col) { + this.pos = pos; + this.line = line; + this.col = col; + } + + @Override + public Position clone() { + try { + return (Position) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + + public int getPos() { + return pos; + } + + public int getLine() { + return line; + } + + public int getCol() { + return col; + } + + public void advance(boolean isNewline) { + ++pos; + if (isNewline) { + col = 1; + ++line; + } else { + ++col; + } + } + + @Override + public String toString() { + return line + "," + col + "@" + pos; + } + } + + /** + * Represents a span of characters in a source file. + */ + public static class Location implements Cloneable { + public static final Location NONE = null; + private Position begin, end; + + public Location(Position begin, Position end) { + this.begin = begin; + this.end = end; + } + + public Location(Position begin) { + this.begin = this.end = begin; + } + + @Override + public Location clone() { + try { + Location clone = (Location) super.clone(); + clone.begin = begin.clone(); + clone.end = end.clone(); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + + public Position getBegin() { + return begin; + } + + public Position getEnd() { + return end; + } + + @Override + public String toString() { + return begin.toString() + "::" + end.toString(); + } + } + + public static class State { + State(int baseOffset) { + this.baseOffset = baseOffset; + } + + static class RollbackToken { + public final int absoluteOffset; + final Token replacedToken; + + public RollbackToken(int tokenOffset, Token token) { + absoluteOffset = tokenOffset; + replacedToken = token; + } + } + + /* Stack of tokens present before setPeek() */ + Stack rollbackTokens = null; + final int baseOffset; + + @Override + public String toString() { + return "ofs=" + baseOffset; + } + } + + /** + * Stores the entire state for the scanner. + */ + protected static class InternalState { + enum Mode { + DEFAULT, + + IN_STRING, + + /** + * Inside a string, scanning a string-interpolation expression. + * Ex: "${foo}". + */ + IN_STRING_EMBEDDED_EXPRESSION, + + /** + * Inside a string, scanning a string-interpolation identifier. + *
    +       * Ex: "$foo bc".
    +       *        ^
    +       * 
    + */ + IN_STRING_EMBEDDED_EXPRESSION_IDENTIFIER, + + /** + * Inside a string, just after having scanned a string-interpolation identifier. + *
    +       * Ex: "$foo bc".
    +       *          ^
    +       * 
    + */ + IN_STRING_EMBEDDED_EXPRESSION_END + } + + /** + * Maintains the state of scanning strings, including interpolated + * expressions/identifiers, nested braces for terminating an interpolated + * expression, the quote character used to start/end the string, and whether + * it is a multiline string. + */ + public static class StringState { + private int bracesCount; + private Mode mode; + private final boolean multiLine; + private final int quote; + + /** + * Push a new mode on state stack. If the new mode is + * {@link Mode#IN_STRING_EMBEDDED_EXPRESSION}, mark that we have seen an + * opening brace. + * + * @param mode + * @param quote + * @param multiLine + */ + public StringState(Mode mode, int quote, boolean multiLine) { + this.bracesCount = mode == Mode.IN_STRING_EMBEDDED_EXPRESSION ? 1 : 0; + this.mode = mode; + this.quote = quote; + this.multiLine = multiLine; + } + + /** + * Mark that we have seen an opening brace. + */ + public void openBrace() { + if (mode == Mode.IN_STRING_EMBEDDED_EXPRESSION) { + bracesCount++; + } + } + + /** + * Mark that we have seen a closing brace. + * + * @return true if the current mode is now complete and should be popped + * off the stack + */ + public boolean closeBrace() { + if (mode == Mode.IN_STRING_EMBEDDED_EXPRESSION) { + return --bracesCount == 0; + } + return false; + } + + /** + * @return the string scanning mode. + */ + public Mode getMode() { + return mode; + } + + /** + * @return the codepoint of the quote character used to bound the current + * string. + */ + public int getQuote() { + return quote; + } + + /** + * @return true if the current string is a multi-line string. + */ + public boolean isMultiLine() { + return multiLine; + } + + /** + * @param mode the string scanning mode. + */ + public void setMode(Mode mode) { + this.mode = mode; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(mode).append("/quote=").appendCodePoint(quote); + if (multiLine) { + buf.append("/multiline"); + } + return buf.toString(); + } + } + + private int lookahead[] = new int[NUM_LOOKAHEAD]; + private Position lookaheadPos[] = new Position[NUM_LOOKAHEAD]; + private Position nextLookaheadPos; + private ArrayList tokens; + private TokenData lastToken; + + // Current offset in the token list + int currentOffset; + + // The following fields store data used for parsing string interpolation. + // The scanner splits the interpolated string in segments, alternating + // strings and expressions so that the parser can construct the embedded + // expressions as it goes. The following information is used to ensure that + // the string is closed with matching quotes, and to deal with parsing + // ambiguity of "}" (which closes both embedded expressions and braces + // within embedded expressions). + + /** The string scanning state stack. */ + private List stringStateStack = new ArrayList(); + + public InternalState() { + currentOffset = 0; + } + + @Override + public String toString() { + StringBuilder ret = new StringBuilder(); + + ret.append("currentOffset("); + ret.append(currentOffset); + ret.append(")"); + if ( currentOffset > -1 ) { + TokenData tok = tokens.get(currentOffset); + ret.append(" = ["); + ret.append(tok.token); + if (tok.value != null) { + ret.append(" (" + tok.value + ")"); + } + ret.append("], "); + } + + ret.append("["); + for (int i = 0; i < tokens.size(); i++) { + TokenData tok = tokens.get(i); + ret.append(tok.token); + if (tok.value != null) { + ret.append(" (" + tok.value + ")"); + } + if (i < tokens.size() - 1) { + ret.append(", "); + } + } + ret.append("]"); + if (getMode() != InternalState.Mode.DEFAULT) { + ret.append("(within string starting with "); + ret.appendCodePoint(getQuote()); + if (isMultiLine()) { + ret.appendCodePoint(getQuote()); + ret.appendCodePoint(getQuote()); + } + ret.append(')'); + } + return ret.toString(); + } + + /** + * @return the current scanning mode + */ + protected Mode getMode() { + return stringStateStack.isEmpty() ? Mode.DEFAULT : getCurrentState().getMode(); + } + + /** + * Mark that we have seen an open brace. + */ + protected void openBrace() { + if (!stringStateStack.isEmpty()) { + getCurrentState().openBrace(); + } + } + + /** + * Mark that we have seen a close brace. + * + * @return true if the current mode is now complete and should be popped + */ + protected boolean closeBrace() { + if (!stringStateStack.isEmpty()) { + return getCurrentState().closeBrace(); + } + return false; + } + + /** + * Pop the current mode. + */ + protected void popMode() { + if (!stringStateStack.isEmpty()) { + stringStateStack.remove(stringStateStack.size() - 1); + } + } + + /** + * @param mode the mode to push + */ + protected void pushMode(Mode mode, int quote, boolean multiLine) { + stringStateStack.add(new StringState(mode, quote, multiLine)); + } + + /** + * @param mode the mode to push + */ + protected void replaceMode(Mode mode) { + getCurrentState().setMode(mode); + } + + /** + * Remove all modes, returning to the default state. + */ + public void resetModes() { + stringStateStack.clear(); + } + + /** + * @return the quote + */ + private int getQuote() { + return getCurrentState().getQuote(); + } + + /** + * @return the current string scanning state + */ + private StringState getCurrentState() { + assert !stringStateStack.isEmpty() : "called with empty state stack"; + return stringStateStack.get(stringStateStack.size() - 1); + } + + /** + * @return the multiLine + */ + private boolean isMultiLine() { + return getCurrentState().isMultiLine(); + } + } + + private static class TokenData implements Cloneable { + Token token; + Location location; + String value; + + @Override + protected TokenData clone() { + try { + TokenData clone = (TokenData) super.clone(); + clone.location = location == null ? null : location.clone(); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + + @Override + public String toString() { + String str = token.toString(); + return (value != null) ? str + "(" + value + ")" : str; + } + } + + private static final int NUM_LOOKAHEAD = 2; + + private static boolean isDecimalDigit(int c) { + return c >= '0' && c <= '9'; + } + + private static boolean isHexDigit(int c) { + return isDecimalDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } + + private static boolean isIdentifierPart(int c) { + return isIdentifierStart(c) || isDecimalDigit(c); + } + + private static boolean isIdentifierStart(int c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c == '$'); + } + + private static boolean isLineTerminator(int c) { + return c == '\r' || c == '\n'; + } + + private static boolean isWhiteSpace(int c) { + return c == ' ' || c == '\t'; + } + + private int commentLineCount; + private int commentCharCount; + private int lastCommentStart; + private int lastCommentStop; + private String source; + private InternalState internalState; + + public DartScanner(String source) { + this(source, 0); + } + + public DartScanner(String source, int start) { + final TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.SCANNER) : null; + try { + this.source = source; + internalState = new InternalState(); + internalState.tokens = new ArrayList(source.length()/2); + + // Initialize lookahead positions. + // TODO Determine if line & column should be relative to 0 or 'start' + internalState.nextLookaheadPos = new Position(start, 1, 1); + for (int i = 0; i < internalState.lookaheadPos.length; ++i) { + internalState.lookaheadPos[i] = new Position(start, 1, 1); + } + + // Fill all the characters in the look-ahead and all the peek + // elements in the tokens buffer. + for (int i = 0; i < NUM_LOOKAHEAD; i++) { + advance(); + } + + // Scan all the tokens up front + scanFile(); + } finally { + Tracer.end(logEvent); + } + } + + /** + * Returns the number of lines of source that were scanned, excluding the number of lines + * consumed by comments. + */ + public int getNonCommentLineCount() { + return getLineCount() - commentLineCount; + } + + /** + * Returns the number of lines of source that were scanned. + */ + public int getLineCount() { + int lineCount = internalState.nextLookaheadPos.line; + if (isEos()) { + // At the end of the file the next line has advanced one past the end + lineCount -= 1; + } + return lineCount; + } + + /** + * Returns the number of characters of source code that were scanned. + */ + public int getCharCount() { + return internalState.nextLookaheadPos.pos; + } + + /** + * Returns the number of characters of source code that were scanned excluding the number of + * characters consumed by comments. + */ + public int getNonCommentCharCount() { + return getCharCount() - commentCharCount; + } + + /** + * Get the token value for one of the look-ahead tokens. + */ + public String getPeekTokenValue(int n) { + assert (0 <= n && (internalState.currentOffset + n + 1) < internalState.tokens.size()); + return internalState.tokens.get(internalState.currentOffset + n + 1).value; + } + + /** + * Gets a copy of the current scanner state. This state can be passed to {@link + * #restoreState(State)}. + */ + public State getState() { + return new State(internalState.currentOffset); + } + + /** + * Gets the current offset of the scanner. + */ + public int getOffset() { + return internalState.currentOffset; + } + + /** + * Gets the current token. + */ + public Token getToken() { + return internalState.tokens.get(internalState.currentOffset).token; + } + + /** + * Gets the location of the current token. + */ + public Location getTokenLocation() { + return internalState.tokens.get(internalState.currentOffset).location; + } + + public Location peekTokenLocation(int n) { + if ((internalState.currentOffset + n + 1) < internalState.tokens.size()) { + return internalState.tokens.get(internalState.currentOffset + n + 1).location; + } else { + // It is not valid to read beyond the end of the token stream, so we + // return the Location of the EOS token. + return internalState.tokens.get(internalState.tokens.size() - 1).location; + } + + } + + /** + * Get the token value or location for the current token previously returned + * by a call to next(). + */ + public String getTokenValue() { + return internalState.tokens.get(internalState.currentOffset).value; + } + + public String peekTokenValue(int n) { + if ((internalState.currentOffset + n + 1) < internalState.tokens.size()) { + return internalState.tokens.get(internalState.currentOffset + n + 1).value; + } else { + // It is not valid to read beyond the end of the token stream, so we + // return the null, the default value of an EOS token. + return null; + } + } + + /** + * Returns the next token. + */ + public Token next() { + // Do not advance the current offset beyond the end of the stoken stream + if (internalState.currentOffset + 1 < internalState.tokens.size()) { + internalState.currentOffset++; + } + return getToken(); + } + + /** + * Token look-ahead - past the token returned by next(). + */ + public Token peek(int n) { + if ((internalState.currentOffset + n + 1) < internalState.tokens.size()) { + return internalState.tokens.get(internalState.currentOffset + n + 1).token; + } else { + // It is not valid to read beyond the end of the token stream, so we + // return the EOS token + return Token.EOS; + } + } + + /** + * Sets the scanner's state, using a state object returned from {@link #getState()}. + */ + public void restoreState(State oldState) { + // reset offset + internalState.currentOffset = oldState.baseOffset; + } + + /** + * Sets the token at the specified slot in the lookahead buffer. + */ + public void setPeek(int n, Token token) { + assert (0 <= n && (internalState.currentOffset + n + 1) < internalState.tokens.size()); + internalState.tokens.get(internalState.currentOffset + n + 1).token = token; + } + + /** + * Sets the token at the specified slot in the lookahead buffer. + */ + public void setAbsolutePeek(int n, Token token) { + assert (0 <= n && n < internalState.tokens.size()); + internalState.tokens.get(n).token = token; + } + + @Override + public String toString() { + if (internalState == null) { + return super.toString(); + } + return internalState.toString(); + } + + /** + * A hook into low-level scanning machinery. Use with care and only as directed.

    + * Record the location of a comment. Given a source string source, + * the actual comment string is source.substring(start - 1, stop) + * because the comment cannot be recognized until its second character is + * scanned.

    + * Note: A single comment may be scanned multiple times. If the scanner has + * to backtrack it will re-scan comments until it no longer has to backtrack. + * Clients are responsible for filtering duplicate comment locations.

    + * Warning: This method may be called during initialization of the scanner in + * the DartScanner constructor. Fields defined in the subclass + * that implements this method may not have been initialized before the first + * invocation. + * @param start the character position of the second character in the comment + * @param stop the character position of the final character in the comment + * @param line the line number at start + * @param col the column number at start + */ + protected void recordCommentLocation(int start, int stop, int line, int col) { + } + + private void advance() { + for (int i = 0; i < NUM_LOOKAHEAD - 1; ++i) { + internalState.lookahead[i] = internalState.lookahead[i + 1]; + internalState.lookaheadPos[i] = internalState.lookaheadPos[i + 1].clone(); + } + if (internalState.nextLookaheadPos.pos < source.length()) { + int ch = source.codePointAt(internalState.nextLookaheadPos.pos); + internalState.lookahead[NUM_LOOKAHEAD - 1] = ch; + internalState.lookaheadPos[NUM_LOOKAHEAD - 1] = internalState.nextLookaheadPos.clone(); + internalState.nextLookaheadPos.advance(ch == '\n'); + } else { + // Let the last look-ahead position be past the source. This makes + // the position information for the last token correct. + internalState.lookahead[NUM_LOOKAHEAD - 1] = -1; + internalState.lookaheadPos[NUM_LOOKAHEAD - 1] = new Position(source.length(), + internalState.nextLookaheadPos.line, internalState.nextLookaheadPos.col); + + // Leave the nextLookahead position pointing to the line after the last line + internalState.nextLookaheadPos = new Position(source.length(), + internalState.nextLookaheadPos.line + 1, 1); + } + } + + /** + * Called when comments are identified to aggregate the total number of comment lines and comment + * characters then delegate to {@link #recordCommentLocation(int, int, int, int)}. This provides + * a light weight way to track how much of the code is made up of comments without having to keep + * all comments. + * + * @param start the character position of the second character in the comment + * @param stop the character position of the final character in the comment + * @param startLine the line number at start + * @param endLine the line number of the last line of the comment + * @param col the column number at start + */ + private void commentLocation(int start, int stop, int startLine, int endLine, int col) { + if (start <= lastCommentStart && stop <= lastCommentStop) { + return; + } + + lastCommentStart = start; + lastCommentStop = stop; + commentLineCount += endLine - startLine + 1; + commentCharCount += stop - start + 1; + + recordCommentLocation(start, stop, startLine, col); + } + + private boolean is(int c) { + return internalState.lookahead[0] == c; + } + + private boolean isEos() { + return internalState.lookahead[0] < 0; + } + + private int lookahead(int n) { + assert (0 <= n && n < NUM_LOOKAHEAD); + return internalState.lookahead[n]; + } + + // Get the current source code position. + private Position position() { + return internalState.lookaheadPos[0]; + } + + private void scanFile() { + // First node inserted as a dummy. + internalState.lastToken = new TokenData(); + internalState.tokens.add(internalState.lastToken); + + while (true) { + internalState.lastToken = new TokenData(); + Token token; + Position begin, end; + do { + skipWhiteSpace(); + begin = position(); + token = scanToken(); + } while (token == Token.COMMENT); + end = position(); + + internalState.lastToken.token = token; + internalState.lastToken.location = new Location(begin, end); + internalState.tokens.add(internalState.lastToken); + if (token == Token.EOS) { + return; + } + } + } + + private Token scanIdentifier(boolean allowDollars) { + assert (isIdentifierStart(lookahead(0))); + Position begin = position(); + while (true) { + int nextChar = lookahead(0); + if (!isIdentifierPart(nextChar) || (!allowDollars && nextChar == '$')) { + break; + } + advance(); + } + int size = position().pos - begin.pos; + + // Use a substring of the source string instead of copying all the + // characters to the token value buffer. + String result = source.substring(begin.pos, begin.pos + size); + internalState.lastToken.value = result; + return Token.lookup(result); + } + + private Token scanNumber() { + boolean isDouble = false; + assert (isDecimalDigit(lookahead(0)) || is('.')); + Position begin = position(); + while (isDecimalDigit(lookahead(0))) + advance(); + if (is('.') && isDecimalDigit(lookahead(1))) { + isDouble = true; + advance(); // Consume . + while (isDecimalDigit(lookahead(0))) + advance(); + } + if (isE()) { + isDouble = true; + advance(); + if (is('+') || is('-')) { + advance(); + } + if (!isDecimalDigit(lookahead(0))) { + return Token.ILLEGAL; + } + while (isDecimalDigit(lookahead(0))) + advance(); + } else if (isIdentifierStart(lookahead(0))) { + // Number literals must not be followed directly by an identifier. + return Token.ILLEGAL; + } + int size = position().pos - begin.pos; + internalState.lastToken.value = source.substring(begin.pos, begin.pos + size); + return isDouble ? Token.DOUBLE_LITERAL : Token.INTEGER_LITERAL; + } + + private boolean isE() { + return is('e') || is('E'); + } + + private Token scanHexNumber() { + assert (isDecimalDigit(lookahead(0)) && (lookahead(1) == 'x' || lookahead(1) == 'X')); + // Skip 0x/0X. + advance(); + advance(); + + Position begin = position(); + if (!isHexDigit(lookahead(0))) { + return Token.ILLEGAL; + } + advance(); + while (isHexDigit(lookahead(0))) { + advance(); + } + if (isIdentifierStart(lookahead(0))) { + return Token.ILLEGAL; + } + internalState.lastToken.value = source.substring(begin.pos, position().pos); + return Token.HEX_LITERAL; + } + + private Token scanString(boolean isRaw) { + int quote = lookahead(0); + assert (is('\'') || is('"')); + boolean multiLine = false; + advance(); + + // detect whether this is a multi-line string: + if (lookahead(0) == quote && lookahead(1) == quote) { + multiLine = true; + advance(); + advance(); + // according to the dart guide, when multi-line strings start immediatelly + // with a \n, the \n is not part of the string: + if (is('\n')) { + advance(); + } + } + internalState.pushMode(InternalState.Mode.IN_STRING, quote, multiLine); + if (isRaw) { + return scanRawString(); + } else { + return scanWithinString(true); + } + } + + private Token scanRawString() { + assert (internalState.getMode() == InternalState.Mode.IN_STRING); + int quote = internalState.getQuote(); + boolean multiLine = internalState.isMultiLine(); + // TODO(floitsch): Do we really need a StringBuffer to accumulate the characters? + StringBuffer tokenValueBuffer = new StringBuffer(); + while (true) { + if (isEos()) { + // Unterminated string (either multi-line or not). + internalState.popMode(); + return Token.ILLEGAL; + } + int c = lookahead(0); + advance(); + if (c == quote) { + if (!multiLine) { + // Done parsing the string literal. + break; + } else if (lookahead(0) == quote && lookahead(1) == quote) { + // Done parsing the multi-line string literal. + advance(); + advance(); + break; + } + } + tokenValueBuffer.appendCodePoint(c); + } + internalState.lastToken.value = tokenValueBuffer.toString(); + internalState.popMode(); + return Token.STRING; + } + + /** + * Scan within a string watching for embedded expressions (string + * interpolation). This function returns 4 kinds of tokens: + *

      + *
    • {@link Token#STRING} when {@code start} is true and no embedded + * expressions are found (default to string literals when no interpolation + * was used). + *
    • {@link Token#STRING_SEGMENT} when the string is interrupted with an + * embedded expression. + *
    • {@link Token#STRING_EMBED_EXP_START} when an embedded expression is + * found right away (the lookahead is "${"). + *
    • {@link Token#STRING_LAST_SEGMENT} when {@code start} is false and no + * more embedded expressions are found. + *
    + */ + private Token scanWithinString(boolean start) { + assert (internalState.getMode() == InternalState.Mode.IN_STRING); + int quote = internalState.getQuote(); + boolean multiLine = internalState.isMultiLine(); + StringBuffer tokenValueBuffer = new StringBuffer(); + while (true) { + if (isEos()) { + // Unterminated string (either multi-line or not). + internalState.resetModes(); + return Token.EOS; + } + int c = lookahead(0); + if (c == quote) { + advance(); + if (!multiLine) { + // Done parsing string constant. + break; + } else if (lookahead(0) == quote && lookahead(1) == quote) { + // Done parsing multi-line string constant. + advance(); + advance(); + break; + } + } else if (c == '\n' && !multiLine) { + advance(); + internalState.popMode(); + // unterminated (non multi-line) string + return Token.ILLEGAL; + } else if (c == '\\') { + advance(); + c = lookahead(0); + advance(); + switch (c) { + case 'b': + c = 0x08; + break; + case 'f': + c = 0x0C; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = 0x0B; + break; + case 'x': + case 'u': + // Parse Unicode escape sequences, which are of the form (backslash) xXX, (backslash) + // uXXXX or (backslash) u{X*} where X is a hexadecimal digit - the delimited form must + // be between 1 and 6 digits. + int len = (c == 'u') ? 4 : 2; + c = lookahead(0); + int unicodeCodePoint = 0; + // count of characters remaining or negative if delimited + if (c == '{') { + len = -1; + advance(); + c = lookahead(0); + } + while (len != 0) { + advance(); + int digit = Character.getNumericValue(c); + if (digit < 0 || digit > 15) { + // TODO(jat): how to handle an error? We would prefer to give a better error + // message about an invalid Unicode escape sequence + return Token.ILLEGAL; + } + unicodeCodePoint = unicodeCodePoint * 16 + digit; + c = lookahead(0); + if (len-- < 0 && c == '}') { + advance(); + break; + } + if (len < -6) { + // TODO(jat): better way to indicate error + // too many characters for a delimited character + return Token.ILLEGAL; + } + } + c = unicodeCodePoint; + // Unicode escapes must specify a valid Unicode scalar value, and may not specify + // UTF16 surrogates. + if (!Character.isValidCodePoint(c) || (c < 0x10000 + && (Character.isHighSurrogate((char) c) || Character.isLowSurrogate((char) c)))) { + // TODO(jat): better way to indicate error + return Token.ILLEGAL; + } + // TODO(jat): any other checks? We could use Character.isDefined, but then we risk + // version skew with the JRE's Unicode data. For now, assume anything in the Unicode + // range besides surrogates are fine. + break; + + default: + // any other character following a backslash is just itself + // see Dart guide 3.3 + break; + } + } else if (c == '$') { + // TODO(sigmund): add support for named embedded expressions and + // function embedded expressions for string templates. + if (tokenValueBuffer.length() == 0) { + advance(); + int nextChar = lookahead(0); + if (nextChar == '{') { + advance(); + internalState.pushMode(InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION, quote, + multiLine); + } else { + internalState.pushMode(InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION_IDENTIFIER, + quote, multiLine); + } + return Token.STRING_EMBED_EXP_START; + } else { + // Encountered the beginning of an embedded expression (string + // interpolation), return the current segment, and keep the "$" for + // the next token. + internalState.lastToken.value = tokenValueBuffer.toString(); + return Token.STRING_SEGMENT; + } + } else { + advance(); + } + tokenValueBuffer.appendCodePoint(c); + } + + internalState.lastToken.value = tokenValueBuffer.toString(); + internalState.popMode(); + if (start) { + return Token.STRING; + } else { + return Token.STRING_LAST_SEGMENT; + } + } + + private Token scanToken() { + switch (internalState.getMode()) { + case IN_STRING: + return scanWithinString(false); + case IN_STRING_EMBEDDED_EXPRESSION_IDENTIFIER: + // We are inside a string looking for an identifier. Ex: "$foo". + internalState.replaceMode(InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION_END); + int c = lookahead(0); + if (isIdentifierStart(c) && c != '$') { + boolean allowDollars = false; + return scanIdentifier(allowDollars); + } else { + internalState.popMode(); + if (!isEos()) { + internalState.lastToken.value = String.valueOf(c); + } + return Token.ILLEGAL; + } + case IN_STRING_EMBEDDED_EXPRESSION_END: + // We scanned the identifier of a string-interpolation. New we return the + // end-of-embedded-expression token. + internalState.popMode(); + return Token.STRING_EMBED_EXP_END; + default: + // fall through + } + + switch (lookahead(0)) { + case '"': + case '\'': { + boolean isRaw = false; + return scanString(isRaw); + } + + case '<': + // < <= << <<= + advance(); + if (is('=')) + return select(Token.LTE); + if (is('<')) + return select('=', Token.ASSIGN_SHL, Token.SHL); + return Token.LT; + + case '>': + // > >= >> >>= >>> >>>= + advance(); + if (is('=')) + return select(Token.GTE); + if (is('>')) { + // >> >>= >>> >>>= + advance(); + if (is('=')) + return select(Token.ASSIGN_SAR); + if (is('>')) + return select('=', Token.ASSIGN_SHR, Token.SHR); + return Token.SAR; + } + return Token.GT; + + case '=': + // = == === => + advance(); + if (is('>')) { + return select(Token.ARROW); + } + if (is('=')) + return select('=', Token.EQ_STRICT, Token.EQ); + return Token.ASSIGN; + + case '!': + // ! != !== + advance(); + if (is('=')) + return select('=', Token.NE_STRICT, Token.NE); + return Token.NOT; + + case '+': + // + ++ += + advance(); + if (is('+')) + return select(Token.INC); + if (is('=')) + return select(Token.ASSIGN_ADD); + return Token.ADD; + + case '-': + // - -- -= + advance(); + if (is('-')) + return select(Token.DEC); + if (is('=')) + return select(Token.ASSIGN_SUB); + return Token.SUB; + + case '*': + // * *= + return select('=', Token.ASSIGN_MUL, Token.MUL); + + case '%': + // % %= + return select('=', Token.ASSIGN_MOD, Token.MOD); + + case '/': + // / // /* /= + advance(); + if (is('/')) + return skipSingleLineComment(); + if (is('*')) + return skipMultiLineComment(); + if (is('=')) + return select(Token.ASSIGN_DIV); + return Token.DIV; + + case '&': + // & && &= + advance(); + if (is('&')) + return select(Token.AND); + if (is('=')) + return select(Token.ASSIGN_BIT_AND); + return Token.BIT_AND; + + case '|': + // | || |= + advance(); + if (is('|')) + return select(Token.OR); + if (is('=')) + return select(Token.ASSIGN_BIT_OR); + return Token.BIT_OR; + + case '^': + // ^ ^= + return select('=', Token.ASSIGN_BIT_XOR, Token.BIT_XOR); + + case '.': + // . + if (isDecimalDigit(lookahead(1))) { + return scanNumber(); + } else { + advance(); + if (lookahead(0) == '.' && lookahead(1) == '.') { + advance(); + advance(); + return Token.ELLIPSIS; + } + return Token.PERIOD; + } + + case ':': + return select(Token.COLON); + + case ';': + return select(Token.SEMICOLON); + + case ',': + return select(Token.COMMA); + + case '(': + return select(Token.LPAREN); + + case ')': + return select(Token.RPAREN); + + case '[': + advance(); + if (is(']')) { + return select('=', Token.ASSIGN_INDEX, Token.INDEX); + } + return Token.LBRACK; + + case ']': + return select(Token.RBRACK); + + case '{': + internalState.openBrace(); + return select(Token.LBRACE); + + case '}': + if (internalState.closeBrace()) { + internalState.popMode(); + return select(Token.STRING_EMBED_EXP_END); + } + return select(Token.RBRACE); + + case '?': + return select(Token.CONDITIONAL); + + case '~': + // ~ ~/ ~/= + advance(); + if (is('/')) { + if (lookahead(1) == '=') { + advance(); + return select(Token.ASSIGN_TRUNC); + } else { + return select(Token.TRUNC); + } + } else { + return Token.BIT_NOT; + } + + case '@': + // Raw strings. + advance(); + if (is('\'') || is('"')) { + boolean isRaw = true; + return scanString(isRaw); + } else { + return select(Token.ILLEGAL); + } + + case '#': + return scanDirective(); + + default: + if (isIdentifierStart(lookahead(0))) { + boolean allowDollars = true; + return scanIdentifier(allowDollars); + } + if (isDecimalDigit(lookahead(0))) { + if (lookahead(0) == '0' && (lookahead(1) == 'x' || lookahead(1) == 'X')) { + return scanHexNumber(); + } else { + return scanNumber(); + } + } + if (isEos()) + return Token.EOS; + return select(Token.ILLEGAL); + } + } + + /** + * Scan for #library, #import, #source, and #resource directives + */ + private Token scanDirective() { + assert (is('#')); + Position currPos = position(); + int start = currPos.pos; + int line = currPos.line; + int col = currPos.col; + + // Skip over the #! if it exists and consider it a comment + if (start == 0) { + if (lookahead(1) == '!') { + while (!isEos() && !isLineTerminator(lookahead(0))) + advance(); + int stop = internalState.lookaheadPos[0].pos; + commentLocation(start, stop, line, internalState.lookaheadPos[0].line, col); + return Token.COMMENT; + } + } + + // Directives must start at the beginning of a line + if (start > 0 && !isLineTerminator(source.codePointBefore(start))) + return select(Token.ILLEGAL); + + // Determine which directive is being specified + advance(); + while (true) { + int ch = lookahead(0); + if (ch < 'a' || ch > 'z') { + break; + } + advance(); + } + String syntax = source.substring(start, position().pos); + Token token = Token.lookup(syntax); + return token == Token.IDENTIFIER ? Token.ILLEGAL : token; + } + + private Token select(int next, Token yes, Token no) { + advance(); + if (lookahead(0) != next) + return no; + advance(); + return yes; + } + + private Token select(Token token) { + advance(); + return token; + } + + private Token skipMultiLineComment() { + assert (is('*')); + Position currPos = internalState.lookaheadPos[0]; + int start = currPos.pos - 1; + int line = currPos.line; + int col = currPos.col; + advance(); + while (!isEos()) { + int first = lookahead(0); + advance(); + if (first == '*' && is('/')) { + Token result = select(Token.COMMENT); + int stop = internalState.lookaheadPos[0].pos; + commentLocation(start, stop, line, internalState.lookaheadPos[0].line, col); + return result; + } + } + int stop = internalState.lookaheadPos[0].pos; + commentLocation(start, stop, line, internalState.lookaheadPos[0].line, col); + // Unterminated multi-line comment. + return Token.ILLEGAL; + } + + private Token skipSingleLineComment() { + assert (is('/')); + Position currPos = internalState.lookaheadPos[0]; + int start = currPos.pos - 1; + int line = currPos.line; + int col = currPos.col; + advance(); + while (!isEos() && !isLineTerminator(lookahead(0))) + advance(); + int stop = internalState.lookaheadPos[0].pos; + commentLocation(start, stop, line, internalState.lookaheadPos[0].line, col); + return Token.COMMENT; + } + + private void skipWhiteSpace() { + if ((internalState.getMode() != InternalState.Mode.DEFAULT) + && (internalState.getMode() != InternalState.Mode.IN_STRING_EMBEDDED_EXPRESSION)) { + return; + } + while (true) { + if (isLineTerminator(lookahead(0))) { + } else if (!isWhiteSpace(lookahead(0))) { + break; + } + advance(); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/parser/DartScannerParserContext.java b/compiler/java/com/google/dart/compiler/parser/DartScannerParserContext.java new file mode 100644 index 00000000000..fe4c4bb75c2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/DartScannerParserContext.java @@ -0,0 +1,194 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.common.HasSourceInfo; +import com.google.dart.compiler.metrics.CompilerMetrics; +import com.google.dart.compiler.parser.DartScanner.State; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Stack; + +/** + * A ParserContext backed by a DartScanner. + */ +public class DartScannerParserContext implements ParserContext { + private DartScanner scanner; + private Deque stateStack = new ArrayDeque(); + private Deque positionStack = new ArrayDeque(); + private Source source; + private DartCompilerListener listener; + private final CompilerMetrics compilerMetrics; + + public DartScannerParserContext(Source source, String sourceCode, + DartCompilerListener listener) { + this(source, sourceCode, listener, null); + } + + public DartScannerParserContext(Source source, String sourceCode, + DartCompilerListener listener, CompilerMetrics compilerMetrics) { + this.source = source; + this.scanner = createScanner(sourceCode); + this.listener = listener; + this.compilerMetrics = compilerMetrics; + } + + @Override + public void begin() { + stateStack.push(scanner.getState()); + positionStack.push(getBeginLocation(0)); + } + + private DartScanner.Position getBeginLocation(int n) { + DartScanner.Location tokenLocation = scanner.peekTokenLocation(n); + return tokenLocation != null ? tokenLocation.getBegin() : new DartScanner.Position(0, 1, 1); + } + + private DartScanner.Position getEndLocation() { + DartScanner.Location tokenLocation = scanner.getTokenLocation(); + return tokenLocation != null ? tokenLocation.getEnd() : new DartScanner.Position(0, 1, 1); + } + + @Override + public T done(T result) { + DartScanner.State oldState = stateStack.pop(); + DartScanner.State newState = stateStack.peek(); + + // If there is more state left, push the newer token changes to them. + if (newState != null) { + if (oldState.rollbackTokens != null) { + if (newState.rollbackTokens != null) { + oldState.rollbackTokens.addAll(newState.rollbackTokens); + } + newState.rollbackTokens = oldState.rollbackTokens; + } + } + + setSourcePosition(result, positionStack.pop()); + + if (result instanceof DartUnit) { + if (compilerMetrics != null) { + compilerMetrics.unitParsed(scanner.getCharCount(), scanner.getNonCommentCharCount(), + scanner.getLineCount(), scanner.getNonCommentLineCount()); + } + } + + // want next begin() call to seek to the next token and skip whitespace after previous done() + return result; + } + + /** + * Set the source position on a result, if it is a {@link HasSourceInfo}. + * + * @param result type + * @param result + * @param startPos + */ + private void setSourcePosition(T result, DartScanner.Position startPos) { + if (result instanceof HasSourceInfo) { + HasSourceInfo node = (HasSourceInfo) result; + int start = startPos.getPos(); + int end = getEndLocation().getPos(); + if (start != -1 && end < start) { + // handle 0-length tokens, including where there is trailing whitespace + end = start; + } + node.setSourceLocation( + source, + startPos.getLine(), startPos.getCol(), + start, end - start); + } + } + + @Override + public T doneWithoutConsuming(T result) { + // do not throw away state + setSourcePosition(result, positionStack.peek()); + + // want next begin() call to seek to the next token and skip whitespace after previous done() + return result; + } + + @Override + public void error(DartCompilationError dartError) { + listener.compilationError(dartError); + } + + @Override + public void warning(DartCompilationError dartError) { + listener.compilationWarning(dartError); + } + + @Override + public void advance() { + scanner.next(); + } + + @Override + public Token getCurrentToken() { + return scanner.getToken(); + } + + @Override + public Token peek(int steps) { + return scanner.peek(steps); + } + + @Override + public void rollback() { + // undo changes made to scanner tokens + DartScanner.State oldState = stateStack.pop(); + scanner.restoreState(oldState); + + // Restore the replaced tokens to their state. + if (oldState.rollbackTokens != null) { + for (State.RollbackToken token : oldState.rollbackTokens) { + scanner.setAbsolutePeek(token.absoluteOffset, token.replacedToken); + } + } + positionStack.pop(); + } + + @Override + public String getTokenString() { + return scanner.getTokenValue(); + } + + @Override + public String peekTokenString(int steps) { + return scanner.peekTokenValue(steps); + } + + @Override + public void replaceNextToken(Token token) { + DartScanner.State state = stateStack.peek(); + DartScanner.State.RollbackToken oldToken + = new DartScanner.State.RollbackToken(scanner.getOffset() + 1, scanner.peek(0)); + if (state.rollbackTokens == null) { + state.rollbackTokens = new Stack(); + } + state.rollbackTokens.push(oldToken); + scanner.setPeek(0, token); + } + + @Override + public DartScanner.Location getTokenLocation() { + return scanner.getTokenLocation(); + } + + protected DartScanner createScanner(String sourceCode) { + return new DartScanner(sourceCode); + } + + @Override + public Source getSource() { + return source; + } +} diff --git a/compiler/java/com/google/dart/compiler/parser/ParserContext.java b/compiler/java/com/google/dart/compiler/parser/ParserContext.java new file mode 100644 index 00000000000..e3220d36b63 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/ParserContext.java @@ -0,0 +1,128 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.Source; + +/** + * Abstracts parser and permits marking lexical ranges via event driven methods. Certain IDEs need + * more location information than just the source line/column/position of an AST node, such as the + * complete set of lexemes that encompass a given node, e.g. function foo() {}} => + * [FUNCTION, SPACE, IDENTIFIER, LPAREN, RPAREN, SPACE, LBRACE, RBRACE].This interface allows a + * parser to mark the begin and end of each non-terminal AST node in a lexical stream. + */ +public interface ParserContext { + + /** + * Consume the current token, and advance to the next one, skipping whitespace and comment + * tokens. + */ + void advance(); + + /** + * Called at the beginning of a non-terminal rule. The purpose for this method + * is to record any information that might be needed at the end of the rule + * (such as the current source position) as well as any state necessary to be + * able to roll back to the state just prior to the invocation of this method. + * + * @see #done(T) + * @see #doneWithoutConsuming(T) + * @see #rollback() + */ + void begin(); + + /** + * Called at the end of a non-terminal rule to mark the end of the non-terminal + * node. This method consumes any information saved by the {@link #begin()} + * method, updating the node with any saved information (such as its position + * in the source) as appropriate. + * + * @param result the non-terminal node being ended + * + * @return the non-terminal node that should be included in the AST structure, + * which is typically the same as the argument + * + * @see #begin() + * @see #doneWithoutConsuming(T) + * @see #rollback() + */ + T done(T result); + + /** + * Called at the end of a non-terminal rule to mark the end of the non-terminal + * node. Unlike {@link #done()}, this method does not consume any information + * saved by the {@link #begin()} method, but does update the node with any + * saved information (such as its position in the source) as appropriate. + * + * @param result the non-terminal node being ended + * + * @return the non-terminal node that should be included in the AST structure, + * which is typically the same as the argument + * + * @see #begin() + * @see #doneWithoutConsuming(T) + * @see #rollback() + */ + T doneWithoutConsuming(T result); + + /** + * Log a parse error for the current lexical range. + * @param dartError helpful error messaging describing what the expected tokens were + */ + void error(DartCompilationError dartError); + + /** + * Log a parse warning for the current lexical range. + * @param dartError helpful error messaging describing what the expected tokens were + */ + void warning(DartCompilationError dartError); + + /** + * Return the current token. + */ + Token getCurrentToken(); + + /** + * Return Source if present. + */ + Source getSource(); + + /** + * Return location information for the current token. + */ + DartScanner.Location getTokenLocation(); + + /** + * Return the string value, if any, of the current token (e.g. IDENTIFIER) + */ + String getTokenString(); + + /** + * Peek ahead without advancing the lexer. + */ + Token peek(int steps); + + /** + * Set the next token to be returned. + */ + void replaceNextToken(Token token); + + /** + * Rolls back the current token to the position when {@link begin()} was + * called. + * + * @see #begin() + * @see #done(T) + * @see #doneWithoutConsuming(T) + */ + void rollback(); + + /** + * Peek ahead, for the value, without advancing the lexer. + */ + String peekTokenString(int steps); + +} diff --git a/compiler/java/com/google/dart/compiler/parser/Token.java b/compiler/java/com/google/dart/compiler/parser/Token.java new file mode 100644 index 00000000000..f425d19eedd --- /dev/null +++ b/compiler/java/com/google/dart/compiler/parser/Token.java @@ -0,0 +1,259 @@ +// 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. + +package com.google.dart.compiler.parser; + +import java.util.HashMap; +import java.util.Map; + +/** + * Dart tokens and associated data. + * + * Note: Token ordinals matter for some accessors, so don't change the order of these without + * knowing what you're doing. + */ +public enum Token { + /* End-of-stream. */ + EOS(null, 0), + + /* Punctuators. */ + LPAREN("(", 0), + RPAREN(")", 0), + LBRACK("[", 0), + RBRACK("]", 0), + LBRACE("{", 0), + RBRACE("}", 0), + COLON(":", 0), + SEMICOLON(";", 0), + PERIOD(".", 0), + ELLIPSIS("...", 0), + COMMA(",", 0), + CONDITIONAL("?", 3), + ARROW("=>", 0), + + /* Assignment operators. */ + ASSIGN("=", 2), + ASSIGN_BIT_OR("|=", 2), + ASSIGN_BIT_XOR("^=", 2), + ASSIGN_BIT_AND("&=", 2), + ASSIGN_SHL("<<=", 2), + ASSIGN_SAR(">>=", 2), + ASSIGN_SHR(">>>=", 2), + ASSIGN_ADD("+=", 2), + ASSIGN_SUB("-=", 2), + ASSIGN_MUL("*=", 2), + ASSIGN_DIV("/=", 2), + ASSIGN_MOD("%=", 2), + ASSIGN_TRUNC("~/=", 2), + + /* Binary operators sorted by precedence. */ + OR("||", 4), + AND("&&", 5), + BIT_OR("|", 6), + BIT_XOR("^", 7), + BIT_AND("&", 8), + SHL("<<", 11), + SAR(">>", 11), + SHR(">>>", 11), + ADD("+", 12), + SUB("-", 12), + MUL("*", 13), + DIV("/", 13), + TRUNC("~/", 13), + MOD("%", 13), + + /* Compare operators sorted by precedence. */ + EQ("==", 9), + NE("!=", 9), + EQ_STRICT("===", 9), + NE_STRICT("!==", 9), + LT("<", 10), + GT(">", 10), + LTE("<=", 10), + GTE(">=", 10), + IS("is", 10), + + /* Unary operators. */ + NOT("!", 0), + BIT_NOT("~", 0), + + /* Count operators (also unary). */ + INC("++", 0), + DEC("--", 0), + + /* [] operator overloading. */ + INDEX("[]", 0), + ASSIGN_INDEX("[]=", 0), + + /* Keywords. */ + BREAK("break", 0), + CASE("case", 0), + CATCH("catch", 0), + CONST("const", 0), + CONTINUE("continue", 0), + DEFAULT("default", 0), + DO("do", 0), + ELSE("else", 0), + FINAL("final", 0), + FINALLY("finally", 0), + FOR("for", 0), + IF("if", 0), + IN("in", 0), + NEW("new", 0), + RETURN("return", 0), + SUPER("super", 0), + SWITCH("switch", 0), + THIS("this", 0), + THROW("throw", 0), + TRY("try", 0), + VAR("var", 0), + VOID("void", 0), + WHILE("while", 0), + + /* Literals. */ + NULL_LITERAL("null", 0), + TRUE_LITERAL("true", 0), + FALSE_LITERAL("false", 0), + HEX_LITERAL(null, 0), + INTEGER_LITERAL(null, 0), + DOUBLE_LITERAL(null, 0), + STRING(null, 0), + + /** String interpolation and string templates. */ + STRING_SEGMENT(null, 0), + STRING_LAST_SEGMENT(null, 0), + // STRING_EMBED_EXP_START does not have a unique string representation in the code: + // "$id" yields the token STRING_EMBED_EXP_START after the '$', and similarly + // "${id}" yield the same token for '${'. + STRING_EMBED_EXP_START(null, 0), + STRING_EMBED_EXP_END(null, 0), + + // Note: STRING_EMBED_EXP_END uses the same symbol as RBRACE, but it is + // recognized by the scanner when closing embedded expressions in string + // interpolation and string templates. + + /* Directives */ + LIBRARY("#library", 0), + IMPORT("#import", 0), + SOURCE("#source", 0), + RESOURCE("#resource", 0), + NATIVE("#native", 0), + + /* Identifiers (not keywords). */ + IDENTIFIER(null, 0), + WHITESPACE(null, 0), + + /* Pseudo tokens. */ + // If you add another pseudo token, don't forget to update the predicate below. + ILLEGAL(null, 0), + COMMENT(null, 0), + + /** + * Non-token to be used by tools where a value outside the range of anything + * returned by the scanner is needed. This is the equivalent of -1 in a C + * tokenizer. + * + * This token is never returned by the scanner. It must have an ordinal + * value outside the range of all tokens returned by the scanner. + */ + NON_TOKEN(null, 0); + + private static Map tokens = new HashMap(); + + static { + for (Token tok : Token.values()) { + if (tok.syntax_ != null) { + tokens.put(tok.syntax_, tok); + } + } + } + + /** + * Given a string finds the corresponding token. Pseudo tokens (EOS, ILLEGAL and COMMENT) are + * ignored. + */ + public static Token lookup(String syntax) { + Token token = tokens.get(syntax); + if (token == null) { + return IDENTIFIER; + } + return token; + } + + private final String syntax_; + private final int precedence_; + + /** + * The syntax parameter serves two purposes: 1. map tokens that + * look like identifiers ("null", "true", etc.) to their correct token. + * 2. Find the string-representation of operators.
    + * When it is null then the token either doesn't have a unique + * representation, or it is a pseudo token (which doesn't physically appear + * in the source). + */ + Token(String syntax, int precedence) { + syntax_ = syntax; + precedence_ = precedence; + } + + public Token asBinaryOperator() { + int ordinal = ordinal() - ASSIGN_BIT_OR.ordinal() + BIT_OR.ordinal(); + return values()[ordinal]; + } + + public int getPrecedence() { + return precedence_; + } + + public String getSyntax() { + return syntax_; + } + + public boolean isEqualityOperator() { + int ordinal = ordinal(); + return EQ.ordinal() <= ordinal && ordinal <= NE_STRICT.ordinal(); + } + + public boolean isRelationalOperator() { + int ordinal = ordinal(); + return LT.ordinal() <= ordinal && ordinal <= GTE.ordinal(); + } + + public boolean isAssignmentOperator() { + int ordinal = ordinal(); + return ASSIGN.ordinal() <= ordinal && ordinal <= ASSIGN_TRUNC.ordinal(); + } + + public boolean isBinaryOperator() { + int ordinal = ordinal(); + return (ASSIGN.ordinal() <= ordinal && ordinal <= IS.ordinal()) + || (ordinal == COMMA.ordinal()); + } + + public boolean isCountOperator() { + int ordinal = ordinal(); + return INC.ordinal() <= ordinal && ordinal <= DEC.ordinal(); + } + + public boolean isUnaryOperator() { + int ordinal = ordinal(); + return NOT.ordinal() <= ordinal && ordinal <= DEC.ordinal(); + } + + public boolean isUserDefinableOperator() { + int ordinal = ordinal(); + return ((BIT_OR.ordinal() <= ordinal && ordinal <= GTE.ordinal()) + || this == BIT_NOT || this == INDEX || this == ASSIGN_INDEX) + && this != NE && this != EQ_STRICT && this != NE_STRICT; + } + + @Override + public String toString() { + String result = getSyntax(); + if (result == null) { + return name(); + } + return result; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/AbstractElement.java b/compiler/java/com/google/dart/compiler/resolver/AbstractElement.java new file mode 100644 index 00000000000..3dbb951f3d3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/AbstractElement.java @@ -0,0 +1,74 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.Types; + +abstract class AbstractElement implements Element { + private DartNode node; + private final String name; + + AbstractElement(DartNode node, String name) { + this.node = node; + this.name = name; + } + + @Override + public DartNode getNode() { + return node; + } + + // This method can be removed if NormalizeAst is integrated in Normalizer. + @Override + public void setNode(DartLabel node) { + this.node = node; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getOriginalSymbolName() { + return name; + } + + @Override + public abstract ElementKind getKind(); + + @Override + public final String toString() { + return getKind() + " " + getName(); + } + + @Override + public Type getType() { + return Types.newDynamicType(); + } + + void setType(Type type) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDynamic() { + return false; + } + + @Override + public Modifiers getModifiers() { + return Modifiers.NONE; + } + + @Override + public EnclosingElement getEnclosingElement() { + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ClassElement.java b/compiler/java/com/google/dart/compiler/resolver/ClassElement.java new file mode 100644 index 00000000000..1ddb1229710 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ClassElement.java @@ -0,0 +1,50 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; + +import java.util.List; +import java.util.Set; + +public interface ClassElement extends EnclosingElement { + void setType(InterfaceType type); + + @Override + InterfaceType getType(); + + List getTypeParameters(); + + InterfaceType getSupertype(); + + InterfaceType getDefaultClass(); + + void setSupertype(InterfaceType element); + + List getConstructors(); + + LibraryElement getLibrary(); + + List getInterfaces(); + + /** + * Returns the static subtypes of a declared type including itself. This method should only be + * called once all elements have been built. The results are cached so subtypes added after + * building will not be reflected. + */ + Set getSubtypes(); + + List getAllSupertypes() + throws CyclicDeclarationException, DuplicatedInterfaceException; + + String getNativeName(); + + boolean isObject(); + + boolean isObjectChild(); + + ConstructorElement lookupConstructor(String name); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ClassElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/ClassElementImplementation.java new file mode 100644 index 00000000000..98b9b3ed9b9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ClassElementImplementation.java @@ -0,0 +1,366 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartDeclaration; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +class ClassElementImplementation extends AbstractElement implements ClassElement { + private InterfaceType type; + private InterfaceType supertype; + private InterfaceType defaultClass; + private List interfaces; + private Set immediateSubtypes = new HashSet(); + private final boolean isInterface; + private final String nativeName; + private final AtomicReference> allSupertypes = + new AtomicReference>(); + + // declared volatile for thread-safety + private volatile Set subtypes; + + private final List constructors; + private final Map members; + + private final LibraryElement library; + + private static ThreadLocal> seenSupertypes = new ThreadLocal>() { + @Override + protected Set initialValue() { + return new HashSet(); + } + }; + + ClassElementImplementation(DartClass node, String name, String nativeName, + LibraryElement library) { + super(node, name); + this.nativeName = nativeName; + this.library = library; + constructors = new ArrayList(); + members = new LinkedHashMap(); + interfaces = new ArrayList(); + if (node != null) { + isInterface = node.isInterface(); + } else { + isInterface = false; + } + } + + @Override + public DartDeclaration getNode() { + return (DartClass) super.getNode(); + } + + @Override + public void setType(InterfaceType type) { + this.type = type; + } + + @Override + public InterfaceType getType() { + return type; + } + + @Override + public List getTypeParameters() { + return getType().getArguments(); + } + + private void computeTransitiveSubtypes(Set computedSubtypes) { + if (computedSubtypes.addAll(immediateSubtypes)) { + for (InterfaceType subtype : immediateSubtypes) { + ClassElementImplementation classElement = (ClassElementImplementation) subtype.getElement(); + classElement.computeTransitiveSubtypes(computedSubtypes); + } + } + } + + @Override + public Set getSubtypes() { + if (subtypes == null) { + // add double-checked locking, with subtypes being declared volatile, for + // thread-safety + synchronized (this) { + if (subtypes == null) { + // Compute once, this will be an issue when we get to code + // generation... + HashSet newSubtypes = new HashSet(); + newSubtypes.add(getType()); + computeTransitiveSubtypes(newSubtypes); + subtypes = newSubtypes; + } + } + } + return subtypes; + } + + @Override + public InterfaceType getSupertype() { + return supertype; + } + + @Override + public InterfaceType getDefaultClass() { + return defaultClass; + } + + @Override + public void setSupertype(InterfaceType supertype) { + this.supertype = supertype; + if (TypeKind.of(supertype) == TypeKind.INTERFACE) { + ClassElementImplementation superClassElement = + (ClassElementImplementation) supertype.getElement(); + superClassElement.immediateSubtypes.add(this.getType()); + } + } + + void setDefaultClass(InterfaceType element) { + this.defaultClass = element; + } + + @Override + public Iterable getMembers() { + return new Iterable() { + // The only use case for calling getMembers() is for iterating through the + // members. You should not be able to add or remove members through the + // object returned by this method. Returning members or members.value() + // would allow such direct manipulation which might be problematic for + // keeping the element model consistent. + // + // On the other hand, we don't want to make a defensive copy of the list + // because that makes this method expensive. This method should not be + // expensive because the IDE may be using it in interactive scenarios. + // Strictly speaking, we should also wrap the iterator as we don't want + // the method Iterator.remove to be used either. + @Override + public Iterator iterator() { + return members.values().iterator(); + } + }; + } + + @Override + public List getConstructors() { + return constructors; + } + + @Override + public List getInterfaces() { + return interfaces; + } + + @Override + public ElementKind getKind() { + return ElementKind.CLASS; + } + + @Override + public boolean isInterface() { + return isInterface; + } + + @Override + public LibraryElement getLibrary() { + return library; + } + + @Override + public String getNativeName() { + return nativeName; + } + + void addMethod(MethodElement member) { + String name = member.getName(); + if (member.getModifiers().isOperator()) { + name = "operator " + name; + } + members.put(name, member); + } + + void addConstructor(ConstructorElement member) { + constructors.add(member); + } + + void addField(FieldElement member) { + members.put(member.getName(), member); + } + + void addInterface(InterfaceType type) { + interfaces.add(type); + + if (TypeKind.of(type) == TypeKind.INTERFACE) { + ClassElementImplementation interfaceElement = (ClassElementImplementation) type.getElement(); + interfaceElement.immediateSubtypes.add(this.getType()); + } + } + + Element findElement(String name) { + // Temporary find all strategy to get things working. + Element element = lookupLocalField(name); + if (element != null) { + return element; + } + element = lookupLocalMethod(name); + if (element != null) { + return element; + } + if (type != null) { + for (Type arg : type.getArguments()) { + if (arg.getElement().getName().equals(name)) { + return arg.getElement(); + } + } + } + // Don't look for constructors, they are in a different namespace. + return null; + } + + /** + * Lookup a constructor declared in this class. Note that a class may define + * constructors for interfaces in case the class is a default implementation. + * + * @param type The type of the object this constructor is creating. + * @param name The constructor name ("" if unnamed). + * + * @return The constructor found in the class, or null if not found. + */ + ConstructorElement lookupConstructor(ClassElement type, String name) { + for (ConstructorElement element : constructors) { + if (element.getConstructorType().equals(type) && element.getName().equals(name)) { + return element; + } + } + return null; + } + + @Override + public ConstructorElement lookupConstructor(String name) { + // Lookup a constructor that creates instances of this class. + return lookupConstructor(this, name); + } + + @Override + public Element lookupLocalElement(String name) { + return members.get(name); + } + + FieldElement lookupLocalField(String name) { + Element element = lookupLocalElement(name); + if (ElementKind.of(element).equals(ElementKind.FIELD)) { + return (FieldElement) element; + } + return null; + } + + MethodElement lookupLocalMethod(String name) { + Element element = lookupLocalElement(name); + if (ElementKind.of(element).equals(ElementKind.METHOD)) { + return (MethodElement) element; + } + return null; + } + + public static ClassElementImplementation fromNode(DartClass node, LibraryElement library) { + DartStringLiteral nativeName = node.getNativeName(); + String nativeNameString = (nativeName == null ? null : nativeName.getValue()); + return new ClassElementImplementation(node, node.getClassName(), nativeNameString, library); + } + + public static ClassElementImplementation named(String name) { + return new ClassElementImplementation(null, name, null, null); + } + + @Override + public boolean isObject() { + return supertype == null; + } + + @Override + public boolean isObjectChild() { + return supertype != null && supertype.getElement().isObject(); + } + + @Override + public EnclosingElement getEnclosingElement() { + return library; + } + + @Override + public List getAllSupertypes() + throws CyclicDeclarationException, DuplicatedInterfaceException { + List list = allSupertypes.get(); + if (list == null) { + allSupertypes.compareAndSet(null, computeAllSupertypes()); + list = allSupertypes.get(); + } + return list; + } + + private List computeAllSupertypes() + throws CyclicDeclarationException, DuplicatedInterfaceException { + Map interfaces = new HashMap(); + if (!seenSupertypes.get().add(this)) { + throw new CyclicDeclarationException(this); + } + ArrayList supertypes = new ArrayList(); + for (InterfaceType intf : getInterfaces()) { + addCheckDuplicated(interfaces, supertypes, intf); + } + for (InterfaceType intf : getInterfaces()) { + for (InterfaceType t : intf.getElement().getAllSupertypes()) { + if (!t.getElement().isObject()) { + addCheckDuplicated(interfaces, supertypes, + t.subst(intf.getArguments(), + intf.getElement().getTypeParameters())); + } + } + } + if (supertype != null) { + for (InterfaceType t : supertype.getElement().getAllSupertypes()) { + if (t.getElement().isInterface()) { + addCheckDuplicated(interfaces, supertypes, + t.subst(supertype.getArguments(), + supertype.getElement().getTypeParameters())); + } + } + supertypes.add(supertype); + for (InterfaceType t : supertype.getElement().getAllSupertypes()) { + if (!t.getElement().isInterface()) { + supertypes.add(t.subst(supertype.getArguments(), + supertype.getElement().getTypeParameters())); + } + } + } + seenSupertypes.get().remove(this); + return supertypes; + } + + private void addCheckDuplicated(Map interfaces, + ArrayList supertypes, + InterfaceType intf) throws DuplicatedInterfaceException { + InterfaceType existing = interfaces.put(intf.getElement(), intf); + if (existing == null) { + supertypes.add(intf); + } else { + if (!existing.equals(intf)) { + throw new DuplicatedInterfaceException(existing, intf); + } + } + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ClassScope.java b/compiler/java/com/google/dart/compiler/resolver/ClassScope.java new file mode 100644 index 00000000000..ecec13a9ee5 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ClassScope.java @@ -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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.InterfaceType; + +/** + * Lexical scope corresponding to a class body. + */ +class ClassScope extends Scope { + private final ClassElement classElement; + + ClassScope(ClassElement classElement, Scope parent) { + super(classElement.getName(), parent); + this.classElement = classElement; + } + + @Override + public Element declareElement(String name, Element element) { + throw new AssertionError("not supported yet"); + } + + @Override + public Element findElement(String name) { + Element element = super.findElement(name); + if (element != null) { + return element; + } + InterfaceType superclass = classElement.getSupertype(); + if (superclass != null) { + InterfaceType.Member member = superclass.lookupMember(name); + if (member != null) { + return member.getElement(); + } + } + for (InterfaceType supertype : classElement.getInterfaces()) { + InterfaceType.Member member = supertype.lookupMember(name); + if (member != null) { + return member.getElement(); + } + } + return null; + } + + @Override + public Element findLocalElement(String name) { + return Elements.findElement(classElement, name); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ConstructorElement.java b/compiler/java/com/google/dart/compiler/resolver/ConstructorElement.java new file mode 100644 index 00000000000..52ce51356f6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ConstructorElement.java @@ -0,0 +1,14 @@ +// 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. + +package com.google.dart.compiler.resolver; + +public interface ConstructorElement extends MethodElement { + /** + * Returns the type of the instances created by this constructor. Note + * that a constructor in a class may be a default implementation of + * an interface's constructor. + */ + ClassElement getConstructorType(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ConstructorElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/ConstructorElementImplementation.java new file mode 100644 index 00000000000..e5ad047570d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ConstructorElementImplementation.java @@ -0,0 +1,55 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.Modifiers; + +class ConstructorElementImplementation extends MethodElementImplementation + implements ConstructorElement { + private final ClassElement constructorType; + + private ConstructorElementImplementation(DartMethodDefinition node, + String name, + ClassElement declaringClass, + ClassElement constructorType) { + super(node, name, declaringClass); + this.constructorType = constructorType; + } + + private ConstructorElementImplementation(String name, + ClassElement declaringClass, + ClassElement constructorType) { + super(name, declaringClass, Modifiers.NONE.makeFactory()); + this.constructorType = constructorType; + } + + public ClassElement getConstructorType() { + return constructorType; + } + + @Override + public ElementKind getKind() { + return ElementKind.CONSTRUCTOR; + } + + @Override + public boolean isConstructor() { + return true; + } + + public static ConstructorElementImplementation fromMethodNode(DartMethodDefinition node, + String name, + ClassElement declaringClass, + ClassElement constructorType) { + return new ConstructorElementImplementation(node, name, declaringClass, constructorType); + } + + public static ConstructorElementImplementation named(String name, + ClassElement declaringClass, + ClassElement constructorType) { + return new ConstructorElementImplementation(name, declaringClass, constructorType); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/CoreTypeProvider.java b/compiler/java/com/google/dart/compiler/resolver/CoreTypeProvider.java new file mode 100644 index 00000000000..e49080f3768 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/CoreTypeProvider.java @@ -0,0 +1,48 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.DynamicType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; + +public interface CoreTypeProvider { + + InterfaceType getIntType(); + + InterfaceType getDoubleType(); + + InterfaceType getBoolType(); + + InterfaceType getStringType(); + + InterfaceType getFunctionType(); + + Type getNullType(); + + Type getVoidType(); + + DynamicType getDynamicType(); + + InterfaceType getFallThroughError(); + + InterfaceType getArrayType(Type elementType); + + InterfaceType getArrayLiteralType(Type elementType); + + InterfaceType getMapType(Type key, Type value); + + InterfaceType getMapLiteralType(Type key, Type value); + + InterfaceType getObjectArrayType(); + + InterfaceType getObjectType(); + + InterfaceType getNumType(); + + InterfaceType getStringImplementationType(); + + InterfaceType getIsolateType(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/CoreTypeProviderImplementation.java b/compiler/java/com/google/dart/compiler/resolver/CoreTypeProviderImplementation.java new file mode 100644 index 00000000000..770bb531f5c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/CoreTypeProviderImplementation.java @@ -0,0 +1,161 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.parser.DartScanner.Location; +import com.google.dart.compiler.type.DynamicType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.Types; + +import java.util.Arrays; + +public class CoreTypeProviderImplementation implements CoreTypeProvider { + private final InterfaceType intType; + private final InterfaceType doubleType; + private final InterfaceType numType; + private final InterfaceType boolType; + private final InterfaceType stringType; + private final InterfaceType functionType; + private final InterfaceType arrayType; + private final DynamicType dynamicType; + private final Type voidType; + private final Type nullType; + private final InterfaceType fallThroughError; + private final InterfaceType mapType; + private final InterfaceType mapLiteralType; + private final InterfaceType objectArrayType; + private final InterfaceType objectType; + private final InterfaceType isolateType; + private final InterfaceType stringImplementation; + + public CoreTypeProviderImplementation(Scope scope, DartCompilerListener listener) { + this.intType = getType("int", scope, listener); + this.doubleType = getType("double", scope, listener); + this.boolType = getType("bool", scope, listener); + this.numType = getType("num", scope, listener); + this.stringType = getType("String", scope, listener); + this.functionType = getType("Function", scope, listener); + this.arrayType = getType("Array", scope, listener); + this.dynamicType = Types.newDynamicType(); + this.voidType = Types.newVoidType(); + // Currently, there is no need for a special null type. + this.nullType = dynamicType; + this.fallThroughError = getType("FallThroughError", scope, listener); + this.mapType = getType("Map", scope, listener); + this.mapLiteralType = getType("LinkedHashMapImplementation", scope, listener); + this.objectArrayType = getType("ObjectArray", scope, listener); + this.objectType = getType("Object", scope, listener); + this.isolateType = getType("Isolate", scope, listener); + this.stringImplementation = getType("StringImplementation", scope, listener); + } + + private static InterfaceType getType(String name, Scope scope, DartCompilerListener listener) { + ClassElement element = (ClassElement) scope.findElement(name); + if (element == null) { + Location location = null; + DartCompilationError error = + new DartCompilationError(location, DartCompilerErrorCode.CANNOT_BE_RESOLVED, name); + listener.compilationError(error); + return Types.newDynamicType(); + } + return element.getType(); + } + + @Override + public InterfaceType getIntType() { + return intType; + } + + @Override + public InterfaceType getDoubleType() { + return doubleType; + } + + @Override + public InterfaceType getBoolType() { + return boolType; + } + + @Override + public InterfaceType getStringType() { + return stringType; + } + + @Override + public InterfaceType getFunctionType() { + return functionType; + } + + @Override + public InterfaceType getArrayType(Type elementType) { + return arrayType.subst(Arrays.asList(elementType), arrayType.getElement().getTypeParameters()); + } + + @Override + public InterfaceType getArrayLiteralType(Type elementType) { + return objectArrayType.subst( + Arrays.asList(elementType), objectArrayType.getElement().getTypeParameters()); + } + + @Override + public DynamicType getDynamicType() { + return dynamicType; + } + + @Override + public Type getVoidType() { + return voidType; + } + + @Override + public Type getNullType() { + return nullType; + } + + @Override + public InterfaceType getFallThroughError() { + return fallThroughError; + } + + @Override + public InterfaceType getMapType(Type key, Type value) { + return mapType.subst(Arrays.asList(key, value), mapType.getElement().getTypeParameters()); + } + + @Override + public InterfaceType getMapLiteralType(Type key, Type value) { + return mapLiteralType.subst( + Arrays.asList(key, value), mapLiteralType.getElement().getTypeParameters()); + } + + @Override + public InterfaceType getObjectArrayType() { + return objectArrayType; + } + + @Override + public InterfaceType getObjectType() { + return objectType; + } + + @Override + public InterfaceType getNumType() { + return numType; + } + + @Override + public InterfaceType getStringImplementationType() { + return stringImplementation; + } + + @Override + public InterfaceType getIsolateType() { + return isolateType; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/CyclicDeclarationException.java b/compiler/java/com/google/dart/compiler/resolver/CyclicDeclarationException.java new file mode 100644 index 00000000000..a0e413c75d9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/CyclicDeclarationException.java @@ -0,0 +1,21 @@ +// 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. + +package com.google.dart.compiler.resolver; + +/** + * Exception thrown if a cycle is detected in the supertype graph of a class or interface. + */ +public class CyclicDeclarationException extends Exception { + private final ClassElement element; + + public CyclicDeclarationException(ClassElement element) { + super(element.getName()); + this.element = element; + } + + public ClassElement getElement() { + return element; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/DuplicatedInterfaceException.java b/compiler/java/com/google/dart/compiler/resolver/DuplicatedInterfaceException.java new file mode 100644 index 00000000000..a7ee5b07f75 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/DuplicatedInterfaceException.java @@ -0,0 +1,30 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.InterfaceType; + +/** + * Exception thrown if a duplicated interface is detected in the supertype graph of a class or + * interface. + */ +public class DuplicatedInterfaceException extends Exception { + private final InterfaceType first; + private final InterfaceType second; + + public DuplicatedInterfaceException(InterfaceType first, InterfaceType second) { + super(first + " & " + second); + this.first = first; + this.second = second; + } + + public InterfaceType getFirst() { + return first; + } + + public InterfaceType getSecond() { + return second; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/DynamicElement.java b/compiler/java/com/google/dart/compiler/resolver/DynamicElement.java new file mode 100644 index 00000000000..b971ce29312 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/DynamicElement.java @@ -0,0 +1,17 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.DynamicType; + +/** + * Dummy element corresponding to {@link DynamicType}. + */ +public interface DynamicElement extends FunctionAliasElement, LibraryElement, FieldElement, + LabelElement, SuperElement, VariableElement, + TypeVariableElement, ConstructorElement { + @Override + DynamicType getType(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/DynamicElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/DynamicElementImplementation.java new file mode 100644 index 00000000000..46cd619d84c --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/DynamicElementImplementation.java @@ -0,0 +1,251 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.type.DynamicType; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.Types; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Dummy element corresponding to {@link DynamicType}. + */ +class DynamicElementImplementation extends AbstractElement implements DynamicElement { + private final Set subtypes = + Collections.singleton(Types.newDynamicType()); + + private DynamicElementImplementation() { + super(null, ""); + } + + @Override + public ElementKind getKind() { + return ElementKind.DYNAMIC; + } + + public static DynamicElementImplementation getInstance() { + return new DynamicElementImplementation(); + } + + @Override + public void setType(InterfaceType type) { + throw new UnsupportedOperationException(); + } + + @Override + public List getTypeParameters() { + return Collections.emptyList(); + } + + @Override + public InterfaceType getSupertype() { + return null; + } + + @Override + public InterfaceType getDefaultClass() { + return null; + } + + @Override + public void setSupertype(InterfaceType element) { + throw new UnsupportedOperationException(); + } + + @Override + public List getMembers() { + return Collections.emptyList(); + } + + @Override + public List getConstructors() { + return Collections.emptyList(); + } + + @Override + public List getInterfaces() { + return Collections.emptyList(); + } + + @Override + public DynamicType getType() { + return null; + } + + @Override + public DynamicType getTypeVariable() { + return getType(); + } + + @Override + public ClassElement getEnclosingElement() { + return this; + } + + @Override + public boolean isConstructor() { + return false; + } + + @Override + public boolean isStatic() { + return false; + } + + @Override + public boolean isInterface() { + return false; + } + + @Override + public String getNativeName() { + return null; + } + + @Override + public List getParameters() { + return Collections.emptyList(); + } + + @Override + public Type getReturnType() { + return getType(); + } + + @Override + public boolean isDynamic() { + return true; + } + + @Override + public boolean isObject() { + return false; + } + + @Override + public boolean isObjectChild() { + return false; + } + + @Override + public Element lookupLocalElement(String name) { + return this; + } + + @Override + public LibraryElement getLibrary() { + return null; + } + + @Override + public void setBound(Type bound) { + } + + @Override + public Type getBound() { + return getType(); + } + + @Override + public Element getDeclaringElement() { + return this; + } + + @Override + public ConstructorElement lookupConstructor(String name) { + return null; + } + + @Override + public Set getSubtypes() { + return subtypes; + } + + @Override + public FunctionType getFunctionType() { + return null; + } + + @Override + public void setFunctionType(FunctionType functionType) { + } + + @Override + public List getAllSupertypes() { + return Collections.emptyList(); + } + + @Override + public Scope getScope() { + return null; + } + + @Override + public LibraryUnit getLibraryUnit() { + return null; + } + + @Override + public void setEntryPoint(MethodElement element) { + throw new AssertionError(); + } + + @Override + public MethodElement getEntryPoint() { + return null; + } + + @Override + public MethodElement getGetter() { + return null; + } + + @Override + public MethodElement getSetter() { + return null; + } + + @Override + public MethodElement getEnclosingFunction() { + return this; + } + + @Override + public ClassElement getClassElement() { + return this; + } + + @Override + public FieldElement getParameterInitializerElement() { + return this; + } + + @Override + public boolean isNamed() { + return false; + } + + @Override + public DartExpression getDefaultValue() { + return null; + } + + @Override + public ClassElement getConstructorType() { + return this; + } + + @Override + public void setType(Type type) { + super.setType(type); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/Element.java b/compiler/java/com/google/dart/compiler/resolver/Element.java new file mode 100644 index 00000000000..fdab892417e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/Element.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.common.Symbol; +import com.google.dart.compiler.type.Type; + +public interface Element extends Symbol { + void setNode(DartLabel node); + + String getName(); + + ElementKind getKind(); + + Type getType(); + + boolean isDynamic(); + + Modifiers getModifiers(); + + EnclosingElement getEnclosingElement(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ElementKind.java b/compiler/java/com/google/dart/compiler/resolver/ElementKind.java new file mode 100644 index 00000000000..1c8d89ddf7d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ElementKind.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.common.Symbol; + +/** + * Kinds of elements. Use kinds instead of instanceof for maximum flexibility + * and sharing of similar implementation classes. + */ +public enum ElementKind { + CLASS, + CONSTRUCTOR, + FIELD, + FUNCTION_OBJECT, + LABEL, + METHOD, + PARAMETER, + TYPE_VARIABLE, + VARIABLE, + FUNCTION_TYPE_ALIAS, + DYNAMIC, + LIBRARY, + SUPER, + NONE, + VOID; + + public static ElementKind of(Symbol symbol) { + if (symbol instanceof Element) { + Element element = (Element) symbol; + return element.getKind(); + } else { + return NONE; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/Elements.java b/compiler/java/com/google/dart/compiler/resolver/Elements.java new file mode 100644 index 00000000000..9241cf7fc02 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/Elements.java @@ -0,0 +1,212 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.common.annotations.VisibleForTesting; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartSuperExpression; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeVariable; + +import java.util.Arrays; +import java.util.List; + +/** + * Utility and factory methods for elements. + */ +public class Elements { + private Elements() {} // Prevent subclassing and instantiation. + + static void setParameterInitializerElement(VariableElement varElement, FieldElement element) { + ((VariableElementImplementation) varElement).setParameterInitializerElement(element); + } + + static void setDefaultClass(ClassElement classElement, InterfaceType defaultClass) { + ((ClassElementImplementation) classElement).setDefaultClass(defaultClass); + } + + static void addInterface(ClassElement classElement, InterfaceType type) { + ((ClassElementImplementation) classElement).addInterface(type); + } + + static LabelElement labelElement(DartLabel node, String name, MethodElement enclosingFunction) { + return new LabelElementImplementation(node, name, enclosingFunction); + } + + public static LibraryElement libraryElement(LibraryUnit libraryUnit) { + return new LibraryElementImplementation(libraryUnit); + } + + @VisibleForTesting + public static MethodElement methodElement(DartFunctionExpression node, String name) { + return new MethodElementImplementation(node, name, Modifiers.NONE); + } + + public static TypeVariableElement typeVariableElement(DartNode node, String name, Element owner) { + return new TypeVariableElementImplementation(node, name, owner); + } + + public static VariableElement variableElement(DartVariable node, String name, + Modifiers modifiers) { + return new VariableElementImplementation(node, name, ElementKind.VARIABLE, modifiers, false, + null); + } + + public static VariableElement parameterElement(DartParameter node, String name, + Modifiers modifiers) { + return new VariableElementImplementation(node, name, ElementKind.PARAMETER, modifiers, + node.getModifiers().isNamed(), + node.getDefaultExpr()); + } + + public static VariableElement makeVariable(String name) { + return new VariableElementImplementation(null, name, + ElementKind.VARIABLE, Modifiers.NONE, false, null); + } + + public static SuperElement superElement(DartSuperExpression node, ClassElement cls) { + return new SuperElementImplementation(node, cls); + } + + static void addConstructor(ClassElement cls, ConstructorElement constructor) { + ((ClassElementImplementation) cls).addConstructor(constructor); + } + + static void addField(EnclosingElement holder, FieldElement field) { + if (ElementKind.of(holder).equals(ElementKind.CLASS)) { + ((ClassElementImplementation) holder).addField(field); + } else if (ElementKind.of(holder).equals(ElementKind.LIBRARY)) { + ((LibraryElementImplementation) holder).addField(field); + } else { + throw new IllegalArgumentException(); + } + } + + static void addMethod(EnclosingElement holder, MethodElement method) { + if (ElementKind.of(holder).equals(ElementKind.CLASS)) { + ((ClassElementImplementation) holder).addMethod(method); + } else if (ElementKind.of(holder).equals(ElementKind.LIBRARY)) { + ((LibraryElementImplementation) holder).addMethod(method); + } else { + throw new IllegalArgumentException(); + } + } + + public static void addParameter(MethodElement method, VariableElement parameter) { + ((MethodElementImplementation) method).addParameter(parameter); + } + + static Element findElement(ClassElement cls, String name) { + return ((ClassElementImplementation) cls).findElement(name); + } + + public static MethodElement methodFromFunctionExpression(DartFunctionExpression node, + Modifiers modifiers) { + return MethodElementImplementation.fromFunctionExpression(node, modifiers); + } + + static MethodElement methodFromMethodNode(DartMethodDefinition node, EnclosingElement holder) { + return MethodElementImplementation.fromMethodNode(node, holder); + } + + static ConstructorElement constructorFromMethodNode(DartMethodDefinition node, + String name, + ClassElement declaringClass, + ClassElement constructorType) { + return ConstructorElementImplementation.fromMethodNode(node, name, declaringClass, + constructorType); + } + + static void setType(Element element, Type type) { + ((AbstractElement) element).setType(type); + } + + static FieldElementImplementation fieldFromNode(DartField node, + EnclosingElement holder, + Modifiers modifiers) { + return FieldElementImplementation.fromNode(node, holder, modifiers); + } + + static ClassElement classFromNode(DartClass node, LibraryElement library) { + return ClassElementImplementation.fromNode(node, library); + } + + public static ClassElement classNamed(String name) { + return ClassElementImplementation.named(name); + } + + static TypeVariableElement typeVariableFromNode(DartTypeParameter node, Element element) { + return TypeVariableElementImplementation.fromNode(node, element); + } + + public static DynamicElement dynamicElement() { + return DynamicElementImplementation.getInstance(); + } + + static ConstructorElement lookupConstructor(ClassElement cls, ClassElement type, String name) { + return ((ClassElementImplementation) cls).lookupConstructor(type, name); + } + + static ConstructorElement lookupConstructor(ClassElement cls, String name) { + return ((ClassElementImplementation) cls).lookupConstructor(name); + } + + public static MethodElement lookupLocalMethod(ClassElement cls, String name) { + return ((ClassElementImplementation) cls).lookupLocalMethod(name); + } + + public static FieldElement lookupLocalField(ClassElement cls, String name) { + return ((ClassElementImplementation) cls).lookupLocalField(name); + } + + static ConstructorElement constructorNamed(String name, ClassElement declaringClass, + ClassElement constructorType) { + return ConstructorElementImplementation.named(name, declaringClass, constructorType); + } + + public static FunctionAliasElement functionTypeAliasFromNode(DartFunctionTypeAlias node, + LibraryElement library) { + return FunctionAliasElementImplementation.fromNode(node, library); + } + + public static boolean isNonFactoryConstructor(Element method) { + return !method.getModifiers().isFactory() + && ElementKind.of(method).equals(ElementKind.CONSTRUCTOR); + } + + public static boolean isTopLevel(Element element) { + return ElementKind.of(element.getEnclosingElement()).equals(ElementKind.LIBRARY); + } + + static List makeTypeVariables(List parameterNodes, + Element element) { + if (parameterNodes == null) { + return Arrays.asList(); + } + TypeVariable[] typeVariables = new TypeVariable[parameterNodes.size()]; + int i = 0; + for (DartTypeParameter parameterNode : parameterNodes) { + typeVariables[i++] = + Elements.typeVariableFromNode(parameterNode, element).getTypeVariable(); + } + return Arrays.asList(typeVariables); + } + + public static Element voidElement() { + return VoidElement.getInstance(); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/EnclosingElement.java b/compiler/java/com/google/dart/compiler/resolver/EnclosingElement.java new file mode 100644 index 00000000000..55d7f85ce19 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/EnclosingElement.java @@ -0,0 +1,13 @@ +// 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. + +package com.google.dart.compiler.resolver; + +public interface EnclosingElement extends Element { + Iterable getMembers(); + + Element lookupLocalElement(String name); + + boolean isInterface(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/FieldElement.java b/compiler/java/com/google/dart/compiler/resolver/FieldElement.java new file mode 100644 index 00000000000..76ed8998d5d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/FieldElement.java @@ -0,0 +1,17 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.Type; + +public interface FieldElement extends Element { + boolean isStatic(); + + void setType(Type type); + + MethodElement getGetter(); + + MethodElement getSetter(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/FieldElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/FieldElementImplementation.java new file mode 100644 index 00000000000..ff14b5e742d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/FieldElementImplementation.java @@ -0,0 +1,92 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.Type; + +class FieldElementImplementation extends AbstractElement implements FieldElement { + private final EnclosingElement holder; + private Modifiers modifiers; + private Type type; + private MethodElement getter; + private MethodElement setter; + + FieldElementImplementation(DartNode node, + String name, + EnclosingElement holder, + Modifiers modifiers) { + super(node, name); + this.holder = holder; + this.modifiers = modifiers; + } + + @Override + public Type getType() { + return type; + } + + @Override + public void setType(Type type) { + this.type = type; + } + + @Override + public ElementKind getKind() { + return ElementKind.FIELD; + } + + @Override + public EnclosingElement getEnclosingElement() { + return holder; + } + + @Override + public Modifiers getModifiers() { + return modifiers; + } + + @Override + public boolean isStatic() { + return modifiers.isStatic(); + } + + public static FieldElementImplementation fromNode(DartField node, + EnclosingElement holder, + Modifiers modifiers) { + return new FieldElementImplementation(node, node.getName().getTargetName(), holder, modifiers); + } + + public static FieldElementImplementation fromNode(DartMethodDefinition node, + EnclosingElement holder, + Modifiers modifiers) { + return new FieldElementImplementation(node, + ((DartIdentifier) node.getName()).getTargetName(), + holder, + modifiers); + } + + @Override + public MethodElement getGetter() { + return getter; + } + + @Override + public MethodElement getSetter() { + return setter; + } + + void setGetter(MethodElement getter) { + this.getter = getter; + } + + void setSetter(MethodElement setter) { + this.setter = setter; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/FunctionAliasElement.java b/compiler/java/com/google/dart/compiler/resolver/FunctionAliasElement.java new file mode 100644 index 00000000000..982677ee473 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/FunctionAliasElement.java @@ -0,0 +1,20 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.FunctionAliasType; +import com.google.dart.compiler.type.FunctionType; + +/** + * A function type alias. + */ +public interface FunctionAliasElement extends ClassElement { + @Override + FunctionAliasType getType(); + + FunctionType getFunctionType(); + + void setFunctionType(FunctionType functionType); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/FunctionAliasElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/FunctionAliasElementImplementation.java new file mode 100644 index 00000000000..05c94b2bbfe --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/FunctionAliasElementImplementation.java @@ -0,0 +1,60 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.type.FunctionAliasType; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.InterfaceType; + +// Could be a direct subclass of AbstractElement. +public class FunctionAliasElementImplementation extends ClassElementImplementation + implements FunctionAliasElement { + + private FunctionType functionType; + private final DartFunctionTypeAlias node; + + FunctionAliasElementImplementation(DartFunctionTypeAlias node, String name, LibraryElement library) { + super(null, name, null, library); + this.node = node; + } + + @Override + public DartFunctionTypeAlias getNode() { + return node; + } + + @Override + public ElementKind getKind() { + return ElementKind.FUNCTION_TYPE_ALIAS; + } + + @Override + public FunctionAliasType getType() { + return (FunctionAliasType) super.getType(); + } + + @Override + public FunctionType getFunctionType() { + return functionType; + } + + @Override + public void setType(InterfaceType type) { + FunctionAliasType ftype = (FunctionAliasType) type; + super.setType(ftype); + } + + @Override + public void setFunctionType(FunctionType functionType) { + this.functionType = functionType; + } + + public static FunctionAliasElement fromNode(DartFunctionTypeAlias node, + LibraryElement library) { + return new FunctionAliasElementImplementation( + node, node.getName().getTargetName(), library); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/LabelElement.java b/compiler/java/com/google/dart/compiler/resolver/LabelElement.java new file mode 100644 index 00000000000..07a891c0801 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/LabelElement.java @@ -0,0 +1,12 @@ +// 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. + +package com.google.dart.compiler.resolver; + +public interface LabelElement extends Element { + /** + * Returns the innermost function where this label is defined. + */ + public MethodElement getEnclosingFunction(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/LabelElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/LabelElementImplementation.java new file mode 100644 index 00000000000..7d44587bf5d --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/LabelElementImplementation.java @@ -0,0 +1,32 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartLabel; + +class LabelElementImplementation extends AbstractElement implements LabelElement { + + private MethodElement enclosingFunction; + + LabelElementImplementation(DartLabel node, String name, MethodElement enclosingFunction) { + super(node, name); + this.enclosingFunction = enclosingFunction; + } + + @Override + public ElementKind getKind() { + return ElementKind.LABEL; + } + + @Override + public MethodElement getEnclosingFunction() { + return enclosingFunction; + } + + @Override + public void setNode(DartLabel node) { + super.setNode(node); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/LibraryElement.java b/compiler/java/com/google/dart/compiler/resolver/LibraryElement.java new file mode 100644 index 00000000000..39dd251a2b2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/LibraryElement.java @@ -0,0 +1,17 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.LibraryUnit; + +public interface LibraryElement extends EnclosingElement { + Scope getScope(); + + LibraryUnit getLibraryUnit(); + + void setEntryPoint(MethodElement element); + + MethodElement getEntryPoint(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/LibraryElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/LibraryElementImplementation.java new file mode 100644 index 00000000000..7af7c02ef6b --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/LibraryElementImplementation.java @@ -0,0 +1,72 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.LibraryUnit; + +import java.util.Collection; + +class LibraryElementImplementation extends AbstractElement implements LibraryElement { + + private final Scope scope = new Scope("library"); + private LibraryUnit libraryUnit; + private MethodElement entryPoint; + + public LibraryElementImplementation(LibraryUnit libraryUnit) { + // TODO(ngeoffray): What should we pass the super? Should a LibraryUnit be a node? + super(null, libraryUnit.getSource().getName()); + this.libraryUnit = libraryUnit; + } + + @Override + public boolean isInterface() { + return false; + } + + + @Override + public Scope getScope() { + return scope; + } + + @Override + public ElementKind getKind() { + return ElementKind.LIBRARY; + } + + @Override + public LibraryUnit getLibraryUnit() { + return libraryUnit; + } + + @Override + public void setEntryPoint(MethodElement element) { + this.entryPoint = element; + } + + @Override + public MethodElement getEntryPoint() { + return entryPoint; + } + + @Override + public Collection getMembers() { + // TODO(ngeoffray): have a proper way to get all the declared top level elements. + return scope.getElements().values(); + } + + @Override + public Element lookupLocalElement(String name) { + return scope.findLocalElement(name); + } + + void addField(FieldElement field) { + scope.declareElement(field.getName(), field); + } + + void addMethod(MethodElement method) { + scope.declareElement(method.getName(), method); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/MemberBuilder.java b/compiler/java/com/google/dart/compiler/resolver/MemberBuilder.java new file mode 100644 index 00000000000..a1909d0e5f3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/MemberBuilder.java @@ -0,0 +1,459 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartParameterizedNode; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.Types; + +import java.util.ArrayList; +import java.util.List; + +/** + * Builds the method, field and constructor elements of classes and the library in a DartUnit. + */ +public class MemberBuilder { + private ResolutionContext topLevelContext; + private LibraryElement libraryElement; + + public void exec(DartUnit unit, DartCompilerContext context, CoreTypeProvider typeProvider) { + libraryElement = unit.getLibrary().getElement(); + exec(unit, context, libraryElement.getScope(), typeProvider); + } + + public void exec(DartUnit unit, DartCompilerContext compilerContext, Scope scope, + CoreTypeProvider typeProvider) { + topLevelContext = new ResolutionContext(scope, compilerContext, typeProvider); + unit.accept(new MemberElementBuilder(typeProvider)); + } + + /** + * Creates elements for the fields, methods and constructors of a class. The + * elements are added to the ClassElement. + * + * TODO(ngeoffray): Errors reported: + * - Duplicate member names in the same class. + * - Unresolved types. + */ + private class MemberElementBuilder extends ResolveVisitor { + EnclosingElement currentHolder; + private ResolutionContext context; + private boolean isStatic; + + MemberElementBuilder(CoreTypeProvider typeProvider) { + super(typeProvider); + context = topLevelContext; + currentHolder = libraryElement; + } + + @Override + ResolutionContext getContext() { + return context; + } + + @Override + boolean isStaticContext() { + return isStatic; + } + + @Override + public Element visitClass(DartClass node) { + assert !ElementKind.of(currentHolder).equals(ElementKind.CLASS) : "nested class?"; + beginClassContext(node); + this.visit(node.getMembers()); + endClassContext(); + return null; + } + + @Override + public Element visitFunctionTypeAlias(DartFunctionTypeAlias node) { + isStatic = false; + assert !ElementKind.of(currentHolder).equals(ElementKind.CLASS) : "nested class?"; + FunctionAliasElement element = node.getSymbol(); + currentHolder = element; + context = context.extend((ClassElement) currentHolder); // Put type variables in scope. + visit(node.getTypeParameters()); + List parameters = new ArrayList(); + for (DartParameter parameter : node.getParameters()) { + parameters.add((VariableElement) parameter.accept(this)); + } + Type returnType = resolveType(node.getReturnTypeNode(), false); + ClassElement functionElement = getTypeProvider().getFunctionType().getElement(); + element.setFunctionType(Types.makeFunctionType(getContext(), functionElement, + parameters, returnType, null)); + currentHolder = libraryElement; + context = topLevelContext; + return null; + } + + @Override + public Element visitMethodDefinition(final DartMethodDefinition method) { + isStatic = method.getModifiers().isStatic() || method.getModifiers().isFactory(); + MethodElement element = method.getSymbol(); + if (element == null) { + switch (getMethodKind(method)) { + case CONSTRUCTOR: + element = buildConstructor(method); + addConstructor((ClassElement) currentHolder, (ConstructorElement) element); + break; + + case METHOD: + element = Elements.methodFromMethodNode(method, currentHolder); + addMethod(currentHolder, element); + break; + } + } else { + // This is a top-level element, and an element was already created in + // TopLevelElementBuilder. + Elements.addMethod(currentHolder, element); + assertTopLevel(method); + } + if (element != null) { + checkModifiers(element, method); + recordElement(method, element); + ResolutionContext previous = context; + context = context.extend(element.getName()); + List parameterNodes = method.getTypeParameters(); + resolveFunction(method.getFunction(), element, + Elements.makeTypeVariables(parameterNodes, element)); + context = previous; + } + return null; + } + + @Override + public Element visitFieldDefinition(DartFieldDefinition node) { + isStatic = false; + for (DartField fieldNode : node.getFields()) { + if (fieldNode.getModifiers().isStatic()) { + isStatic = true; + } + } + Type type = resolveType(node.getTypeNode(), isStatic); + for (DartField fieldNode : node.getFields()) { + if (fieldNode.getModifiers().isAbstractField()) { + buildAbstractField(fieldNode); + } else { + buildField(fieldNode, type); + } + } + return null; + } + + private void beginClassContext(final DartClass node) { + assert !ElementKind.of(currentHolder).equals(ElementKind.CLASS) : "nested class?"; + currentHolder = node.getSymbol(); + context = context.extend((ClassElement) currentHolder); + } + + private void endClassContext() { + currentHolder = libraryElement; + context = topLevelContext; + } + + private MethodElement buildConstructor(final DartMethodDefinition method) { + // Resolve the constructor's name and class name. + Element e = method.getName().accept(new DartNodeTraverser() { + @Override public Element visitPropertyAccess(DartPropertyAccess node) { + Element element = node.getQualifier().accept(this); + if (ElementKind.of(element).equals(ElementKind.CLASS)) { + return Elements.constructorFromMethodNode( + method, node.getPropertyName(), (ClassElement) currentHolder, (ClassElement) element); + } else { + topLevelContext.internalError(node, + "Library prefixes not implemented yet"); + return getTypeProvider().getDynamicType().getElement(); + } + } + @Override public Element visitIdentifier(DartIdentifier node) { + return context.resolveType(node, node, null, true).getElement(); + } + @Override public Element visitParameterizedNode(DartParameterizedNode node) { + Element element = node.getExpression().accept(this); + if (ElementKind.of(element).equals(ElementKind.CONSTRUCTOR)) { + recordElement(node.getExpression(), currentHolder); + } else { + recordElement(node.getExpression(), element); + } + return element; + } + @Override public Element visitNode(DartNode node) { + throw new RuntimeException("Unexpected node " + node); + } + }); + + switch (ElementKind.of(e)) { + default: + // Report an error and create a fake constructor element below. + resolutionError(method.getName(), DartCompilerErrorCode.INVALID_TYPE_NAME_IN_CONSTRUCTOR); + break; + + case DYNAMIC: + case CLASS: + break; + + case CONSTRUCTOR: + return (ConstructorElement) e; + } + // If the constructor name resolves to a class or there was an error, + // create the unnamed constructor. + return Elements.constructorFromMethodNode(method, "", (ClassElement) currentHolder, + (ClassElement) e); + } + + private FieldElement buildField(DartField fieldNode, Type type) { + assert !fieldNode.getModifiers().isAbstractField(); + FieldElement fieldElement = fieldNode.getSymbol(); + if (fieldElement == null) { + fieldElement = Elements.fieldFromNode(fieldNode, currentHolder, fieldNode.getModifiers()); + addField(currentHolder, fieldElement); + } else { + // This is a top-level element, and an element was already created in + // TopLevelElementBuilder. + Elements.addField(currentHolder, fieldElement); + assertTopLevel(fieldNode); + } + fieldElement.setType(type); + return recordElement(fieldNode, fieldElement); + } + + private void assertTopLevel(DartNode node) throws AssertionError { + if (!currentHolder.getKind().equals(ElementKind.LIBRARY)) { + throw topLevelContext.internalError(node, "expected top-level node"); + } + } + + /** + * Creates FieldElement for AST getters and setters. + * + * class A { + * int get foo() { ... } + * set foo(x) { ... } + * } + * + * The AST will have the shape (simplified): + * DartClass + * members + * DartFieldDefinition + * DartField + * + name: foo + * + modifiers: abstractfield + * + accessor: int get foo() { ... } + * DartFieldDefinition + * DartField + * + name: foo + * + modifiers: abstractfield + * + accessor: set foo(x) { ... } + * + * MemberBuilder will reduce to one class element as below (simplified): + * ClassElement + * members: + * FieldElement + * + name: foo + * + getter: + * MethodElement + * + name: foo + * + function: int get foo() { ... } + * + setter: + * MethodElement + * + name: foo + * + function: set foo(x) { ... } + * + */ + private FieldElement buildAbstractField(DartField fieldNode) { + assert fieldNode.getModifiers().isAbstractField(); + DartMethodDefinition accessorNode = fieldNode.getAccessor(); + MethodElement accessorElement = Elements.methodFromMethodNode(accessorNode, currentHolder); + recordElement(accessorNode, accessorElement); + resolveFunction(accessorNode.getFunction(), accessorElement, null); + + String name = fieldNode.getName().getTargetName(); + Element element = currentHolder.lookupLocalElement(name); + FieldElementImplementation fieldElement = null; + if (element == null || element.getKind().equals(ElementKind.FIELD)) { + fieldElement = (FieldElementImplementation) element; + } else { + resolutionError(fieldNode, DartCompilerErrorCode.FIELD_CONFLICTS, name, element.getKind()); + } + + if (fieldElement == null) { + fieldElement = Elements.fieldFromNode(fieldNode, currentHolder, fieldNode.getModifiers()); + Elements.addField(currentHolder, fieldElement); + } + + if (accessorNode.getModifiers().isGetter()) { + if (fieldElement.getGetter() != null) { + resolutionError(fieldNode, DartCompilerErrorCode.FIELD_CONFLICTS, name, "getter"); + } else { + fieldElement.setGetter(accessorElement); + fieldElement.setType(accessorElement.getReturnType()); + } + } else if (accessorNode.getModifiers().isSetter()) { + if (fieldElement.getSetter() != null) { + resolutionError(fieldNode, DartCompilerErrorCode.FIELD_CONFLICTS, name, "setter"); + } else { + fieldElement.setSetter(accessorElement); + List parameters = accessorElement.getParameters(); + Type type; + if (parameters.size() != 1) { + resolutionError(fieldNode, DartCompilerErrorCode.EXPECTED_ONE_ARGUMENT); + type = getTypeProvider().getDynamicType(); + } else { + type = parameters.get(0).getType(); + } + fieldElement.setType(type); + } + } + return recordElement(fieldNode, fieldElement); + } + + private void addField(EnclosingElement holder, FieldElement element) { + checkUniqueName(holder, element); + Elements.addField(holder, element); + } + + private void addMethod(EnclosingElement holder, MethodElement element) { + checkUniqueName(holder, element); + Elements.addMethod(holder, element); + } + + private void addConstructor(ClassElement cls, MethodElement element) { + checkUniqueName(cls, element); + Elements.addConstructor(cls, (ConstructorElement) element); + } + + private ElementKind getMethodKind(DartMethodDefinition method) { + if (!ElementKind.of(currentHolder).equals(ElementKind.CLASS)) { + return ElementKind.METHOD; + } + + if (method.getModifiers().isFactory()) { + return ElementKind.CONSTRUCTOR; + } + + DartExpression name = method.getName(); + if (name instanceof DartIdentifier) { + if (((DartIdentifier) name).getTargetName().equals(currentHolder.getName())) { + return ElementKind.CONSTRUCTOR; + } else { + return ElementKind.METHOD; + } + } else { + DartPropertyAccess property = (DartPropertyAccess) name; + DartIdentifier qualifier = (DartIdentifier) property.getQualifier(); + if (qualifier.getTargetName().equals(currentHolder.getName())) { + return ElementKind.CONSTRUCTOR; + } else { + resolutionError(method.getName(), + DartCompilerErrorCode.CANNOT_DECLARE_NON_FACTORY_CONSTRUCTOR); + } + } + + return ElementKind.NONE; + } + + private void checkModifiers(MethodElement element, DartMethodDefinition method) { + Modifiers modifiers = method.getModifiers(); + boolean isNonFactoryConstructor = Elements.isNonFactoryConstructor(element); + // TODO(ngeoffray): The errors should report the position of the modifier. + if (isNonFactoryConstructor) { + if (modifiers.isStatic()) { + resolutionError(method.getName(), DartCompilerErrorCode.CONSTRUCTOR_CANNOT_BE_STATIC); + } + if (modifiers.isAbstract()) { + resolutionError(method.getName(), DartCompilerErrorCode.CONSTRUCTOR_CANNOT_BE_ABSTRACT); + } + // TODO(ngeoffray): This is already checked in the parser. + // Like operators/getters/setters. Should we all check them here? + if (modifiers.isConstant() && method.getFunction().getBody() != null) { + resolutionError(method.getName(), + DartCompilerErrorCode.CONST_CONSTRUCTOR_CANNOT_HAVE_BODY); + } + } + + if (modifiers.isFactory()) { + if (modifiers.isStatic()) { + resolutionError(method.getName(), DartCompilerErrorCode.FACTORY_CANNOT_BE_STATIC); + } + if (modifiers.isAbstract()) { + resolutionError(method.getName(), DartCompilerErrorCode.FACTORY_CANNOT_BE_ABSTRACT); + } + // TODO(ngeoffray): This is already checked in the parser. + // Like operators/getters/setters. Should we all check them here? + if (modifiers.isConstant()) { + resolutionError(method.getName(), DartCompilerErrorCode.FACTORY_CANNOT_BE_CONST); + } + } + // TODO(ngeoffray): Add more checks on the modifiers. For + // example const and missing body. + } + + private void checkUniqueName(EnclosingElement holder, Element e) { + Element other = lookupElementByName(holder, e.getName(), e.getModifiers()); + assert e != other : "forgot to call checkUniqueName() before adding to the class?"; + if (other != null) { + ElementKind eKind = ElementKind.of(e); + ElementKind oKind = ElementKind.of(other); + + // Constructors have a separate namespace. + boolean oIsConstructor = oKind.equals(ElementKind.CONSTRUCTOR); + boolean eIsConstructor = eKind.equals(ElementKind.CONSTRUCTOR); + if (oIsConstructor != eIsConstructor) { + return; + } + + boolean eIsOperator = e.getModifiers().isOperator(); + boolean oIsOperator = other.getModifiers().isOperator(); + if (oIsOperator != eIsOperator) { + return; + } + + // Operators and methods can share the same name. + boolean oIsMethod = oKind.equals(ElementKind.METHOD); + boolean eIsMethod = eKind.equals(ElementKind.METHOD); + if ((oIsOperator && eIsMethod) || (oIsMethod && eIsOperator)) { + return; + } + + resolutionError(e.getNode(), DartCompilerErrorCode.NAME_CLASSES_EXISTING_MEMBER); + } + } + + private Element lookupElementByName(EnclosingElement holder, String name, Modifiers modifiers) { + Element element = holder.lookupLocalElement(name); + if (element == null && ElementKind.of(holder).equals(ElementKind.CLASS)) { + ClassElement cls = (ClassElement) holder; + String ctorName = name.equals(holder.getName()) ? "" : name; + for (Element e : cls.getConstructors()) { + if (e.getName().equals(ctorName)) { + return e; + } + } + } + return element; + } + + void resolutionError(DartNode node, ErrorCode errorCode, Object... arguments) { + topLevelContext.resolutionError(node, errorCode, arguments); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/MethodElement.java b/compiler/java/com/google/dart/compiler/resolver/MethodElement.java new file mode 100644 index 00000000000..544280f85f2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/MethodElement.java @@ -0,0 +1,22 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.Type; + +import java.util.List; + +public interface MethodElement extends Element { + boolean isConstructor(); + + boolean isStatic(); + + List getParameters(); + + Type getReturnType(); + + FunctionType getFunctionType(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/MethodElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/MethodElementImplementation.java new file mode 100644 index 00000000000..a52d6348f57 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/MethodElementImplementation.java @@ -0,0 +1,128 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.common.annotations.VisibleForTesting; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.Type; + +import java.util.ArrayList; +import java.util.List; + +class MethodElementImplementation extends AbstractElement implements MethodElement { + private final Modifiers modifiers; + private final EnclosingElement holder; + private final ElementKind kind; + private final List parameters = new ArrayList(); + private FunctionType type; + + // TODO(ngeoffray): name, return type, argument types. + @VisibleForTesting + MethodElementImplementation(DartFunctionExpression node, String name, Modifiers modifiers) { + super(node, name); + this.modifiers = modifiers; + this.holder = null; + this.kind = ElementKind.FUNCTION_OBJECT; + } + + protected MethodElementImplementation(DartMethodDefinition node, String name, + EnclosingElement holder) { + super(node, name); + // TODO(jgw): Pass in modifiers directly, not referencing node. + if (node != null) { + modifiers = node.getModifiers(); + } else { + modifiers = Modifiers.NONE; + } + this.holder = holder; + this.kind = ElementKind.METHOD; + } + + protected MethodElementImplementation(String name, EnclosingElement holder, + Modifiers modifiers) { + super(null, name); + this.modifiers = modifiers; + this.holder = holder; + this.kind = ElementKind.METHOD; + } + + private MethodElementImplementation(DartParameter node) { + super(node, ""); + this.holder = null; + this.kind = ElementKind.FUNCTION_OBJECT; + this.modifiers = Modifiers.NONE; + } + + @Override + public Modifiers getModifiers() { + return modifiers; + } + + @Override + public ElementKind getKind() { + return kind; + } + + @Override + public EnclosingElement getEnclosingElement() { + return holder; + } + + @Override + public boolean isConstructor() { + return false; + } + + @Override + public boolean isStatic() { + return getModifiers().isStatic(); + } + + @Override + public List getParameters() { + return parameters; + } + + void addParameter(VariableElement parameter) { + parameters.add(parameter); + } + + @Override + public Type getReturnType() { + return getType().getReturnType(); + } + + @Override + void setType(Type type) { + this.type = (FunctionType) type; + } + + @Override + public FunctionType getType() { + return type; + } + + @Override + public FunctionType getFunctionType() { + return getType(); + } + + public static MethodElementImplementation fromMethodNode(DartMethodDefinition node, + EnclosingElement holder) { + assert node.getName() instanceof DartIdentifier; + String targetName = ((DartIdentifier) node.getName()).getTargetName(); + return new MethodElementImplementation(node, targetName, holder); + } + + public static MethodElementImplementation fromFunctionExpression(DartFunctionExpression node, + Modifiers modifiers) { + return new MethodElementImplementation(node, node.getFunctionName(), modifiers); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java b/compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java new file mode 100644 index 00000000000..f490a72cddf --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ResolutionContext.java @@ -0,0 +1,295 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.common.annotations.VisibleForTesting; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeKind; +import com.google.dart.compiler.type.TypeVariable; + +import java.util.Arrays; +import java.util.List; + +/** + * Resolution context for resolution of Dart programs. The initial context is + * derived from the library scope, which is then extended with class scope, + * method scope, and block scope as the program is traversed. + */ +@VisibleForTesting +public class ResolutionContext implements ResolutionErrorListener { + private Scope scope; + private final DartCompilerContext context; + private final CoreTypeProvider typeProvider; + + ResolutionContext(String name, DartCompilerContext context, CoreTypeProvider typeProvider) { + this(new Scope(name), context, typeProvider); + } + + @VisibleForTesting + public ResolutionContext(Scope scope, DartCompilerContext context, + CoreTypeProvider typeProvider) { + this.scope = scope; + this.context = context; + this.typeProvider = typeProvider; + } + + ResolutionContext(LibraryUnit unit, DartCompilerContext context, CoreTypeProvider typeProvider) { + this(unit.getElement().getScope(), context, typeProvider); + } + + @VisibleForTesting + public ResolutionContext extend(ClassElement element) { + return new ResolutionContext(new ClassScope(element, scope), context, typeProvider); + } + + ResolutionContext extend(String name) { + return new ResolutionContext(new Scope(name, scope), context, typeProvider); + } + + Scope getScope() { + return scope; + } + + void declare(Element element) { + Element existingElement = scope.declareElement(element.getName(), element); + if (existingElement != null) { + resolutionError(element.getNode(), DartCompilerErrorCode.DUPLICATE_DEFINITION, + element.getName()); + } + } + + void pushScope(String name) { + scope = new Scope(name, scope); + } + + void popScope() { + scope = scope.getParent(); + } + + /** + * Returns true if the type is dynamic or an interface type where + * {@link ClassElement#isInterface()} equals isInterface. + */ + private boolean isInterfaceEquals(Type type, boolean isInterface) { + switch (type.getKind()) { + case DYNAMIC: + // Considered to be a match. + return true; + + case INTERFACE: + InterfaceType interfaceType = (InterfaceType) type; + ClassElement element = interfaceType.getElement(); + return (element != null && element.isInterface() == isInterface); + + default: + break; + } + + return false; + } + + /** + * Returns true if the type is dynamic or is a class type. + */ + private boolean isClassType(Type type) { + return isInterfaceEquals(type, false); + } + + /** + * Returns true if the type is a class or interface type. + */ + private boolean isClassOrInterfaceType(Type type) { + return type.getKind() == TypeKind.INTERFACE + && ((InterfaceType) type).getElement() != null; + } + + InterfaceType resolveClass(DartTypeNode node, boolean isStatic) { + if (node == null) { + return null; + } + + Type type = resolveType(node, isStatic); + if (!isClassType(type)) { + resolutionError(node.getIdentifier(), DartCompilerErrorCode.NOT_A_CLASS, type); + type = typeProvider.getDynamicType(); + } + + node.setType(type); + return (InterfaceType) type; + } + + InterfaceType resolveInterface(DartTypeNode node, boolean isStatic) { + if (node == null) { + return null; + } + + Type type = resolveType(node, isStatic); + if (!isClassOrInterfaceType(type)) { + resolutionError(node.getIdentifier(), DartCompilerErrorCode.NOT_A_CLASS_OR_INTERFACE, type); + type = typeProvider.getDynamicType(); + } + + node.setType(type); + return (InterfaceType) type; + } + + Type resolveType(DartTypeNode node, boolean isStatic) { + if (node == null) { + return null; + } else { + return resolveType(node, node.getIdentifier(), node.getTypeArguments(), isStatic); + } + } + + Type resolveType(DartNode diagnosticNode, DartNode identifier, List typeArguments, + boolean isStatic) { + Element element = resolveName(identifier); + switch (ElementKind.of(element)) { + case TYPE_VARIABLE: { + TypeVariableElement typeVariableElement = (TypeVariableElement) element; + if (isStatic && + typeVariableElement.getDeclaringElement().getKind().equals(ElementKind.CLASS)) { + resolutionError(identifier, DartCompilerErrorCode.TYPE_VARIABLE_IN_STATIC_CONTEXT, + identifier); + return typeProvider.getDynamicType(); + } + return makeTypeVariable(typeVariableElement, typeArguments); + } + case CLASS: + case FUNCTION_TYPE_ALIAS: + return instantiateParameterizedType((ClassElement) element, diagnosticNode, typeArguments, + isStatic); + case NONE: + if (identifier.toString().equals("void")) { + return typeProvider.getVoidType(); + } + break; + } + if (!allowNoSuchType()) { + resolutionError(identifier, DartCompilerErrorCode.NO_SUCH_TYPE, identifier); + } + return typeProvider.getDynamicType(); + } + + InterfaceType instantiateParameterizedType(ClassElement element, DartNode node, + List typeArgumentNodes, + boolean isStatic) { + List typeParameters = element.getTypeParameters(); + Type[] typeArguments; + if (typeArgumentNodes == null || typeArgumentNodes.size() != typeParameters.size()) { + typeArguments = new Type[typeParameters.size()]; + for (int i = 0; i < typeArguments.length; i++) { + typeArguments[i] = typeProvider.getDynamicType(); + } + if (typeArgumentNodes != null && typeArgumentNodes.size() > 0) { + typeError(node, DartCompilerErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, element.getType()); + } + int index = 0; + if (typeArgumentNodes != null) { + for (DartTypeNode typeNode : typeArgumentNodes) { + Type type = resolveType(typeNode, isStatic); + typeNode.setType(type); + if (index < typeArguments.length) { + typeArguments[index] = type; + } + index++; + } + } + } else { + typeArguments = new Type[typeArgumentNodes.size()]; + for (int i = 0; i < typeArguments.length; i++) { + typeArguments[i] = resolveType(typeArgumentNodes.get(i), isStatic); + typeArgumentNodes.get(i).setType(typeArguments[i]); + } + } + return element.getType().subst(Arrays.asList(typeArguments), typeParameters); + } + + private TypeVariable makeTypeVariable(TypeVariableElement element, + List typeArguments) { + for (DartTypeNode typeArgument : typeArguments) { + resolutionError(typeArgument, DartCompilerErrorCode.EXTRA_TYPE_ARGUMENT); + } + return element.getTypeVariable(); + } + + Element resolveName(DartNode node) { + return node.accept(new Selector()); + } + + MethodElement declareFunction(DartFunctionExpression node) { + MethodElement element = Elements.methodFromFunctionExpression(node, Modifiers.NONE); + if (node.getFunctionName() != null) { + declare(element); + } + return element; + } + + void pushFunctionScope(DartFunctionExpression x) { + pushScope(x.getFunctionName() == null ? "" : x.getFunctionName()); + } + + AssertionError internalError(DartNode node, String message, Object... arguments) { + message = String.format(message, arguments); + context.compilationError(new DartCompilationError(node, DartCompilerErrorCode.INTERNAL_ERROR, + message)); + return new AssertionError("Internal error: " + message); + } + + @Override + public void resolutionError(DartNode node, ErrorCode errorCode, Object... arguments) { + context.compilationError(new DartCompilationError(node, errorCode, arguments)); + } + + @Override + public void typeError(DartNode node, ErrorCode errorCode, Object... arguments) { + context.typeError(new DartCompilationError(node, errorCode, arguments)); + } + + public boolean allowNoSuchType() { + return context.allowNoSuchType(); + } + + class Selector extends DartNodeTraverser { + @Override + public Element visitNode(DartNode node) { + throw internalError(node, "Unexpected node: %s", node); + } + + @Override + public Element visitPropertyAccess(DartPropertyAccess node) { + Element element = node.getQualifier().accept(this); + switch (element.getKind()) { + case LIBRARY: + return ((LibraryElement) element).getScope().findElement(node.getPropertyName()); + + case CLASS: + return Elements.findElement((ClassElement) element, node.getPropertyName()); + + default: + return null; + } + } + + @Override + public Element visitIdentifier(DartIdentifier node) { + String name = node.getTargetName(); + return scope.findElement(name); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ResolutionErrorListener.java b/compiler/java/com/google/dart/compiler/resolver/ResolutionErrorListener.java new file mode 100644 index 00000000000..d92e36a5f58 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ResolutionErrorListener.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.ast.DartNode; + +public interface ResolutionErrorListener { + + void resolutionError(DartNode node, ErrorCode errorCode, Object... arguments); + + void typeError(DartNode node, ErrorCode errorCode, Object... arguments); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/ResolveVisitor.java b/compiler/java/com/google/dart/compiler/resolver/ResolveVisitor.java new file mode 100644 index 00000000000..a9a1f08cfa6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/ResolveVisitor.java @@ -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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.type.DynamicType; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeVariable; +import com.google.dart.compiler.type.Types; + +import java.util.ArrayList; +import java.util.List; + +/** + * Shared visitor between Resolver and MemberBuilder. + */ +abstract class ResolveVisitor extends DartNodeTraverser { + private final CoreTypeProvider typeProvider; + + ResolveVisitor(CoreTypeProvider typeProvider) { + this.typeProvider = typeProvider; + } + + abstract ResolutionContext getContext(); + + final MethodElement resolveFunction(DartFunction node, MethodElement element, + List typeVariables) { + if (typeVariables != null) { + for (TypeVariable typeParameter : typeVariables) { + TypeVariableElement variable = (TypeVariableElement) typeParameter.getElement(); + getContext().getScope().declareElement(variable.getName(), variable); + DartTypeParameter typeParameterNode = (DartTypeParameter) variable.getNode(); + DartTypeNode boundNode = typeParameterNode.getBound(); + Type bound; + if (boundNode != null) { + bound = getContext().resolveType(boundNode, true); + boundNode.setType(bound); + } else { + bound = typeProvider.getObjectType(); + } + variable.setBound(bound); + } + } + for (DartParameter parameter : node.getParams()) { + Elements.addParameter(element, (VariableElement) parameter.accept(this)); + } + Type returnType = resolveType(node.getReturnTypeNode(), element.getModifiers().isStatic()); + ClassElement functionElement = typeProvider.getFunctionType().getElement(); + FunctionType type = Types.makeFunctionType(getContext(), functionElement, + element.getParameters(), returnType, + typeVariables); + Elements.setType(element, type); + return element; + } + + abstract boolean isStaticContext(); + + @Override + public Element visitParameter(DartParameter node) { + Type type = resolveType(node.getTypeNode(), isStaticContext()); + if (node.getModifiers().isVariadic()) { + type = typeProvider.getArrayType(type); + } + VariableElement element = Elements.parameterElement(node, node.getParameterName(), + node.getModifiers()); + List functionParameters = node.getFunctionParameters(); + if (functionParameters != null) { + List parameterElements = + new ArrayList(functionParameters.size()); + for (DartParameter parameter: functionParameters) { + parameterElements.add((VariableElement) parameter.accept(this)); + } + ClassElement functionElement = typeProvider.getFunctionType().getElement(); + type = Types.makeFunctionType(getContext(), functionElement, parameterElements, type, null); + } + Elements.setType(element, type); + return recordElement(node, element); + } + + final Type resolveType(DartTypeNode node, boolean isStatic) { + if (node == null) { + return getTypeProvider().getDynamicType(); + } + assert node.getType() == null || node.getType() instanceof DynamicType; + Type type = getContext().resolveType(node, isStatic); + if (type == null) { + type = getTypeProvider().getDynamicType(); + } + node.setType(type); + recordElement(node.getIdentifier(), type.getElement()); + return type; + } + + protected E recordElement(DartNode node, E element) { + node.getClass(); + if (element == null) { + // TypeAnalyzer will diagnose unresolved identifiers. + return null; + } + node.setSymbol(element); + return element; + } + + CoreTypeProvider getTypeProvider() { + return typeProvider; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/Resolver.java b/compiler/java/com/google/dart/compiler/resolver/Resolver.java new file mode 100644 index 00000000000..96248ef0ac6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/Resolver.java @@ -0,0 +1,1268 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Sets; +import com.google.dart.compiler.DartCompilationPhase; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartBinaryExpression; +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.DartCatchBlock; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartDoWhileStatement; +import com.google.dart.compiler.ast.DartDoubleLiteral; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartGotoStatement; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIfStatement; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartInvocation; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartLiteral; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNamedExpression; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartStringInterpolation; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartSuperConstructorInvocation; +import com.google.dart.compiler.ast.DartSuperExpression; +import com.google.dart.compiler.ast.DartSwitchMember; +import com.google.dart.compiler.ast.DartSwitchStatement; +import com.google.dart.compiler.ast.DartThisExpression; +import com.google.dart.compiler.ast.DartTryStatement; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypedLiteral; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartUnqualifiedInvocation; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.DartVariableStatement; +import com.google.dart.compiler.ast.DartWhileStatement; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.InterfaceType.Member; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeVariable; + +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Resolves unqualified symbols in a compilation unit. + */ +public class Resolver { + + private final ResolutionContext topLevelContext; + private final CoreTypeProvider typeProvider; + private final InterfaceType rawArrayType; + private final InterfaceType defaultLiteralMapType; + + + private static final EnumSet INVOKABLE_ELEMENTS = EnumSet.of( + ElementKind.FIELD, + ElementKind.PARAMETER, + ElementKind.VARIABLE, + ElementKind.FUNCTION_OBJECT, + ElementKind.METHOD); + + @VisibleForTesting + public Resolver(DartCompilerContext compilerContext, Scope libraryScope, + CoreTypeProvider typeProvider) { + compilerContext.getClass(); // Fast null-check. + libraryScope.getClass(); // Fast null-check. + typeProvider.getClass(); // Fast null-check. + this.topLevelContext = new ResolutionContext(libraryScope, compilerContext, typeProvider); + this.typeProvider = typeProvider; + Type dynamicType = typeProvider.getDynamicType(); + Type stringType = typeProvider.getStringType(); + this.defaultLiteralMapType = typeProvider.getMapType(stringType, dynamicType); + this.rawArrayType = typeProvider.getArrayType(dynamicType); + } + + @VisibleForTesting + public DartUnit exec(DartUnit unit) { + // Visits all top level elements of a compilation unit and resolves names used in method + // bodies. + LibraryElement library = unit.getLibrary() != null ? unit.getLibrary().getElement() : null; + unit.accept(new ResolveElementsVisitor(topLevelContext, library)); + return unit; + } + + /** + * Main entry point for IDE. Resolves a member (method or field) + * incrementally in the given context. + * + * @param classElement the class enclosing the member. + * @param member the member to resolve. + * @param context a resolution context corresponding to classElement. + */ + public void resolveMember(ClassElement classElement, Element member, ResolutionContext context) { + ResolveElementsVisitor visitor; + switch (member.getKind()) { + case CONSTRUCTOR: + case METHOD: + ResolutionContext methodContext = context.extend(member.getName()); + visitor = new ResolveElementsVisitor(methodContext, classElement, + (MethodElement) member); + break; + + case FIELD: + ResolutionContext fieldContext = context; + if (member.getModifiers().isAbstractField()) { + fieldContext = context.extend(member.getName()); + } + visitor = new ResolveElementsVisitor(fieldContext, classElement); + break; + + default: + throw topLevelContext.internalError(member.getNode(), + "unexpected element kind: %s", member.getKind()); + } + member.getNode().accept(visitor); + } + + /** + * Resolves names in a method body. + * + * TODO(ngeoffray): Errors reported: + * - A default implementation not providing the default methods. + * - An interface with default methods but without a default implementation. + * - A member method shadowing a super property. + * - A member property shadowing a super method. + * - A formal parameter in a non-constructor shadowing a member. + * - A local variable shadowing another variable. + * - A local variable shadowing a formal parameter. + * - A local variable shadowing a class member. + * - Using 'this' or 'super' in a static or factory method, or in an initializer. + * - Using 'super' in a class without a super class. + * - Incorrectly using a resolved element. + */ + @VisibleForTesting + public class ResolveElementsVisitor extends ResolveVisitor { + private EnclosingElement currentHolder; + private MethodElement currentMethod; + private boolean inInitializer; + private MethodElement innermostFunction; + private ResolutionContext context; + private LabelElement currentLabel; + private Set referencedLabels = Sets.newHashSet(); + private Set labelsInScopes = Sets.newHashSet(); + + @VisibleForTesting + public ResolveElementsVisitor(ResolutionContext context, + EnclosingElement currentHolder, + MethodElement currentMethod) { + super(typeProvider); + this.context = context; + this.currentMethod = currentMethod; + this.innermostFunction = currentMethod; + this.currentHolder = currentHolder; + this.inInitializer = false; + } + + private ResolveElementsVisitor(ResolutionContext context, EnclosingElement currentHolder) { + this(context, currentHolder, null); + } + + @Override + ResolutionContext getContext() { + return context; + } + + @Override + public Element visitUnit(DartUnit unit) { + for (DartNode node : unit.getTopLevelNodes()) { + node.accept(this); + } + return null; + } + + @Override + public Element visitFunctionTypeAlias(DartFunctionTypeAlias alias) { + return null; + } + + @Override + public Element visitClass(DartClass cls) { + assert currentMethod == null : "nested class?"; + ClassElement classElement = cls.getSymbol(); + try { + classElement.getAllSupertypes(); + } catch (CyclicDeclarationException e) { + DartNode node = e.getElement().getNode(); + if (node == null) { + node = cls; + } + resolutionError(node, DartCompilerErrorCode.CYCLIC_CLASS, e.getElement().getName()); + } catch (DuplicatedInterfaceException e) { + resolutionError(cls, DartCompilerErrorCode.DUPLICATED_INTERFACE, + e.getFirst(), e.getSecond()); + } + ResolutionContext previousContext = context; + EnclosingElement previousHolder = currentHolder; + currentHolder = classElement; + context = topLevelContext.extend(classElement); + + for (Element element : classElement.getMembers()) { + element.getNode().accept(this); + } + + for (Element element : classElement.getConstructors()) { + element.getNode().accept(this); + } + + checkRedirectConstructorCycle(classElement.getConstructors(), context); + context = previousContext; + currentHolder = previousHolder; + return classElement; + } + + private Element resolve(DartNode node) { + if (node == null) { + return null; + } else { + return node.accept(this); + } + } + + @Override + public MethodElement visitMethodDefinition(DartMethodDefinition node) { + MethodElement member = node.getSymbol(); + ResolutionContext previousContext = context; + context = context.extend(member.getName()); + assert currentMethod == null : "Nested methods?"; + innermostFunction = currentMethod = member; + + DartFunction functionNode = node.getFunction(); + List parameters = functionNode.getParams(); + + FunctionType type = (FunctionType) member.getType(); + for (TypeVariable typeVariable : type.getTypeVariables()) { + context.declare(typeVariable.getElement()); + } + + // First declare all normal parameters in the scope, putting them in the + // scope of the default expressions so we can report better errors. + for (DartParameter parameter : parameters) { + assert parameter.getSymbol() != null; + if (parameter.getQualifier() instanceof DartThisExpression) { + checkParameterInitializer(node, parameter); + } else { + getContext().declare(parameter.getSymbol()); + } + } + for (DartParameter parameter : parameters) { + // Then resolve the default values. + resolveConstantExpression(parameter.getDefaultExpr()); + } + + if ((functionNode.getBody() == null) + && !Elements.isNonFactoryConstructor(member) + && !member.getModifiers().isAbstract() + && !member.getEnclosingElement().isInterface()) { + resolutionError(functionNode, DartCompilerErrorCode.METHOD_MUST_HAVE_BODY); + } + resolve(functionNode.getBody()); + + if (Elements.isNonFactoryConstructor(member)) { + resolveInitializers(node); + } + + context = previousContext; + innermostFunction = currentMethod = null; + return member; + } + + private void resolveConstantExpression(DartExpression defaultExpr) { + resolve(defaultExpr); + checkConstantExpression(defaultExpr); + } + + private void checkConstantExpression(DartExpression defaultExpr) { + // See bug 4568007. + } + + private void checkConstantLiteral(DartExpression defaultExpr) { + if ((!(defaultExpr instanceof DartLiteral)) + || ((defaultExpr instanceof DartTypedLiteral) + && (!((DartTypedLiteral) defaultExpr).isConst()))) { + resolutionError(defaultExpr, DartCompilerErrorCode.EXPECTED_CONSTANT_LITERAL); + } + } + + @Override + public Element visitField(DartField node) { + DartExpression expression = node.getValue(); + Modifiers modifiers = node.getModifiers(); + boolean isStatic = modifiers.isStatic(); + boolean isFinal = modifiers.isFinal(); + boolean isTopLevel = ElementKind.of(currentHolder).equals(ElementKind.LIBRARY); + + if (expression != null) { + if (isStatic || isTopLevel) { + checkConstantExpression(expression); + } else { + // TODO(5200401): Only allow constant literals for inline field initializers for now. + checkConstantLiteral(expression); + } + resolve(expression); + } else if (isStatic && isFinal) { + resolutionError(node, DartCompilerErrorCode.STATIC_FINAL_REQUIRES_VALUE); + } + + // If field is an acessor, both getter and setter need to be visited (if present). + FieldElement field = node.getSymbol(); + if (field.getGetter() != null) { + resolve(field.getGetter().getNode()); + } + if (field.getSetter() != null) { + resolve(field.getSetter().getNode()); + } + return null; + } + + @Override + public Element visitFieldDefinition(DartFieldDefinition node) { + visit(node.getFields()); + return null; + } + + @Override + public Element visitFunction(DartFunction node) { + throw context.internalError(node, "should not be called."); + } + + @Override + public Element visitParameter(DartParameter x) { + Element element = super.visitParameter(x); + resolveConstantExpression(x.getDefaultExpr()); + getContext().declare(element); + return element; + } + + public Element resolveVariable(DartVariable x, Modifiers modifiers) { + // Visit the initializer first. + resolve(x.getValue()); + VariableElement element = Elements.variableElement(x, x.getVariableName(), modifiers); + getContext().declare(recordElement(x, element)); + return element; + } + + @Override + public Element visitVariableStatement(DartVariableStatement node) { + resolveVariableStatement(node, false); + return null; + } + + private void resolveVariableStatement(DartVariableStatement node, + boolean isImplicitlyInitialized) { + Type type = resolveType(node.getTypeNode(), inStaticContext(currentMethod)); + for (DartVariable variable : node.getVariables()) { + Elements.setType(resolveVariable(variable, node.getModifiers()), type); + checkVariableStatement(node, variable, isImplicitlyInitialized); + } + } + + @Override + public Element visitLabel(DartLabel x) { + LabelElement previousLabel = currentLabel; + currentLabel = Elements.labelElement(x, x.getName(), innermostFunction); + recordElement(x, currentLabel); + x.visitChildren(this); + if (!labelsInScopes.contains(currentLabel)) { + // TODO(zundel): warning, not type error. + // topLevelContext.typeError(x, DartCompilerErrorCode.USELESS_LABEL, x.getName()); + } else if (!referencedLabels.contains(currentLabel)) { + // TODO(zundel): warning, not type error. + // topLevelContext.typeError(x, DartCompilerErrorCode.UNREFERENCED_LABEL, x.getName()); + } + currentLabel = previousLabel; + return null; + } + + @Override + public Element visitFunctionExpression(DartFunctionExpression x) { + MethodElement element; + if (x.isStatement()) { + // Function statement names live in the outer scope. + element = getContext().declareFunction(x); + getContext().pushFunctionScope(x); + } else { + // Function expression names live in their own scope. + getContext().pushFunctionScope(x); + element = getContext().declareFunction(x); + } + MethodElement previousFunction = innermostFunction; + innermostFunction = element; + DartFunction functionNode = x.getFunction(); + resolveFunction(functionNode, element, null); + resolve(functionNode.getBody()); + innermostFunction = previousFunction; + getContext().popScope(); + return recordElement(x, element); + } + + @Override + public Element visitBlock(DartBlock x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitBreakStatement(DartBreakStatement x) { + // Handle corner case of L: break L; + DartNode parent = x.getParent(); + if (parent instanceof DartLabel && x.getLabel() != null) { + if (((DartLabel) parent).getLabel().getTargetName().equals(x.getLabel().getTargetName())) { + getContext().pushScope(""); + addLabelToStatement(x); + visitGotoStatement(x); + getContext().popScope(); + return null; + } + } + return visitGotoStatement(x); + } + + @Override + public Element visitTryStatement(DartTryStatement x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitCatchBlock(DartCatchBlock x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitDoWhileStatement(DartDoWhileStatement x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitWhileStatement(DartWhileStatement x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitIfStatement(DartIfStatement x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitForInStatement(DartForInStatement x) { + getContext().pushScope(""); + addLabelToStatement(x); + + if (x.introducesVariable()) { + resolveVariableStatement(x.getVariableStatement(), true); + } else { + x.getIdentifier().accept(this); + } + x.getIterable().accept(this); + x.getBody().accept(this); + getContext().popScope(); + return null; + } + + private void addLabelToStatement(DartStatement x) { + if (currentLabel != null) { + DartNode parent = x.getParent(); + if (parent instanceof DartLabel) { + getContext().getScope().setLabel(currentLabel); + labelsInScopes.add(currentLabel); + } + } + } + + @Override + public Element visitForStatement(DartForStatement x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + + @Override + public Element visitSwitchStatement(DartSwitchStatement x) { + getContext().pushScope(""); + addLabelToStatement(x); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitSwitchMember(DartSwitchMember x) { + getContext().pushScope(""); + x.visitChildren(this); + getContext().popScope(); + return null; + } + + @Override + public Element visitThisExpression(DartThisExpression x) { + if (currentMethod.getModifiers().isStatic()) { + resolutionError(x, DartCompilerErrorCode.STATIC_METHOD_ACCESS_THIS); + } else if (ElementKind.of(currentHolder).equals(ElementKind.LIBRARY)) { + resolutionError(x, DartCompilerErrorCode.TOP_LEVEL_METHOD_ACCESS_THIS); + } + return null; + } + + @Override + public Element visitSuperExpression(DartSuperExpression x) { + if (ElementKind.of(currentHolder).equals(ElementKind.LIBRARY)) { + resolutionError(x, DartCompilerErrorCode.TOP_LEVEL_METHOD_ACCESS_SUPER); + } else if (currentMethod == null) { + resolutionError(x, DartCompilerErrorCode.SUPER_OUTSIDE_OF_METHOD); + } else if (currentMethod.getModifiers().isStatic()) { + resolutionError(x, DartCompilerErrorCode.STATIC_METHOD_ACCESS_SUPER); + } else if (currentMethod.getModifiers().isFactory()) { + resolutionError(x, DartCompilerErrorCode.FACTORY_ACCESS_SUPER); + } else { + return recordElement(x, Elements.superElement( + x, ((ClassElement) currentHolder).getSupertype().getElement())); + } + return null; + } + + @Override + public Element visitSuperConstructorInvocation(DartSuperConstructorInvocation x) { + visit(x.getArgs()); + String name = x.getName() == null ? "" : x.getName().getTargetName(); + InterfaceType supertype = ((ClassElement) currentHolder).getSupertype(); + ConstructorElement element = (supertype == null) ? + null : Elements.lookupConstructor(supertype.getElement(), name); + if (element == null) { + resolutionError(x, DartCompilerErrorCode.CANNOT_RESOLVE_SUPER_CONSTRUCTOR, name); + } + return recordElement(x, element); + } + + @Override + public Element visitNamedExpression(DartNamedExpression node) { + // Intentionally skip the expression's name -- it's stored as an identifier, but doesn't need + // to be resolved. + return node.getExpression().accept(this); + } + + @Override + public Element visitIdentifier(DartIdentifier x) { + return resolveIdentifier(x, false); + } + + private Element resolveIdentifier(DartIdentifier x, boolean isQualifier) { + Element element = getContext().getScope().findElement(x.getTargetName()); + if (element == null) { + if (isStaticContextOrInitializer()) { + if (!context.allowNoSuchType()) { + resolutionError(x, DartCompilerErrorCode.CANNOT_BE_RESOLVED, x.getTargetName()); + } + } + } else { + switch (element.getKind()) { + case FIELD: + if (inStaticContext(currentMethod) && !inStaticContext(element)) { + resolutionError(x, DartCompilerErrorCode.ILLEGAL_FIELD_ACCESS_FROM_STATIC, + x.getTargetName()); + } + break; + case METHOD: + if (inStaticContext(currentMethod) && !inStaticContext(element)) { + resolutionError(x, DartCompilerErrorCode.ILLEGAL_METHOD_ACCESS_FROM_STATIC, + x.getTargetName()); + } + break; + case CLASS: + if (!isQualifier) { + resolutionError(x, DartCompilerErrorCode.IS_A_CLASS, x.getTargetName()); + } + break; + + default: + break; + } + } + + if (inInitializer && (element != null && element.getKind().equals(ElementKind.FIELD))) { + if (!element.getModifiers().isStatic() && !Elements.isTopLevel(element)) { + resolutionError(x, DartCompilerErrorCode.CANNOT_ACCESS_FIELD_IN_INIT); + } + } + + // If we we haven't resolved the identifier, it will be normalized to + // this.. + + return recordElement(x, element); + } + + @Override + public Element visitTypeNode(DartTypeNode x) { + return resolveType(x, inStaticContext(currentMethod)).getElement(); + } + + @Override + public Element visitPropertyAccess(DartPropertyAccess x) { + Element qualifier = resolveQualifier(x.getQualifier()); + Element element = null; + switch (ElementKind.of(qualifier)) { + case CLASS: + // Must be a static field. + element = Elements.findElement(((ClassElement) qualifier), x.getPropertyName()); + switch (ElementKind.of(element)) { + case FIELD: + FieldElement field = (FieldElement) element; + if (!field.getModifiers().isStatic()) { + resolutionError(x.getName(), DartCompilerErrorCode.NOT_A_STATIC_FIELD, + x.getPropertyName()); + } + break; + + case NONE: + resolutionError(x.getName(), DartCompilerErrorCode.CANNOT_BE_RESOLVED, + x.getPropertyName()); + break; + + case METHOD: + MethodElement method = (MethodElement) element; + if (!method.getModifiers().isStatic()) { + resolutionError(x.getName(), DartCompilerErrorCode.NOT_A_STATIC_METHOD, + x.getPropertyName()); + } + break; + + default: + resolutionError(x.getName(), DartCompilerErrorCode.EXPECTED_STATIC_FIELD, + element.getKind()); + break; + } + break; + + case SUPER: + ClassElement cls = ((SuperElement) qualifier).getClassElement(); + Member member = cls.getType().lookupMember(x.getPropertyName()); + if (member != null) { + element = member.getElement(); + } + switch (ElementKind.of(element)) { + case FIELD: + FieldElement field = (FieldElement) element; + if (field.getModifiers().isStatic()) { + resolutionError(x.getName(), DartCompilerErrorCode.NOT_AN_INSTANCE_FIELD, + x.getPropertyName()); + } + break; + case METHOD: + MethodElement method = (MethodElement) element; + if (method.isStatic()) { + resolutionError(x.getName(), DartCompilerErrorCode.NOT_AN_INSTANCE_FIELD, + x.getPropertyName()); + } + break; + + case NONE: + resolutionError(x.getName(), DartCompilerErrorCode.CANNOT_BE_RESOLVED, + x.getPropertyName()); + break; + + default: + resolutionError(x.getName(), + DartCompilerErrorCode.EXPECTED_AN_INSTANCE_FIELD_IN_SUPER_CLASS, + element.getKind()); + break; + } + break; + + case LIBRARY: + // Library prefix, lookup the element in the reference library. + element = ((LibraryElement) qualifier).getScope().findElement(x.getPropertyName()); + if (element == null) { + resolutionError(x, DartCompilerErrorCode.CANNOT_BE_RESOLVED_LIBRARY, + x.getPropertyName(), qualifier.getName()); + } + break; + + default: + break; + } + return recordElement(x, element); + } + + private Element resolveQualifier(DartNode qualifier) { + return (qualifier instanceof DartIdentifier) + ? resolveIdentifier((DartIdentifier) qualifier, true) + : qualifier.accept(this); + } + + @Override + public Element visitMethodInvocation(DartMethodInvocation x) { + Element target = resolveQualifier(x.getTarget()); + Element element = null; + + switch (ElementKind.of(target)) { + case CLASS: { + // Must be a static method or field. + ClassElement classElement = (ClassElement) target; + element = Elements.lookupLocalMethod(classElement, x.getFunctionNameString()); + if (element == null) { + element = Elements.lookupLocalField(classElement, x.getFunctionNameString()); + } + if (element == null || !element.getModifiers().isStatic()) { + diagnoseErrorInMethodInvocation(x, (ClassElement) target, element); + } + break; + } + + case SUPER: { + // Must be a superclass' method or field. + ClassElement classElement = ((SuperElement) target).getClassElement(); + InterfaceType type = classElement.getType(); + Member member = type.lookupMember(x.getFunctionNameString()); + if (member != null) { + if (!member.getElement().getModifiers().isStatic()) { + element = member.getElement(); + } + } + break; + } + } + + checkInvocationTarget(x, currentMethod, target); + // TODO(ngeoffray): handle library prefix + visit(x.getArgs()); + return recordElement(x, element); + } + + @Override + public Element visitUnqualifiedInvocation(DartUnqualifiedInvocation x) { + Element element = getContext().getScope().findElement(x.getTarget().getTargetName()); + ElementKind kind = ElementKind.of(element); + if (!INVOKABLE_ELEMENTS.contains(kind)) { + diagnoseErrorInUnqualifiedInvocation(x); + } else { + checkInvocationTarget(x, currentMethod, element); + } + recordElement(x.getTarget(), element); + visit(x.getArgs()); + return null; + } + + @Override + public Element visitFunctionObjectInvocation(DartFunctionObjectInvocation x) { + x.getTarget().accept(this); + visit(x.getArgs()); + return null; + } + + @Override + public Element visitNewExpression(DartNewExpression x) { + this.visit(x.getArgs()); + + Element element = x.getConstructor().accept(getContext().new Selector() { + // Only 'new' expressions can have a type in a property access. + @Override public Element visitTypeNode(DartTypeNode type) { + return recordType(type, resolveType(type, inStaticContext(currentMethod))); + } + + @Override public Element visitPropertyAccess(DartPropertyAccess node) { + Element element = node.getQualifier().accept(this); + if (ElementKind.of(element).equals(ElementKind.CLASS)) { + return Elements.lookupConstructor(((ClassElement) element), node.getPropertyName()); + } else { + return null; + } + } + }); + + if (ElementKind.of(element).equals(ElementKind.CLASS)) { + // Just calling the unnamed constructor. + element = Elements.lookupConstructor(((ClassElement) element), ""); + } + + // If there is a default implementation, lookup the constructor in the + // default class. + ConstructorElement constructor = checkIsConstructor(x, element); + if (constructor != null + && constructor.getConstructorType().getDefaultClass() != null) { + ClassElement originalClass = constructor.getConstructorType(); + ClassElement defaultClass = + constructor.getConstructorType().getDefaultClass().getElement(); + element = Elements.lookupConstructor(defaultClass, originalClass, constructor.getName()); + if (element == null) { + // If the constructor hasn't been found, try the constructor of the default class. + // TODO(ngeoffray): check earlier if the default class implements the interface. + element = Elements.lookupConstructor(defaultClass, constructor.getName()); + } + // Will check that element is not null. + constructor = checkIsConstructor(x, element); + } + + return recordElement(x, constructor); + } + + @Override + public Element visitGotoStatement(DartGotoStatement x) { + // Don't bother unless there's a target. + if (x.getTargetName() != null) { + Element element = getContext().getScope().findLabel(x.getTargetName(), innermostFunction); + if (ElementKind.of(element).equals(ElementKind.LABEL)) { + LabelElement labelElement = (LabelElement) element; + MethodElement enclosingFunction = (labelElement).getEnclosingFunction(); + if (enclosingFunction == innermostFunction) { + referencedLabels.add(labelElement); + return recordElement(x, element); + } + } + diagnoseErrorInGotoStatement(x, element); + } + return null; + } + + public void diagnoseErrorInGotoStatement(DartGotoStatement x, Element element) { + if (element == null) { + resolutionError(x.getLabel(), DartCompilerErrorCode.CANNOT_RESOLVE_LABEL, + x.getTargetName()); + } else if (ElementKind.of(element).equals(ElementKind.LABEL)) { + resolutionError(x.getLabel(), DartCompilerErrorCode.CANNOT_ACCESS_OUTER_LABEL, + x.getTargetName()); + } else { + resolutionError(x.getLabel(), DartCompilerErrorCode.NOT_A_LABEL, x.getTargetName()); + } + } + + private void diagnoseErrorInMethodInvocation(DartMethodInvocation node, ClassElement klass, + Element element) { + String name = node.getFunctionNameString(); + ElementKind kind = ElementKind.of(element); + DartNode errorNode = node.getFunctionName(); + switch (kind) { + case NONE: + resolutionError(errorNode, DartCompilerErrorCode.CANNOT_RESOLVE_METHOD, name); + break; + + case CONSTRUCTOR: + resolutionError(errorNode, DartCompilerErrorCode.IS_A_CONSTRUCTOR, klass.getName(), + name); + break; + + case METHOD: { + assert !((MethodElement) element).getModifiers().isStatic(); + resolutionError(errorNode, DartCompilerErrorCode.IS_AN_INSTANCE_METHOD, + klass.getName(), name); + break; + } + + default: + throw context.internalError(errorNode, "Unexpected kind of element: %s", kind); + } + } + + private void diagnoseErrorInUnqualifiedInvocation(DartUnqualifiedInvocation node) { + String name = node.getTarget().getTargetName(); + Element element = getContext().getScope().findElement(name); + ElementKind kind = ElementKind.of(element); + switch (kind) { + case NONE: + if (isStaticContextOrInitializer()) { + resolutionError(node, DartCompilerErrorCode.CANNOT_RESOLVE_METHOD, name); + } + break; + + case CONSTRUCTOR: + resolutionError(node, DartCompilerErrorCode.DID_YOU_MEAN_NEW, name, "constructor"); + break; + + case CLASS: + resolutionError(node, DartCompilerErrorCode.DID_YOU_MEAN_NEW, name, "class"); + break; + + case TYPE_VARIABLE: + resolutionError(node, DartCompilerErrorCode.DID_YOU_MEAN_NEW, name, "type variable"); + break; + + case LABEL: + resolutionError(node, DartCompilerErrorCode.CANNOT_CALL_LABEL); + break; + + default: + throw context.internalError(node, "Unexpected kind of element: %s", kind); + } + } + + private void diagnoseErrorInInitializer(DartIdentifier x) { + String name = x.getTargetName(); + Element element = getContext().getScope().findElement(name); + ElementKind kind = ElementKind.of(element); + switch (kind) { + case NONE: + resolutionError(x, DartCompilerErrorCode.CANNOT_RESOLVE_FIELD, name); + break; + + case FIELD: + FieldElement field = (FieldElement) element; + if (field.isStatic()) { + resolutionError(x, DartCompilerErrorCode.CANNOT_INIT_STATIC_FIELD_IN_INITIALIZER); + } else { + resolutionError(x, DartCompilerErrorCode.CANNOT_INIT_FIELD_FROM_SUPERCLASS); + } + break; + + case METHOD: + resolutionError(x, DartCompilerErrorCode.EXPECTED_FIELD_NOT_METHOD, name); + break; + + case CLASS: + resolutionError(x, DartCompilerErrorCode.EXPECTED_FIELD_NOT_CLASS, name); + break; + + case PARAMETER: + resolutionError(x, DartCompilerErrorCode.EXPECTED_FIELD_NOT_PARAMETER, name); + break; + + case TYPE_VARIABLE: + resolutionError(x, DartCompilerErrorCode.EXPECTED_FIELD_NOT_TYPE_VAR, name); + break; + + case VARIABLE: + case LABEL: + default: + throw context.internalError(x, "Unexpected kind of element: %s", kind); + } + } + + @Override + public Element visitInitializer(DartInitializer x) { + if (x.getName() != null) { + // Make sure the identifier is a local instance field. + FieldElement element = Elements.lookupLocalField( + (ClassElement) currentHolder, x.getName().getTargetName()); + if (element == null || element.isStatic()) { + diagnoseErrorInInitializer(x.getName()); + } + recordElement(x.getName(), element); + } + + assert !inInitializer; + inInitializer = true; + Element element = x.getValue().accept(this); + inInitializer = false; + return element; + } + + @Override + public Element visitRedirectConstructorInvocation(DartRedirectConstructorInvocation x) { + visit(x.getArgs()); + String name = x.getName() != null ? x.getName().getTargetName() : ""; + ConstructorElement element = Elements.lookupConstructor((ClassElement) currentHolder, name); + if (element == null) { + resolutionError(x, DartCompilerErrorCode.CANNOT_RESOLVE_CONSTRUCTOR, name); + } + return recordElement(x, element); + } + + @Override + public Element visitIntegerLiteral(DartIntegerLiteral node) { + recordType(node, typeProvider.getIntType()); + return null; + } + + @Override + public Element visitDoubleLiteral(DartDoubleLiteral node) { + recordType(node, typeProvider.getDoubleType()); + return null; + } + + @Override + public Element visitBooleanLiteral(DartBooleanLiteral node) { + recordType(node, typeProvider.getBoolType()); + return null; + } + + @Override + public Element visitStringLiteral(DartStringLiteral node) { + recordType(node, typeProvider.getStringType()); + return null; + } + + @Override + public Element visitStringInterpolation(DartStringInterpolation node) { + node.visitChildren(this); + recordType(node, typeProvider.getStringType()); + return null; + } + + Element recordType(DartNode node, Type type) { + node.setType(type); + return type.getElement(); + } + + @Override + public Element visitBinaryExpression(DartBinaryExpression node) { + Element lhs = resolve(node.getArg1()); + resolve(node.getArg2()); + if (node.getOperator().isAssignmentOperator()) { + switch (ElementKind.of(lhs)) { + case FIELD: + case PARAMETER: + case VARIABLE: + if (lhs.getModifiers().isFinal()) { + topLevelContext.resolutionError(node, DartCompilerErrorCode.CANNOT_ASSIGN_TO_FINAL, lhs.getName()); + } + break; + } + } + return null; + } + + @Override + public Element visitMapLiteral(DartMapLiteral node) { + List typeArgs = node.getTypeArguments(); + InterfaceType type = topLevelContext.instantiateParameterizedType( + defaultLiteralMapType.getElement(), node, typeArgs, inStaticContext(currentMethod)); + // instantiateParametersType() will complain for wrong number of parameters (!=2) + recordType(node, type); + visit(node.getEntries()); + return null; + } + + @Override + public Element visitArrayLiteral(DartArrayLiteral node) { + List typeArgs = node.getTypeArguments(); + InterfaceType type = topLevelContext.instantiateParameterizedType(rawArrayType.getElement(), + node, typeArgs, inStaticContext(currentMethod)); + // instantiateParametersType() will complain for wrong number of parameters (!=1) + recordType(node, type); + visit(node.getExpressions()); + return null; + } + + private ConstructorElement checkIsConstructor(DartNewExpression source, Element element) { + if (!ElementKind.of(element).equals(ElementKind.CONSTRUCTOR)) { + if (!context.allowNoSuchType()) { + resolutionError(source.getConstructor(), + DartCompilerErrorCode.NEW_EXPRESSION_NOT_CONSTRUCTOR); + } + return null; + } + return (ConstructorElement) element; + } + + private void checkConstructor(DartMethodDefinition node, + ConstructorElement superCall, + boolean firstIsSuper) { + ClassElement currentClass = (ClassElement) currentHolder; + if ((superCall == null) + && !currentClass.isObject() + && !currentClass.isObjectChild()) { + resolutionError(node, DartCompilerErrorCode.CONSTRUCTOR_MUST_CALL_SUPER); + } else if (!firstIsSuper + && !currentClass.isObject() + && !currentClass.isObjectChild()) { + resolutionError(node, DartCompilerErrorCode.SUPER_CALL_MUST_BE_FIRST); + } else if ((superCall != null) + && node.getModifiers().isConstant() + && !superCall.getModifiers().isConstant()) { + resolutionError(node, + DartCompilerErrorCode.CONST_CONSTRUCTOR_MUST_CALL_CONST_SUPER); + } + } + + private void checkInvocationTarget(DartInvocation node, + MethodElement callSite, + Element target) { + if (callSite != null + && callSite.isStatic() + && ElementKind.of(target).equals(ElementKind.METHOD)) { + if (!target.getModifiers().isStatic() && !Elements.isTopLevel(target)) { + resolutionError(node, DartCompilerErrorCode.INSTANCE_METHOD_FROM_STATIC); + } + } + } + + private void checkVariableStatement(DartVariableStatement node, + DartVariable variable, + boolean isImplicitlyInitialized) { + if (node.getModifiers().isFinal()) { + if (!isImplicitlyInitialized && (variable.getValue() == null)) { + resolutionError(variable.getName(), DartCompilerErrorCode.CONSTANTS_MUST_BE_INITIALIZED); + } else if (isImplicitlyInitialized && (variable.getValue() != null)) { + resolutionError(variable.getName(), DartCompilerErrorCode.CANNOT_BE_INITIALIZED); + } else { + checkConstantExpression(variable.getValue()); + } + } + } + + private void checkParameterInitializer(DartMethodDefinition method, DartParameter parameter) { + if (Elements.isNonFactoryConstructor(method.getSymbol())) { + if (method.getModifiers().isRedirectedConstructor()) { + resolutionError(parameter.getName(), + DartCompilerErrorCode.PARAMETER_INIT_WITH_REDIR_CONSTRUCTOR); + } + + FieldElement element = + Elements.lookupLocalField((ClassElement) currentHolder, parameter.getParameterName()); + if (element == null) { + resolutionError(parameter, DartCompilerErrorCode.PARAMETER_NOT_MATCH_FIELD, + parameter.getName()); + } else if (element.isStatic()) { + resolutionError(parameter, + DartCompilerErrorCode.PARAMETER_INIT_STATIC_FIELD, + parameter.getName()); + } + + // Field parameters are not visible as parameters, so we do not declare them + // in the context. Instead we record the resolved field element. + Elements.setParameterInitializerElement(parameter.getSymbol(), element); + } else { + resolutionError(parameter.getName(), + DartCompilerErrorCode.PARAMETER_INIT_OUTSIDE_CONSTRUCTOR); + } + } + + private void resolveInitializers(DartMethodDefinition node) { + assert null != node; + Element firstElement = null; + DartNode firstNode = null; + Iterator initializers = node.getInitializers().iterator(); + if (initializers.hasNext()) { + firstNode = initializers.next(); + firstElement = resolve(firstNode); + } + while (initializers.hasNext()) { + resolve(initializers.next()); + } + boolean firstIsConstructorInvocation = + ElementKind.of(firstElement).equals(ElementKind.CONSTRUCTOR); + if (firstElement != null && !firstIsConstructorInvocation) { + firstElement = null; + } + checkConstructor(node, (ConstructorElement) firstElement, firstIsConstructorInvocation); + } + + private void resolutionError(DartNode node, DartCompilerErrorCode errorCode, + Object... arguments) { + context.resolutionError(node, errorCode, arguments); + } + + private boolean inStaticContext(Element element) { + return element == null || Elements.isTopLevel(element) + || element.getModifiers().isStatic() || element.getModifiers().isFactory(); + } + + @Override + boolean isStaticContext() { + return inStaticContext(currentMethod); + } + + boolean isStaticContextOrInitializer() { + return inStaticContext(currentMethod) || inInitializer; + } + } + + public static class Phase implements DartCompilationPhase { + /** + * Executes symbol resolution on the given compilation unit. + * + * @param context The listener through which compilation errors are reported + * (not null) + */ + @Override + public DartUnit exec(DartUnit unit, DartCompilerContext context, + CoreTypeProvider typeProvider) { + Scope unitScope = unit.getLibrary().getElement().getScope(); + return new Resolver(context, unitScope, typeProvider).exec(unit); + } + } + + @SuppressWarnings("deprecation") + private void checkRedirectConstructorCycle(List constructors, + ResolutionContext context) { + for (ConstructorElement element : constructors) { + if (hasRedirectedConstructorCycle(element)) { + context.resolutionError(element.getNode(), + DartCompilerErrorCode.REDIRECTED_CONSTRUCTOR_CYCLE); + } + } + } + + private boolean hasRedirectedConstructorCycle(ConstructorElement constructorElement) { + ConstructorElement next = getNextConstructorInvocation(constructorElement); + while (next != null) { + if (constructorElement.getName().equals(next.getName())) { + return true; + } + next = getNextConstructorInvocation(next); + } + return false; + } + + private ConstructorElement getNextConstructorInvocation(ConstructorElement constructor) { + List inits = ((DartMethodDefinition) constructor.getNode()).getInitializers(); + // The parser ensures that redirected constructors can be the only item in the initialization + // list. + if (inits.size() == 1) { + Element element = (Element) inits.get(0).getValue().getSymbol(); + if (ElementKind.of(element).equals(ElementKind.CONSTRUCTOR)) { + ConstructorElement nextConstructorElement = (ConstructorElement) element; + ClassElement nextClass = (ClassElement) nextConstructorElement.getEnclosingElement(); + ClassElement currentClass = (ClassElement) constructor.getEnclosingElement(); + if (nextClass.getName().equals(currentClass.getName())) { + return nextConstructorElement; + } + } + } + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/Scope.java b/compiler/java/com/google/dart/compiler/resolver/Scope.java new file mode 100644 index 00000000000..3e85c1c70cb --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/Scope.java @@ -0,0 +1,90 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.common.annotations.VisibleForTesting; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A scope used by {@link Resolver}. + */ +public class Scope { + + private final Map elements = new LinkedHashMap(); + private final Scope parent; + private final String name; + private LabelElement label; + + @VisibleForTesting + public Scope(String name, Scope parent) { + this.name = name; + this.parent = parent; + } + + @VisibleForTesting + public Scope(String name) { + this(name, null); + } + + public void clear() { + elements.clear(); + } + + public Element declareElement(String name, Element element) { + return elements.put(name, element); + } + + public Element findLocalElement(String name) { + return elements.get(name); + } + + public Element findElement(String name) { + Element element = findLocalElement(name); + if (element == null && parent != null) { + element = parent.findElement(name); + } + return element; + } + + public Element findLabel(String targetName, MethodElement innermostFunction) { + if (label != null && label.getName().equals(targetName) + && innermostFunction == label.getEnclosingFunction()) { + return label; + } + return parent == null ? null : + parent.findLabel(targetName, innermostFunction); + } + + public Map getElements() { + return elements; + } + + public Element getLabel() { + return label; + } + + public String getName() { + return name; + } + + public Scope getParent() { + return parent; + } + + public boolean isClear() { + return elements.size() == 0; + } + + public void setLabel(LabelElement label) { + this.label = label; + } + + @Override + public String toString() { + return getName() + " : \n" + elements.toString(); + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/SuperElement.java b/compiler/java/com/google/dart/compiler/resolver/SuperElement.java new file mode 100644 index 00000000000..0d3a69a5570 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/SuperElement.java @@ -0,0 +1,13 @@ +// 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. + +package com.google.dart.compiler.resolver; + +/** + * Resolved element for a Dart 'super' expression. + */ +public interface SuperElement extends Element { + + public ClassElement getClassElement(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/SuperElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/SuperElementImplementation.java new file mode 100644 index 00000000000..f42b8796eb2 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/SuperElementImplementation.java @@ -0,0 +1,29 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartSuperExpression; + +/** + * Resolved element for a Dart 'super' expression. + */ +public class SuperElementImplementation extends AbstractElement implements SuperElement { + + public ClassElement classElement; + + public SuperElementImplementation(DartSuperExpression node, ClassElement cls) { + super(node, ""); + this.classElement = cls; + } + + public ClassElement getClassElement() { + return classElement; + } + + @Override + public ElementKind getKind() { + return ElementKind.SUPER; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/SupertypeResolver.java b/compiler/java/com/google/dart/compiler/resolver/SupertypeResolver.java new file mode 100644 index 00000000000..1fdb007f361 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/SupertypeResolver.java @@ -0,0 +1,100 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; + +/** + * Resolves the super class, interfaces, default implementation and + * bounds type parameters of classes in a DartUnit. + */ +public class SupertypeResolver { + + private ResolutionContext topLevelContext; + private CoreTypeProvider typeProvider; + + public void exec(DartUnit unit, DartCompilerContext context, CoreTypeProvider typeProvider) { + exec(unit, context, unit.getLibrary().getElement().getScope(), typeProvider); + } + + public void exec(DartUnit unit, DartCompilerContext compilerContext, Scope libraryScope, + CoreTypeProvider typeProvider) { + this.typeProvider = typeProvider; + this.topLevelContext = new ResolutionContext(libraryScope, compilerContext, typeProvider); + unit.accept(new ClassElementResolver()); + } + + // Resolves super class, interfaces and default class of all classes. + private class ClassElementResolver extends DartNodeTraverser { + @Override + public Void visitClass(DartClass node) { + ClassElement classElement = node.getSymbol(); + + // Make sure that the type parameters are in scope before resolving the + // super class and interfaces + ResolutionContext classContext = topLevelContext.extend(classElement); + + DartTypeNode superclassNode = node.getSuperclass(); + InterfaceType supertype; + if (superclassNode == null) { + supertype = typeProvider.getObjectType(); + if (supertype.equals(classElement.getType())) { + // Object has no supertype. + supertype = null; + } + } else { + supertype = classContext.resolveClass(superclassNode, false); + supertype.getClass(); // Quick null check. + } + if (supertype != null) { + classElement.setSupertype(supertype); + } else { + assert classElement.getName().equals("Object") : classElement; + } + + InterfaceType defaultClass = classContext.resolveClass(node.getDefaultClass(), false); + if (defaultClass != null) { + Elements.setDefaultClass(classElement, defaultClass); + node.getDefaultClass().setType(defaultClass); + } + + if (node.getInterfaces() != null) { + for (DartTypeNode cls : node.getInterfaces()) { + Elements.addInterface(classElement, classContext.resolveInterface(cls, false)); + } + } + + for (Type typeParameter : classElement.getTypeParameters()) { + TypeVariableElement variable = (TypeVariableElement) typeParameter.getElement(); + DartTypeParameter typeParameterNode = (DartTypeParameter) variable.getNode(); + DartTypeNode boundNode = typeParameterNode.getBound(); + Type bound; + if (boundNode != null) { + bound = classContext.resolveType(boundNode, false); + boundNode.setType(bound); + } else { + bound = typeProvider.getObjectType(); + } + variable.setBound(bound); + } + + return null; + } + + @Override + public Void visitFunctionTypeAlias(DartFunctionTypeAlias node) { + Elements.addInterface(node.getSymbol(), typeProvider.getFunctionType()); + return null; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java b/compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java new file mode 100644 index 00000000000..116f0a84ed6 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/TopLevelElementBuilder.java @@ -0,0 +1,157 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.common.annotations.VisibleForTesting; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.type.Types; + +import java.util.List; + +/** + * Builds all class elements and types of a library. Once all libraries + * of an application have built their types, the library scope per + * library can be computed. + */ +public class TopLevelElementBuilder { + + public void exec(LibraryUnit library, DartCompilerContext context) { + assert library.getElement().getScope().isClear(); + for (DartUnit unit : library.getUnits()) { + unit.accept(new Builder(library.getElement())); + } + } + + public void exec(DartUnit unit, DartCompilerContext context) { + unit.accept(new Builder()); + } + + public void exec(DartClass cls, DartCompilerContext context) { + cls.accept(new Builder()); + } + + /** + * Create the scope for this library. First declare imported elements, then declare top-level + * elements of this library. + * + * @param library a library (that must have an empty scope). + */ + public void fillInLibraryScope(LibraryUnit library, DartCompilerListener listener) { + Scope scope = library.getElement().getScope(); + assert scope.getElements().isEmpty(); + + for (LibraryUnit lib : library.getImports()) { + String prefix = library.getPrefixOf(lib); + if (prefix != null) { + // Put the prefix in the scope. + scope.declareElement(prefix, lib.getElement()); + } else { + // Put the elements of the library in the scope. + for (DartUnit unit : lib.getUnits()) { + fillInUnitScope(unit, listener, scope); + } + } + } + + for (DartUnit unit : library.getUnits()) { + fillInUnitScope(unit, listener, scope); + } + } + + @VisibleForTesting + void fillInUnitScope(DartUnit unit, DartCompilerListener listener, Scope scope) { + for (DartNode node : unit.getTopLevelNodes()) { + if (node instanceof DartFieldDefinition) { + for (DartField field : ((DartFieldDefinition) node).getFields()) { + declare(field.getSymbol(), listener, scope); + } + } else { + declare((Element) node.getSymbol(), listener, scope); + } + } + } + + void compilationError(DartCompilerListener listener, SourceInfo node, ErrorCode errorCode, + Object... args) { + DartCompilationError error = new DartCompilationError(node, errorCode, args); + listener.compilationError(error); + } + + private void declare(Element element, DartCompilerListener listener, Scope scope) { + Element originalElement = scope.declareElement(element.getName(), element); + if (originalElement != null) { + DartNode originalNode = originalElement.getNode(); + if (originalNode != null) { + compilationError(listener, originalNode, DartCompilerErrorCode.DUPLICATE_DEFINITION, + originalElement.getName()); + } + compilationError(listener, element.getNode(), DartCompilerErrorCode.DUPLICATE_DEFINITION, + originalElement.getName()); + } + } + + /** + * Creates a ClassElement for a class. + */ + private class Builder extends DartNodeTraverser { + + private LibraryElement library; + + public Builder() { + this(null); + } + + public Builder(LibraryElement library) { + this.library = library; + } + + @Override + public Void visitClass(DartClass node) { + ClassElement element = Elements.classFromNode(node, library); + List parameterNodes = node.getTypeParameters(); + element.setType(Types.interfaceType(element, + Elements.makeTypeVariables(parameterNodes, element))); + node.setSymbol(element); + return null; + } + + @Override + public Void visitFunctionTypeAlias(DartFunctionTypeAlias node) { + FunctionAliasElement element = Elements.functionTypeAliasFromNode(node, library); + List parameterNodes = node.getTypeParameters(); + element.setType(Types.functionAliasType(element, + Elements.makeTypeVariables(parameterNodes, element))); + node.setSymbol(element); + return null; + } + + @Override + public Void visitMethodDefinition(DartMethodDefinition node) { + node.setSymbol(Elements.methodFromMethodNode(node, library)); + return null; + } + + @Override + public Void visitField(DartField node) { + node.setSymbol(Elements.fieldFromNode(node, library, node.getModifiers())); + return null; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/TypeVariableElement.java b/compiler/java/com/google/dart/compiler/resolver/TypeVariableElement.java new file mode 100644 index 00000000000..64138ecb985 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/TypeVariableElement.java @@ -0,0 +1,25 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeVariable; + +/** + * Represention of a type variable. + * + *

    For example, in {@code class Foo { ... }}, {@code T} is a + * type variable. + */ +public interface TypeVariableElement extends Element { + // Workaround JDK 6 bug. Should @Override getType(). + TypeVariable getTypeVariable(); + + Type getBound(); + + void setBound(Type bound); + + Element getDeclaringElement(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/TypeVariableElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/TypeVariableElementImplementation.java new file mode 100644 index 00000000000..921221d0750 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/TypeVariableElementImplementation.java @@ -0,0 +1,71 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeVariable; +import com.google.dart.compiler.type.Types; + +/** + * Represention of a type variable. + * + *

    For example, in {@code class Foo { ... }}, {@code T} is a + * type variable. + */ +class TypeVariableElementImplementation extends AbstractElement implements TypeVariableElement { + + private final Element owner; + private TypeVariable type; + private Type bound; + + TypeVariableElementImplementation(DartNode node, String name, Element owner) { + super(node, name); + this.owner = owner; + } + + @Override + public TypeVariable getType() { + return type; + } + + @Override + public ElementKind getKind() { + return ElementKind.TYPE_VARIABLE; + } + + static TypeVariableElementImplementation fromNode(DartTypeParameter node, Element owner) { + TypeVariableElementImplementation element = + new TypeVariableElementImplementation(node, node.getName().getTargetName(), owner); + element.setType(Types.typeVariable(element)); + return element; + } + + @Override + public TypeVariable getTypeVariable() { + return getType(); + } + + @Override + void setType(Type type) { + this.type = (TypeVariable) type; + } + + @Override + public void setBound(Type bound) { + this.bound = bound; + } + + @Override + public Type getBound() { + return bound; + } + + @Override + public Element getDeclaringElement() { + return owner; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/VariableElement.java b/compiler/java/com/google/dart/compiler/resolver/VariableElement.java new file mode 100644 index 00000000000..f11f7b05aec --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/VariableElement.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartExpression; + +public interface VariableElement extends Element { + FieldElement getParameterInitializerElement(); + + boolean isNamed(); + + DartExpression getDefaultValue(); +} diff --git a/compiler/java/com/google/dart/compiler/resolver/VariableElementImplementation.java b/compiler/java/com/google/dart/compiler/resolver/VariableElementImplementation.java new file mode 100644 index 00000000000..2eba4c9a957 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/VariableElementImplementation.java @@ -0,0 +1,70 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.type.Type; + +class VariableElementImplementation extends AbstractElement implements VariableElement { + private final ElementKind kind; + private final Modifiers modifiers; + private final boolean isNamed; + private final DartExpression defaultValue; + + // The field element is set for constructor parameters of the form + // this.foo by the resolver. + private FieldElement fieldElement; + private Type type; + + VariableElementImplementation(DartNode node, String name, ElementKind kind, Modifiers modifiers, + boolean isNamed, DartExpression defaultValue) { + super(node, name); + this.isNamed = isNamed; + this.kind = kind; + this.modifiers = modifiers; + this.defaultValue = defaultValue; + } + + @Override + public ElementKind getKind() { + return kind; + } + + @Override + public Modifiers getModifiers() { + return modifiers; + } + + @Override + void setType(Type type) { + this.type = type; + } + + @Override + public Type getType() { + return type; + } + + @Override + public boolean isNamed() { + return isNamed; + } + + @Override + public DartExpression getDefaultValue() { + return defaultValue; + } + + void setParameterInitializerElement(FieldElement element) { + this.fieldElement = element; + } + + @Override + public FieldElement getParameterInitializerElement() { + return fieldElement; + } +} diff --git a/compiler/java/com/google/dart/compiler/resolver/VoidElement.java b/compiler/java/com/google/dart/compiler/resolver/VoidElement.java new file mode 100644 index 00000000000..7450ade9ddd --- /dev/null +++ b/compiler/java/com/google/dart/compiler/resolver/VoidElement.java @@ -0,0 +1,34 @@ +// 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. + +package com.google.dart.compiler.resolver; + +/** + * Implementation of "void". There is no public interface for this class as Element already exposes + * all the functionality needed. + */ +class VoidElement extends AbstractElement { + private VoidElement() { + super(null, "void"); + } + + @Override + public ElementKind getKind() { + return ElementKind.VOID; + } + + static Element getInstance() { + return new VoidElement(); + } + + @Override + public boolean equals(Object other) { + return other instanceof VoidElement; + } + + @Override + public int hashCode() { + return VoidElement.class.hashCode(); + } +} diff --git a/compiler/java/com/google/dart/compiler/testing/TestCompilerConfiguration.java b/compiler/java/com/google/dart/compiler/testing/TestCompilerConfiguration.java new file mode 100644 index 00000000000..055b452bd05 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/testing/TestCompilerConfiguration.java @@ -0,0 +1,112 @@ +// 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. + +package com.google.dart.compiler.testing; + +import com.google.dart.compiler.Backend; +import com.google.dart.compiler.CompilerConfiguration; +import com.google.dart.compiler.DartCompilationPhase; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.SystemLibraryManager; +import com.google.dart.compiler.UrlLibrarySource; +import com.google.dart.compiler.metrics.CompilerMetrics; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.List; + +/** + * A mock configuration for use in tests. + */ +public class TestCompilerConfiguration implements CompilerConfiguration { + private final SystemLibraryManager systemLibraryManager = new SystemLibraryManager(); + + @Override + public boolean warningsAreFatal() { + return false; + } + + @Override + public boolean typeErrorsAreFatal() { + return false; + } + + @Override + public boolean shouldOptimize() { + return false; + } + + @Override + public boolean resolveDespiteParseErrors() { + return true; + } + + @Override + public boolean incremental() { + return false; + } + + @Override + public List getPhases() { + return Collections.emptyList(); + } + + @Override + public File getOutputFilename() { + return null; + } + + @Override + public File getOutputDirectory() { + throw new AssertionError(); + } + + @Override + public CompilerMetrics getCompilerMetrics() { + return null; + } + + @Override + public String getJvmMetricOptions() { + return null; + } + + @Override + public List getBackends() { + return Collections.emptyList(); + } + + @Override + public boolean checkOnly() { + return true; + } + + @Override + public boolean expectEntryPoint() { + return false; + } + + @Override + public boolean allowNoSuchType() { + return false; + } + + @Override + public boolean collectComments() { + return false; + } + + @Override + public LibrarySource getSystemLibraryFor(String importSpec) { + URI systemUri; + try { + systemUri = new URI(importSpec); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + return new UrlLibrarySource(systemUri, this.systemLibraryManager); + } +} diff --git a/compiler/java/com/google/dart/compiler/testing/TestCompilerContext.java b/compiler/java/com/google/dart/compiler/testing/TestCompilerContext.java new file mode 100644 index 00000000000..35d75560faa --- /dev/null +++ b/compiler/java/com/google/dart/compiler/testing/TestCompilerContext.java @@ -0,0 +1,152 @@ +// 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. + +package com.google.dart.compiler.testing; + +import com.google.dart.compiler.CompilerConfiguration; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.metrics.CompilerMetrics; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +/** + * Common context for test cases. + */ +public class TestCompilerContext extends DartCompilerListener implements DartCompilerContext { + + private final Set ignoredEvents; + final List errors; + private int typeErrorCount; + private int warningCount; + private int errorCount; + + /** + * @param ignored list of events that will be ignored. All other events cause an AssertionError. + */ + public TestCompilerContext(EventKind... ignored) { + EnumSet set = EnumSet.noneOf(EventKind.class); + for (EventKind kind : ignored) { + set.add(kind); + } + this.ignoredEvents = Collections.unmodifiableSet(set); + this.errors = new ArrayList(); + } + + @Override + public LibraryUnit getApplicationUnit() { + throw new AssertionError(); + } + + @Override + public LibraryUnit getAppLibraryUnit() { + throw new AssertionError(); + } + + @Override + public LibraryUnit getLibraryUnit(LibrarySource lib) { + throw new AssertionError(lib.getName()); + } + + @Override + public void compilationError(DartCompilationError event) { + errorCount++; + handleEvent(event, EventKind.ERROR); + } + + @Override + public void compilationWarning(DartCompilationError event) { + warningCount++; + handleEvent(event, EventKind.WARNING); + } + + @Override + public void typeError(DartCompilationError event) { + typeErrorCount++; + handleEvent(event, EventKind.TYPE_ERROR); + } + + protected void handleEvent(DartCompilationError event, EventKind kind) { + errors.add(event.getErrorCode()); + if (!ignoredEvents.contains(kind)) { + throw new AssertionError(event); + } + } + + @Override + public Reader getArtifactReader(Source source, String part, String extension) throws IOException { + throw new AssertionError(source.getName() + " " + part + "." + extension); + } + + @Override + public URI getArtifactUri(DartSource source, String part, String extension) { + throw new AssertionError(source.getName() + " " + part + "." + extension); + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) throws IOException { + throw new AssertionError(source.getName() + " " + part + "." + extension); + } + + @Override + public boolean isOutOfDate(Source source, Source base, String extension) { + throw new AssertionError(source.getName() + " " + base.getName() + " " + extension); + } + + @Override + public CompilerMetrics getCompilerMetrics() { + return null; + } + + public int getErrorCount() { + return errorCount; + } + + public int getWarningCount() { + return warningCount; + } + + public int getTypeErrorCount() { + return typeErrorCount; + } + + public List getErrorCodes() { + return errors; + } + + @Override + public boolean allowNoSuchType() { + return false; + } + + @Override + public CompilerConfiguration getCompilerConfiguration() { + return null; + } + + public enum EventKind { + ERROR, + TYPE_ERROR, + WARNING; + } + + @Override + public LibrarySource getSystemLibraryFor(String importSpec) { + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/testing/TestDartArtifactProvider.java b/compiler/java/com/google/dart/compiler/testing/TestDartArtifactProvider.java new file mode 100644 index 00000000000..70405ae8a46 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/testing/TestDartArtifactProvider.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.testing; + +import com.google.dart.compiler.DartArtifactProvider; +import com.google.dart.compiler.Source; + +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; + +/** + * A mock artifact provider for use in tests. + */ +public class TestDartArtifactProvider extends DartArtifactProvider { + @Override + public boolean isOutOfDate(Source source, Source base, String extension) { + return true; + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) { + return new StringWriter(); + } + + @Override + public URI getArtifactUri(Source source, String part, String extension) { + throw new AssertionError(); + } + + @Override + public Reader getArtifactReader(Source source, String part, String extension) { + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/testing/TestLibrarySource.java b/compiler/java/com/google/dart/compiler/testing/TestLibrarySource.java new file mode 100644 index 00000000000..d1628f45851 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/testing/TestLibrarySource.java @@ -0,0 +1,143 @@ +// 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. + +package com.google.dart.compiler.testing; + +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; + +import java.io.Reader; +import java.io.StringReader; +import java.net.URI; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A mock library source for use in tests. + */ +public class TestLibrarySource implements LibrarySource { + private abstract class TestDartSource implements DartSource { + private final String srcName; + private final URI srcUri; + + private TestDartSource(String name, URI uri) { + this.srcName = name; + this.srcUri = uri; + } + + @Override + public URI getUri() { + return srcUri; + } + + @Override + public String getName() { + return srcName; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public LibrarySource getLibrary() { + return TestLibrarySource.this; + } + + @Override + public String getRelativePath() { + return srcName; + } + } + + private final String name; + private URI uri; + private final Map sourceMap = new LinkedHashMap(); + + public TestLibrarySource(String name) { + this.name = name; + uri = URI.create(name); + } + + @Override + public URI getUri() { + return uri; + } + + @Override + public Reader getSourceReader() { + StringBuilder sb = new StringBuilder(); + sb.append("#library('"); + sb.append(name); + sb.append("');\n"); + for (DartSource source : sourceMap.values()) { + sb.append("#source('"); + sb.append(source.getName()); + sb.append("');\n"); + } + return new StringReader(sb.toString()); + } + + /** + * Add a source file to this library. + * @param name the relative name (uri) of the source file. + * @param sourceLines the lines of the source (automatically separated by newlines) + */ + public DartSource addSource(final String name, String... sourceLines) { + StringBuilder sb = new StringBuilder(); + for (String line : sourceLines) { + sb.append(line); + sb.append("\n"); + } + final String source = sb.toString(); + final URI uri = URI.create(name); + DartSource dartSource = new TestDartSource(name, uri){ + @Override + public Reader getSourceReader() { + return new StringReader(source); + }}; + return sourceMap.put(dartSource.getName(), dartSource); + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public DartSource getSourceFor(String relPath) { + if (!name.equals(relPath)) { + return sourceMap.get(relPath); + } + + // Return DartSource for the library itself + return new TestDartSource(name, uri) { + @Override + public Reader getSourceReader() { + return TestLibrarySource.this.getSourceReader(); + } + }; + } + + @Override + public LibrarySource getImportFor(String relPath) { + throw new AssertionError(relPath); + } +} diff --git a/compiler/java/com/google/dart/compiler/type/AbstractType.java b/compiler/java/com/google/dart/compiler/type/AbstractType.java new file mode 100644 index 00000000000..c34f0cb24c0 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/AbstractType.java @@ -0,0 +1,11 @@ +// 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. + +package com.google.dart.compiler.type; + +/** + * Common superclass for all types. + */ +abstract class AbstractType implements Type { +} diff --git a/compiler/java/com/google/dart/compiler/type/DynamicType.java b/compiler/java/com/google/dart/compiler/type/DynamicType.java new file mode 100644 index 00000000000..b47dfcbfa07 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/DynamicType.java @@ -0,0 +1,24 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.DynamicElement; + +import java.util.List; + +/** + * Type of untyped expressions. + */ +public interface DynamicType extends FunctionAliasType, TypeVariable, FunctionType { + + @Override + DynamicType subst(List arguments, List parameters); + + @Override + public DynamicElement getElement(); + + @Override + public DynamicType asRawType(); +} diff --git a/compiler/java/com/google/dart/compiler/type/DynamicTypeImplementation.java b/compiler/java/com/google/dart/compiler/type/DynamicTypeImplementation.java new file mode 100644 index 00000000000..17ae0cde9b9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/DynamicTypeImplementation.java @@ -0,0 +1,126 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.DynamicElement; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.Elements; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Type of untyped expressions. + */ +class DynamicTypeImplementation extends AbstractType implements DynamicType { + + @Override + public DynamicTypeImplementation subst(List arguments, + List parameters) { + return this; + } + + @Override + public DynamicElement getElement() { + return Elements.dynamicElement(); + } + + @Override + public DynamicElement getTypeVariableElement() { + return getElement(); + } + + @Override + public String toString() { + return ""; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof DynamicType; + } + + @Override + public int hashCode() { + return DynamicType.class.hashCode(); + } + + @Override + public List getArguments() { + return Collections.emptyList(); + } + + @Override + public boolean isRaw() { + return false; + } + + @Override + public boolean hasDynamicTypeArgs() { + return false; + } + + @Override + public DynamicType asRawType() { + return this; + } + + @Override + public TypeKind getKind() { + return TypeKind.DYNAMIC; + } + + @Override + public Type getReturnType() { + return this; + } + + @Override + public List getParameterTypes() { + return Collections.emptyList(); + } + + @Override + public Member lookupMember(String name) { + return new Member() { + + @Override + public Type getType() { + return DynamicTypeImplementation.this; + } + + @Override + public InterfaceType getHolder() { + return DynamicTypeImplementation.this; + } + + @Override + public Element getElement() { + return Elements.dynamicElement(); + } + }; + } + + @Override + public Type getRest() { + return null; + } + + @Override + public boolean hasRest() { + return false; + } + + @Override + public Map getNamedParameterTypes() { + return null; + } + + @Override + public List getTypeVariables() { + return null; + } +} diff --git a/compiler/java/com/google/dart/compiler/type/FunctionAliasType.java b/compiler/java/com/google/dart/compiler/type/FunctionAliasType.java new file mode 100644 index 00000000000..8682b930f7e --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/FunctionAliasType.java @@ -0,0 +1,15 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.FunctionAliasElement; + +/** + * A type corresponding to a function alias definition. + */ +public interface FunctionAliasType extends InterfaceType { + @Override + public FunctionAliasElement getElement(); +} diff --git a/compiler/java/com/google/dart/compiler/type/FunctionAliasTypeImplementation.java b/compiler/java/com/google/dart/compiler/type/FunctionAliasTypeImplementation.java new file mode 100644 index 00000000000..0bcda1deb07 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/FunctionAliasTypeImplementation.java @@ -0,0 +1,36 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.FunctionAliasElement; + +import java.util.List; + +class FunctionAliasTypeImplementation extends InterfaceTypeImplementation + implements FunctionAliasType { + + FunctionAliasTypeImplementation(FunctionAliasElement element, List arguments) { + super(element, arguments); + } + + @Override + public TypeKind getKind() { + return TypeKind.FUNCTION_ALIAS; + } + + @Override + public FunctionAliasElement getElement() { + return (FunctionAliasElement) super.getElement(); + } + + @Override + public FunctionAliasType subst(List arguments, List parameters) { + if (arguments.isEmpty() && parameters.isEmpty()) { + return this; + } + List substitutedArguments = Types.subst(getArguments(), arguments, parameters); + return new FunctionAliasTypeImplementation(getElement(), substitutedArguments); + } +} diff --git a/compiler/java/com/google/dart/compiler/type/FunctionType.java b/compiler/java/com/google/dart/compiler/type/FunctionType.java new file mode 100644 index 00000000000..019de5cb5f9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/FunctionType.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.MethodElement; + +import java.util.List; +import java.util.Map; + +/** + * Function type representation. A function type may correspond to method, + * constructor, or function in which case its element is the corresponding + * {@link MethodElement}. Otherwise, the function type can correspond to a named + * function-type alias or variable type, in which case its element is the class + * element of the Dart interface Function. + */ +public interface FunctionType extends Type { + Type getReturnType(); + + List getParameterTypes(); + + /** + * Return the class element corresponding to the interface Function. + */ + @Override + ClassElement getElement(); + + Type getRest(); + + boolean hasRest(); + + Map getNamedParameterTypes(); + + List getTypeVariables(); +} diff --git a/compiler/java/com/google/dart/compiler/type/FunctionTypeImplementation.java b/compiler/java/com/google/dart/compiler/type/FunctionTypeImplementation.java new file mode 100644 index 00000000000..166768b9906 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/FunctionTypeImplementation.java @@ -0,0 +1,185 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.ClassElement; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +class FunctionTypeImplementation extends AbstractType implements FunctionType { + private static final Map EMPTY_MAP = Collections.emptyMap(); + private final ClassElement classElement; + private final List parameterTypes; + private final Type returnType; + private final Map namedParameterTypes; + private final Type rest; + private final List typeVariables; + + private FunctionTypeImplementation(ClassElement element, + List parameterTypes, + Map namedParameterTypes, + Type rest, + Type returnType, + List typeVariables) { + this.classElement = element; + this.parameterTypes = parameterTypes; + this.namedParameterTypes = namedParameterTypes == null ? EMPTY_MAP : namedParameterTypes; + this.rest = rest; + this.returnType = returnType; + this.typeVariables = typeVariables; + } + + @Override + public Type subst(List arguments, + List parameters) { + List substitutedParameterTypes = Types.subst(getParameterTypes(), arguments, parameters); + Map substitutedNamedParameterTypes = null; + if (!getNamedParameterTypes().isEmpty()) { + substitutedNamedParameterTypes = new LinkedHashMap(); + for (Map.Entry entry : getNamedParameterTypes().entrySet()) { + substitutedNamedParameterTypes.put(entry.getKey(), + entry.getValue().subst(arguments, parameters)); + } + } + Type substitutedRest = null; + if (getRest() != null) { + substitutedRest = getRest().subst(arguments, parameters); + } + Type substitutedReturnType = getReturnType().subst(arguments, parameters); + return new FunctionTypeImplementation(getElement(), + substitutedParameterTypes, substitutedNamedParameterTypes, + substitutedRest, substitutedReturnType, getTypeVariables()); + } + + @Override + public ClassElement getElement() { + return classElement; + } + + @Override + public Type getReturnType() { + return returnType; + } + + @Override + public List getParameterTypes() { + return parameterTypes; + } + + @Override + public TypeKind getKind() { + return TypeKind.FUNCTION; + } + + @Override + public Map getNamedParameterTypes() { + return namedParameterTypes; + } + + public List getTypeVariables() { + return typeVariables; + } + + @Override + public Type getRest() { + return rest; + } + + @Override + public boolean hasRest() { + return rest != null; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("("); + boolean first = true; + for (Type argument : getParameterTypes()) { + if (!first) { + sb.append(", "); + } + sb.append(argument); + first = false; + } + Type rest = getRest(); + if (rest != null) { + if (!first) { + sb.append(", "); + } + sb.append(rest); + sb.append("..."); + first = false; + } + Map namedParameterTypes = getNamedParameterTypes(); + if (!namedParameterTypes.isEmpty()) { + if (!first) { + sb.append(", "); + } + sb.append("["); + first = true; + for (Entry entry : namedParameterTypes.entrySet()) { + if (!first) { + sb.append(", "); + } + sb.append(entry.getValue()); + sb.append(" "); + sb.append(entry.getKey()); + first = false; + } + sb.append("]"); + } + sb.append(") -> "); + sb.append(getReturnType()); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + // Two FunctionType objects representing the "same" type may not be equal, + // because they may have different elements. + if (o instanceof FunctionType) { + FunctionType other = (FunctionType) o; + return getElement().equals(other.getElement()) + && getReturnType().equals(other.getReturnType()) + && getParameterTypes().equals(other.getParameterTypes()) + && hasRest() == other.hasRest() + && (!hasRest() || getRest().equals(other.getRest())) + && getNamedParameterTypes().equals(other.getNamedParameterTypes()); + } + return false; + } + + @Override + public int hashCode() { + Type rest = getRest(); + Map namedParameterTypes = getNamedParameterTypes(); + return getElement().hashCode() + + getReturnType().hashCode() + + getParameterTypes().hashCode() + + (rest == null ? 0 : rest.hashCode()) + + (namedParameterTypes == null ? 0 : namedParameterTypes.hashCode()); + } + + /** + * Returns a function type with the given parameter types and return type. The + * {@link ClassElement} should always be the element corresponding to the + * interface Function in the core library. + */ + static FunctionType of(ClassElement element, List parameterTypes, + Map namedParameterTypes, Type rest, Type returnType, + List typeVariables) { + assert element.isDynamic() || element.getName().equals("Function"); + if (typeVariables == null) { + typeVariables = Collections.emptyList(); + } + return new FunctionTypeImplementation(element, parameterTypes, namedParameterTypes, rest, + returnType, typeVariables); + } +} diff --git a/compiler/java/com/google/dart/compiler/type/InterfaceType.java b/compiler/java/com/google/dart/compiler/type/InterfaceType.java new file mode 100644 index 00000000000..553f1b0ddaa --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/InterfaceType.java @@ -0,0 +1,41 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.Element; + +import java.util.List; + +/** + * An interface type. + */ +public interface InterfaceType extends Type { + @Override + InterfaceType subst(List arguments, + List parameters); + + @Override + ClassElement getElement(); + + List getArguments(); + + boolean isRaw(); + + /** + * @return Whether type args for this interface instance is of type DYNAMIC. + */ + boolean hasDynamicTypeArgs(); + + InterfaceType asRawType(); + + Member lookupMember(String name); + + interface Member { + InterfaceType getHolder(); + Element getElement(); + Type getType(); + } +} diff --git a/compiler/java/com/google/dart/compiler/type/InterfaceTypeImplementation.java b/compiler/java/com/google/dart/compiler/type/InterfaceTypeImplementation.java new file mode 100644 index 00000000000..4331e2d0b2a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/InterfaceTypeImplementation.java @@ -0,0 +1,167 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.Element; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * An interface type. + */ +class InterfaceTypeImplementation extends AbstractType implements InterfaceType { + private final ClassElement element; + private final List arguments; + + InterfaceTypeImplementation(ClassElement element, List arguments) { + this.element = element; + this.arguments = arguments; + } + + @Override + public ClassElement getElement() { + return element; + } + + @Override + public List getArguments() { + return arguments; + } + + @Override + public String toString() { + if (getArguments().isEmpty()) { + return getElement().getName(); + } else { + StringBuilder sb = new StringBuilder(); + sb.append(getElement().getName()); + Types.printTypesOn(sb, getArguments(), "<", ">"); + return sb.toString(); + } + } + + @Override + public boolean hasDynamicTypeArgs() { + for (Type t : getArguments()) { + if (t.getKind() == TypeKind.DYNAMIC) { + return true; + } + } + return false; + } + + @Override + public boolean isRaw() { + return getArguments().size() != getElement().getTypeParameters().size(); + } + + @Override + public InterfaceType subst(List arguments, List parameters) { + if (arguments.isEmpty() && parameters.isEmpty()) { + return this; + } + List substitutedArguments = Types.subst(getArguments(), arguments, parameters); + return new InterfaceTypeImplementation(getElement(), substitutedArguments); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof InterfaceType) { + InterfaceType other = (InterfaceType) obj; + return getElement().equals(other.getElement()) && getArguments().equals(other.getArguments()); + } + return false; + } + + @Override + public int hashCode() { + int hashCode = 31; + hashCode += getElement().hashCode(); + hashCode += 31 * hashCode + getArguments().hashCode(); + return hashCode; + } + + @Override + public InterfaceType asRawType() { + return new InterfaceTypeImplementation(getElement(), Arrays.asList()); + } + + @Override + public TypeKind getKind() { + return TypeKind.INTERFACE; + } + + @Override + public Member lookupMember(String name) { + Element element = getElement().lookupLocalElement(name); + if (element != null) { + return new MemberImplementation(this, element); + } + InterfaceType supertype = getSupertype(); + if (supertype != null) { + Member member = supertype.lookupMember(name); + if (member != null) { + return member; + } + } + for (InterfaceType intrface : getInterfaces()) { + Member member = intrface.lookupMember(name); + if (member != null) { + return member; + } + } + return null; + } + + private InterfaceType getSupertype() { + InterfaceType supertype = getElement().getSupertype(); + if (supertype == null) { + return null; + } else { + return supertype.subst(getArguments(), getElement().getTypeParameters()); + } + } + + private List getInterfaces() { + List interfaces = getElement().getInterfaces(); + List result = new ArrayList(interfaces.size()); + List typeArguments = getArguments(); + List typeParameters = getElement().getTypeParameters(); + for (InterfaceType type : interfaces) { + result.add(type.subst(typeArguments, typeParameters)); + } + return result; + } + + private static class MemberImplementation implements Member { + private final InterfaceType holder; + private final Element member; + + MemberImplementation(InterfaceType holder, Element member) { + this.holder = holder; + this.member = member; + } + + @Override + public InterfaceType getHolder() { + return holder; + } + + @Override + public Element getElement() { + return member; + } + + @Override + public Type getType() { + List typeArguments = getHolder().getArguments(); + List typeParameters = getHolder().getElement().getTypeParameters(); + return getElement().getType().subst(typeArguments, typeParameters); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/type/Type.java b/compiler/java/com/google/dart/compiler/type/Type.java new file mode 100644 index 00000000000..0040a163276 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/Type.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.Element; + +import java.util.List; + +/** + * Common supertype of all types. + */ +public interface Type { + /** + * Performs the substitution [arguments[i]/parameters[i]]this. + * The notation is known from this lambda calculus rule: + * (lambda x.e0)e1 -> [e1/x]e0. + *

    See {@link TypeVariable} for a motivation for this method. + */ + Type subst(List arguments, List parameters); + + Element getElement(); + + TypeKind getKind(); +} diff --git a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java new file mode 100644 index 00000000000..3e7ff3d2245 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java @@ -0,0 +1,1558 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilationPhase; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartAssertion; +import com.google.dart.compiler.ast.DartBinaryExpression; +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.DartConditional; +import com.google.dart.compiler.ast.DartContinueStatement; +import com.google.dart.compiler.ast.DartDeclaration; +import com.google.dart.compiler.ast.DartDefault; +import com.google.dart.compiler.ast.DartDoWhileStatement; +import com.google.dart.compiler.ast.DartDoubleLiteral; +import com.google.dart.compiler.ast.DartEmptyStatement; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIfStatement; +import com.google.dart.compiler.ast.DartImportDirective; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartInvocation; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartLibraryDirective; +import com.google.dart.compiler.ast.DartLiteral; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMapLiteralEntry; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNamedExpression; +import com.google.dart.compiler.ast.DartNativeBlock; +import com.google.dart.compiler.ast.DartNativeDirective; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNodeTraverser; +import com.google.dart.compiler.ast.DartNullLiteral; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartParameterizedNode; +import com.google.dart.compiler.ast.DartParenthesizedExpression; +import com.google.dart.compiler.ast.DartPlainVisitor; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; +import com.google.dart.compiler.ast.DartResourceDirective; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartSourceDirective; +import com.google.dart.compiler.ast.DartStringInterpolation; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartSuperConstructorInvocation; +import com.google.dart.compiler.ast.DartSuperExpression; +import com.google.dart.compiler.ast.DartSwitchStatement; +import com.google.dart.compiler.ast.DartSyntheticErrorExpression; +import com.google.dart.compiler.ast.DartSyntheticErrorStatement; +import com.google.dart.compiler.ast.DartThisExpression; +import com.google.dart.compiler.ast.DartThrowStatement; +import com.google.dart.compiler.ast.DartTryStatement; +import com.google.dart.compiler.ast.DartTypeExpression; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartUnqualifiedInvocation; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.DartVariableStatement; +import com.google.dart.compiler.ast.DartWhileStatement; +import com.google.dart.compiler.ast.ElementReference; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.CyclicDeclarationException; +import com.google.dart.compiler.resolver.DuplicatedInterfaceException; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.EnclosingElement; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.resolver.VariableElement; +import com.google.dart.compiler.type.InterfaceType.Member; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Analyzer of static type information. + */ +public class TypeAnalyzer implements DartCompilationPhase { + private final ConcurrentHashMap> unimplementedElements = + new ConcurrentHashMap>(); + private final Set diagnosedAbstractClasses = + Collections.newSetFromMap(new ConcurrentHashMap()); + + /** + * Perform type analysis on the given AST rooted at node. + * + * @param node The root of the tree to analyze + * @param typeProvider The source of pre-defined type definitions + * @param context The compilation context (DartCompilerContext) + * @param currentClass The class that contains node. Will be null + * for top-level declarations. + * @return The type of node. + */ + public static Type analyze(DartNode node, CoreTypeProvider typeProvider, + DartCompilerContext context, InterfaceType currentClass) { + ConcurrentHashMap> unimplementedElements = + new ConcurrentHashMap>(); + Set diagnosed = + Collections.newSetFromMap(new ConcurrentHashMap()); + Analyzer analyzer = new Analyzer(context, typeProvider, unimplementedElements, diagnosed); + analyzer.setCurrentClass(currentClass); + return node.accept(analyzer); + } + + @Override + public DartUnit exec(DartUnit unit, DartCompilerContext context, + CoreTypeProvider typeProvider) { + unit.accept(new Analyzer(context, typeProvider, unimplementedElements, + diagnosedAbstractClasses)); + return unit; + } + + @VisibleForTesting + static class Analyzer implements DartPlainVisitor { + private final DynamicType dynamicType; + private final Type stringType; + private final InterfaceType defaultLiteralMapType; + private final Type voidType; + private final DartCompilerContext context; + private final Types types; + private Type expected; + private InterfaceType currentClass; + private final ConcurrentHashMap> unimplementedElements; + private final Set diagnosedAbstractClasses; + private final InterfaceType boolType; + private final InterfaceType numType; + private final InterfaceType intType; + private final Type nullType; + private final InterfaceType functionType; + + Analyzer(DartCompilerContext context, CoreTypeProvider typeProvider, + ConcurrentHashMap> unimplementedElements, + Set diagnosedAbstractClasses) { + this.context = context; + this.unimplementedElements = unimplementedElements; + this.diagnosedAbstractClasses = diagnosedAbstractClasses; + this.types = Types.getInstance(typeProvider); + this.dynamicType = typeProvider.getDynamicType(); + this.stringType = typeProvider.getStringType(); + this.defaultLiteralMapType = typeProvider.getMapType(stringType, dynamicType); + this.voidType = typeProvider.getVoidType(); + this.boolType = typeProvider.getBoolType(); + this.numType = typeProvider.getNumType(); + this.intType = typeProvider.getIntType(); + this.nullType = typeProvider.getNullType(); + this.functionType = typeProvider.getFunctionType(); + } + + @VisibleForTesting + void setCurrentClass(InterfaceType type) { + currentClass = type; + } + + private InterfaceType getCurrentClass() { + return currentClass; + } + + private DynamicType typeError(DartNode node, ErrorCode code, Object... arguments) { + context.typeError(new DartCompilationError(node, code, arguments)); + return dynamicType; + } + + private void resolutionError(DartNode node, ErrorCode code, Object... arguments) { + context.compilationError(new DartCompilationError(node, code, arguments)); + } + + AssertionError internalError(DartNode node, String message, Object... arguments) { + message = String.format(message, arguments); + context.compilationError(new DartCompilationError(node, DartCompilerErrorCode.INTERNAL_ERROR, + message)); + return new AssertionError("Internal error: " + message); + } + + private Type typeOfLiteral(DartLiteral node) { + return node.getType(); + } + + private Token getBasicOperator(DartNode diagnosticNode, Token op) { + switch(op) { + case INC: + return Token.ADD; + case DEC: + return Token.SUB; + case ASSIGN_BIT_OR: + return Token.BIT_OR; + case ASSIGN_BIT_XOR: + return Token.BIT_XOR; + case ASSIGN_BIT_AND: + return Token.BIT_AND; + case ASSIGN_SHL: + return Token.SHL; + case ASSIGN_SAR: + return Token.SAR; + case ASSIGN_SHR: + return Token.SHR; + case ASSIGN_ADD: + return Token.ADD; + case ASSIGN_SUB: + return Token.SUB; + case ASSIGN_MUL: + return Token.MUL; + case ASSIGN_DIV: + return Token.DIV; + case ASSIGN_MOD: + return Token.MOD; + case ASSIGN_TRUNC: + return Token.TRUNC; + default: + internalError(diagnosticNode, "unexpected operator %s", op.name()); + return null; + } + } + + @Override + public Type visitRedirectConstructorInvocation(DartRedirectConstructorInvocation node) { + return checkConstructorForwarding(node, node.getSymbol()); + } + + private String methodNameForUnaryOperator(DartNode diagnosticNode, Token operator) { + if (operator == Token.SUB) { + return "operator negate"; + } else if (operator == Token.BIT_NOT) { + return "operator ~"; + } + return "operator " + getBasicOperator(diagnosticNode, operator).getSyntax(); + } + + private String methodNameForBinaryOperator(Token operator) { + return "operator " + operator.getSyntax(); + } + + private Type analyzeBinaryOperator(ElementReference node, Type lhs, Token operator, + DartNode diagnosticNode, DartExpression rhs) { + Type rhsType = nonVoidTypeOf(rhs); + String methodName = methodNameForBinaryOperator(operator); + Member member = lookupMember(lhs, methodName, diagnosticNode); + if (member != null) { + node.setReferencedElement(member.getElement()); + return analyzeMethodInvocation(lhs, member, methodName, diagnosticNode, + Collections.singletonList(rhsType), + Collections.singletonList(rhs)); + } else { + return dynamicType; + } + } + + @Override + public Type visitBinaryExpression(DartBinaryExpression node) { + DartExpression lhsNode = node.getArg1(); + Type lhs = nonVoidTypeOf(lhsNode); + DartExpression rhsNode = node.getArg2(); + Token operator = node.getOperator(); + switch (operator) { + case ASSIGN: { + Type rhs = nonVoidTypeOf(rhsNode); + checkAssignable(rhsNode, lhs, rhs); + return rhs; + } + + case ASSIGN_SHR: + case ASSIGN_ADD: + case ASSIGN_SUB: + case ASSIGN_MUL: + case ASSIGN_DIV: + case ASSIGN_MOD: + case ASSIGN_TRUNC: { + Token basicOperator = getBasicOperator(node, operator); + Type type = analyzeBinaryOperator(node, lhs, basicOperator, lhsNode, rhsNode); + checkAssignable(node, lhs, type); + return type; + } + + case OR: + case AND: { + checkAssignable(lhsNode, boolType, lhs); + checkAssignable(boolType, rhsNode); + return boolType; + } + + case ASSIGN_BIT_OR: + case ASSIGN_BIT_XOR: + case ASSIGN_BIT_AND: + case ASSIGN_SHL: + case ASSIGN_SAR: { + // Bit operations are only supported by integers and + // thus cannot be looked up on num. To ease usage of + // bit operations, we currently allow them to be used + // if the left-hand-side is of type num. + // TODO(karlklose) find a clean solution, i.e., without a special case for num. + if (lhs.equals(numType)) { + checkAssignable(rhsNode, numType, typeOf(rhsNode)); + return intType; + } else { + Token basicOperator = getBasicOperator(node, operator); + Type type = analyzeBinaryOperator(node, lhs, basicOperator, lhsNode, rhsNode); + checkAssignable(node, lhs, type); + return type; + } + } + + case BIT_OR: + case BIT_XOR: + case BIT_AND: + case SHL: + case SAR: { + // Bit operations are only supported by integers and + // thus cannot be looked up on num. To ease usage of + // bit operations, we currently allow them to be used + // if the left-hand-side is of type num. + // TODO(karlklose) find a clean solution, i.e., without a special case for num. + if (lhs.equals(numType)) { + checkAssignable(rhsNode, numType, typeOf(rhsNode)); + return intType; + } else { + return analyzeBinaryOperator(node, lhs, operator, lhsNode, rhsNode); + } + } + + case SHR: + case ADD: + case SUB: + case MUL: + case DIV: + case TRUNC: + case MOD: + case LT: + case GT: + case LTE: + case GTE: + return analyzeBinaryOperator(node, lhs, operator, lhsNode, rhsNode); + + case EQ: + case NE: + case EQ_STRICT: + case NE_STRICT: + nonVoidTypeOf(rhsNode); + return boolType; + + case IS: + if (rhsNode instanceof DartUnaryExpression) { + assert ((DartUnaryExpression) rhsNode).getOperator() == Token.NOT; + nonVoidTypeOf(((DartUnaryExpression) rhsNode).getArg()); + } else { + nonVoidTypeOf(rhsNode); + } + return boolType; + + case COMMA: + return typeOf(rhsNode); + + default: + throw new AssertionError("Unknown operator: " + operator); + } + } + + @Override + public Type visitVariableStatement(DartVariableStatement node) { + Type type = typeOf(node.getTypeNode()); + visit(node.getVariables()); + return type; + } + + private List analyzeArgumentTypes(List argumentNodes) { + List argumentTypes = new ArrayList(argumentNodes.size()); + for (DartExpression argumentNode : argumentNodes) { + argumentTypes.add(nonVoidTypeOf(argumentNode)); + } + return argumentTypes; + } + + private Member lookupMember(Type receiver, String methodName, DartNode diagnosticNode) { + InterfaceType itype = types.getInterfaceType(receiver); + if (itype == null) { + diagnoseNonInterfaceType(diagnosticNode, receiver); + return null; + } + Member member = itype.lookupMember(methodName); + if (member == null) { + typeError(diagnosticNode, DartCompilerErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, + receiver, methodName); + return null; + } + return member; + } + + private void checkAssignable(DartNode node, Type t, Type s) { + t.getClass(); // Null check. + s.getClass(); // Null check. + if (!types.isAssignable(t, s)) { + typeError(node, DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); + } + } + + private void checkAssignable(Type targetType, DartExpression node) { + checkAssignable(node, targetType, nonVoidTypeOf(node)); + } + + private Type analyzeMethodInvocation(Type receiver, Member member, String name, + DartNode diagnosticNode, + List argumentTypes, + List argumentNodes) { + if (member == null) { + return dynamicType; + } + FunctionType ftype; + switch (ElementKind.of(member.getElement())) { + case METHOD: { + MethodElement method = (MethodElement) member.getElement(); + if (method.getModifiers().isStatic()) { + return typeError(diagnosticNode, DartCompilerErrorCode.IS_STATIC_METHOD_IN, + name, receiver); + } + ftype = (FunctionType) member.getType(); + break; + } + case FIELD: { + FieldElement field = (FieldElement) member.getElement(); + if (field.getModifiers().isStatic()) { + return typeError(diagnosticNode, DartCompilerErrorCode.IS_STATIC_FIELD_IN, + name, receiver); + } + switch (TypeKind.of(member.getType())) { + case FUNCTION: + ftype = (FunctionType) member.getType(); + break; + case FUNCTION_ALIAS: + ftype = types.asFunctionType((FunctionAliasType) member.getType()); + break; + case DYNAMIC: + return member.getType(); + default: + return typeError(diagnosticNode, DartCompilerErrorCode.NOT_A_METHOD_IN, + name, receiver); + } + break; + } + default: + return typeError(diagnosticNode, DartCompilerErrorCode.NOT_A_METHOD_IN, name, receiver); + } + return checkArguments(diagnosticNode, argumentNodes, argumentTypes.iterator(), ftype); + } + + private Type diagnoseNonInterfaceType(DartNode node, Type type) { + switch (TypeKind.of(type)) { + case DYNAMIC: + return type; + + case FUNCTION: + case FUNCTION_ALIAS: + case INTERFACE: + case VARIABLE: + // Cannot happen. + throw internalError(node, type.toString()); + + case NONE: + throw internalError(node, "type is null"); + + case VOID: + return typeError(node, DartCompilerErrorCode.VOID); + + default: + throw internalError(node, type.getKind().name()); + } + } + + private Type checkArguments(DartNode diagnosticNode, + List argumentNodes, + Iterator argumentTypes, FunctionType ftype) { + int argumentCount = 0; + List parameterTypes = ftype.getParameterTypes(); + for (Type parameterType : parameterTypes) { + if (argumentTypes.hasNext()) { + checkAssignable(argumentNodes.get(argumentCount), parameterType, argumentTypes.next()); + argumentCount++; + } else { + typeError(diagnosticNode, DartCompilerErrorCode.MISSING_ARGUMENT, parameterType); + } + } + Map namedParameterTypes = ftype.getNamedParameterTypes(); + Iterator named = namedParameterTypes.values().iterator(); + while (named.hasNext() && argumentTypes.hasNext()) { + checkAssignable(argumentNodes.get(argumentCount), named.next(), argumentTypes.next()); + argumentCount++; + } + while (ftype.hasRest() && argumentTypes.hasNext()) { + checkAssignable(argumentNodes.get(argumentCount), ftype.getRest(), argumentTypes.next()); + argumentCount++; + } + while (argumentTypes.hasNext()) { + argumentTypes.next(); + typeError(argumentNodes.get(argumentCount), DartCompilerErrorCode.EXTRA_ARGUMENT); + argumentCount++; + } + return ftype.getReturnType(); + } + + @Override + public Type visitTypeNode(DartTypeNode node) { + return validateTypeNode(node, false); + } + + private Type validateTypeNode(DartTypeNode node, boolean badBoundIsError) { + Type type = node.getType(); // Already calculated by resolver. + switch (TypeKind.of(type)) { + case NONE: + return typeError(node, DartCompilerErrorCode.INTERNAL_ERROR, + String.format("type \"%s\" is null", node)); + + case INTERFACE: { + InterfaceType itype = (InterfaceType) type; + validateBounds(node.getTypeArguments(), + itype.getArguments(), itype.getElement().getTypeParameters(), + badBoundIsError); + return itype; + } + + default: + return type; + } + } + + private void validateBounds(List diagnosticNodes, + List arguments, + List parameters, + boolean badBoundIsError) { + if (arguments.size() == parameters.size() && arguments.size() == diagnosticNodes.size()) { + List bounds = new ArrayList(parameters.size()); + for (Type parameter : parameters) { + TypeVariable variable = (TypeVariable) parameter; + Type bound = variable.getTypeVariableElement().getBound(); + if (bound == null) { + internalError(variable.getElement().getNode(), "bound is null"); + } + bounds.add(bound); + } + bounds = Types.subst(bounds, arguments, parameters); + for (int i = 0; i < arguments.size(); i++) { + Type t = bounds.get(i); + Type s = arguments.get(i); + if (!types.isAssignable(t, s)) { + if (badBoundIsError) { + resolutionError(diagnosticNodes.get(i), + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); + } else { + typeError(diagnosticNodes.get(i), + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, s, t); + } + } + } + } + } + + Type typeOf(DartNode node) { + if (node == null) { + return dynamicType; + } + return node.accept(this); + } + + private Type nonVoidTypeOf(DartNode node) { + Type type = typeOf(node); + if (type.getKind().equals(TypeKind.VOID)) { + return typeError(node, DartCompilerErrorCode.VOID); + } + return type; + } + + @Override + public Type visitArrayAccess(DartArrayAccess node) { + Type target = typeOf(node.getTarget()); + return analyzeBinaryOperator(node, target, Token.INDEX, node, node.getKey()); + } + + @Override + public Type visitAssertion(DartAssertion node) { + DartExpression conditionNode = node.getExpression(); + Type condition = nonVoidTypeOf(conditionNode); + switch (condition.getKind()) { + case FUNCTION: + FunctionType ftype = (FunctionType) condition; + Type returnType = ftype.getReturnType(); + if (returnType.getKind().equals(TypeKind.VOID)) { + typeError(conditionNode, DartCompilerErrorCode.VOID); + } + checkAssignable(conditionNode, boolType, returnType); + break; + + default: + checkAssignable(conditionNode, boolType, condition); + break; + } + checkAssignable(stringType, node.getMessage()); + return voidType; + } + + @Override + public Type visitBlock(DartBlock node) { + return typeAsVoid(node); + } + + private Type typeAsVoid(DartNode node) { + node.visitChildren(this); + return voidType; + } + + @Override + public Type visitBreakStatement(DartBreakStatement node) { + return voidType; + } + + @Override + public Type visitFunctionObjectInvocation(DartFunctionObjectInvocation node) { + return checkInvocation(node, node, null, typeOf(node.getTarget())); + } + + @Override + public Type visitMethodInvocation(DartMethodInvocation node) { + String name = node.getFunctionNameString(); + Element element = (Element) node.getTargetSymbol(); + if (element != null && element.getModifiers().isStatic()) { + return checkInvocation(node, node, name, element.getType()); + } + Type receiver = nonVoidTypeOf(node.getTarget()); + List arguments = node.getArgs(); + return analyzeMethodInvocation(receiver, lookupMember(receiver, name, node), name, + node.getFunctionName(), analyzeArgumentTypes(arguments), + arguments); + } + + @Override + public Type visitSuperConstructorInvocation(DartSuperConstructorInvocation node) { + return checkConstructorForwarding(node, node.getSymbol()); + } + + private Type checkConstructorForwarding(DartInvocation node, ConstructorElement element) { + if (element == null) { + visit(node.getArgs()); + return voidType; + } else { + checkInvocation(node, node, null, typeAsMemberOf(element, currentClass)); + return voidType; + } + } + + @Override + public Type visitCase(DartCase node) { + node.visitChildren(this); + return voidType; + } + + @Override + public Type visitClass(DartClass node) { + ClassElement element = node.getSymbol(); + InterfaceType type = element.getType(); + findUnimplementedMembers(element); + setCurrentClass(type); + visit(node.getTypeParameters()); + if (node.getSuperclass() != null) { + validateTypeNode(node.getSuperclass(), true); + } + if (node.getInterfaces() != null) { + for (DartTypeNode interfaceNode : node.getInterfaces()) { + validateTypeNode(interfaceNode, true); + } + } + if (node.getDefaultClass() != null) { + validateTypeNode(node.getDefaultClass(), true); + } + visit(node.getMembers()); + setCurrentClass(null); + return type; + } + + private List findUnimplementedMembers(ClassElement element) { + if (element.isInterface()) { + element.getNode().accept(new AbstractMethodFinder(element.getType())); + return Collections.emptyList(); + } + List members = unimplementedElements.get(element); + if (members != null) { + return members; + } + synchronized (element) { + members = unimplementedElements.get(element); + if (members != null) { + return members; + } + AbstractMethodFinder finder = new AbstractMethodFinder(element.getType()); + element.getNode().accept(finder); + unimplementedElements.put(element, finder.unimplementedElements); + return finder.unimplementedElements; + } + } + + @Override + public Type visitConditional(DartConditional node) { + checkCondition(node.getCondition()); + Type left = typeOf(node.getThenExpression()); + Type right = typeOf(node.getElseExpression()); + return types.leastUpperBound(left, right); + } + + private Type checkCondition(DartExpression condition) { + Type type = nonVoidTypeOf(condition); + checkAssignable(condition, boolType, type); + return type; + } + + @Override + public Type visitContinueStatement(DartContinueStatement node) { + return voidType; + } + + @Override + public Type visitDefault(DartDefault node) { + node.visitChildren(this); + return typeAsVoid(node); + } + + @Override + public Type visitDoWhileStatement(DartDoWhileStatement node) { + checkCondition(node.getCondition()); + typeOf(node.getBody()); + return voidType; + } + + @Override + public Type visitEmptyStatement(DartEmptyStatement node) { + return typeAsVoid(node); + } + + @Override + public Type visitExprStmt(DartExprStmt node) { + typeOf(node.getExpression()); + return voidType; + } + + @Override + public Type visitFieldDefinition(DartFieldDefinition node) { + node.visitChildren(this); + return voidType; + } + + @Override + public Type visitForInStatement(DartForInStatement node) { + return typeAsVoid(node); + } + + @Override + public Type visitForStatement(DartForStatement node) { + typeOf(node.getInit()); + checkCondition(node.getCondition()); + typeOf(node.getIncrement()); + typeOf(node.getBody()); + return voidType; + } + + @Override + public Type visitFunction(DartFunction node) { + Type previous = expected; + visit(node.getParams()); + expected = typeOf(node.getReturnTypeNode()); + typeOf(node.getBody()); + expected = previous; + return voidType; + } + + @Override + public Type visitFunctionExpression(DartFunctionExpression node) { + node.visitChildren(this); + return ((Element) node.getSymbol()).getType(); + } + + @Override + public Type visitFunctionTypeAlias(DartFunctionTypeAlias node) { + return typeAsVoid(node); + } + + @Override + public Type visitIdentifier(DartIdentifier node) { + Element element = node.getTargetSymbol(); + Type type; + switch (ElementKind.of(element)) { + case VARIABLE: + case PARAMETER: + case FUNCTION_OBJECT: + type = element.getType(); + break; + + case FIELD: + case METHOD: + type = typeAsMemberOf(element, currentClass); + break; + + case NONE: + return typeError(node, DartCompilerErrorCode.CANNOT_BE_RESOLVED, node.getTargetName()); + + case DYNAMIC: + return element.getType(); + + default: + return voidType; + } + node.setReferencedElement(element); + return type; + } + + @Override + public Type visitIfStatement(DartIfStatement node) { + checkCondition(node.getCondition()); + typeOf(node.getThenStatement()); + typeOf(node.getElseStatement()); + return voidType; + } + + @Override + public Type visitInitializer(DartInitializer node) { + DartIdentifier name = node.getName(); + if (name != null) { + checkAssignable(typeOf(name), node.getValue()); + } else { + typeOf(node.getValue()); + } + return voidType; + } + + @Override + public Type visitLabel(DartLabel node) { + return typeAsVoid(node); + } + + @Override + public Type visitMapLiteral(DartMapLiteral node) { + visit(node.getTypeArguments()); + InterfaceType type = node.getType(); + + // This ensures that the declared type is assignable to Map. + // For example, the user should not write Map. + checkAssignable(node, type, defaultLiteralMapType); + + Type valueType = type.getArguments().get(1); + // Check the map literal entries against the return type. + for (DartMapLiteralEntry literalEntry : node.getEntries()) { + checkAssignable(literalEntry, typeOf(literalEntry), valueType); + } + return type; + } + + @Override + public Type visitMapLiteralEntry(DartMapLiteralEntry node) { + nonVoidTypeOf(node.getKey()); + return nonVoidTypeOf(node.getValue()); + } + + @Override + public Type visitMethodDefinition(DartMethodDefinition node) { + MethodElement methodElement = node.getSymbol(); + FunctionType type = methodElement.getFunctionType(); + if (methodElement.getModifiers().isFactory()) { + analyzeFactory(node.getName(), (ConstructorElement) methodElement); + } else { + if (!type.getTypeVariables().isEmpty()) { + internalError(node, "generic methods are not supported"); + } + } + return typeAsVoid(node); + } + + private void analyzeFactory(DartExpression name, final ConstructorElement methodElement) { + DartNodeTraverser visitor = new DartNodeTraverser() { + @Override + public Void visitParameterizedNode(DartParameterizedNode node) { + DartExpression expression = node.getExpression(); + Element e = null; + if (expression instanceof DartIdentifier) { + e = ((DartIdentifier) expression).getTargetSymbol(); + } else if (expression instanceof DartPropertyAccess) { + e = ((DartPropertyAccess) expression).getTargetSymbol(); + } + if (!ElementKind.of(e).equals(ElementKind.CLASS)) { + return null; + } + ClassElement cls = (ClassElement) e; + InterfaceType type = cls.getType(); + List parameterNodes = node.getTypeParameters(); + List arguments = type.getArguments(); + if (parameterNodes.size() == 0) { + return null; + } + Analyzer.this.visit(parameterNodes); + List typeVariables = methodElement.getFunctionType().getTypeVariables(); + validateBounds(parameterNodes, arguments, typeVariables, true); + return null; + } + }; + name.accept(visitor); + } + + @Override + public Type visitNewExpression(DartNewExpression node) { + ConstructorElement element = node.getSymbol(); + DartTypeNode typeNode = Types.constructorTypeNode(node); + DartNode typeName = typeNode.getIdentifier(); + InterfaceType type = (InterfaceType) validateTypeNode(typeNode, true); + if (element == null) { + visit(node.getArgs()); + } else { + ClassElement cls = (ClassElement) element.getEnclosingElement(); + + List unimplementedMembers = findUnimplementedMembers(cls); + if (unimplementedMembers.size() > 0) { + if (diagnosedAbstractClasses.add(cls)) { + StringBuilder sb = new StringBuilder(); + for (Element member : unimplementedMembers) { + sb.append("\n # From "); + ClassElement enclosingElement = (ClassElement) member.getEnclosingElement(); + InterfaceType instance = types.asInstanceOf(cls.getType(), enclosingElement); + Type memberType = member.getType().subst(instance.getArguments(), + enclosingElement.getTypeParameters()); + sb.append(enclosingElement.getName()); + sb.append(":\n "); + if (memberType.getKind().equals(TypeKind.FUNCTION)) { + FunctionType ftype = (FunctionType) memberType; + sb.append(ftype.getReturnType()); + sb.append(" "); + sb.append(member.getName()); + String string = ftype.toString(); + sb.append(string, 0, string.lastIndexOf(" -> ")); + } else { + sb.append(memberType); + sb.append(" "); + sb.append(member.getName()); + } + } + DartNode clsNode = cls.getNode(); + if (clsNode != null) { + typeError(typeName, DartCompilerErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, + cls.getName()); + typeError(clsNode, DartCompilerErrorCode.ABSTRACT_CLASS, cls.getName(), sb); + } else { + typeError(typeName, DartCompilerErrorCode.ABSTRACT_CLASS, cls.getName(), sb); + } + } else { + typeError(typeName, DartCompilerErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, + cls.getName()); + } + } + FunctionType ftype = (FunctionType) element.getType(); + List arguments = type.getArguments(); + ftype = (FunctionType) ftype.subst(arguments, + type.getElement().getTypeParameters()); + List typeVariables = ftype.getTypeVariables(); + if (arguments.size() == typeVariables.size()) { + ftype = (FunctionType) ftype.subst(arguments, typeVariables); + } + checkInvocation(node, node, null, ftype); + } + return type; + } + + @Override + public Type visitNullLiteral(DartNullLiteral node) { + return nullType; + } + + @Override + public Type visitParameter(DartParameter node) { + VariableElement parameter = node.getSymbol(); + FieldElement initializerElement = parameter.getParameterInitializerElement(); + if (initializerElement != null) { + checkAssignable(node, parameter.getType(), initializerElement.getType()); + } + return checkInitializedDeclaration(node, node.getDefaultExpr()); + } + + @Override + public Type visitParenthesizedExpression(DartParenthesizedExpression node) { + return node.getExpression().accept(this); + } + + @Override + public Type visitPropertyAccess(DartPropertyAccess node) { + Element element = node.getTargetSymbol(); + if (element != null && element.getModifiers().isStatic()) { + return element.getType(); + } + DartNode qualifier = node.getQualifier(); + Type receiver = nonVoidTypeOf(qualifier); + InterfaceType cls = types.getInterfaceType(receiver); + if (cls == null) { + return diagnoseNonInterfaceType(qualifier, receiver); + } + // Do not visit the name, it may not have been resolved. + String name = node.getPropertyName(); + InterfaceType.Member member = cls.lookupMember(name); + if (member == null) { + return typeError(node.getName(), DartCompilerErrorCode.NOT_A_MEMBER_OF, name, cls); + } + element = member.getElement(); + Modifiers modifiers = element.getModifiers(); + if (modifiers.isStatic()) { + return typeError(node.getName(), + DartCompilerErrorCode.STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE, + name, element.getName()); + } + switch (element.getKind()) { + case CONSTRUCTOR: + return typeError(node.getName(), DartCompilerErrorCode.MEMBER_IS_A_CONSTRUCTOR, + name, element.getName()); + + case METHOD: + case FIELD: + return member.getType(); + + default: + throw internalError(node.getName(), "unexpected kind %s", element.getKind()); + } + } + + @Override + public Type visitReturnStatement(DartReturnStatement node) { + DartExpression value = node.getValue(); + Type type; + if (value == null) { + if (!types.isSubtype(voidType, expected)) { + typeError(node, DartCompilerErrorCode.MISSING_RETURN_VALUE, expected); + } + } else { + type = typeOf(value); + if (expected.equals(voidType)) { + if (value != null) { + typeError(value, DartCompilerErrorCode.VOID_CANNOT_RETURN_VALUE); + return voidType; + } + } + checkAssignable(value == null ? node : value, expected, type); + } + return voidType; + } + + @Override + public Type visitSuperExpression(DartSuperExpression node) { + if (currentClass == null) { + return dynamicType; + } else { + return currentClass.getElement().getSupertype(); + } + } + + @Override + public Type visitSwitchStatement(DartSwitchStatement node) { + return typeAsVoid(node); + } + + @Override + public Type visitSyntheticErrorExpression(DartSyntheticErrorExpression node) { + return dynamicType; + } + + @Override + public Type visitSyntheticErrorStatement(DartSyntheticErrorStatement node) { + return dynamicType; + } + + @Override + public Type visitThisExpression(DartThisExpression node) { + return getCurrentClass(); + } + + @Override + public Type visitThrowStatement(DartThrowStatement node) { + return typeAsVoid(node); + } + + @Override + public Type visitCatchBlock(DartCatchBlock node) { + typeOf(node.getException()); + // TODO(karlklose) Check type of stack trace variable. + typeOf(node.getStackTrace()); + typeOf(node.getBlock()); + return voidType; + } + + @Override + public Type visitTryStatement(DartTryStatement node) { + return typeAsVoid(node); + } + + @Override + public Type visitUnaryExpression(DartUnaryExpression node) { + DartExpression expression = node.getArg(); + Type type = nonVoidTypeOf(expression); + Token operator = node.getOperator(); + switch (operator) { + case BIT_NOT: + // Bit operations are only supported by integers and + // thus cannot be looked up on num. To ease usage of + // bit operations, we currently allow them to be used + // if the left-hand-side is of type num. + // TODO(karlklose) find a clean solution, i.e., without a special case for num. + if (type.equals(numType)) { + return intType; + } else { + String name = methodNameForUnaryOperator(node, operator); + Member member = lookupMember(type, name, node); + if (member != null) { + node.setReferencedElement(member.getElement()); + return analyzeMethodInvocation(type, member, name, node, + Collections.emptyList(), + Collections.emptyList()); + } else { + return dynamicType; + } + } + case NOT: + checkAssignable(boolType, expression); + return boolType; + case SUB: + case INC: + case DEC: { + if (type.getElement().isDynamic()) { + return type; + } + InterfaceType itype = types.getInterfaceType(type); + String operatorMethodName = methodNameForUnaryOperator(node, operator); + Member member = itype.lookupMember(operatorMethodName); + if (member == null) { + return typeError(expression, DartCompilerErrorCode.CANNOT_BE_RESOLVED, + operatorMethodName); + } + MethodElement element = ((MethodElement) member.getElement()); + node.setReferencedElement(element); + Type returnType = ((FunctionType) member.getType()).getReturnType(); + if (operator == Token.INC || operator == Token.DEC) { + // For INC and DEC, "operator +" and "operator -" are used to add and subtract one, + // respectively. Check that the resolved operator has a compatible parameter type. + Iterator it = element.getParameters().iterator(); + if (!types.isAssignable(numType, it.next().getType())) { + typeError(node, DartCompilerErrorCode.OPERATOR_WRONG_OPERAND_TYPE, + operatorMethodName, numType.toString()); + } + // Check that the return type of the operator is compatible with the receiver. + checkAssignable(node, type, returnType); + } + return node.isPrefix() ? returnType : type; + } + default: + throw internalError(node, "unknown operator %s", operator.toString()); + } + } + + @Override + public Type visitUnit(DartUnit node) { + return typeAsVoid(node); + } + + @Override + public Type visitUnqualifiedInvocation(DartUnqualifiedInvocation node) { + DartIdentifier target = node.getTarget(); + String name = target.getTargetName(); + Element element = target.getTargetSymbol(); + Type type; + switch (ElementKind.of(element)) { + case FIELD: + case METHOD: + type = typeAsMemberOf(element, currentClass); + break; + case NONE: + return typeError(target, DartCompilerErrorCode.NOT_A_METHOD_IN, name, currentClass); + default: + type = element.getType(); + break; + } + return checkInvocation(node, target, name, type); + } + + private Type checkInvocation(DartInvocation node, DartNode diagnosticNode, String name, + Type type) { + List argumentNodes = node.getArgs(); + List argumentTypes = new ArrayList(argumentNodes.size()); + for (DartExpression argumentNode : argumentNodes) { + argumentTypes.add(nonVoidTypeOf(argumentNode)); + } + switch (TypeKind.of(type)) { + case FUNCTION_ALIAS: + return checkArguments(node, argumentNodes, argumentTypes.iterator(), + types.asFunctionType((FunctionAliasType) type)); + case FUNCTION: + return checkArguments(node, argumentNodes, argumentTypes.iterator(), (FunctionType) type); + case DYNAMIC: + return type; + default: + if (types.isAssignable(functionType, type)) { + // A subtype of interface Function. + return dynamicType; + } else if (name == null) { + return typeError(diagnosticNode, DartCompilerErrorCode.NOT_A_FUNCTION, type); + } else { + return typeError(diagnosticNode, DartCompilerErrorCode.NOT_A_METHOD_IN, name, + currentClass); + } + } + } + + /** + * Return the type of member as if it was a member of subtype. For example, the type of t in Sub + * should be String, not T: + * + *

    +     *   class Super<T> {
    +     *     T t;
    +     *   }
    +     *   class Sub extends Super<String> {
    +     *   }
    +     * 
    + */ + private Type typeAsMemberOf(Element member, InterfaceType subtype) { + Element holder = member.getEnclosingElement(); + if (!ElementKind.of(holder).equals(ElementKind.CLASS)) { + return member.getType(); + } + ClassElement superclass = (ClassElement) holder; + InterfaceType supertype = types.asInstanceOf(subtype, superclass); + Type type = member.getType().subst(supertype.getArguments(), + supertype.getElement().getTypeParameters()); + return type; + } + + @Override + public Type visitVariable(DartVariable node) { + return checkInitializedDeclaration(node, node.getValue()); + } + + @Override + public Type visitWhileStatement(DartWhileStatement node) { + checkCondition(node.getCondition()); + typeOf(node.getBody()); + return voidType; + } + + @Override + public Type visitNamedExpression(DartNamedExpression node) { + // TODO(jgw): Checking of named parameters in progress. + + // Intentionally skip the expression's name -- it's stored as an identifier, but doesn't need + // to be resolved or type-checked. + return node.getExpression().accept(this); + } + + @Override + public Type visitTypeExpression(DartTypeExpression node) { + return typeOf(node.getTypeNode()); + } + + @Override + public Type visitTypeParameter(DartTypeParameter node) { + if (node.getBound() != null) { + validateTypeNode(node.getBound(), true); + } + return voidType; + } + + @Override + public Type visitNativeBlock(DartNativeBlock node) { + return typeAsVoid(node); + } + + @Override + public void visit(List nodes) { + if (nodes != null) { + for (DartNode node : nodes) { + node.accept(this); + } + } + } + + @Override + public Type visitArrayLiteral(DartArrayLiteral node) { + visit(node.getTypeArguments()); + InterfaceType type = node.getType(); + Type elementType = type.getArguments().get(0); + for (DartExpression expression : node.getExpressions()) { + checkAssignable(elementType, expression); + } + return type; + } + + @Override + public Type visitBooleanLiteral(DartBooleanLiteral node) { + return typeOfLiteral(node); + } + + @Override + public Type visitDoubleLiteral(DartDoubleLiteral node) { + return typeOfLiteral(node); + } + + @Override + public Type visitField(DartField node) { + DartMethodDefinition accessor = node.getAccessor(); + if (accessor != null) { + return typeOf(accessor); + } else { + return checkInitializedDeclaration(node, node.getValue()); + } + } + + private Type checkInitializedDeclaration(DartDeclaration node, DartExpression value) { + if (value != null) { + checkAssignable(node.getSymbol().getType(), value); + } + return voidType; + } + + @Override + public Type visitIntegerLiteral(DartIntegerLiteral node) { + return typeOfLiteral(node); + } + + @Override + public Type visitStringLiteral(DartStringLiteral node) { + return typeOfLiteral(node); + } + + @Override + public Type visitStringInterpolation(DartStringInterpolation node) { + visit(node.getExpressions()); + return typeOfLiteral(node); + } + + @Override + public Type visitParameterizedNode(DartParameterizedNode node) { + throw internalError(node, "unexpected node"); + } + + @Override + public Type visitImportDirective(DartImportDirective node) { + return typeAsVoid(node); + } + + @Override + public Type visitLibraryDirective(DartLibraryDirective node) { + return typeAsVoid(node); + } + + @Override + public Type visitNativeDirective(DartNativeDirective node) { + return typeAsVoid(node); + } + + @Override + public Type visitResourceDirective(DartResourceDirective node) { + return typeAsVoid(node); + } + + @Override + public Type visitSourceDirective(DartSourceDirective node) { + return typeAsVoid(node); + } + + @SuppressWarnings("hiding") + private class AbstractMethodFinder extends DartNodeTraverser { + private final InterfaceType currentClass; + private final Multimap superMembers; + private final List unimplementedElements; + + private AbstractMethodFinder(InterfaceType currentClass) { + this.currentClass = currentClass; + this.superMembers = LinkedListMultimap.create(); + this.unimplementedElements = new ArrayList(); + } + + @Override + public Void visitNode(DartNode node) { + throw new AssertionError(); + } + + @Override + public Void visitClass(DartClass node) { + assert node.getSymbol().getType() == currentClass; + List supertypes = Collections.emptyList(); + try { + supertypes = currentClass.getElement().getAllSupertypes(); + } catch (CyclicDeclarationException e) { + // Already reported by resolver. + } catch (DuplicatedInterfaceException e) { + // Already reported by resolver. + } + EnclosingElement currentLibrary = currentClass.getElement().getEnclosingElement(); + for (InterfaceType supertype : supertypes) { + for (Element member : supertype.getElement().getMembers()) { + String name = member.getName(); + if (name.startsWith("_")) { + if (currentLibrary != member.getEnclosingElement().getEnclosingElement()) { + continue; + } + } + superMembers.put(name, member); + } + } + this.visit(node.getMembers()); + if (currentClass.getElement().isInterface()) { + return null; + } + InterfaceType supertype = currentClass.getElement().getSupertype(); + while (supertype != null) { + ClassElement superclass = supertype.getElement(); + for (Element member : superclass.getMembers()) { + superMembers.removeAll(member.getName()); + } + supertype = supertype.getElement().getSupertype(); + } + for (String name : superMembers.keys()) { + Collection elements = superMembers.removeAll(name); + for (Element element : elements) { + if (!element.getModifiers().isStatic()) { + unimplementedElements.add(element); + break; // Only report the first unimplemented element with this name. + } + } + } + return null; + } + + @Override + public Void visitFieldDefinition(DartFieldDefinition node) { + this.visit(node.getFields()); + return null; + } + + @Override + public Void visitField(DartField node) { + if (superMembers != null) { + FieldElement field = node.getSymbol(); + String name = field.getName(); + List overridden = new ArrayList(superMembers.removeAll(name)); + for (Element element : overridden) { + if (canOverride(node.getName(), field.getModifiers(), element)) { + switch (element.getKind()) { + case FIELD: + checkOverride(node.getName(), field, element); + break; + case METHOD: + typeError(node, DartCompilerErrorCode.SUPERTYPE_HAS_METHOD, name, + element.getEnclosingElement().getName()); + break; + + default: + typeError(node, DartCompilerErrorCode.INTERNAL_ERROR, element); + break; + } + } + } + } + return null; + } + + @Override + public Void visitMethodDefinition(DartMethodDefinition node) { + MethodElement method = node.getSymbol(); + String name = method.getName(); + if (superMembers != null && !method.isConstructor()) { + Collection overridden = superMembers.removeAll(name); + for (Element element : overridden) { + if (canOverride(node.getName(), method.getModifiers(), element)) { + switch (element.getKind()) { + case METHOD: + checkOverride(node.getName(), method, element); + break; + + case FIELD: + typeError(node, DartCompilerErrorCode.SUPERTYPE_HAS_FIELD, element.getName(), + element.getEnclosingElement().getName()); + break; + + default: + typeError(node, DartCompilerErrorCode.INTERNAL_ERROR, element); + break; + } + } + } + } + return null; + } + + /** + * Report a compile-time error if either modifiers or elements.getModifiers() is static. + * @returns true if no compile-time error was reported + */ + private boolean canOverride(DartExpression node, Modifiers modifiers, Element element) { + if (element.getModifiers().isStatic()) { + resolutionError(node, DartCompilerErrorCode.CANNOT_OVERRIDE_STATIC_MEMBER, + element.getName(), element.getEnclosingElement().getName()); + return false; + } else if (modifiers.isStatic()) { + resolutionError(node, DartCompilerErrorCode.CANNOT_OVERRIDE_INSTANCE_MEMBER, + element.getName(), element.getEnclosingElement().getName()); + return false; + } + return true; + } + + /** + * Report a static type error if member cannot override superElement, that is, they are not + * assignable. + */ + private void checkOverride(DartExpression node, Element member, Element superElement) { + String name = member.getName(); + Type superMember = typeAsMemberOf(superElement, currentClass); + if (!types.isAssignable(superMember, member.getType())) { + typeError(node, DartCompilerErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, + name, superElement.getEnclosingElement().getName(), + member.getType(), superMember); + } + } + } + } +} diff --git a/compiler/java/com/google/dart/compiler/type/TypeKind.java b/compiler/java/com/google/dart/compiler/type/TypeKind.java new file mode 100644 index 00000000000..8564dbbccdc --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/TypeKind.java @@ -0,0 +1,27 @@ +// 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. + +package com.google.dart.compiler.type; + +/** + * Kinds of types. Use kinds instead of instanceof for maximum flexibility + * and sharing of similar implementation classes. + */ +public enum TypeKind { + DYNAMIC, + FUNCTION, + FUNCTION_ALIAS, + INTERFACE, + NONE, + VOID, + VARIABLE; + + public static TypeKind of(Type type) { + if (type == null) { + return NONE; + } else { + return type.getKind(); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/type/TypeVariable.java b/compiler/java/com/google/dart/compiler/type/TypeVariable.java new file mode 100644 index 00000000000..b3b98b4bdbe --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/TypeVariable.java @@ -0,0 +1,34 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.TypeVariableElement; + +/** + * Represents a type variable, that is the type parameters of a class + * or interface type. For example, in {@code class Array { ... }}, + * E is a type variable. + * + *

    Each class/interface should have its own unique type variables, + * one for each type parameter. A class/interface with type parameters + * is said to be parameterized or generic. + * + *

    Non-static members, constructors, and factories of generic + * class/interface can refer to type variables of the current class + * (not of supertypes). + * + *

    When using a generic type, also known as an application or + * instantiation of the type, the actual type arguments should be + * substituted for the type variables in the class declaration. + * + *

    For example, given a box, {@code class Box { T value; }}, the + * type of the expression {@code new Box().value} is + * {@code String} because we must substitute {@code String} for the + * the type variable {@code T}. + */ +public interface TypeVariable extends Type { + // Work around JDK 6 bug. Should @Override getElement(). + TypeVariableElement getTypeVariableElement(); +} diff --git a/compiler/java/com/google/dart/compiler/type/TypeVariableImplementation.java b/compiler/java/com/google/dart/compiler/type/TypeVariableImplementation.java new file mode 100644 index 00000000000..636c4247935 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/TypeVariableImplementation.java @@ -0,0 +1,74 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.TypeVariableElement; + +import java.util.Iterator; +import java.util.List; + +/** + * Default implementation of {@link TypeVariable}. + */ +class TypeVariableImplementation extends AbstractType implements TypeVariable { + private final TypeVariableElement element; + + public TypeVariableImplementation(TypeVariableElement element) { + this.element = element; + } + + @Override + public TypeVariableElement getElement() { + return element; + } + + @Override + public TypeVariableElement getTypeVariableElement() { + return getElement(); + } + + @Override + public String toString() { + Element owner = element.getDeclaringElement(); + if (owner == null) { + return element.getName(); + } else { + return owner.getName() + "." + element.getName(); + } + } + + @Override + public Type subst(List arguments, List parameters) { + Iterator itA = arguments.iterator(); + Iterator itP = parameters.iterator(); + while (itA.hasNext() && itP.hasNext()) { + Type argument = itA.next(); + Type parameter = itP.next(); + if (equals(parameter)) { + return argument; + } + } + + // O(1) check to assert arguments and parameters are of same size. + assert itA.hasNext() == itP.hasNext() : + "arguments: " + arguments + " parameters: " + parameters; + return this; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TypeVariable) { + TypeVariable other = (TypeVariable) obj; + return element.equals(other.getElement()); + } + return false; + } + + @Override + public TypeKind getKind() { + return TypeKind.VARIABLE; + } +} diff --git a/compiler/java/com/google/dart/compiler/type/Types.java b/compiler/java/com/google/dart/compiler/type/Types.java new file mode 100644 index 00000000000..48ddc1985a9 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/Types.java @@ -0,0 +1,428 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.common.annotations.VisibleForTesting; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.ConstructorElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.FunctionAliasElement; +import com.google.dart.compiler.resolver.ResolutionErrorListener; +import com.google.dart.compiler.resolver.TypeVariableElement; +import com.google.dart.compiler.resolver.VariableElement; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Utility class for types. + */ +public class Types { + private final CoreTypeProvider typeProvider; + + private Types(CoreTypeProvider typeProvider) { // Prevent subclassing. + this.typeProvider = typeProvider; + } + + public Type leastUpperBound(Type t, Type s) { + if (isSubtype(t, s)) { + return s; + } else if (isSubtype(s, t)) { + return t; + } else { + // TODO(karlklose) Return the intersection of the implemented interfaces. + return typeProvider.getDynamicType(); + } + } + + /** + * Return an interface type representing the given interface, function or + * variable type. + * @return An interface type or null if the argument is neither an interface + * function or variable type. + */ + public InterfaceType getInterfaceType(Type type) { + switch (type.getKind()) { + case VARIABLE: { + TypeVariableElement element = ((TypeVariable) type).getTypeVariableElement(); + if (element.getBound() == null) { + return typeProvider.getObjectType(); + } else { + return getInterfaceType(element.getBound()); + } + } + case FUNCTION: + case FUNCTION_ALIAS: + return typeProvider.getFunctionType(); + case INTERFACE: + return (InterfaceType) type; + case DYNAMIC: + case NONE: + case VOID: + default: + return null; + } + } + + /** + * Returns true if t is a subtype of s. + */ + public boolean isSubtype(Type t, Type s) { + if (t.getKind().equals(TypeKind.DYNAMIC)) { + return true; + } + switch (s.getKind()) { + case DYNAMIC: + return true; + + case INTERFACE: + return isSubtypeOfInterface(t, (InterfaceType) s); + + case FUNCTION_ALIAS: + return isSubtypeOfAlias(t, (FunctionAliasType) s); + + case FUNCTION: + switch (t.getKind()) { + case FUNCTION_ALIAS: + return isSubtypeOfFunction(asFunctionType((FunctionAliasType) t), (FunctionType) s); + + case FUNCTION: + return isSubtypeOfFunction((FunctionType) t, (FunctionType) s); + + default: + return false; + } + + case VARIABLE: + return isSubtypeOfTypeVariable(t, (TypeVariable) s); + + case VOID: + return t.equals(s); + + default: + throw new AssertionError(s.getKind()); + } + } + + FunctionType asFunctionType(FunctionAliasType alias) { + FunctionAliasElement element = alias.getElement(); + FunctionType type = + (FunctionType) element.getFunctionType().subst(alias.getArguments(), + element.getTypeParameters()); + return type; + } + + private boolean isSubtypeOfAlias(Type t, FunctionAliasType s) { + if (isSubtypeOfInterface(t, s)) { + return true; + } + if (t.getKind().equals(TypeKind.FUNCTION_ALIAS)) { + return isSubtypeOfFunction(asFunctionType((FunctionAliasType) t), asFunctionType(s)); + } + return false; + } + + private boolean isSubtypeOfTypeVariable(Type t, TypeVariable sv) { + return sv.equals(t); + } + + private boolean isSubtypeOfInterface(Type t, InterfaceType s) { + final Type sup = asInstanceOf(t, s.getElement()); + + if (TypeKind.of(sup).equals(TypeKind.INTERFACE)) { + InterfaceType ti = (InterfaceType) sup; + assert ti.getElement().equals(s.getElement()); + if (ti.isRaw() || s.isRaw()) { + return true; + } + // Type arguments are covariant. + return areSubtypes(ti.getArguments().iterator(), s.getArguments().iterator()); + } + return false; + } + + /** + * Implement the Dart function subtype rule. Unlike the classic arrow rule (return type is + * covariant, and paramter types are contravariant), in Dart they must just be assignable. + */ + private boolean isSubtypeOfFunction(FunctionType t, FunctionType s) { + // Classic: return type is covariant; Dart: assignable. + if (!isAssignable(t.getReturnType(), s.getReturnType())) { + // A function that returns a value can be used as a function where you ignore the value. + if (!s.getReturnType().equals(typeProvider.getVoidType())) { + return false; + } + } + Type tRest = t.getRest(); + Type sRest = s.getRest(); + if ((tRest == null) != (sRest == null)) { + return false; + } + if (tRest != null) { + // Classic: parameter types are contravariant; Dart: assignable. + if (!isAssignable(sRest, tRest)) { + return false; + } + } + Map tNamed = t.getNamedParameterTypes(); + Map sNamed = s.getNamedParameterTypes(); + if ((tNamed == null) != (sNamed == null)) { + return false; + } + if (tNamed != null) { + Map tMap = new LinkedHashMap(tNamed); + for (Entry sEntry : sNamed.entrySet()) { + Type type = tMap.remove(sEntry.getKey()); + if (type == null) { + return false; + } + // Classic: parameter types are contravariant; Dart: assignable. + if (!isAssignable(sEntry.getValue(), type)) { + return false; + } + } + if (!tMap.isEmpty()) { + return false; + } + } + // Classic: parameter types are contravariant; Dart: assignable. + return areAssignable(s.getParameterTypes().iterator(), t.getParameterTypes().iterator()); + } + + private boolean areSubtypes(Iterator t, Iterator s) { + while (t.hasNext() && s.hasNext()) { + if (!isSubtype(t.next(), s.next())) { + return false; + } + } + + // O(1) check to assert t and s are of same size. + return t.hasNext() == s.hasNext(); + } + + private boolean areAssignable(Iterator t, Iterator s) { + while (t.hasNext() && s.hasNext()) { + if (!isAssignable(t.next(), s.next())) { + return false; + } + } + + // O(1) check to assert t and s are of same size. + return t.hasNext() == s.hasNext(); + } + + /** + * Returns true if s is assignable to t. + */ + public boolean isAssignable(Type t, Type s) { + t.getClass(); // Quick null check. + s.getClass(); // Quick null check. + return isSubtype(t, s) || isSubtype(s, t); + } + + /** + * Translates the given type into an instantiation of the given + * element. This is done by walking the supertype hierarchy and + * substituting in the appropriate type arguments. + * + *

    For example, if {@code GrowableArray} is a subtype of + * {@code Array}, then + * {@code asInstanceOf("GrowableArray", "Array")} would + * return {@code Array} + * + * @return null if t is not a subtype of element + */ + @VisibleForTesting + public InterfaceType asInstanceOf(Type t, ClassElement element) { + switch (t.getKind()) { + case FUNCTION_ALIAS: + case INTERFACE: { + if (t.getElement().equals(element)) { + return (InterfaceType) t; + } + InterfaceType ti = (InterfaceType) t; + ClassElement tElement = ti.getElement(); + InterfaceType supertype = tElement.getSupertype(); + if (supertype != null) { + InterfaceType result = asInstanceOf(asSupertype(ti, supertype), element); + if (result != null) { + return result; + } + } + for (InterfaceType intrface : tElement.getInterfaces()) { + InterfaceType result = asInstanceOf(asSupertype(ti, intrface), element); + if (result != null) { + return result; + } + } + return null; + } + case FUNCTION: { + Element e = t.getElement(); + switch (e.getKind()) { + case CLASS: + // e should be the interface Function in the core library. See the + // documentation comment on FunctionType. + InterfaceType ti = (InterfaceType) e.getType(); + return asInstanceOf(ti, element); + default: + return null; + } + } + case VARIABLE: { + TypeVariable v = (TypeVariable) t; + Type bound = v.getTypeVariableElement().getBound(); + return asInstanceOf(bound, element); + } + default: + return null; + } + } + + private InterfaceType asSupertype(InterfaceType type, InterfaceType supertype) { + if (supertype == null) { + return null; + } + if (type.isRaw()) { + return supertype.asRawType(); + } + List arguments = type.getArguments(); + List parameters = type.getElement().getTypeParameters(); + return supertype.subst(arguments, parameters); + } + + static void printTypesOn(StringBuilder sb, List types, + String start, String end) { + sb.append(start); + boolean first = true; + for (Type argument : types) { + if (!first) { + sb.append(", "); + } + sb.append(argument); + first = false; + } + sb.append(end); + } + + public static List subst(List types, + List arguments, List parameters) { + ArrayList result = new ArrayList(types.size()); + for (Type type : types) { + result.add(type.subst(arguments, parameters)); + } + return result; + } + + public static FunctionType makeFunctionType(ResolutionErrorListener listener, + ClassElement element, + List parameters, Type returnType, + List typeVariables) { + List parameterTypes = new ArrayList(parameters.size()); + Map namedParameterTypes = null; + Type restParameter = null; + for (VariableElement parameter : parameters) { + Type type = parameter.getType(); + if (parameter.getModifiers().isVariadic()) { + if (restParameter != null) { + listener.resolutionError(parameter.getNode(), + DartCompilerErrorCode.MULTIPLE_REST_PARAMETERS); + } + restParameter = ((InterfaceType) type).getArguments().get(0); + } else if (parameter.isNamed()) { + if (namedParameterTypes == null) { + namedParameterTypes = new LinkedHashMap(); + } + namedParameterTypes.put(parameter.getName(), type); + } else { + parameterTypes.add(type); + } + } + return FunctionTypeImplementation.of(element, parameterTypes, namedParameterTypes, + restParameter, returnType, typeVariables); + } + + public static Types getInstance(CoreTypeProvider typeProvider) { + return new Types(typeProvider); + } + + public static InterfaceType interfaceType(ClassElement element, List arguments) { + return new InterfaceTypeImplementation(element, arguments); + } + + public static FunctionAliasType functionAliasType(FunctionAliasElement element, + List typeVariables) { + return new FunctionAliasTypeImplementation(element, typeVariables); + } + + public static TypeVariable typeVariable(TypeVariableElement element) { + return new TypeVariableImplementation(element); + } + + public static DynamicType newDynamicType() { + return new DynamicTypeImplementation(); + } + + public static InterfaceType ensureInterface(Type type) { + TypeKind kind = TypeKind.of(type); + switch (kind) { + case INTERFACE: + return (InterfaceType) type; + case NONE: + case DYNAMIC: + return null; + default: + throw new AssertionError("unexpected kind " + kind); + } + } + + public static Type newVoidType() { + return new VoidType(); + } + + /** + * Returns the type node corresponding to the instantiated class or interface. + */ + public static DartTypeNode constructorTypeNode(DartNewExpression node) { + DartNode constructor = node.getConstructor(); + if (constructor instanceof DartPropertyAccess) { + return (DartTypeNode) ((DartPropertyAccess) constructor).getQualifier(); + } else { + return (DartTypeNode) constructor; + } + } + + /** + * Returns the interface type being instantiated by the given node. + */ + public static InterfaceType constructorType(DartNewExpression node) { + DartTypeNode typeNode = constructorTypeNode(node); + return (InterfaceType) typeNode.getType(); + } + + /** + * Returns the list of type variables on the factory invoked by the given node. + * This method never returns null. + */ + public static List factoryTypeVariables(DartNewExpression node) { + ConstructorElement factory = node.getSymbol(); + if (factory == null) { + return Collections.emptyList(); + } + FunctionType type = (FunctionType) factory.getType(); + return type.getTypeVariables(); + } +} diff --git a/compiler/java/com/google/dart/compiler/type/VoidType.java b/compiler/java/com/google/dart/compiler/type/VoidType.java new file mode 100644 index 00000000000..69f8061c457 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/type/VoidType.java @@ -0,0 +1,46 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.Elements; + +import java.util.List; + +/** + * Implementation of "void". There is no public interface for this class as Type already exposes + * all the functionality needed. + */ +class VoidType extends AbstractType { + @Override + public Type subst(List arguments, List parameters) { + return this; + } + + @Override + public TypeKind getKind() { + return TypeKind.VOID; + } + + @Override + public Element getElement() { + return Elements.voidElement(); + } + + @Override + public String toString() { + return "void"; + } + + @Override + public boolean equals(Object other) { + return other instanceof VoidType; + } + + @Override + public int hashCode() { + return VoidType.class.hashCode(); + } +} diff --git a/compiler/java/com/google/dart/compiler/util/AbstractTextOutput.java b/compiler/java/com/google/dart/compiler/util/AbstractTextOutput.java new file mode 100644 index 00000000000..9c8d2b1cd16 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/AbstractTextOutput.java @@ -0,0 +1,132 @@ +// 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. + +package com.google.dart.compiler.util; + +import java.io.PrintWriter; +import java.util.Arrays; + +/** + * An abstract base type to build TextOutput implementations. + */ +public abstract class AbstractTextOutput implements TextOutput { + private final boolean compact; + private int identLevel = 0; + private int indentGranularity = 2; + private char[][] indents = new char[][] {new char[0]}; + private boolean justNewlined; + private PrintWriter out; + private int position = 0; + private int line = 0; + private int column = 0; + + protected AbstractTextOutput(boolean compact) { + this.compact = compact; + } + + public int getPosition() { + return position; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } + + public void indentIn() { + ++identLevel; + if (identLevel >= indents.length) { + // Cache a new level of indentation string. + // + char[] newIndentLevel = new char[identLevel * indentGranularity]; + Arrays.fill(newIndentLevel, ' '); + char[][] newIndents = new char[indents.length + 1][]; + System.arraycopy(indents, 0, newIndents, 0, indents.length); + newIndents[identLevel] = newIndentLevel; + indents = newIndents; + } + } + + public void indentOut() { + --identLevel; + } + + public void newline() { + out.print('\n'); + position++; + line++; + column = 0; + justNewlined = true; + } + + public void newlineOpt() { + if (!compact) { + newline(); + } + } + + public void print(char c) { + maybeIndent(); + out.print(c); + position++; + column++; + justNewlined = false; + } + + public void print(char[] s) { + maybeIndent(); + printAndCount(s); + justNewlined = false; + } + + public void print(String s) { + maybeIndent(); + printAndCount(s.toCharArray()); + justNewlined = false; + } + + // Why don't the "Opt" methods update "justNewLined"? + public void printOpt(char c) { + if (!compact) { + maybeIndent(); + out.print(c); + position += 1; + column++; + } + } + + public void printOpt(char[] s) { + if (!compact) { + maybeIndent(); + printAndCount(s); + } + } + + public void printOpt(String s) { + if (!compact) { + maybeIndent(); + printAndCount(s.toCharArray()); + } + } + + protected void setPrintWriter(PrintWriter out) { + this.out = out; + } + + private void maybeIndent() { + if (justNewlined && !compact) { + printAndCount(indents[identLevel]); + justNewlined = false; + } + } + + private void printAndCount(char[] chars) { + position += chars.length; + column += chars.length; + out.print(chars); + } +} diff --git a/compiler/java/com/google/dart/compiler/util/AstUtil.java b/compiler/java/com/google/dart/compiler/util/AstUtil.java new file mode 100644 index 00000000000..61dffe264c1 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/AstUtil.java @@ -0,0 +1,208 @@ +// 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. + +package com.google.dart.compiler.util; + +import com.google.dart.compiler.InternalCompilerException; +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsCase; +import com.google.dart.compiler.backend.js.ast.JsDefault; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsParameter; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsScope; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsSwitch; +import com.google.dart.compiler.backend.js.ast.JsSwitchMember; +import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; +import com.google.dart.compiler.backend.js.ast.JsVars; +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import com.google.dart.compiler.common.SourceInfo; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class AstUtil { + + public static JsInvocation newInvocation( + JsExpression target, JsExpression ... params) { + JsInvocation invoke = new JsInvocation(); + invoke.setQualifier(target); + for (JsExpression expr : params) { + invoke.getArguments().add(expr); + } + return invoke; + } + + public static JsNameRef newQualifiedNameRef(String name) { + JsNameRef node = null; + int endPos = -1; + int startPos = 0; + do { + endPos = name.indexOf('.', startPos); + String part = (endPos == -1 + ? name.substring(startPos) + : name.substring(startPos, endPos)); + node = newNameRef(node, part); + startPos = endPos + 1; + } while (endPos != -1); + + return node; + } + + public static JsNameRef newNameRef(JsExpression qualifier, String prop) { + JsNameRef nameRef = new JsNameRef(prop); + if (qualifier != null) { + nameRef.setQualifier(qualifier); + } + return nameRef; + } + + public static JsNameRef newNameRef(JsExpression qualifier, JsName prop) { + JsNameRef nameRef = new JsNameRef(prop); + if (qualifier != null) { + nameRef.setQualifier(qualifier); + } + return nameRef; + } + + public static JsNameRef newPrototypeNameRef(JsExpression qualifier) { + return newNameRef(qualifier, "prototype"); + } + + public static JsArrayAccess newArrayAccess(JsExpression target, JsExpression key) { + JsArrayAccess arr = new JsArrayAccess(); + arr.setArrayExpr(target); + arr.setIndexExpr(key); + return arr; + } + + public static JsBlock newBlock(JsStatement ... stmts) { + JsBlock jsBlock = new JsBlock(); + for (JsStatement stmt : stmts) { + jsBlock.getStatements().add(stmt); + } + return jsBlock; + } + + /** + * Returns a sequence of expressions (using the binary sequence operator). + * @param exprs - expressions to add to sequence + * @return a sequence of expressions. + */ + public static JsBinaryOperation newSequence(JsExpression ... exprs) { + if (exprs.length < 2) { + throw new InternalCompilerException("newSequence expects at least two arguments"); + } + JsExpression result = exprs[exprs.length - 1]; + for (int i = exprs.length - 2; i >= 0; i--) { + result = new JsBinaryOperation(JsBinaryOperator.COMMA, exprs[i], result); + } + return (JsBinaryOperation) result; + } + + // Ensure a valid LHS + public static JsBinaryOperation newAssignment( + JsNameRef nameRef, JsExpression expr) { + return new JsBinaryOperation(JsBinaryOperator.ASG, nameRef, expr); + } + + public static JsBinaryOperation newAssignment( + JsArrayAccess target, JsExpression expr) { + return new JsBinaryOperation(JsBinaryOperator.ASG, target, expr); + } + + public static JsVars newVar(SourceInfo info, JsName name, JsExpression expr) { + JsVar var = new JsVar(name).setSourceRef(info); + var.setInitExpr(expr); + JsVars vars = new JsVars(); + vars.add(var); + return vars; + } + + public static JsSwitch newSwitch( + JsExpression expr, JsSwitchMember ... cases) { + JsSwitch jsSwitch = new JsSwitch(); + jsSwitch.setExpr(expr); + for (JsSwitchMember jsCase : cases) { + jsSwitch.getCases().add(jsCase); + } + return jsSwitch; + } + + public static JsCase newCase(JsExpression expr, JsStatement ... stmts) { + JsCase jsCase = new JsCase(); + jsCase.setCaseExpr(expr); + for (JsStatement stmt : stmts) { + jsCase.getStmts().add(stmt); + } + return jsCase; + } + + public static JsDefault newDefaultCase(JsStatement ... stmts) { + JsDefault jsCase = new JsDefault(); + for (JsStatement stmt : stmts) { + jsCase.getStmts().add(stmt); + } + return jsCase; + } + + public static JsFunction newFunction( + JsScope scope, JsName name, JsParameter[] params, JsStatement ... stmts) { + JsFunction fn = new JsFunction(scope); + if (name != null) { + fn.setName(name); + } + if (params != null) { + for (JsParameter param : params) { + fn.getParameters().add(param); + } + } + fn.setBody(newBlock(stmts)); + return fn; + } + + public static JsInvocation call(SourceInfo src, JsExpression target, JsExpression ... params) { + return (JsInvocation) newInvocation(target, params).setSourceRef(src); + } + + public static JsExpression comma(SourceInfo src, JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.COMMA, op1, op2).setSourceRef(src); + } + + public static JsNameRef nameref(SourceInfo src, String name) { + return (JsNameRef) new JsNameRef(name).setSourceRef(src); + } + + public static JsNameRef nameref(SourceInfo src, JsName qualifier, String prop) { + return AstUtil.nameref(src, qualifier.makeRef().setSourceRef(src), prop); + } + + public static JsNameRef nameref(SourceInfo src, JsExpression qualifier, String prop) { + return (JsNameRef) newNameRef(qualifier, prop).setSourceRef(src); + } + + public static JsExpression assign(SourceInfo src, JsNameRef op1, JsExpression op2) { + return newAssignment(op1, op2).setSourceRef(src); + } + + public static JsExpression neq(SourceInfo src, JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.NEQ, op1, op2).setSourceRef(src); + } + + public static JsExpression not(SourceInfo src, JsExpression op1) { + return new JsPrefixOperation(JsUnaryOperator.NOT, op1).setSourceRef(src); + } + + public static JsExpression and(SourceInfo src, JsExpression op1, JsExpression op2) { + return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2); + } +} diff --git a/compiler/java/com/google/dart/compiler/util/DartSourceString.java b/compiler/java/com/google/dart/compiler/util/DartSourceString.java new file mode 100644 index 00000000000..e5cfec39c98 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/DartSourceString.java @@ -0,0 +1,73 @@ +// 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. + +package com.google.dart.compiler.util; + +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; + +import java.io.Reader; +import java.io.StringReader; +import java.net.URI; + +/** + * Instances of the class DartSourceString represent a source + * composed from a string rather than an external file. + */ +public class DartSourceString implements DartSource { + /** + * The name of the source being represented. + */ + private String name; + + /** + * The source being represented. + */ + private String source; + + /** + * Initialize a new Dart source to have the given content. + * + * @param source the source being represented + */ + public DartSourceString(String name, String source) { + this.name = name; + this.source = source; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public LibrarySource getLibrary() { + return null; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getRelativePath() { + return name; + } + + @Override + public Reader getSourceReader() { + return new StringReader(source); + } + + @Override + public URI getUri() { + return URI.create(getName()).normalize(); + } +} diff --git a/compiler/java/com/google/dart/compiler/util/DefaultTextOutput.java b/compiler/java/com/google/dart/compiler/util/DefaultTextOutput.java new file mode 100644 index 00000000000..2a064ff87fd --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/DefaultTextOutput.java @@ -0,0 +1,32 @@ +// 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. + +package com.google.dart.compiler.util; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Adapts {@link TextOutput} to an internal text buffer. + */ +public class DefaultTextOutput extends AbstractTextOutput { + + private final StringWriter sw = new StringWriter(); + private final PrintWriter out; + + public DefaultTextOutput(boolean compact) { + super(compact); + setPrintWriter(out = new PrintWriter(sw)); + } + + @Override + public String toString() { + out.flush(); + if (sw != null) { + return sw.toString(); + } else { + return super.toString(); + } + } +} diff --git a/compiler/java/com/google/dart/compiler/util/Hack.java b/compiler/java/com/google/dart/compiler/util/Hack.java new file mode 100644 index 00000000000..642f416394f --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/Hack.java @@ -0,0 +1,16 @@ +// 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. + +package com.google.dart.compiler.util; + +/** + * Utility to hack around untypable (or yet to be) code. + */ +public class Hack { + public static T cast(Object o) { + @SuppressWarnings("unchecked") + T t = (T) o; + return t; + } +} diff --git a/compiler/java/com/google/dart/compiler/util/Lists.java b/compiler/java/com/google/dart/compiler/util/Lists.java new file mode 100644 index 00000000000..5d7f6ddc545 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/Lists.java @@ -0,0 +1,296 @@ +// 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. + +package com.google.dart.compiler.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Utility methods for operating on memory-efficient lists. All lists of size 0 + * or 1 are assumed to be immutable. All lists of size greater than 1 are + * assumed to be mutable. + */ +public class Lists { + + private static final Class MULTI_LIST_CLASS = ArrayList.class; + private static final Class SINGLETON_LIST_CLASS = Collections.singletonList(null).getClass(); + + public static List add(List list, int index, T toAdd) { + switch (list.size()) { + case 0: + // Empty -> Singleton + if (index != 0) { + throw newIndexOutOfBounds(list, index); + } + return Collections.singletonList(toAdd); + case 1: { + // Singleton -> ArrayList + List result = new ArrayList(2); + switch (index) { + case 0: + result.add(toAdd); + result.add(list.get(0)); + return result; + case 1: + result.add(list.get(0)); + result.add(toAdd); + return result; + default: + throw newIndexOutOfBounds(list, index); + } + } + default: + // ArrayList + list.add(index, toAdd); + return list; + } + } + + public static List add(List list, T toAdd) { + switch (list.size()) { + case 0: + // Empty -> Singleton + return Collections.singletonList(toAdd); + case 1: { + // Singleton -> ArrayList + List result = new ArrayList(2); + result.add(list.get(0)); + result.add(toAdd); + return result; + } + default: + // ArrayList + list.add(toAdd); + return list; + } + } + + public static List addAll(List list, int index, List toAdd) { + switch (toAdd.size()) { + case 0: + // No-op. + return list; + case 1: + // Add one element. + return add(list, index, toAdd.get(0)); + default: + // True list merge, result >= 2. + switch (list.size()) { + case 0: + if (index != 0) { + throw newIndexOutOfBounds(list, index); + } + return new ArrayList(toAdd); + case 1: { + List result = new ArrayList(1 + toAdd.size()); + switch (index) { + case 0: + result.addAll(toAdd); + result.add(list.get(0)); + return result; + case 1: + result.add(list.get(0)); + result.addAll(toAdd); + return result; + default: + throw newIndexOutOfBounds(list, index); + } + } + default: + list.addAll(index, toAdd); + return list; + } + } + } + + public static List addAll(List list, List toAdd) { + switch (toAdd.size()) { + case 0: + // No-op. + return list; + case 1: + // Add one element. + return add(list, toAdd.get(0)); + default: + // True list merge, result >= 2. + switch (list.size()) { + case 0: + return new ArrayList(toAdd); + case 1: { + List result = new ArrayList(1 + toAdd.size()); + result.add(list.get(0)); + result.addAll(toAdd); + return result; + } + default: + list.addAll(toAdd); + return list; + } + } + } + + public static List addAll(List list, T... toAdd) { + switch (toAdd.length) { + case 0: + // No-op. + return list; + case 1: + // Add one element. + return add(list, toAdd[0]); + default: + // True list merge, result >= 2. + switch (list.size()) { + case 0: + return new ArrayList(Arrays.asList(toAdd)); + case 1: { + List result = new ArrayList(1 + toAdd.length); + result.add(list.get(0)); + result.addAll(Arrays.asList(toAdd)); + return result; + } + default: + list.addAll(Arrays.asList(toAdd)); + return list; + } + } + } + + public static List create() { + return Collections.emptyList(); + } + + public static List create(Collection collection) { + switch (collection.size()) { + case 0: + return create(); + default: + return new ArrayList(collection); + } + } + + public static List create(List list) { + switch (list.size()) { + case 0: + return create(); + case 1: + return create(list.get(0)); + default: + return new ArrayList(list); + } + } + + public static List create(T item) { + return Collections.singletonList(item); + } + + public static List create(T... items) { + switch (items.length) { + case 0: + return create(); + case 1: + return create(items[0]); + default: + return new ArrayList(Arrays.asList(items)); + } + } + + public static List normalize(List list) { + switch (list.size()) { + case 0: + return create(); + case 1: { + if (list.getClass() == SINGLETON_LIST_CLASS) { + return list; + } + return create(list.get(0)); + } + default: + if (list.getClass() == MULTI_LIST_CLASS) { + return list; + } + return new ArrayList(list); + } + } + + public static List normalizeUnmodifiable(List list) { + if (list.size() < 2) { + return normalize(list); + } else { + List copy = new ArrayList(list.size()); + Collections.copy(copy, list); + return copy; + } + } + + public static List remove(List list, int toRemove) { + switch (list.size()) { + case 0: + // Empty + throw newIndexOutOfBounds(list, toRemove); + case 1: + // Singleton -> Empty + if (toRemove == 0) { + return Collections.emptyList(); + } else { + throw newIndexOutOfBounds(list, toRemove); + } + case 2: + // ArrayList -> Singleton + switch (toRemove) { + case 0: + return Collections.singletonList(list.get(1)); + case 1: + return Collections.singletonList(list.get(0)); + default: + throw newIndexOutOfBounds(list, toRemove); + } + default: + // ArrayList + list.remove(toRemove); + return list; + } + } + + public static List set(List list, int index, T e) { + switch (list.size()) { + case 0: + // Empty + throw newIndexOutOfBounds(list, index); + case 1: + // Singleton + if (index == 0) { + return Collections.singletonList(e); + } else { + throw newIndexOutOfBounds(list, index); + } + default: + // ArrayList + list.set(index, e); + return list; + } + } + + public static > List sort(List list) { + if (list.size() > 1) { + Collections.sort(list); + } + return list; + } + + public static List sort(List list, Comparator sort) { + if (list.size() > 1) { + Collections.sort(list, sort); + } + return list; + } + + private static IndexOutOfBoundsException newIndexOutOfBounds(List list, int index) { + return new IndexOutOfBoundsException("Index: " + index + ", Size: " + list.size()); + } +} diff --git a/compiler/java/com/google/dart/compiler/util/Maps.java b/compiler/java/com/google/dart/compiler/util/Maps.java new file mode 100644 index 00000000000..89ea29fdd2a --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/Maps.java @@ -0,0 +1,160 @@ +// 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. + +package com.google.dart.compiler.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.LinkedHashMap; + +/** + * Utility methods for operating on memory-efficient maps. All maps of size 0 or + * 1 are assumed to be immutable. All maps of size greater than 1 are assumed to + * be mutable. + */ +public class Maps { + + private static final Class MULTI_MAP_CLASS = HashMap.class; + private static final Class SINGLETON_MAP_CLASS = + Collections.singletonMap(null, null).getClass(); + + public static Map create() { + return Collections.emptyMap(); + } + + public static Map create(K key, V value) { + return Collections.singletonMap(key, value); + } + + public static Map normalize(Map map) { + switch (map.size()) { + case 0: + return create(); + case 1: { + if (map.getClass() == SINGLETON_MAP_CLASS) { + return map; + } + K key = map.keySet().iterator().next(); + return create(key, map.get(key)); + } + default: + if (map.getClass() == MULTI_MAP_CLASS) { + return map; + } + return new HashMap(map); + } + } + + public static Map normalizeUnmodifiable(Map map) { + if (map.size() < 2) { + return normalize(map); + } else { + // TODO: implement an UnmodifiableHashMap? + return Collections.unmodifiableMap(normalize(map)); + } + } + + public static Map put(Map map, K key, V value) { + switch (map.size()) { + case 0: + // Empty -> Singleton + return Collections.singletonMap(key, value); + case 1: { + if (map.containsKey(key)) { + return create(key, value); + } + // Singleton -> HashMap + Map result = new HashMap(); + result.put(map.keySet().iterator().next(), map.values().iterator().next()); + result.put(key, value); + return result; + } + default: + // HashMap + map.put(key, value); + return map; + } + } + + public static Map putAll(Map map, Map toAdd) { + switch (toAdd.size()) { + case 0: + // No-op. + return map; + case 1: { + // Add one element. + K key = toAdd.keySet().iterator().next(); + return put(map, key, toAdd.get(key)); + } + default: + // True list merge, result >= 2. + switch (map.size()) { + case 0: + return new HashMap(toAdd); + case 1: { + HashMap result = new HashMap(); + K key = map.keySet().iterator().next(); + result.put(key, map.get(key)); + result.putAll(toAdd); + return result; + } + default: + map.putAll(toAdd); + return map; + } + } + } + + /** + * A variation of the put method which uses a LinkedHashMap. + */ + public static Map putOrdered(Map map, K key, V value) { + switch (map.size()) { + case 0: + // Empty -> Singleton + return Collections.singletonMap(key, value); + case 1: { + if (map.containsKey(key)) { + return create(key, value); + } + // Singleton -> LinkedHashMap + Map result = new LinkedHashMap(); + result.put(map.keySet().iterator().next(), map.values().iterator().next()); + result.put(key, value); + return result; + } + default: + // LinkedHashMap + map.put(key, value); + return map; + } + } + + public static Map remove(Map map, K key) { + switch (map.size()) { + case 0: + // Empty + return map; + case 1: + // Singleton -> Empty + if (map.containsKey(key)) { + return create(); + } + return map; + case 2: + // HashMap -> Singleton + if (map.containsKey(key)) { + map.remove(key); + key = map.keySet().iterator().next(); + return create(key, map.get(key)); + } + return map; + default: + // IdentityHashMap + map.remove(key); + return map; + } + } +} diff --git a/compiler/java/com/google/dart/compiler/util/Paths.java b/compiler/java/com/google/dart/compiler/util/Paths.java new file mode 100644 index 00000000000..d2a6b0de5c3 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/Paths.java @@ -0,0 +1,100 @@ +// 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. + +package com.google.dart.compiler.util; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility methods for obtaining relative paths from files and translating + * relative paths into files. + */ +public class Paths { + + /** + * Answer the relative path from baseFile to relFile + * + * @param baseFile the base file (not null) from which the + * desired path starts + * @param relFile the file referenced by the desired relative path (not + * null) + * @return a relative path (not null) + */ + public static String relativePathFor(File baseFile, File relFile) { + String baseFilePath = baseFile.getPath().replace(File.separatorChar, '/'); + String relFilePath = relFile.getPath().replace(File.separatorChar, '/'); + int baseFilePathLen = baseFilePath.length(); + int relFilePathLen = relFilePath.length(); + + // Find the common path elements + int index = 0; + while (index < baseFilePathLen - 1 && index < relFilePathLen - 1) { + if (baseFilePath.charAt(index) != relFilePath.charAt(index)) { + break; + } + index++; + } + while (index >= 0 + && (baseFilePath.charAt(index) != '/' || relFilePath.charAt(index) != '/')) { + index--; + } + int commonStart = index; + + // Build a path up from the base file + StringBuilder relPath = new StringBuilder(baseFilePathLen + relFilePathLen + - commonStart * 2); + index = commonStart + 1; + while (true) { + index = baseFilePath.indexOf('/', index); + if (index == -1) { + break; + } + relPath.append("../"); + index++; + } + relPath.append(relFilePath.substring(commonStart + 1)); + return relPath.toString(); + } + + /** + * Answer the file relative to the specified file + * + * @param baseFile the base file (not null) + * @param relPath the path to the desired file relative to baseFile + * @return the file (not null) + */ + public static File relativePathToFile(File baseFile, String relPath) { + if (relPath.startsWith("/")) { + return new File(relPath); + } + File parentFile = baseFile.getParentFile(); + String name; + if (parentFile == null) { + name = "."; + } else { + name = parentFile.getPath().replace(File.separatorChar, '/'); + } + name = URI.create(name + "/" + relPath).normalize().getPath(); + return new File(name); + } + + /** + * Given a collection of paths, return a collection of files + * + * @param a collection of paths to various files (not null, + * contains no nulls) + * @return a collection of files (not null, contains no + * nulls) + */ + public static List toFiles(List filePaths) { + List files = new ArrayList(); + for (String path : filePaths) { + files.add(new File(path)); + } + return files; + } +} diff --git a/compiler/java/com/google/dart/compiler/util/TextOutput.java b/compiler/java/com/google/dart/compiler/util/TextOutput.java new file mode 100644 index 00000000000..99a465b1532 --- /dev/null +++ b/compiler/java/com/google/dart/compiler/util/TextOutput.java @@ -0,0 +1,37 @@ +// 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. + +package com.google.dart.compiler.util; + +/** + * Interface used for printing text output. + */ +public interface TextOutput { + + int getPosition(); + + int getLine(); + + int getColumn(); + + void indentIn(); + + void indentOut(); + + void newline(); + + void newlineOpt(); + + void print(char c); + + void print(char[] s); + + void print(String s); + + void printOpt(char c); + + void printOpt(char[] s); + + void printOpt(String s); +} diff --git a/compiler/java/com/google/dart/runner/BundleLibrarySource.java b/compiler/java/com/google/dart/runner/BundleLibrarySource.java new file mode 100644 index 00000000000..4b2f62b1fc9 --- /dev/null +++ b/compiler/java/com/google/dart/runner/BundleLibrarySource.java @@ -0,0 +1,62 @@ +// 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. + +package com.google.dart.runner; + +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.UrlDartSource; +import com.google.dart.compiler.UrlSource; + +import java.net.URISyntaxException; + +class BundleLibrarySource extends UrlSource implements LibrarySource { + + private final class BundleDartSource extends UrlDartSource { + private BundleDartSource(ClassLoader loader, String basePath, String filename) + throws URISyntaxException { + super(loader.getResource(basePath + filename).toURI(), filename, BundleLibrarySource.this); + } + + @Override + public String getName() { + return basePath + getRelativePath(); + } + } + + private final ClassLoader loader; + private final String basePath; + private final String filename; + + public BundleLibrarySource(ClassLoader loader, String basePath, String filename) + throws URISyntaxException { + super(loader.getResource(basePath + filename).toURI()); + this.loader = loader; + this.basePath = basePath; + this.filename = filename; + } + + @Override + public LibrarySource getImportFor(String filename) { + try { + return new BundleLibrarySource(loader, basePath, filename); + } catch (URISyntaxException e) { + throw new AssertionError(); + } + } + + @Override + public DartSource getSourceFor(final String relPath) { + try { + return new BundleDartSource(loader, basePath, relPath); + } catch (URISyntaxException e) { + throw new AssertionError(e); + } + } + + @Override + public String getName() { + return basePath + filename; + } +} diff --git a/compiler/java/com/google/dart/runner/DartRunner.java b/compiler/java/com/google/dart/runner/DartRunner.java new file mode 100644 index 00000000000..613ca9a9c96 --- /dev/null +++ b/compiler/java/com/google/dart/runner/DartRunner.java @@ -0,0 +1,407 @@ +// 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. + +package com.google.dart.runner; + +import com.google.common.base.Joiner; +import com.google.common.io.CharStreams; +import com.google.common.io.Files; +import com.google.dart.compiler.Backend; +import com.google.dart.compiler.CommandLineOptions; +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.dart.compiler.CommandLineOptions.DartRunnerOptions; +import com.google.dart.compiler.CompilerConfiguration; +import com.google.dart.compiler.DartArtifactProvider; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.DefaultCompilerConfiguration; +import com.google.dart.compiler.DefaultDartCompilerListener; +import com.google.dart.compiler.DefaultErrorFormatter; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.UrlLibrarySource; +import com.google.dart.compiler.backend.js.ClosureJsBackend; +import com.google.dart.compiler.backend.js.JavascriptBackend; +import com.google.debugging.sourcemap.SourceMapConsumerFactory; +import com.google.debugging.sourcemap.SourceMapParseException; +import com.google.debugging.sourcemap.SourceMapSupplier; +import com.google.debugging.sourcemap.SourceMapping; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class DartRunner { + + private static DartRunnerOptions processCommandLineOptions(String[] args) throws RunnerError { + CmdLineParser cmdLineParser = null; + DartRunnerOptions parsedOptions = null; + try { + parsedOptions = new DartRunnerOptions(); + cmdLineParser = CommandLineOptions.parse(args, parsedOptions); + if (args.length == 0 || parsedOptions.showHelp()) { + printUsageAndThrow(cmdLineParser, ""); + System.exit(1); + } + } catch (CmdLineException e) { + printUsageAndThrow(cmdLineParser, e.getLocalizedMessage()); + System.exit(1); + } + + assert parsedOptions != null; + return parsedOptions; + } + + public static void main(String[] args) { + try { + DartRunnerOptions options = processCommandLineOptions(args); + throwingMain(options, args, Collections.emptyList(), System.out, System.err); + } catch (RunnerError error) { + System.err.println(error.getLocalizedMessage()); + System.exit(1); + } catch (Throwable e) { + e.printStackTrace(); + DartCompiler.crash(); + } + } + + public static void throwingMain(DartRunnerOptions options, + String[] args, + List imports, + PrintStream stdout, + final PrintStream stderr) + throws RunnerError { + + if (options.getSourceFiles().isEmpty()) { + throw new RunnerError("No script files specified on the command line: " + Joiner.on(" ").join(args)); + } + + String script = options.getSourceFiles().get(0); + ArrayList scriptArguments = new ArrayList(); + + LibrarySource app = new UrlLibrarySource(new File(script)); + + // TODO(zundel): Replace RunnerFlag enum with DartRunnerOptions + final Set flags = EnumSet.noneOf(RunnerFlag.class); + if (options.shouldOptimize()) { + flags.add(RunnerFlag.OPTIMIZE); + } + if (options.verbose()) { + flags.add(RunnerFlag.VERBOSE); + } + if (options.shouldProfile()) { + flags.add(RunnerFlag.PROFILE); + } + if (options.shouldCompileOnly()) { + flags.add(RunnerFlag.COMPILE_ONLY); + } + if (options.typeErrorsAreFatal()) { + flags.add(RunnerFlag.FATAL_TYPE_ERRORS); + } + if (options.useRhino()) { + flags.add(RunnerFlag.USE_RHINO); + } + if (options.shouldExposeCoreImpl()) { + imports = new ArrayList(imports); + // use a place-holder LibrarySource instance, to be replaced when embedded + // in the compiler, where the dart uri can be resolved. + imports.add(new NamedPlaceHolderLibrarySource("dart:coreimpl")); + } + + File outFile = options.getOutputFilename(); + + DefaultDartCompilerListener listener = new DefaultDartCompilerListener() { + { + ((DefaultErrorFormatter) formatter).setOutputStream(stderr); + } + @Override + public void compilationWarning(DartCompilationError event) { + compilationError(event); + } + }; + + CompilationResult compiled; + compiled = compileApp(app, imports, flags, listener); + + if (listener.getProblemCount() != 0) { + throw new RunnerError("Compilation failed."); + } + + if (outFile != null) { + File dir = outFile.getParentFile(); + if (dir != null) { + if (!dir.exists()) { + throw new RunnerError("Cannot create: " + outFile.getName() + + ". " + dir + " does not exist"); + } + if (!dir.canWrite()) { + throw new RunnerError("Cannot write " + outFile.getName() + " to " + + dir + ": Permission denied."); + } + } else { + dir = new File ("."); + if (!dir.canWrite()) { + throw new RunnerError("Cannot write " + outFile.getName() + " to " + + dir + ": Permission denied."); + } + } + try { + Files.write(compiled.js, outFile, Charset.defaultCharset()); + } catch (IOException e) { + throw new RunnerError(e); + } + } + + if (!flags.contains(RunnerFlag.COMPILE_ONLY)) { + runApp(compiled, app.getName(), flags, scriptArguments.toArray(new String[0]), stdout, stderr); + } + } + + private static void printUsageAndThrow(CmdLineParser cmdLineParser, String reason) + throws RunnerError { + + StringBuilder usage = new StringBuilder(); + usage.append(reason); + usage.append("\n"); + usage.append("Usage: "); + usage.append(System.getProperty("com.google.dart.runner.progname", + DartRunner.class.getSimpleName())); + usage.append(" [] []\n"); + usage.append("\n"); + + OutputStream s = new ByteArrayOutputStream(); + if (cmdLineParser == null) { + cmdLineParser = new CmdLineParser(new DartRunnerOptions()); + } + usage.append(s); + throw new RunnerError(usage.toString()); + } + + private static class NamedPlaceHolderLibrarySource implements LibrarySource { + private final String name; + + public NamedPlaceHolderLibrarySource(String name) { + this.name = name; + } + + @Override + public boolean exists() { + throw new AssertionError(); + } + + @Override + public long getLastModified() { + throw new AssertionError(); + } + + @Override + public String getName() { + return name; + } + + @Override + public Reader getSourceReader() { + throw new AssertionError(); + } + + @Override + public URI getUri() { + throw new AssertionError(); + } + + @Override + public LibrarySource getImportFor(String relPath) { + throw new AssertionError(); + } + + @Override + public DartSource getSourceFor(String relPath) { + throw new AssertionError(); + } + } + + private static class RunnerDartArtifactProvider extends DartArtifactProvider { + private final Map artifacts = new ConcurrentHashMap(); + + @Override + public Reader getArtifactReader(Source source, String part, String ext) { + String key = getKey(source, part, ext); + StringWriter w = artifacts.get(key); + if (w == null) { + return null; + } + return new StringReader(w.toString()); + } + + @Override + public URI getArtifactUri(Source source, String part, String ext) { + String key = getKey(source, part, ext); + return URI.create(key); + } + + @Override + public Writer getArtifactWriter(Source source, String part, String ext) { + StringWriter w = new StringWriter(); + String key = getKey(source, part, ext); + StringWriter oldValue = artifacts.put(key, w); + if (oldValue != null) { + throw new RuntimeException("Can only write artifact once for " + key); + } + return w; + } + + private String getKey(Source source, String part, String ext) { + String keyPart = (part.isEmpty()) ? "" : "$" + part; + return source.getName() + keyPart + "." + ext; + } + + public String getGeneratedFileContents(String name) { + StringWriter w = artifacts.get(name); + if (w == null) { + return null; + } + return w.toString(); + } + + @Override + public boolean isOutOfDate(Source source, Source base, String ext) { + return true; + } + } + + public static void compileAndRunApp(LibrarySource app, + Set flags, + CompilerConfiguration config, + DartCompilerListener listener, + String[] dartArguments, + PrintStream stdout, + PrintStream stderr) + throws RunnerError { + CompilationResult compiled = compileApp( + app, Collections.emptyList(), config, listener); + runApp(compiled, app.getName(), flags, dartArguments, stdout, stderr); + } + + private static void runApp(CompilationResult compiled, + String sourceName, + Set flags, + String[] scriptArguments, + PrintStream stdout, + PrintStream stderr) + throws RunnerError { + if (flags.contains(RunnerFlag.USE_RHINO)) { + new RhinoLauncher().execute(compiled.js, sourceName, scriptArguments, flags, stdout, stderr); + } else { + new V8Launcher(compiled.mapping).execute(compiled.js, sourceName, scriptArguments, flags, + stdout, stderr); + } + } + + private static class CompilationResult { + final SourceMapping mapping; + final String js; + + public CompilationResult(String js, SourceMapping mapping) { + this.mapping = mapping; + this.js = js; + } + } + + private static CompilationResult compileApp(LibrarySource app, + List imports, + final Set flags, + DartCompilerListener listener) + throws RunnerError { + // TODO(johnlenz): create a "OptimizingCompilerConfiguration" + + Backend backend; + CompilerOptions defaultOptions = new CompilerOptions(); + if (flags.contains(RunnerFlag.OPTIMIZE)) { + backend = new ClosureJsBackend(); + defaultOptions.optimize(true); + } else { + backend = new JavascriptBackend(); + } + CompilerConfiguration config = new DefaultCompilerConfiguration(backend, defaultOptions) { + @Override + public boolean expectEntryPoint() { + return true; + } + + @Override + public boolean typeErrorsAreFatal() { + return flags.contains(RunnerFlag.FATAL_TYPE_ERRORS); + } + }; + return compileApp(app, imports, config, listener); + } + + /** + * Parses and compiles an application to Javascript. + */ + private static CompilationResult compileApp(LibrarySource app, + List imports, + CompilerConfiguration config, + DartCompilerListener listener) throws RunnerError { + try { + final RunnerDartArtifactProvider provider = new RunnerDartArtifactProvider(); + String errmsg = DartCompiler.compileLib(app, imports, config, provider, listener); + if (errmsg != null) { + throw new RunnerError(errmsg); + } + Backend backend = config.getBackends().get(0); + + SourceMapping mapping = null; + Reader mr = provider.getArtifactReader(app, "", backend.getSourceMapExtension()); + if (mr != null) { + String mapContents = CharStreams.toString(mr); + try { + mapping = SourceMapConsumerFactory.parse(mapContents, new SourceMapSupplier() { + + @Override + public String getSourceMap(String url) { + String contents = provider.getGeneratedFileContents(url); + if (contents == null || contents.isEmpty()) { + return null; + } + return contents; + } + + }); + } catch (SourceMapParseException e) { + throw new AssertionError(e); + } + mr.close(); + } + + Reader r = provider.getArtifactReader(app, "", backend.getAppExtension()); + String js = CharStreams.toString(r); + r.close(); + return new CompilationResult(js, mapping); + } catch (IOException e) { + // This can't happen; it's just a StringWriter. + throw new AssertionError(e); + } + } +} diff --git a/compiler/java/com/google/dart/runner/JavaScriptLauncher.java b/compiler/java/com/google/dart/runner/JavaScriptLauncher.java new file mode 100644 index 00000000000..d1821177120 --- /dev/null +++ b/compiler/java/com/google/dart/runner/JavaScriptLauncher.java @@ -0,0 +1,20 @@ +// 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. + +package com.google.dart.runner; + +import java.io.PrintStream; +import java.util.Set; + +/** + * @author floitsch@google.com (Florian Loitsch) + * + */ +interface JavaScriptLauncher { + + void execute(String jsScript, String sourceName, String[] args, Set flags, + PrintStream stdout, PrintStream stderr) + throws RunnerError; + +} diff --git a/compiler/java/com/google/dart/runner/RhinoLauncher.java b/compiler/java/com/google/dart/runner/RhinoLauncher.java new file mode 100644 index 00000000000..1c927607646 --- /dev/null +++ b/compiler/java/com/google/dart/runner/RhinoLauncher.java @@ -0,0 +1,221 @@ +// 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. + +package com.google.dart.runner; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.FunctionObject; +import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.Undefined; + +import java.io.PrintStream; +import java.lang.reflect.Member; +import java.util.Set; + +/** + * @author floitsch@google.com (Florian Loitsch) + * + * Runs a given JS script. + */ +public class RhinoLauncher implements JavaScriptLauncher { + + /** + * Rhino-callable assert() method. + * + * TODO(acleung): Deprecate it to just follow DartVM's test cases. + */ + private static class AssertFunction extends SimpleFunction { + @Override + public Object call(Context ctx, Scriptable scope, Scriptable thisObj, Object[] args) { + // Validate arguments (god, I hate this dynamic stuff). + if ((args.length < 1) || (args.length > 2)) { + Context.reportError("Invalid call to assertThat(" + args + ")"); + } + if (!(args[0] instanceof Boolean)) { + Context.reportError("Argument 0 must be of type boolean"); + } + if (args.length == 2 && !(args[1] instanceof String)) { + Context.reportError("Argument 1 must be of type String"); + } + + if (!(Boolean) args[0]) { + Context.reportError("assert() failed" + ((args.length == 2) ? ": " + args[1] : "")); + } + return null; + } + } + + /** + * Emulates the Expect_throwException() function. + */ + public static class ThrowException extends FunctionObject { + + public ThrowException(String name, Member methodOrConstructor, Scriptable scope) { + super(name, methodOrConstructor, scope); + } + + @Override + public Object call(Context ctx, Scriptable scope, Scriptable thisObj, Object[] args) { + if (args.length != 1) { + Context.reportError("Invalid call to Expect_throwException(e)"); + } + throwException(args[0]); + return Undefined.instance; + } + + public void throwException(Object arg0) { + Context.reportError(arg0.toString()); + } + } + + /** + * Emulates the V8 'write' function. + */ + public static class Write extends FunctionObject { + private PrintStream out; + + public Write(String name, Member methodOrConstructor, Scriptable scope, PrintStream out) { + super(name, methodOrConstructor, scope); + this.out = out; + } + + @Override + public Object call(Context ctx, Scriptable scope, Scriptable thisObj, Object[] args) { + write(args[0]); + return Undefined.instance; + } + + public void write(Object arg0) { + out.print(arg0); + } + } + + /** + * Simple Rhino-callable function object. + */ + private static abstract class SimpleFunction implements Function { + @Override + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + return null; + } + + @Override + public void delete(int index) { + } + + @Override + public void delete(String name) { + } + + @Override + public Object get(int index, Scriptable start) { + return null; + } + + @Override + public Object get(String name, Scriptable start) { + return null; + } + + @Override + public String getClassName() { + return "Function"; + } + + @Override + public Object getDefaultValue(Class hint) { + return null; + } + + @Override + public Object[] getIds() { + return null; + } + + @Override + public Scriptable getParentScope() { + return null; + } + + @Override + public Scriptable getPrototype() { + return null; + } + + @Override + public boolean has(int index, Scriptable start) { + return false; + } + + @Override + public boolean has(String name, Scriptable start) { + return false; + } + + @Override + public boolean hasInstance(Scriptable instance) { + return false; + } + + @Override + public void put(int index, Scriptable start, Object value) { + } + + @Override + public void put(String name, Scriptable start, Object value) { + } + + @Override + public void setParentScope(Scriptable parent) { + } + + @Override + public void setPrototype(Scriptable prototype) { + } + } + + @Override + public void execute(String jsScript, String sourceName, String[] args, Set flags, + PrintStream stdout, PrintStream stderr) + throws RunnerError { + try { + Context ctx = Context.enter(); + Scriptable scope = ctx.initStandardObjects(); + scope.put("assert", scope, new AssertFunction()); + scope.put("native_Expect__throwException", scope, new ThrowException("Expect__throwException", + ThrowException.class.getMethod("throwException", Object.class), scope)); + scope.put("write", scope, new Write("write", + Write.class.getMethod("write", Object.class), scope, stderr)); + + // The variable 'arguments' is also used in d8. + // Rhino differentiates between Java Strings and JS Strings. If the args-array is not + // converted the JS execution will work, but Rhino will complain. + scope.put("arguments", scope, Context.javaToJS(args, scope)); + + // Evaluate the application. + ctx.evaluateString(scope, jsScript, sourceName, 1, null); + } catch (NoSuchMethodException e) { + throw new RunnerError(e); + } catch (RhinoException e) { + // TODO(jgw): This is a hack to dump the translated source when something goes wrong. It can + // be removed as soon as we have a source map we can use to provide source-level errors. + if (flags.contains(RunnerFlag.VERBOSE)) { + stdout.println(jsScript); + stdout.flush(); + } + + StringBuffer msg = new StringBuffer(); + msg.append(e.sourceName()); + msg.append(" (" + e.lineNumber() + ":" + e.columnNumber() + ")"); + msg.append(" : " + e.details()); + stderr.println(msg.toString()); + throw e; + } finally { + Context.exit(); + } + } + +} diff --git a/compiler/java/com/google/dart/runner/RunnerError.java b/compiler/java/com/google/dart/runner/RunnerError.java new file mode 100644 index 00000000000..a5c8e33457c --- /dev/null +++ b/compiler/java/com/google/dart/runner/RunnerError.java @@ -0,0 +1,19 @@ +// 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. + +package com.google.dart.runner; + +public class RunnerError extends Exception { + public RunnerError(String message) { + super(message); + } + + RunnerError(String message, Throwable cause) { + super(message, cause); + } + + RunnerError(Throwable cause) { + super(cause); + } +} diff --git a/compiler/java/com/google/dart/runner/RunnerFlag.java b/compiler/java/com/google/dart/runner/RunnerFlag.java new file mode 100644 index 00000000000..84bc9198886 --- /dev/null +++ b/compiler/java/com/google/dart/runner/RunnerFlag.java @@ -0,0 +1,17 @@ +// 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. + +package com.google.dart.runner; + +/** + * Flags that can be given to Runners and Launchers. + */ +public enum RunnerFlag { + VERBOSE, + PROFILE, + OPTIMIZE, + COMPILE_ONLY, + USE_RHINO, + FATAL_TYPE_ERRORS; +} diff --git a/compiler/java/com/google/dart/runner/TestRunner.java b/compiler/java/com/google/dart/runner/TestRunner.java new file mode 100644 index 00000000000..adfb1d455d9 --- /dev/null +++ b/compiler/java/com/google/dart/runner/TestRunner.java @@ -0,0 +1,109 @@ +// 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. + +package com.google.dart.runner; + +import com.google.dart.compiler.CommandLineOptions; +import com.google.dart.compiler.CommandLineOptions.TestRunnerOptions; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.UnitTestBatchRunner; +import com.google.dart.compiler.UnitTestBatchRunner.Invocation; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Runs dart programs.
    + * The command-line interface is similar to the VM's command line interface. + *
    + */ +public class TestRunner { + + public static void main(String[] args) { + + try { + boolean runBatch = false; + TestRunnerOptions options = processCommandLineOptions(args); + if (options.isBatch()) { + runBatch = true; + if (args.length > 1) { + System.err.println("(Extra arguments specified with -batch ignored.)"); + } + } + if (runBatch) { + UnitTestBatchRunner.runAsBatch(args, new Invocation() { + @Override + public boolean invoke(String[] args) throws Throwable { + try { + throwingMain(args, System.out, System.err); + } catch (RunnerError e) { + System.out.println(e.getLocalizedMessage()); + return false; + } + return true; + } + }); + } else { + throwingMain(args, System.out, System.err); + } + } catch (RunnerError e) { + System.err.println(e.getLocalizedMessage()); + System.exit(1); + } catch (Throwable e) { + e.printStackTrace(); + DartCompiler.crash(); + } + } + + private static void printUsageAndThrow(CmdLineParser cmdLineParser, String reason) throws RunnerError { + StringBuilder usage = new StringBuilder(); + usage.append(reason); + usage.append("\n"); + usage.append("Usage: "); + usage.append(System.getProperty("com.google.dart.runner.progname", + TestRunner.class.getSimpleName())); + usage.append(" [] []\n"); + usage.append("\n"); + + OutputStream s = new ByteArrayOutputStream(); + if (cmdLineParser == null) { + cmdLineParser = new CmdLineParser(new TestRunnerOptions()); + } + cmdLineParser.printUsage(s); + usage.append(s); + throw new RunnerError(usage.toString()); + } + + private static TestRunnerOptions processCommandLineOptions(String[] args) throws RunnerError { + CmdLineParser cmdLineParser = null; + TestRunnerOptions parsedOptions = null; + try { + parsedOptions = new TestRunnerOptions(); + cmdLineParser = CommandLineOptions.parse(args, parsedOptions); + if (args.length == 0 || parsedOptions.showHelp()) { + printUsageAndThrow(cmdLineParser, ""); + System.exit(1); + } + } catch (CmdLineException e) { + printUsageAndThrow(cmdLineParser, e.getLocalizedMessage()); + System.exit(1); + } + + assert parsedOptions != null; + return parsedOptions; + } + public static void throwingMain(String[] args, PrintStream stdout, PrintStream stderr) + throws RunnerError { + TestRunnerOptions options = processCommandLineOptions(args); + List imports = new ArrayList(); + DartRunner.throwingMain(options, args, imports, stdout, stderr); + } +} diff --git a/compiler/java/com/google/dart/runner/V8Launcher.java b/compiler/java/com/google/dart/runner/V8Launcher.java new file mode 100644 index 00000000000..ed52706adef --- /dev/null +++ b/compiler/java/com/google/dart/runner/V8Launcher.java @@ -0,0 +1,283 @@ +// 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. + +package com.google.dart.runner; + +import com.google.common.collect.Lists; +import com.google.debugging.sourcemap.SourceMapping; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** + * @author floitsch@google.com (Florian Loitsch) + * + * Runs a given JS-script in d8 (part of V8). + */ +public class V8Launcher implements JavaScriptLauncher { + private static class Drainer implements Runnable { + private final InputStream stream; + private final List lines; + private final PrintStream out; + + public Drainer(InputStream stream, PrintStream out, List lines) { + this.stream = stream; + this.lines = lines; + this.out = out; + } + + @Override + public void run() { + BufferedReader r = new BufferedReader(new InputStreamReader(stream)); + String str; + try { + while ((str = r.readLine()) != null) { + if (lines != null) { + lines.add(str); + } + out.println(str); + } + } catch (IOException e) { + throw new AssertionError(e); + } + } + } + + private static final String D8_ENVIRONMENT_VARIABLE = "D8_EXEC"; + + private final SourceMapping appSourceMap; + + private static final String EOL = System.getProperty("line.separator"); + + /** + * + */ + public V8Launcher(SourceMapping appSourceMap) { + this.appSourceMap = appSourceMap; + } + + @Override + public void execute(String jsScript, String sourceName, String[] args, Set flags, + PrintStream stdout, PrintStream stderr) + throws RunnerError { + if (!isConfigured()) { + throw new RunnerError("Please set the " + D8_ENVIRONMENT_VARIABLE + " environment variable."); + } + File sourceFile; + try { + sourceFile = writeTempFile(sourceName, jsScript); + } catch (IOException e) { + throw new RunnerError(e); + } + try { + ArrayList command = new ArrayList(); + command.add(v8Executable().getAbsolutePath()); + command.add(sourceFile.getAbsolutePath()); + if (flags.contains(RunnerFlag.PROFILE)) { + command.add("--prof"); + } + command.add("--"); + command.addAll(Arrays.asList(args)); + int exitValue; + Process p = null; + List stdOutLines = Lists.newArrayList(); + Thread stdOutDrain = null; + Thread stdErrDrain = null; + try { + p = Runtime.getRuntime().exec(command.toArray(new String[0])); + // TODO(floitsch): we should handle timeouts (but how long should we wait?). + stdOutDrain = new Thread(new Drainer(p.getInputStream(), stdout, stdOutLines)); + stdErrDrain = new Thread(new Drainer(p.getErrorStream(), stderr, null)); + stdOutDrain.start(); + stdErrDrain.start(); + try { + exitValue = p.waitFor(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } catch (IOException e) { + throw new RunnerError(e); + } finally { + try { + if (stdOutDrain != null) { + stdOutDrain.join(); + } + if (stdErrDrain != null) { + stdErrDrain.join(); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + try { + p.getInputStream().close(); + p.getOutputStream().close(); + p.getOutputStream().close(); + } catch (IOException e) { + throw new RunnerError(e); + } + p.destroy(); + } + if (exitValue != 0) { + StringWriter stringWriter = new StringWriter(); + PrintWriter out = new PrintWriter(stringWriter); + if (flags.contains(RunnerFlag.VERBOSE)) { + out.println(jsScript); + } + out.println("Execution failed."); + + String str = mapStackEntry(decodeStackTraceFromString(stdOutLines), appSourceMap); + if (str != null) { + out.println("Mapped stack trace:"); + out.println(str); + out.println(""); + } + out.println("V8 execution returned non-zero exit-code: " + p.exitValue()); + out.flush(); + throw new RunnerError(stringWriter.toString()); + } + } finally { + sourceFile.delete(); + } + } + + private String mapStackEntry(List entries, SourceMapping map) { + if (entries != null) { + StringBuilder sb = new StringBuilder(); + for (StackEntry entry : entries) { + SourceMapping sm = getSourceMapForFile(entry.file, map); + // TODO(johnlenz): Try to translate the method name. + String method = (entry.method.isEmpty()) ? "" : " (" + entry.method + ")"; + if (sm != null) { + OriginalMapping mapping = sm.getMappingForLine(entry.line, entry.column); + if (mapping != null) { + String file = mapping.getOriginalFile(); + int line = mapping.getLineNumber(); + int column = mapping.getColumnPosition(); + sb.append(" at MAPPED : " + file + ":" + line + ":" + column + method + EOL); + continue; + } + } + sb.append(" at UNMAPPED: " + + entry.file + ":" + entry.line + ":" + entry.column + method + EOL); + } + + return sb.toString(); + } + return null; + } + + SourceMapping getSourceMapForFile(String file, SourceMapping map) { + return map; + } + + static class StackEntry { + String method; + String file; + int line; + int column; + } + + private List decodeStackTraceFromString(List lines) { + List entries = Lists.newArrayList(); + + boolean seenFirst = false; + for (String str : lines) { + StackEntry entry = decodeStackEntry(str); + if (entry == null) { + if (seenFirst) { + break; + } else { + continue; + } + } else { + seenFirst = true; + } + entries.add(entry); + } + + return entries.isEmpty() ? null : entries; + } + + private StackEntry decodeStackEntry(String str) { + final String PREFIX = " at "; + if (str.startsWith(PREFIX)) { + StackEntry entry = new StackEntry(); + int start = str.indexOf("("); + int end = str.indexOf(")"); + entry.method = ""; + String location; + if (start == -1) { + location = str.substring(PREFIX.length()); + } else { + entry.method = str.substring(7, start-1); + location = str.substring(start+1, end); + } + return decodeLocation(entry, location); + } + return null; + } + + private StackEntry decodeLocation(StackEntry entry, String location) { + String[] parts = location.split(":"); + if (parts.length == 3) { + entry.file = parts[0]; + entry.line = Integer.valueOf(parts[1]); + entry.column = Integer.valueOf(parts[2]); + return entry; + } + return null; + } + + private File writeTempFile(String name, String content) throws IOException { + // The first argument to createTempFile must be at least three characters long, and be a + // valid file-name. + name = name.replace('/', '_'); + File file = File.createTempFile("dart_" + name, ".js"); + FileWriter writer = new FileWriter(file); + try { + writer.write(content); + } finally { + writer.close(); + } + return file; + } + + private static File v8Executable() { + String d8Path = System.getProperty("com.google.dart.runner.d8", + System.getenv(D8_ENVIRONMENT_VARIABLE)); + if (d8Path == null) { + String testSrcDir = System.getenv("TEST_SRCDIR"); + if (testSrcDir == null) { + return null; + } + return new File(new File(new File(new File(testSrcDir, "google3"), + "third_party"), "v8"), "d8"); + } else { + return new File(d8Path); + } + } + + /** + * @return true if the D8_EXEC environment variable is correctly set up. + */ + public static boolean isConfigured() { + File file = v8Executable(); + if (file == null) { + return false; + } + return file.canExecute(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/AbstractSourceFileTest.java b/compiler/javatests/com/google/dart/compiler/AbstractSourceFileTest.java new file mode 100644 index 00000000000..5b9a27dc25d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/AbstractSourceFileTest.java @@ -0,0 +1,56 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.common.LibrarySourceFileTest; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +/** + * Shared behavior for creating a temporary file from a java resource + * to support testing {@link File} based classes + */ +public abstract class AbstractSourceFileTest extends CompilerTestCase { + + private File tempFile; + + /** + * Create a temporary file to be cleaned up when the test is complete + * + * @param filePath the path to the file relative to the test class + * @return the temporary file + */ + protected File createTempFile(String filePath) throws IOException { + String source = readUrl(inputUrlFor(LibrarySourceFileTest.class, filePath)); + return createTempFile(filePath, source); + } + + protected File createTempFile(String filePath, String source) throws IOException { + String fileExt = filePath.substring(filePath.lastIndexOf('.')); + String fileName = filePath.substring(filePath.lastIndexOf('/') + 1, + filePath.length() - fileExt.length()); + tempFile = File.createTempFile(fileName, fileExt); + FileWriter writer = new FileWriter(tempFile); + writer.write(source); + writer.close(); + return tempFile; + } + + /** + * Delete the temporary file if it was created. + * + * @see junit.framework.TestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception { + if (tempFile != null) { + tempFile.delete(); + } + super.tearDown(); + } + +} diff --git a/compiler/javatests/com/google/dart/compiler/CodeCompletionParseTest.java b/compiler/javatests/com/google/dart/compiler/CodeCompletionParseTest.java new file mode 100644 index 00000000000..33f58d710ed --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/CodeCompletionParseTest.java @@ -0,0 +1,86 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.LibraryUnit; + +/** + * Tests the use of the parser and analysis phases as used by the IDE for code + * completion. + */ +public class CodeCompletionParseTest extends CompilerTestCase { + + public void test1() throws Exception { + AnalyzeLibraryResult result = analyzeLibrary("foo", makeCode( + "class CellLocation {", + " int _field1;", + " String _field2;", + "", + " CellLoc", // cursor + "", + " int hashCode() {", + " return _field1 * 31 ^ _field2.hashCode();", + " }", + "}")); + LibraryUnit lib = result.getLibraryUnitResult(); + assertNotNull(lib); + } + + public void test2() throws Exception { + AnalyzeLibraryResult result = analyzeLibrary("foo", makeCode( + "doFoo() {", + " new ", // cursor + "}")); + LibraryUnit lib = result.getLibraryUnitResult(); + assertNotNull(lib); + } + + public void test3() throws Exception { + AnalyzeLibraryResult result = analyzeLibrary("foo", makeCode( + "class Foo {", + " static final Bar b = const ", // cursor + "}", + "", + "class Bar {", + " factory Bar() {}", + "}")); + LibraryUnit lib = result.getLibraryUnitResult(); + assertNotNull(lib); + } + + public void test4() throws Exception { + AnalyzeLibraryResult result = analyzeLibrary("foo", makeCode( + "foo() {", + " int SEED;", + " for (int i = 0; i < S)", // cursor before ) + "}")); + LibraryUnit lib = result.getLibraryUnitResult(); + assertNotNull(lib); + } + + public void test5() throws Exception { + AnalyzeLibraryResult result = analyzeLibrary("foo", makeCode( + "ckass Sunflower {", + " static final int SEED_RADIUS = 2;", + " static final int SCALE_FACTOR = 4;", + " static final num PI2 = Math.PI * 2;", + " static final num PI4 = M", // cursor + "}")); + LibraryUnit lib = result.getLibraryUnitResult(); + assertNotNull(lib); + } + + public void test6() throws Exception { + AnalyzeLibraryResult result = analyzeLibrary("foo", makeCode( + "class Sunflower {", + " static final int SEED_RADIUS = 2;", + " static final int SCALE_FACTOR = 4;", + " static final num PI2 = Math.PI * 2;", + " static final num PI4 = M", // cursor + "}")); + LibraryUnit lib = result.getLibraryUnitResult(); + assertNotNull(lib); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/CompilerTestCase.java b/compiler/javatests/com/google/dart/compiler/CompilerTestCase.java new file mode 100644 index 00000000000..3630516c26b --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/CompilerTestCase.java @@ -0,0 +1,248 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.parser.DartParser; +import com.google.dart.compiler.parser.DartScannerParserContext; +import com.google.dart.compiler.parser.ParserContext; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Base class for compiler tests, with helpful utility methods. + */ +public abstract class CompilerTestCase extends TestCase { + + private static final String UTF8 = "UTF-8"; + + /** + * Read a resource from the given URL. + */ + protected static String readUrl(URL url) { + try { + StringBuffer out = new StringBuffer(); + Reader in = new InputStreamReader(url.openStream(), UTF8); + char[] buf = new char[10240]; + int n; + while ((n = in.read(buf)) > 0) { + out.append(buf, 0, n); + } + in.close(); + return out.toString(); + } catch (IOException e) { + // Just punt a RuntimeException out the top if something goes wrong. + // It will simply cause the test to fail, which is exactly what we want. + throw new RuntimeException(e); + } + } + + /** + * Return a URL that can be used to read an input file for the given test name + * and path. + */ + protected static URL inputUrlFor(Class testClass, String testName) { + String fullPath = testClass.getPackage().getName().replace('.', '/') + "/" + + testName; + URL url = chooseClassLoader().getResource(fullPath); + if (url == null) { + fail("Could not find input file: " + fullPath); + } + return url; + } + + private static ClassLoader chooseClassLoader() { + if (Thread.currentThread().getContextClassLoader() != null) { + return Thread.currentThread().getContextClassLoader(); + } + return CompilerTestCase.class.getClassLoader(); + } + + /** + * Collects the results of running analyzeLibrary. + */ + protected static class AnalyzeLibraryResult extends DartCompilerListener { + private final List compilationErrors; + private final List compilationWarnings; + private final List typeErrors; + private LibraryUnit result; + + public AnalyzeLibraryResult() { + compilationErrors = Lists.newArrayList(); + compilationWarnings = Lists.newArrayList(); + typeErrors = Lists.newArrayList(); + } + + @Override + public void compilationError(DartCompilationError event) { + compilationErrors.add(event); + } + + @Override + public void compilationWarning(DartCompilationError event) { + compilationWarnings.add(event); + } + + @Override + public void typeError(DartCompilationError event) { + typeErrors.add(event); + } + + /** + * @param lib + */ + public void setLibraryUnitResult(LibraryUnit lib) { + result = lib; + } + + /** + * @return the analyzed library + */ + public LibraryUnit getLibraryUnitResult() { + return result; + } + } + + /** + * Build a multi-line string from a list of strings. + * + * @param lines + * @return a single string containing {@code lines}, each terminated by \n + */ + protected static String makeCode(String... lines) { + StringBuilder buf = new StringBuilder(); + for (String line : lines) { + buf.append(line).append('\n'); + } + return buf.toString(); + } + + /** + * Simulate running {@code analyzeLibrary} the way the IDE will. + *

    + * Note: if the IDE changes how it calls analyzeLibrary, this should + * be changed to match. + * + * @param name the name to use for the source file + * @param code the Dart code to parse/analyze + * @return an {@link AnalyzeLibraryResult} containing the {@link LibraryUnit} + * and all the errors/warnings generated from the supplied code + * @throws Exception + */ + protected AnalyzeLibraryResult analyzeLibrary(String name, String code) + throws Exception { + MockLibrarySource lib = new MockLibrarySource(); + DartSourceTest src = new DartSourceTest(name, code, lib); + lib.addSource(src); + final CompilerConfiguration config = new DefaultCompilerConfiguration(new CompilerOptions()) { + @Override + public boolean checkOnly() { + return true; + } + + @Override + public boolean incremental() { + return true; + } + + @Override + public boolean resolveDespiteParseErrors() { + return true; + } + }; + AnalyzeLibraryResult result = new AnalyzeLibraryResult(); + Map testUnits = new HashMap(); + ParserContext context = makeParserContext(src, code, result); + DartUnit unit = makeParser(context).parseUnit(src); + testUnits.put(src.getUri(), unit); + DartArtifactProvider provider = new MockArtifactProvider(); + result.setLibraryUnitResult(DartCompiler.analyzeLibrary(lib, testUnits, config, provider, + result)); + return result; + } + + /** + * Compiles a single unit with a synthesized application, using the specified backend. + */ + protected DartSource compileSingleUnit(String name, String code, + DartArtifactProvider provider, Backend backend) throws IOException { + MockLibrarySource lib = new MockLibrarySource(); + DartSourceTest src = new DartSourceTest(name, code, lib); + lib.addSource(src); + CompilerConfiguration config = new DefaultCompilerConfiguration(backend); + DartCompilerListener listener = new DartCompilerListenerTest(src.getName()); + DartCompiler.compileLib(lib, config, provider, listener); + return src; + } + + /** + * Parse a single compilation unit for the given input file. + */ + protected final DartUnit parseUnit(final String path) { + // final because we delegate to the method below, and only that one should + // be overriden to do extra checks. + URL url = inputUrlFor(getClass(), path); + String source = readUrl(url); + return parseUnit(path, source); + } + + /** + * Parse a single compilation unit for the name and source. + */ + protected DartUnit parseUnit(final String srcName, final String sourceCode) { + // TODO(jgw): We'll need to fill in the library parameter when testing multiple units. + DartSourceTest src = new DartSourceTest(srcName, sourceCode, null); + ParserContext context = makeParserContext(src, sourceCode, + new DartCompilerListenerTest(srcName)); + return makeParser(context).parseUnit(src); + } + + /** + * Parse a single compilation unit for the given input file, and check for a + * set of expected errors. + * + * @param errors a sequence of errors represented as triples of the form + * (String msg, int line, int column) or + * (ErrorCode code, int line, int column) + */ + protected DartUnit parseUnitErrors(final String path, final Object... errors) { + URL url = inputUrlFor(getClass(), path); + String sourceCode = readUrl(url); + // TODO(jgw): We'll need to fill in the library parameter when testing multiple units. + DartSourceTest src = new DartSourceTest(path, sourceCode, null); + DartCompilerListenerTest listener = new DartCompilerListenerTest(path, errors); + ParserContext context = makeParserContext(src, sourceCode, listener); + DartUnit unit = makeParser(context).parseUnit(src); + listener.checkAllErrorsReported(); + return unit; + } + + /** + * Override this method to provide an alternate {@link DartParser}. + */ + protected DartParser makeParser(ParserContext context) { + return new DartParser(context); + } + + /** + * Override this method to provide an alternate {@link ParserContext}. + */ + protected ParserContext makeParserContext(Source src, String sourceCode, + DartCompilerListener listener) { + return new DartScannerParserContext(src, sourceCode, listener); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/DartCompilerListenerTest.java b/compiler/javatests/com/google/dart/compiler/DartCompilerListenerTest.java new file mode 100644 index 00000000000..3864db2bfb4 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/DartCompilerListenerTest.java @@ -0,0 +1,93 @@ +// 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. + +package com.google.dart.compiler; + +/** + * Testing implementation of {@link DartCompilerListener}. + */ +public class DartCompilerListenerTest extends DartCompilerListener { + + private final String srcName; + private String[] messages; + private ErrorCode[] errorCodes; + private int[] line; + private int[] column; + private int total; + private int current; + + /** + * Creates a listener with expected errors (if any). + * + * @param srcName name of the source file + * @param errors a sequence of errors represented as triples of the form + * (String msg, int line, int column) or + * (ErrorCode code, int line, int column) + */ + public DartCompilerListenerTest(String srcName, Object... errors) { + this.srcName = srcName; + CompilerTestCase.assertEquals( + "Invalid sequence of error expectations", 0, errors.length % 3); + this.total = errors.length / 3; + this.current = 0; + this.messages = new String[total]; + this.errorCodes = new ErrorCode[total]; + this.line = new int[total]; + this.column = new int[total]; + for (int i = 0; i < total; i++) { + Object stringOrErrorCode = errors[3 * i]; + if (stringOrErrorCode instanceof ErrorCode) { + this.errorCodes[i] = (ErrorCode) stringOrErrorCode; + } else { + this.messages[i] = (String) stringOrErrorCode; + } + this.line[i] = (Integer) errors[(3 * i) + 1]; + this.column[i] = (Integer) errors[(3 * i) + 2]; + } + } + + @Override + public void compilationError(DartCompilationError event) { + String reportedSrcName = (event.getSource() != null) + ? event.getSource().getName() + : null; + if (reportedSrcName == null) { + reportedSrcName = ""; + } + CompilerTestCase.assertTrue("More errors (" + (current + 1) + + ") than expected (" + total + "):\n" + event, + current < total); + + CompilerTestCase.assertEquals(srcName, reportedSrcName); + + if (errorCodes[current] != null) { + CompilerTestCase.assertEquals( + "Wrong error code", errorCodes[current], event.getErrorCode()); + } else { + CompilerTestCase.assertEquals( + "Wrong error message", messages[current], event.getMessage()); + } + CompilerTestCase.assertEquals( + "Wrong line number", line[current], event.getLineNumber()); + CompilerTestCase.assertEquals( + "Wrong column number", column[current], event.getColumnNumber()); + current++; + } + + @Override + public void compilationWarning(DartCompilationError event) { + compilationError(event); + } + + @Override + public void typeError(DartCompilationError event) { + compilationError(event); + } + + /** Checks that all expected errors were reported. */ + public void checkAllErrorsReported() { + CompilerTestCase.assertEquals("Not all expected errors were reported", + total, current); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/DartLibrarySourceTest.java b/compiler/javatests/com/google/dart/compiler/DartLibrarySourceTest.java new file mode 100644 index 00000000000..34586642ebb --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/DartLibrarySourceTest.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.net.URL; + +public class DartLibrarySourceTest extends SourceTest implements LibrarySource { + private final String src; + private final Class base; + + public DartLibrarySourceTest(Class base, String path) { + super(path); + + this.base = base; + URL url = CompilerTestCase.inputUrlFor(base, path); + src = CompilerTestCase.readUrl(url); + } + + @Override + public Reader getSourceReader() throws IOException { + return new StringReader(src); + } + + @Override + public LibrarySource getImportFor(String relPath) throws IOException { + throw new RuntimeException("Unimplemented"); + } + + @Override + public DartSource getSourceFor(String relPath) { + return new DartSourceTest(base, relPath, this); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/DartSourceTest.java b/compiler/javatests/com/google/dart/compiler/DartSourceTest.java new file mode 100644 index 00000000000..bbb27fd162f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/DartSourceTest.java @@ -0,0 +1,52 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.Reader; +import java.io.StringReader; +import java.net.URL; + +public class DartSourceTest extends SourceTest implements DartSource { + private final String path; + private final String src; + private final LibrarySource lib; + + public DartSourceTest(Class base, String path, LibrarySource lib) { + super(path); + + this.path = path; + this.lib = lib; + URL url = CompilerTestCase.inputUrlFor(base, path); + src = CompilerTestCase.readUrl(url); + } + + public DartSourceTest(String path, String source, LibrarySource lib) { + super(path + ".dart"); + + this.path = path; + this.src = source; + this.lib = lib; + } + + @Override + public LibrarySource getLibrary() { + return lib; + } + + @Override + public String getName() { + return path; + } + + @Override + public Reader getSourceReader() { + return new StringReader(src); + } + + @Override + public String getRelativePath() { + return path; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/DeltaAnalyzerTest.java b/compiler/javatests/com/google/dart/compiler/DeltaAnalyzerTest.java new file mode 100644 index 00000000000..97e2ed0297f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/DeltaAnalyzerTest.java @@ -0,0 +1,107 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.testing.TestCompilerConfiguration; +import com.google.dart.compiler.testing.TestCompilerContext; +import com.google.dart.compiler.testing.TestDartArtifactProvider; +import com.google.dart.compiler.testing.TestLibrarySource; +import com.google.dart.compiler.util.DartSourceString; + +import junit.framework.TestCase; + +import java.io.IOException; + +public class DeltaAnalyzerTest extends TestCase { + private final TestCompilerConfiguration config = new TestCompilerConfiguration(); + private final DartCompilerListener listener = new TestCompilerContext(); + private final DartArtifactProvider provider = new TestDartArtifactProvider(); + + public void testNoChangeSingleFile() throws IOException { + TestLibrarySource librarySource = new TestLibrarySource(getName()); + librarySource.addSource("before.dart", + "class Foo {}", + "m() {}"); + DartUnit change = analyzeNoChange(librarySource); + assertEquals(2, change.getTopLevelNodes().size()); + ClassElement cls = (ClassElement) change.getTopLevelNodes().get(0).getSymbol(); + assertNotNull(cls); + assertEquals("Foo", cls.getName()); + MethodElement method = (MethodElement) change.getTopLevelNodes().get(1).getSymbol(); + assertNotNull(method); + assertEquals("m", method.getName()); + } + + public void testNoChangeTwoFiles() throws IOException { + TestLibrarySource librarySource = new TestLibrarySource(getName()); + librarySource.addSource("before.dart", + "class Foo extends Bar {}", + "m() {}"); + librarySource.addSource("common.dart", + "class Bar {}"); + DartUnit change = analyzeNoChange(librarySource); + assertEquals(2, change.getTopLevelNodes().size()); + ClassElement cls = (ClassElement) change.getTopLevelNodes().get(0).getSymbol(); + assertNotNull(cls); + assertEquals("Foo", cls.getName()); + assertEquals("Bar", cls.getSupertype().toString()); + MethodElement method = (MethodElement) change.getTopLevelNodes().get(1).getSymbol(); + assertNotNull(method); + assertEquals("m", method.getName()); + } + + public void testChangeSingleFile() throws IOException { + TestLibrarySource librarySource = new TestLibrarySource(getName()); + librarySource.addSource("before.dart", + "class Foo {}", + "m() {}"); + DartSource sourceBefore = librarySource.getSourceFor("before.dart"); + DartSource sourceAfter = new DartSourceString("after.dart", "class Foo {}"); + DartUnit change = analyze(librarySource, sourceBefore, sourceAfter); + assertEquals(1, change.getTopLevelNodes().size()); + ClassElement cls = (ClassElement) change.getTopLevelNodes().get(0).getSymbol(); + assertNotNull(cls); + assertEquals("Foo", cls.getName()); + } + + public void testChangeTwoFiles() throws IOException { + TestLibrarySource librarySource = new TestLibrarySource(getName()); + librarySource.addSource("before.dart", + "class Foo extends Bar {}", + "m() {}"); + librarySource.addSource("common.dart", + "class Bar {}"); + DartSource sourceBefore = librarySource.getSourceFor("before.dart"); + DartSource sourceAfter = new DartSourceString("after.dart", "class Foo extends Bar {}"); + DartUnit change = analyze(librarySource, sourceBefore, sourceAfter); + assertEquals(1, change.getTopLevelNodes().size()); + ClassElement cls = (ClassElement) change.getTopLevelNodes().get(0).getSymbol(); + assertNotNull(cls); + assertEquals("Foo", cls.getName()); + assertEquals("Bar", cls.getSupertype().toString()); + } + + private DartUnit analyzeNoChange(LibrarySource librarySource) throws IOException { + DartSource sourceBefore = librarySource.getSourceFor("before.dart"); + DartSource sourceAfter = sourceBefore; + return analyze(librarySource, sourceBefore, sourceAfter); + } + + private DartUnit analyze(LibrarySource librarySource, DartSource sourceBefore, + DartSource sourceAfter) throws IOException { + LibraryUnit libraryUnit = DartCompiler.analyzeLibrary(librarySource, null, + config, provider, listener); + LibraryElement enclosingLibrary = libraryUnit.getElement(); + LibraryElement coreLibrary = libraryUnit.getImports().iterator().next().getElement(); + return (DartUnit) DartCompiler.analyzeDelta(SourceDelta.before(sourceBefore).after(sourceAfter), + enclosingLibrary, coreLibrary, + null, -1, -1, config, listener); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/DeltaBench.java b/compiler/javatests/com/google/dart/compiler/DeltaBench.java new file mode 100644 index 00000000000..8416bd315d2 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/DeltaBench.java @@ -0,0 +1,102 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.resolver.LibraryElement; +import com.google.dart.compiler.testing.TestCompilerConfiguration; +import com.google.dart.compiler.testing.TestCompilerContext; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +public class DeltaBench { + public static void main(String[] args) throws IOException { + File trunkDir = new File(args[0]); + File libraryFile = new File(trunkDir, args[1]); + File outputDirectory = new File(trunkDir, args[2]); + for (int i = 0; i < 100; i++) { + analyze(libraryFile, outputDirectory, null); + for (int j = 3; j < args.length; j++) { + analyze(libraryFile, outputDirectory, args[j]); + } + } + } + + private static void analyze(File libraryFile, File outputDirectory, String interestingFile) + throws IOException { + final boolean incremental = interestingFile == null; + long start = System.currentTimeMillis(); + LibrarySource librarySource = new UrlLibrarySource(libraryFile); + CompilerConfiguration config = new TestCompilerConfiguration() { + @Override + public boolean incremental() { + return incremental; + } + + @Override + public boolean shouldOptimize() { + return false; + } + + @Override + public boolean checkOnly() { + return false; + } + }; + DartCompilerListener listener = new TestCompilerContext(); + DartArtifactProvider provider = new DefaultDartArtifactProvider(outputDirectory); + LibraryUnit libraryUnit = DartCompiler.analyzeLibrary(librarySource, null, config, provider, + listener); + System.err.println("analyzeLibrary" + (incremental ? "+incremental" : "") + + "(" + libraryUnit.getName() + ") took " + + (System.currentTimeMillis() - start) + "ms"); + if (incremental) { + return; + } + LibraryUnit enclosingLibraryUnit = findLibrary(libraryUnit, interestingFile, + new HashSet()); + LibraryUnit coreLibraryUnit = findLibrary(libraryUnit, "object.dart", + new HashSet()); + DartUnit unit = null; + for (DartUnit current : enclosingLibraryUnit.getUnits()) { + if (current.getSource().getName().endsWith(interestingFile)) { + unit = current; + break; + } + } + start = System.currentTimeMillis(); + DartCompiler.analyzeDelta(SourceDelta.before(unit.getSource()), + enclosingLibraryUnit.getElement(), + coreLibraryUnit.getElement(), + null, -1, -1, config, listener); + System.err.println("analyzeDelta(" + unit.getSource().getName() + ") took " + + (System.currentTimeMillis() - start) + "ms"); + } + + private static LibraryUnit findLibrary(LibraryUnit libraryUnit, String uri, + Set seen) { + if (seen.contains(libraryUnit.getElement())) { + return null; + } + seen.add(libraryUnit.getElement()); + for (LibraryNode src : libraryUnit.getSourcePaths()) { + if (src.getText().equals(uri)) { + return libraryUnit; + } + } + for (LibraryUnit importedLibrary : libraryUnit.getImports()) { + LibraryUnit unit = findLibrary(importedLibrary, uri, seen); + if (unit != null) { + return unit; + } + } + return null; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/IdeTest.java b/compiler/javatests/com/google/dart/compiler/IdeTest.java new file mode 100644 index 00000000000..86acdde1e1a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/IdeTest.java @@ -0,0 +1,243 @@ +// 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. + +package com.google.dart.compiler; + +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartUnqualifiedInvocation; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.CoreTypeProviderImplementation; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.resolver.Scope; +import com.google.dart.compiler.testing.TestCompilerConfiguration; +import com.google.dart.compiler.testing.TestCompilerContext; +import com.google.dart.compiler.testing.TestCompilerContext.EventKind; +import com.google.dart.compiler.testing.TestDartArtifactProvider; +import com.google.dart.compiler.testing.TestLibrarySource; +import com.google.dart.compiler.type.FunctionType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.TypeAnalyzer; + +import junit.framework.TestCase; + +import java.io.IOException; + +/** + * Test of the IDE API in DartCompiler. + */ +public class IdeTest extends TestCase { + + private final TestCompilerContext context = new TestCompilerContext(EventKind.ERROR, + EventKind.TYPE_ERROR) { + @Override + protected void handleEvent(DartCompilationError event, EventKind kind) { + super.handleEvent(event, kind); + // For debugging: + // System.err.println(event); + } + }; + + private final DartCompilerListener listener = context; + + private DartArtifactProvider provider = new TestDartArtifactProvider(); + + private CompilerConfiguration config = new TestCompilerConfiguration(); + + public void testAnalyseNoSemicolonPropertyAccess() { + DartUnit unit = analyzeUnit("no_semicolon_property_access", + "class Foo {", + " int i;", + " void foo() {", + " i.y", // Missing semicolon. + " }", + "}"); + assertEquals("errorCount", 1, context.getErrorCount()); // Missing semicolon. + assertEquals("typeErrorCount", 1, context.getTypeErrorCount()); // No member named "y". + DartExprStmt statement = (DartExprStmt) firstStatementOfMethod(unit, "Foo", "foo"); + FieldElement element = (FieldElement) qualifierElement(statement.getExpression()); + assertEquals("int", element.getType().getElement().getName()); + } + + public void testAnalyseNoSemicolonBrokenPropertyAccess() { + DartUnit unit = analyzeUnit("no_semicolon_broken_property_access", + "class Foo {", + " int i;", + " void foo() {", + " i.", // Syntax error and missing semicolon. + " }", + "}"); + // Expected identifier and missing semicolon + assertEquals("errorCount", 2, context.getErrorCount()); + assertEquals("typeErrorCount", 1, context.getTypeErrorCount()); // No member named "". + DartExprStmt statement = (DartExprStmt) firstStatementOfMethod(unit, "Foo", "foo"); + FieldElement element = (FieldElement) qualifierElement(statement.getExpression()); + assertEquals("int", element.getType().getElement().getName()); + } + + public void testAnalyseBrokenPropertyAccess() { + DartUnit unit = analyzeUnit("broken_property_access", + "class Foo {", + " int i;", + " void foo() {", + " i.;", // Syntax error here. + " }", + "}"); + assertEquals("errorCount", 1, context.getErrorCount()); // Expected identifier. + assertEquals("typeErrorCount", 1, context.getTypeErrorCount()); // No member named "". + DartExprStmt statement = (DartExprStmt) firstStatementOfMethod(unit, "Foo", "foo"); + FieldElement element = (FieldElement) qualifierElement(statement.getExpression()); + assertEquals("int", element.getType().getElement().getName()); + } + + public void testAnalyseNoSemicolonIdentifier() { + DartUnit unit = analyzeUnit("no_semicolon_identifier", + "class Foo {", + " int i;", + " void foo() {", + " i", // Missing semicolon. + " }", + "}"); + assertEquals("errorCount", 1, context.getErrorCount()); // Missing semicolon. + assertEquals("typeErrorCount", 0, context.getTypeErrorCount()); + DartExprStmt statement = (DartExprStmt) firstStatementOfMethod(unit, "Foo", "foo"); + FieldElement field = (FieldElement) targetElement(statement.getExpression()); + assertEquals("int", field.getType().getElement().getName()); + } + + public void testAnalyseNoSemicolonMethodCall() { + DartUnit unit = analyzeUnit("no_semicolon_method_call", + "class Foo {", + " int i () { return 0; }", + " void foo() {", + " i()", // Missing semicolon. + " }", + "}"); + assertEquals("errorCount", 1, context.getErrorCount()); // Missing semicolon. + assertEquals("typeErrorCount", 0, context.getTypeErrorCount()); + DartExprStmt statement = (DartExprStmt) firstStatementOfMethod(unit, "Foo", "foo"); + DartExpression expression = statement.getExpression(); + DartUnqualifiedInvocation invocation = (DartUnqualifiedInvocation) expression; + MethodElement method = (MethodElement) targetElement(invocation.getTarget()); + assertEquals("i", method.getName()); + FunctionType type = (FunctionType) method.getType(); + assertEquals("int", type.getReturnType().getElement().getName()); + } + + public void testAnalyseVoidKeyword() { + DartUnit unit = analyzeUnit("void_keyword", + "class Foo {", + " Function voidFunction;", + " void foo() {", + " void", // Missing semicolon and keyword + " }", + "}"); + // Expected identifier and missing semicolon. + assertEquals("errorCount", 2, context.getErrorCount()); + assertEquals("typeErrorCount", 1, context.getTypeErrorCount()); // void cannot be resolved. + DartExprStmt statement = (DartExprStmt) firstStatementOfMethod(unit, "Foo", "foo"); + DartIdentifier expression = (DartIdentifier) statement.getExpression(); + assertEquals("void", expression.getTargetName()); + } + + public void testAnalyseVoidKeywordPropertyAccess() { + DartUnit unit = analyzeUnit("void_keyword_property_access", + "class Foo {", + " Function voidFunction;", + " void foo() {", + " this.void", // Missing semicolon and keyword + " }", + "}"); + // Expected identifier and missing semicolon. + assertEquals("errorCount", 2, context.getErrorCount()); + assertEquals("typeErrorCount", 1, context.getTypeErrorCount()); // No member "void". + DartExprStmt statement = (DartExprStmt) firstStatementOfMethod(unit, "Foo", "foo"); + DartPropertyAccess expression = (DartPropertyAccess) statement.getExpression(); + assertEquals("void", expression.getPropertyName()); + } + + public void testReturnIntTypeAnalysis() { + DartUnit unit = analyzeUnit("return_int_type_analysis", + "class Foo {", + " int i;", + " int foo() {", + " return i;", + " }", + "}"); + Scope unitScope = unit.getLibrary().getElement().getScope(); + CoreTypeProvider typeProvider = new CoreTypeProviderImplementation(unitScope, context); + DartClass classNode = firstClassOfUnit(unit, "Foo"); + DartReturnStatement rtnStmt = (DartReturnStatement) firstStatementOfMethod(unit, "Foo", "foo"); + ClassElement classElement = classNode.getSymbol(); + InterfaceType definingType = classElement.getType(); + Type type = TypeAnalyzer.analyze(rtnStmt.getValue(), typeProvider, context, definingType); + assertNotNull(type); + assertEquals("int", type.getElement().getName()); + } + + private Element targetElement(DartExpression expression) { + DartIdentifier identifier = (DartIdentifier) expression; + Element element = identifier.getTargetSymbol(); + assertNotNull(element); + return element; + } + + private Element qualifierElement(DartExpression node) { + DartPropertyAccess propertyAccess = (DartPropertyAccess) node; + DartIdentifier identifier = (DartIdentifier) propertyAccess.getQualifier(); + Element element = identifier.getTargetSymbol(); + assertNotNull(element); + return element; + } + + private DartClass firstClassOfUnit(DartUnit unit, String cls) { + DartClass dartClass = null; + for (DartNode dartNode : unit.getTopLevelNodes()) { + Element element = (Element) dartNode.getSymbol(); + if (element.getName().equals(cls)) { + dartClass = (DartClass) dartNode; + break; + } + } + assertNotNull(dartClass); + return dartClass; + } + + private DartStatement firstStatementOfMethod(DartUnit unit, String cls, String member) { + ClassElement classElement = firstClassOfUnit(unit, cls).getSymbol(); + MethodElement method = (MethodElement) classElement.lookupLocalElement(member); + assertNotNull(String.format("Member '%s' not found in %s", member, cls), method); + DartMethodDefinition methodNode = (DartMethodDefinition) method.getNode(); + assertNotNull(methodNode); + return methodNode.getFunction().getBody().getStatements().get(0); + } + + private DartUnit analyzeUnit(String name, String... sourceLines) throws AssertionError { + TestLibrarySource lib = new TestLibrarySource(name); + lib.addSource(name + ".dart", sourceLines); + LibraryUnit libraryUnit; + try { + libraryUnit = DartCompiler.analyzeLibrary(lib, null, config, provider, listener); + assertNotNull("libraryUnit == null", libraryUnit); + } catch (IOException e) { + throw new AssertionError(e); + } + DartUnit unit = libraryUnit.getUnit(name + ".dart"); + assertNotNull("unit == null", unit); + return unit; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/IdeTests.java b/compiler/javatests/com/google/dart/compiler/IdeTests.java new file mode 100644 index 00000000000..8cbc1df36d6 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/IdeTests.java @@ -0,0 +1,23 @@ +// 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. + +package com.google.dart.compiler; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class IdeTests extends TestSetup { + + public IdeTests(Test test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("IDE/dartc integration test suite."); + suite.addTestSuite(IdeTest.class); + suite.addTestSuite(DeltaAnalyzerTest.class); + return suite; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/MockArtifactProvider.java b/compiler/javatests/com/google/dart/compiler/MockArtifactProvider.java new file mode 100644 index 00000000000..c856fc72a43 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/MockArtifactProvider.java @@ -0,0 +1,92 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Testing implementation of {@link DartArtifactProvider}. + */ +public class MockArtifactProvider extends DartArtifactProvider { + + private static class Artifact { + StringWriter writer = new StringWriter(); + long lastModified; + } + + private final Map artifacts = new ConcurrentHashMap(); + + public MockArtifactProvider() { + } + + @Override + public Reader getArtifactReader(Source source, String part, String ext) { + Artifact artifact = artifacts.get(keyFor(source, part, ext)); + if (artifact == null) { + return null; + } + return new StringReader(artifact.writer.toString()); + } + + @Override + public URI getArtifactUri(Source source, String part, String ext) { + return URI.create("file:" + keyFor(source, part, ext)); + } + + @Override + public Writer getArtifactWriter(Source source, String part, String ext) { + Artifact artifact = new Artifact(); + artifacts.put(keyFor(source, part, ext), artifact); + artifact.lastModified = new Date().getTime(); + return artifact.writer; + } + + @Override + public boolean isOutOfDate(Source source, Source base, String ext) { + Artifact artifact = artifacts.get(keyFor(base, "", ext)); + if (artifact == null) { + return true; + } + + return source.getLastModified() > artifact.lastModified; + } + + /** + * Quick way to get an artifact without going through the reader. + */ + public String getArtifactString(Source source, String part, String ext) { + Artifact artifact = artifacts.get(keyFor(source, part, ext)); + if (artifact == null) { + return null; + } + + return artifact.writer.toString(); + } + + /** + * Removes the given artifact, by name. + */ + public void removeArtifact(String name, String part, String ext) { + artifacts.remove(keyFor(name, part, ext)); + } + + private String keyFor(Source source, String part, String ext) { + return keyFor(source.getName(), part, ext); + } + + private String keyFor(String sourceName, String part, String ext) { + if (!part.isEmpty()) { + part = "$" + part; + } + return sourceName + part + "/" + ext; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/MockBundleLibrarySource.java b/compiler/javatests/com/google/dart/compiler/MockBundleLibrarySource.java new file mode 100644 index 00000000000..10ec48d511c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/MockBundleLibrarySource.java @@ -0,0 +1,181 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Reader; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A mock application source that uses resources bundled in the classpath, along with methods for + * remapping sources and modifying their timestamps. + */ +public class MockBundleLibrarySource extends UrlLibrarySource implements LibrarySource { + + private class NonexistentDartSource implements DartSource { + private final String relPath; + + public NonexistentDartSource(String relPath) { + this.relPath = relPath; + } + + @Override + public boolean exists() { + return false; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public String getName() { + return relPath; + } + + @Override + public Reader getSourceReader() throws IOException { + throw new FileNotFoundException(); + } + + @Override + public URI getUri() { + try { + return new URI(relPath); + } catch (URISyntaxException e) { + throw new AssertionError(e.getMessage()); + } + } + + @Override + public LibrarySource getLibrary() { + return MockBundleLibrarySource.this; + } + + @Override + public String getRelativePath() { + return relPath; + } + } + + private final String basePath; + private final ClassLoader loader; + private final String libName; + + private final Map imports = + new HashMap(); + + /** + * Source remappings. Each key identifies the name of a source file that will be remapped to an + * alternate source file in {@link #getSourceFor(String)}. + */ + private final Map sourceRemapping = new HashMap(); + private final Set sourceTimestamps = new HashSet(); + + public MockBundleLibrarySource(ClassLoader loader, String basePath, String libName) + throws URISyntaxException { + this(loader, basePath, libName, libName); + } + + public MockBundleLibrarySource(ClassLoader loader, String basePath, String libName, + String altName) throws URISyntaxException { + super(loader.getResource(basePath + libName).toURI()); + this.loader = loader; + this.basePath = basePath; + this.libName = altName; + } + + @Override + public MockBundleLibrarySource getImportFor(String relPath) { + MockBundleLibrarySource libSrc = imports.get(relPath); + if (libSrc == null) { + try { + libSrc = new MockBundleLibrarySource(loader, basePath, relPath); + } catch (URISyntaxException e) { + throw new AssertionError(); + } + imports.put(relPath, libSrc); + } + return libSrc; + } + + @Override + public DartSource getSourceFor(final String relPath) { + String remap = sourceRemapping.get(relPath); + final boolean touched = sourceTimestamps.contains(relPath); + + String fullPath = basePath + ((remap != null) ? remap : relPath); + URI uri; + try { + URL url = loader.getResource(fullPath); + if (url == null) { + return new NonexistentDartSource(relPath); + } + + uri = url.toURI(); + } catch (URISyntaxException e) { + throw new AssertionError(); + } + + return new UrlDartSource(uri, relPath, this) { + @Override + public long getLastModified() { + if (touched) { + return new Date().getTime(); + } + return super.getLastModified(); + } + + @Override + public String getName() { + return relPath; + } + }; + } + + @Override + public String getName() { + return libName; + } + + /** + * Remaps the given source to an alternate. This allows testing of changes to source contents. + * Note that you'll still need to call {@link #touchSource(String)} to cause it to be recompiled. + */ + public void remapSource(String relPath, String remappedRelPath) { + sourceRemapping.put(relPath, remappedRelPath); + } + + /** + * Removes the given source. Any attempt to read it will result in an NPE. + */ + public void removeSource(String relPath) { + sourceRemapping.put(relPath, "does/not/exist"); + } + + /** + * Touches the given source file, forcing a recompile. + */ + public void touchSource(String relPath) { + sourceTimestamps.add(relPath); + } + + /** + * Clears all remappings and source timestamps. + */ + public void resetRemappings() { + sourceRemapping.clear(); + sourceTimestamps.clear(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/MockLibrarySource.java b/compiler/javatests/com/google/dart/compiler/MockLibrarySource.java new file mode 100644 index 00000000000..d5696cb1840 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/MockLibrarySource.java @@ -0,0 +1,91 @@ +// 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. + +package com.google.dart.compiler; + +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +/** + * Testing implementation of {@link LibrarySource}. + */ +public class MockLibrarySource extends SourceTest implements LibrarySource { + + private final static String TEST_APP_NAME = "Test_app"; + + private final List imports = new ArrayList(); + private final List sources = new ArrayList(); + + public MockLibrarySource() { + super(TEST_APP_NAME); + } + + @Override + public Reader getSourceReader() { + ArrayList libNames = new ArrayList(); + for (LibrarySource lib : imports) { + libNames.add(lib.getName()); + } + ArrayList sourceNames = new ArrayList(); + for (DartSource source : sources) { + sourceNames.add(source.getName()); + } + // Passing null for the entryPoint, assuming the source already contains a + // toplevel main() or is a library that doesn't require an entryPoint. + return new StringReader(DefaultLibrarySource.generateSource( + getName(), libNames, sourceNames, null)); + } + + @Override + public LibrarySource getImportFor(String relPath) { + for (LibrarySource lib : imports) { + if (lib.getName().equals(relPath)) { + return lib; + } + } + throw new RuntimeException("Cannot find import for " + relPath); + } + + @Override + public DartSource getSourceFor(String relPath) { + if (relPath.equals(TEST_APP_NAME)) { + return new MockDartSource(this); + } + for (DartSource source : sources) { + if (source.getName().equals(relPath)) { + return source; + } + } + throw new RuntimeException("Cannot find source for " + relPath); + } + + public void addSource(DartSource src) { + sources.add(src); + } + + private static class MockDartSource extends SourceTest implements DartSource { + final MockLibrarySource libSource; + public MockDartSource(MockLibrarySource libSource) { + super(libSource.getName()); + this.libSource = libSource; + } + + @Override + public Reader getSourceReader() { + return libSource.getSourceReader(); + } + + @Override + public LibrarySource getLibrary() { + return libSource; + } + + @Override + public String getRelativePath() { + return libSource.getUri().toString(); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/SourceTest.java b/compiler/javatests/com/google/dart/compiler/SourceTest.java new file mode 100644 index 00000000000..3ea4ba8d9a1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/SourceTest.java @@ -0,0 +1,39 @@ +// 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. + +package com.google.dart.compiler; + +import java.net.URI; + +/** + * Testing implementation of {@link Source}. + */ +public abstract class SourceTest implements Source { + + private final String name; + + public SourceTest(String name) { + this.name = name; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public String getName() { + return name; + } + + @Override + public URI getUri() { + return URI.create(getName()).normalize(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/SystemLibraryManagerTest.java b/compiler/javatests/com/google/dart/compiler/SystemLibraryManagerTest.java new file mode 100644 index 00000000000..f380d99d093 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/SystemLibraryManagerTest.java @@ -0,0 +1,85 @@ +// 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. + +package com.google.dart.compiler; + +import junit.framework.TestCase; + +import java.net.URI; + +public class SystemLibraryManagerTest extends TestCase { + SystemLibraryManager systemLibraryManager = new SystemLibraryManager(); + + public void testExpand1() throws Exception { + URI shortUri = new URI("dart:core.lib"); + URI fullUri = systemLibraryManager.expandRelativeDartUri(shortUri); + assertNotNull(fullUri); + assertEquals("dart", fullUri.getScheme()); + assertEquals("core", fullUri.getHost()); + assertTrue(fullUri.getPath().endsWith("/corelib.lib")); + } + + public void testExpand2() throws Exception { + URI shortUri = new URI("dart:coreimpl.lib"); + URI fullUri = systemLibraryManager.expandRelativeDartUri(shortUri); + assertNotNull(fullUri); + assertEquals("dart", fullUri.getScheme()); + assertEquals("core", fullUri.getHost()); + assertTrue(fullUri.getPath().endsWith("/corelib_impl.lib")); + } + + public void testExpand3() throws Exception { + URI shortUri = new URI("dart:coreimpl.lib"); + URI fullUri1 = systemLibraryManager.expandRelativeDartUri(shortUri); + URI fullUri2 = systemLibraryManager.expandRelativeDartUri(fullUri1); + assertNotNull(fullUri2); + assertEquals("dart", fullUri2.getScheme()); + assertEquals("core", fullUri2.getHost()); + assertTrue(fullUri2.getPath().endsWith("/corelib_impl.lib")); + } + + public void testExpand4() throws Exception { + URI shortUri = new URI("dart:doesnotexist.lib"); + try { + URI fullUri = systemLibraryManager.expandRelativeDartUri(shortUri); + fail("Expected expansion of " + shortUri + " to fail, but returned " + fullUri); + } catch (RuntimeException e) { + String message = e.getMessage(); + assertTrue(message.startsWith("No system library")); + assertTrue(message.contains(shortUri.toString())); + } + } + + public void testTranslate1() throws Exception { + URI shortUri = new URI("dart:core.lib"); + URI fullUri = systemLibraryManager.expandRelativeDartUri(shortUri); + URI translatedURI = systemLibraryManager.translateDartUri(fullUri); + assertNotNull(translatedURI); + String scheme = translatedURI.getScheme(); + assertTrue(scheme.equals("file") || scheme.equals("jar")); + assertTrue(translatedURI.getPath().endsWith("/corelib.lib")); + } + + public void testTranslate2() throws Exception { + URI shortUri = new URI("dart:coreimpl.lib"); + URI fullUri = systemLibraryManager.expandRelativeDartUri(shortUri); + URI translatedURI = systemLibraryManager.translateDartUri(fullUri); + assertNotNull(translatedURI); + String scheme = translatedURI.getScheme(); + assertTrue(scheme.equals("file") || scheme.equals("jar")); + assertTrue(translatedURI.getPath().endsWith("/corelib_impl.lib")); + } + + public void testTranslate3() throws Exception { + URI fullUri = new URI("dart://doesnotexist/some/file.dart"); + try { + URI translatedURI = systemLibraryManager.translateDartUri(fullUri); + fail("Expected translate " + fullUri + " to fail, but returned " + translatedURI); + } catch (RuntimeException e) { + String message = e.getMessage(); + assertTrue(message.startsWith("No system library")); + assertTrue(message.contains(fullUri.toString())); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/ast/AstTests.java b/compiler/javatests/com/google/dart/compiler/ast/AstTests.java new file mode 100644 index 00000000000..c143bde987d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/ast/AstTests.java @@ -0,0 +1,23 @@ +// 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. + +package com.google.dart.compiler.ast; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AstTests extends TestSetup { + + public AstTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart AST test suite."); + + suite.addTestSuite(DartToSourceVisitorTest.class); + return new AstTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/ast/DartToSourceVisitorTest.java b/compiler/javatests/com/google/dart/compiler/ast/DartToSourceVisitorTest.java new file mode 100644 index 00000000000..da79e0c2a89 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/ast/DartToSourceVisitorTest.java @@ -0,0 +1,83 @@ +// 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. + +package com.google.dart.compiler.ast; + +import com.google.dart.compiler.CompilerTestCase; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class DartToSourceVisitorTest extends CompilerTestCase { + public void testDartStatements() { + testStmt("return"); + testStmt("x"); + testStmt("x.y"); + testStmt("x + 1.0"); + testStmt("x.y()"); + testStmt("throw"); + testStmt("throw e"); + testStmt("Array strings"); + } + + public void testStringBackslashEscaping() { + testStmt("String foo = \" \\\\ \""); + } + + public void testDartMembers() { + testClassMemeber( + " m() {\n" + + " }\n"); + testClassMemeber( + " operator negate() {\n" + + " }\n"); + // get is mangled + // testClassMemeber( + // " get f() {\n" + + // " }\n"); + } + + public void testClassWithFactory() { + same( + "// unit testcode\n" + + "class c {\n" + + "\n" + + " factory Array() {\n }\n" + + "}\n" + + "\n"); + } + + public void testNativeClass() { + same( + "// unit testcode\n" + + "class c native \"C\" {\n" + + "}\n" + + "\n"); + } + + private void same(String sourceCode) { + DartUnit unit = parseUnit("testcode", sourceCode); + String result = unit.toSource(); + assertEquals(sourceCode, result); + } + + private void testClassMemeber(String stmt) { + String boilerplated = + "// unit testcode\n" + + "class c {\n" + + "\n" + + ""+stmt+"" + + "}\n" + + "\n"; + same(boilerplated); + } + + private void testStmt(String stmt) { + String boilerplated = + " m() {\n" + + " "+stmt+";\n" + + " }\n"; + testClassMemeber(boilerplated); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/TypeHeuristicImplementationTest.java b/compiler/javatests/com/google/dart/compiler/backend/common/TypeHeuristicImplementationTest.java new file mode 100644 index 00000000000..ad7698d8594 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/TypeHeuristicImplementationTest.java @@ -0,0 +1,937 @@ +// 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. + +package com.google.dart.compiler.backend.common; + +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSourceTest; +import com.google.dart.compiler.DefaultCompilerConfiguration; +import com.google.dart.compiler.DefaultDartArtifactProvider; +import com.google.dart.compiler.MockLibrarySource; +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartBinaryExpression; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.ast.Modifiers; +import com.google.dart.compiler.backend.common.TypeHeuristic.FieldKind; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.CoreTypeProviderImplementation; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.ElementKind; +import com.google.dart.compiler.resolver.FieldElement; +import com.google.dart.compiler.resolver.MethodElement; +import com.google.dart.compiler.type.Type; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class TypeHeuristicImplementationTest extends CompilerTestCase { + + CoreTypeProvider typeProvider; + private static final String NumberImpl = "NumberImplementation"; + private static final String StringImpl = "StringImplementation"; + private static final String BoolImpl = "BoolImplementation"; + private static final String Dynamic = ""; + private static final FieldKind AS_GETTER = FieldKind.GETTER; + private static final FieldKind AS_SETTER = FieldKind.SETTER; + + DartUnit compileUnit(final String filePath) throws IOException { + URL url = inputUrlFor(getClass(), filePath + ".dart"); + String source = readUrl(url); + return compileUnitFromSource(source, filePath); + } + + DartUnit compileUnitFromSource(final CodeBuilder source) throws IOException { + return compileUnitFromSource(source.toString(), getName()); + } + + DartUnit compileUnitFromSource(final String source, final String name) throws IOException { + MockLibrarySource lib = new MockLibrarySource(); + DartSourceTest src = new DartSourceTest(name, source, lib); + lib.addSource(src); + Map parsedUnits = new HashMap(); + DefaultCompilerConfiguration config = new DefaultCompilerConfiguration(); + DefaultDartArtifactProvider provider = new DefaultDartArtifactProvider(Files.createTempDir()); + DartCompilerListener listener = new DartCompilerListener() { + @Override + public void typeError(DartCompilationError event) { + } + + @Override + public void compilationWarning(DartCompilationError event) { + } + + @Override + public void compilationError(DartCompilationError event) { + } + }; + LibraryUnit libUnit = DartCompiler.analyzeLibrary(lib, parsedUnits, config, provider, + listener); + LibraryUnit corelibUnit = libUnit.getImports().iterator().next(); + typeProvider = new CoreTypeProviderImplementation(corelibUnit.getElement().getScope(), + listener); + return libUnit.getUnit(name); + } + + /** + * Check conflicting generic list operator return types. + */ + public void testListIncompatibleListOperator() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class MyList implements List {") + .l("MyList() { }") + .l("String operator[](int index) { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("List xx = new MyList();") + .l("xx[0] = 123;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartMethodDefinition m = getMethod(unit, "MainClass", "main"); + DartExpression arrayIndex = getLHS(getStatementUnderTest(m)); + assertTypesOf(th.getTypesOf(arrayIndex), Dynamic); + + assertMethodImplementations(th.getImplementationsOf(arrayIndex), 2, "[]"); + } + + /** + * Check compatible generic list operator return types. + */ + public void testListCompatibleListOperator() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class MyList implements List {") + .l("MyList() { }") + .l("int operator[](int index) { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("List xx = new MyList();") + .l("xx[0] = 123;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + assertTypesOf(th.getTypesOf(getLHS(getStatementUnderTest(method))), NumberImpl); + } + + /** + * Check compatible binary op; + */ + public void testCompatibleBinaryOp() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class MainClass {") + .l("static final int b = 2;") + .l("static main() {") + .l("int a;") + .l("a = 1 + b;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement stmt = getStatementUnderTest(method); + DartExpression plusExpr = getRHS(stmt); + assertTypesOf(th.getTypesOf(getLHS(plusExpr)), NumberImpl); + assertTypesOf(th.getTypesOf(getRHS(plusExpr)), NumberImpl); + assertTypesOf(th.getTypesOf(plusExpr), NumberImpl); + } + + /** + * Check compatible binary op (2 operands). + */ + public void testIncompatibleBinaryOp1() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class MainClass {") + .l("static final String b = '2';") + .l("static main() {") + .l("int a;") + .l("a = 1 + b;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement stmt = getStatementUnderTest(method); + DartExpression expr = getRHS(stmt); + assertTypesOf(th.getTypesOf(getLHS(expr)), NumberImpl); + assertTypesOf(th.getTypesOf(getRHS(expr)), StringImpl); + assertTypesOf(th.getTypesOf(expr), NumberImpl); + } + + /** + * Check compatible binary op (3 operands). + */ + public void testIncompatibleBinaryOp2() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class MainClass {") + .l("static final int b = 2;") + .l("static main() {") + .l("int a;") + .l("a = b * 'A' + 3;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement stmt = getStatementUnderTest(method); + DartExpression expr = getRHS(stmt); + DartExpression intPlusString = getLHS(expr); + assertTypesOf(th.getTypesOf(getLHS(intPlusString)), NumberImpl); + assertTypesOf(th.getTypesOf(getRHS(intPlusString)), StringImpl); + assertTypesOf(th.getTypesOf(expr), NumberImpl); + } + + /** + * Check mixed binary op (4 operands). b == 1 && "A" + 3; + */ + public void testIncompatibleLogicalOp() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class MainClass {") + .l("static final int b = 2;") + .l("static main() {") + .l("int a;") + .l("a = b == 1 && 'A' + 3;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement stmt = getStatementUnderTest(method); + DartExpression andExpr = getRHS(stmt); + DartExpression eqExpr = getLHS(andExpr); + DartExpression plusExpr = getRHS(andExpr); + + assertTypesOf(th.getTypesOf(getLHS(eqExpr)), NumberImpl); + assertTypesOf(th.getTypesOf(getRHS(eqExpr)), NumberImpl); + assertTypesOf(th.getTypesOf(eqExpr), BoolImpl); + + assertTypesOf(th.getTypesOf(getLHS(plusExpr)), StringImpl); + assertTypesOf(th.getTypesOf(getRHS(plusExpr)), NumberImpl); + assertTypesOf(th.getTypesOf(plusExpr), StringImpl); + + assertTypesOf(th.getTypesOf(andExpr), BoolImpl); + } + + /** + * see dart source testCombinedExpressions.dart + * + * a.foo() - myInt + a.myField * a.bar(); + */ + public void testCombinedExpressions() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("interface A {") + .l("String foo();") + .l("int myField;") + .l("}") + .l() + .l("class B implements A {") + . l("B() { }") + .l("String foo() { }") + .l("}") + .l() + .l("class C extends B {") + .l("C() : super() { }") + .l("double bar() { }") + .l("String myField;") + .l("}") + .l() + .l("class MainClass {") + .l("static final double myInt = 999;") + .l("static main() {") + .l("A a = new C();") + .l("s = myInt - a.foo() + a.myField * a.bar();") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement stmt = getStatementUnderTest(method); + DartExpression plus = getRHS(stmt); + DartExpression minus = getLHS(plus); + DartExpression times = getRHS(plus); + + assertTypesOf(th.getTypesOf(plus), NumberImpl); + + assertTypesOf(th.getTypesOf(minus), NumberImpl); + assertTypesOf(th.getTypesOf(getLHS(minus)), NumberImpl); + assertTypesOf(th.getTypesOf(getRHS(minus)), StringImpl); + + assertTypesOf(th.getTypesOf(times), Dynamic); + assertTypesOf(th.getTypesOf(getLHS(times)), Dynamic); + assertTypesOf(th.getTypesOf(getRHS(times)), NumberImpl); + } + + /** + * Check compatible methods. + */ + public void testCompatibleMethods() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A {") + .l("A() { }") + .l("double foo() { }") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("int foo() { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.foo();") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression mInvocation = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(mInvocation), NumberImpl); + DartExpression qualifier = getQualifier(mInvocation); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertMethodImplementations(th.getImplementationsOf(mInvocation), 2, "foo"); + } + + /** + * Check incompatible methods. + */ + public void testIncompatibleMethods() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A {") + .l("A() { }") + .l("int foo() { }") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("String foo() { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.foo();") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression mInvocation = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(mInvocation), Dynamic); + DartExpression qualifier = getQualifier(mInvocation); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertMethodImplementations(th.getImplementationsOf(mInvocation), 2, "foo"); + } + + /** + * Check compatible types with multiple implementations. + */ + public void testIncompatibleMethods2() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A {") + .l("A() { }") + .l("int foo() { }") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("int foo() { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.foo();") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression mInvocation = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(mInvocation), NumberImpl); + DartExpression qualifier = getQualifier(mInvocation); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertMethodImplementations(th.getImplementationsOf(mInvocation), 2, "foo"); + } + + /** + * Check compatible fields. + */ + public void testCompatibleFields() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("int iField;") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.iField;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression field = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(field), NumberImpl); + DartExpression qualifier = getQualifier(field); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertFields(th.getFieldImplementationsOf(field, AS_GETTER), 1, "iField", AS_GETTER); + assertFields(th.getFieldImplementationsOf(field, AS_SETTER), 1, "iField", AS_SETTER); + } + + /** + * Check compatible field types with 2 implementations. + * implementation type of iField is NumberImplementation with two possible implementations. + */ + public void testIncompatibleFields() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("int iField;") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("int iField;") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.iField;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression field = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(field), NumberImpl); + DartExpression qualifier = getQualifier(field); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertFields(th.getFieldImplementationsOf(field, AS_GETTER), 2, "iField", AS_GETTER); + assertFields(th.getFieldImplementationsOf(field, AS_SETTER), 2, "iField", AS_SETTER); + } + + /** + * Check incompatible fields. + */ + public void testIncompatibleFields2() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("int iField;") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("String iField;") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.iField;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression field = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(field), Dynamic); + DartExpression qualifier = getQualifier(field); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertFields(th.getFieldImplementationsOf(field, AS_GETTER), 2, "iField", AS_GETTER); + assertFields(th.getFieldImplementationsOf(field, AS_SETTER), 2, "iField", AS_SETTER); + } + + /** + * Check incompatible fields. + */ + public void testIncompatibleFieldsWithGetter() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("int iField;") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("int get iField() { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.iField;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression field = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(field), NumberImpl); + DartExpression qualifier = getQualifier(field); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertFields(th.getFieldImplementationsOf(field, AS_GETTER), 2, "iField", AS_GETTER); + assertFields(th.getFieldImplementationsOf(field, AS_SETTER), 1, "iField", AS_SETTER); + } + + /** + * Check incompatible fields. + */ + public void testIncompatibleFieldsWithSetter() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("int iField;") + .l("}") + .l() + .l("class B extends A {") + .l("B() : super() {}") + .l("set iField(x) { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new B();") + .l("test = a.iField;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + DartExpression field = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(field), NumberImpl); + DartExpression qualifier = getQualifier(field); + assertTypesOf(th.getTypesOf(qualifier), "A", "B"); + + assertFields(th.getFieldImplementationsOf(field, AS_GETTER), 1, "iField", AS_GETTER); + assertFields(th.getFieldImplementationsOf(field, AS_SETTER), 2, "iField", AS_SETTER); + } + + /** + * Check array of generic type. + */ + public void testGenericTypeInList() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("int x;") + .l("}") + .l() + .l("class B { ") + .l("B() { } ") + .l("A field;") + .l("}") + .l() + .l("class MyList implements List {") + .l("MyList() {}") + .l("T operator[](int index) { }") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("List b = new MyList();") + .l("test = b[0].field.x;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method); + + // b[0].field.x => NumberImpl + DartExpression expr = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(expr), NumberImpl); + assertFields(th.getFieldImplementationsOf(expr, AS_GETTER), 1, "x", AS_GETTER); + assertFields(th.getFieldImplementationsOf(expr, AS_SETTER), 1, "x", AS_SETTER); + + // b[0].field => A + DartExpression b_zeroIndex_field = getQualifier(expr); + assertTypesOf(th.getTypesOf(b_zeroIndex_field), "A"); + assertFields(th.getFieldImplementationsOf(b_zeroIndex_field, AS_GETTER), 1, "field", AS_GETTER); + assertFields(th.getFieldImplementationsOf(b_zeroIndex_field, AS_SETTER), 1, "field", AS_SETTER); + + // b[0] => B + DartExpression b_zeroIndex = getQualifier(b_zeroIndex_field); + assertTypesOf(th.getTypesOf(b_zeroIndex), "B"); + } + + + /** + * Check ref equality. + */ + public void testRefEquality() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("}") + .l() + .l("class B { ") + .l("B() { } ") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new A();") + .l("B b = new B();") + .l("test = a == b;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method, 2); + DartExpression equals = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(equals), BoolImpl); + + DartExpression lhs = getLHS(equals); + assertTypesOf(th.getTypesOf(lhs), "A"); + DartExpression rhs = getRHS(equals); + assertTypesOf(th.getTypesOf(rhs), "B"); + + assertMethodImplementations(th.getImplementationsOf(equals), 1, "=="); + } + + /** + * Check override ref equality. + */ + public void testOverrideRefEquality() throws IOException { + + CodeBuilder code = CodeBuilder.Create() + .l("class A { ") + .l("A() { } ") + .l("bool operator ==(other) { }") + .l("}") + .l() + .l("class B { ") + .l("B() { } ") + .l("}") + .l() + .l("class MainClass {") + .l("static main() {") + .l("A a = new A();") + .l("B b = new B();") + .l("test = a == b;") + .l("}") + .l("}"); + + DartUnit unit = compileUnitFromSource(code); + + DartMethodDefinition method = getMethodUnderTest(unit); + + TypeHeuristic th = getTypeHeuristics(unit); + + DartStatement assignStmt = getStatementUnderTest(method, 2); + DartExpression equals = getRHS(assignStmt); + assertTypesOf(th.getTypesOf(equals), BoolImpl); + + DartExpression lhs = getLHS(equals); + assertTypesOf(th.getTypesOf(lhs), "A"); + DartExpression rhs = getRHS(equals); + assertTypesOf(th.getTypesOf(rhs), "B"); + + assertMethodImplementations(th.getImplementationsOf(equals), 2, "=="); + } + + // Helpers ////////////////////////////////////////////////////////////////////////////////////// + + private DartExpression getQualifier(DartExpression expr) { + if (expr instanceof DartMethodInvocation) { + return ((DartMethodInvocation) expr).getTarget(); + } else if (expr instanceof DartPropertyAccess) { + return (DartExpression) ((DartPropertyAccess) expr).getQualifier(); + } + return null; + } + + private DartMethodDefinition getMethodUnderTest(DartUnit unit) { + return getMethod(unit, "MainClass", "main"); + } + + private TypeHeuristic getTypeHeuristics(DartUnit unit) { + return new TypeHeuristicImplementation(unit, typeProvider); + } + + private void assertTypesOf(Set actual, String... expectedTypes) { + Set types = new HashSet(); + for (Type t : actual) { + types.add(t.toString()); + } + assertEquals(Sets.newHashSet(expectedTypes), types); + } + + private void assertMethodImplementations(Set actual, int nImplementations, + String name) { + assertEquals(actual.size(), nImplementations); + for (MethodElement e : actual) { + assertEquals(name, e.getName()); + } + } + + private void assertFields(Set actual, int nImplementations, + String name, FieldKind fieldKind) { + assertEquals(actual.size(), nImplementations); + for (FieldElement e : actual) { + assertEquals(name, e.getName()); + Modifiers modifiers = e.getModifiers(); + if (modifiers.isAbstractField()) { + if (fieldKind == FieldKind.GETTER) { + assertTrue(modifiers.isGetter()); + } else { + assertTrue(modifiers.isSetter()); + } + } + } + } + + private DartExpression getLHS(DartStatement stmt) { + DartBinaryExpression expr = (DartBinaryExpression) getExpression(stmt); + return expr.getArg1(); + } + + private DartExpression getLHS(DartExpression expr) { + DartBinaryExpression e = (DartBinaryExpression) expr; + return e.getArg1(); + } + + private DartExpression getRHS(DartStatement stmt) { + DartBinaryExpression expr = (DartBinaryExpression) getExpression(stmt); + return expr.getArg2(); + } + + private DartExpression getRHS(DartExpression expr) { + DartBinaryExpression e = (DartBinaryExpression) expr; + return e.getArg2(); + } + + private DartExpression getExpression(DartStatement stmt) { + if (stmt instanceof DartExprStmt) { + return ((DartExprStmt) stmt).getExpression(); + } + return ((DartExprStmt) stmt).getExpression(); + } + + private DartStatement getStatementUnderTest(DartMethodDefinition m) { + return getStatementUnderTest(m, 1); + } + + private DartStatement getStatementUnderTest(DartMethodDefinition m, int n) { + DartStatement stmt = m.getFunction().getBody().getStatements().get(n); + return stmt; + } + + private DartClass getClass(DartUnit unit, String name) { + DartNode node = unit.getLibrary().getTopLevelNode(name); + if (node instanceof DartClass) { + return (DartClass) node; + } + return null; + } + + private DartMethodDefinition getMethod(DartUnit unit, String className, String name) { + DartClass cls = getClass(unit, className); + Element e = cls.getSymbol().lookupLocalElement(name); + if (e != null && ElementKind.of(e) == ElementKind.METHOD) { + return (DartMethodDefinition) e.getNode(); + + } + return null; + } + + static class CodeBuilder { + StringBuffer sb = new StringBuffer(512); + static final String IDENT = " "; + static final int IDENT_SIZE = 2; + int identSize; + + public static CodeBuilder Create() { + return new CodeBuilder(); + } + + public CodeBuilder() { + } + + public CodeBuilder pnl(String src) { + maybeOutIdent(src); + for (int i = 0; i < identSize; i++) { + sb.append(IDENT); + } + sb.append(src); + return this; + } + + public CodeBuilder l(String src) { + pnl(src); + pnl("\n"); + maybeIndent(src); + return this; + } + + public CodeBuilder l() { + pnl("\n"); + return this; + } + + public CodeBuilder i() { + identSize += IDENT_SIZE; + return this; + } + + public CodeBuilder o() { + identSize -= IDENT_SIZE; + assert (identSize >= 0); + return this; + } + + private void maybeIndent(String src) { + String line = src.trim(); + int last = line.length() - 1; + if (line.length() > 0 && line.charAt(last) == '{') { + i(); + } + } + + private void maybeOutIdent(String src) { + String line = src.trim(); + if (line.length() > 0 && line.charAt(0) == '}') { + o(); + } + } + + @Override + public final String toString() { + return sb.toString(); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testArrayIncompatibleArrayOperator.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testArrayIncompatibleArrayOperator.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testArrayIncompatibleArrayOperator.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testCombinedExpressions.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testCombinedExpressions.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testCombinedExpressions.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleBinaryOp.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleBinaryOp.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleBinaryOp.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleFields.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleFields.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleFields.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleMethods.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleMethods.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testCompatibleMethods.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp1.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp1.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp1.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp2.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp2.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleBinaryOp2.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields2.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields2.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFields2.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithGetter.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithGetter.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithGetter.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithSetter.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithSetter.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleFieldsWithSetter.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleLogicalOp.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleLogicalOp.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleLogicalOp.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleMethods.dart b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleMethods.dart new file mode 100644 index 00000000000..6ce73110b96 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/common/testIncompatibleMethods.dart @@ -0,0 +1,4 @@ +// 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. + diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/ClosureJsCodingConventionTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/ClosureJsCodingConventionTest.java new file mode 100644 index 00000000000..30dd1f469b2 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/ClosureJsCodingConventionTest.java @@ -0,0 +1,30 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.javascript.jscomp.CodingConvention.Bind; +import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.Token; + +import junit.framework.TestCase; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class ClosureJsCodingConventionTest extends TestCase { + public void testBind() { + ClosureJsCodingConvention convention = new ClosureJsCodingConvention(); + + // don't recognize a non-bind call. + Node expr = new Node(Token.CALL, Node.newString(Token.NAME, "foo")); + assertNull(convention.describeFunctionBind(expr)); + + // don't recognize a bind call. + expr = new Node(Token.CALL, + Node.newString(Token.NAME, "$bind"), new Node(Token.THIS)); + Bind bind = convention.describeFunctionBind(expr); + assertNotNull(bind); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/ComparingVisitor.java b/compiler/javatests/com/google/dart/compiler/backend/js/ComparingVisitor.java new file mode 100644 index 00000000000..ff094f16fb2 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/ComparingVisitor.java @@ -0,0 +1,367 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.FlatteningVisitor.TreeNode; +import com.google.dart.compiler.backend.js.ast.JsArrayAccess; +import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsBlock; +import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral; +import com.google.dart.compiler.backend.js.ast.JsBreak; +import com.google.dart.compiler.backend.js.ast.JsCase; +import com.google.dart.compiler.backend.js.ast.JsCatch; +import com.google.dart.compiler.backend.js.ast.JsConditional; +import com.google.dart.compiler.backend.js.ast.JsContext; +import com.google.dart.compiler.backend.js.ast.JsContinue; +import com.google.dart.compiler.backend.js.ast.JsDebugger; +import com.google.dart.compiler.backend.js.ast.JsDefault; +import com.google.dart.compiler.backend.js.ast.JsDoWhile; +import com.google.dart.compiler.backend.js.ast.JsEmpty; +import com.google.dart.compiler.backend.js.ast.JsExprStmt; +import com.google.dart.compiler.backend.js.ast.JsFor; +import com.google.dart.compiler.backend.js.ast.JsForIn; +import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsIf; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsLabel; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.backend.js.ast.JsNullLiteral; +import com.google.dart.compiler.backend.js.ast.JsNumberLiteral; +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral; +import com.google.dart.compiler.backend.js.ast.JsParameter; +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation; +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation; +import com.google.dart.compiler.backend.js.ast.JsProgram; +import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; +import com.google.dart.compiler.backend.js.ast.JsRegExp; +import com.google.dart.compiler.backend.js.ast.JsReturn; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsStringLiteral; +import com.google.dart.compiler.backend.js.ast.JsSwitch; +import com.google.dart.compiler.backend.js.ast.JsThisRef; +import com.google.dart.compiler.backend.js.ast.JsThrow; +import com.google.dart.compiler.backend.js.ast.JsTry; +import com.google.dart.compiler.backend.js.ast.JsVars; +import com.google.dart.compiler.backend.js.ast.JsVisitable; +import com.google.dart.compiler.backend.js.ast.JsVisitor; +import com.google.dart.compiler.backend.js.ast.JsWhile; +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import java.util.List; + +class ComparingVisitor extends JsVisitor { + + public static void exec(List expected, List actual) { + TreeNode expectedTree = FlatteningVisitor.exec(expected); + TreeNode actualTree = FlatteningVisitor.exec(actual); + compare(expectedTree, actualTree); + } + + private static void compare(JsVisitable expected, JsVisitable actual) { + if (expected == actual) { + return; + } + Assert.assertNotNull(expected); + Assert.assertNotNull(actual); + ComparingVisitor visitor = new ComparingVisitor(expected); + visitor.accept(actual); + } + + private static void compare(TreeNode expected, TreeNode actual) { + compare(expected.node, actual.node); + List expectedChildren = expected.children; + List actualChildren = actual.children; + Assert.assertEquals(expectedChildren.size(), actualChildren.size()); + for (int i = 0; i < expectedChildren.size(); i++) { + compare(expectedChildren.get(i), actualChildren.get(i)); + } + } + + /** + * We use a raw type here because Sun's javac will barf all over the casts and + * instanceof tests we do all throughout this file. + */ + private final JsVisitable other; + + private ComparingVisitor(JsVisitable other) { + this.other = other; + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + Assert.assertTrue(other instanceof JsArrayAccess); + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + Assert.assertTrue(other instanceof JsArrayLiteral); + return false; + } + + @Override + public boolean visit(JsBinaryOperation x, JsContext ctx) { + Assert.assertTrue(other instanceof JsBinaryOperation); + Assert.assertEquals(((JsBinaryOperation) other).getOperator().getSymbol(), + x.getOperator().getSymbol()); + return false; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + Assert.assertTrue(other instanceof JsBlock); + Assert.assertEquals(((JsBlock) other).isGlobalBlock(), x.isGlobalBlock()); + return false; + } + + @Override + public boolean visit(JsBooleanLiteral x, JsContext ctx) { + Assert.assertTrue(other instanceof JsBooleanLiteral); + Assert.assertEquals(((JsBooleanLiteral) other).getValue(), x.getValue()); + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + Assert.assertTrue(other instanceof JsBreak); + Assert.assertEquals(((JsBreak) other).getLabel().getIdent(), x.getLabel().getIdent()); + return false; + } + + @Override + public boolean visit(JsCase x, JsContext ctx) { + Assert.assertTrue(other instanceof JsCase); + return false; + } + + @Override + public boolean visit(JsCatch x, JsContext ctx) { + Assert.assertTrue(other instanceof JsCatch); + Assert.assertEquals(((JsCatch) other).getParameter().getName().getIdent(), + x.getParameter().getName().getIdent()); + return false; + } + + @Override + public boolean visit(JsConditional x, JsContext ctx) { + Assert.assertTrue(other instanceof JsConditional); + return false; + } + + @Override + public boolean visit(JsContinue x, JsContext ctx) { + Assert.assertTrue(other instanceof JsContinue); + Assert.assertEquals(((JsContinue) other).getLabel().getIdent(), x.getLabel().getIdent()); + return false; + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + Assert.assertTrue(other instanceof JsDebugger); + return false; + } + + @Override + public boolean visit(JsDefault x, JsContext ctx) { + Assert.assertTrue(other instanceof JsDefault); + return false; + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + Assert.assertTrue(other instanceof JsDoWhile); + return false; + } + + @Override + public boolean visit(JsEmpty x, JsContext ctx) { + Assert.assertTrue(other instanceof JsEmpty); + return false; + } + + @Override + public boolean visit(JsExprStmt x, JsContext ctx) { + Assert.assertTrue(other instanceof JsExprStmt); + return false; + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + Assert.assertTrue(other instanceof JsFor); + return false; + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + Assert.assertTrue(other instanceof JsForIn); + return false; + } + + @Override + public boolean visit(JsFunction x, JsContext ctx) { + Assert.assertTrue(other instanceof JsFunction); + JsFunction otherFunc = (JsFunction) other; + JsName otherName = otherFunc.getName(); + JsName name = x.getName(); + if (name != otherName) { + Assert.assertEquals(otherName.getIdent(), name.getIdent()); + } + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + Assert.assertTrue(other instanceof JsIf); + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + Assert.assertTrue(other instanceof JsInvocation); + return false; + } + + @Override + public boolean visit(JsLabel x, JsContext ctx) { + Assert.assertTrue(other instanceof JsLabel); + Assert.assertEquals(((JsLabel) other).getName().getIdent(), x.getName().getIdent()); + return false; + } + + @Override + public boolean visit(JsNameRef x, JsContext ctx) { + Assert.assertTrue(other instanceof JsNameRef); + Assert.assertEquals(((JsNameRef) other).getIdent(), x.getIdent()); + return false; + } + + @Override + public boolean visit(JsNew x, JsContext ctx) { + Assert.assertTrue(other instanceof JsNew); + return false; + } + + @Override + public boolean visit(JsNullLiteral x, JsContext ctx) { + Assert.assertTrue(other instanceof JsNullLiteral); + return false; + } + + @Override + public boolean visit(JsNumberLiteral x, JsContext ctx) { + Assert.assertTrue(other instanceof JsNumberLiteral); + Assert.assertEquals(((JsNumberLiteral) other).getValue(), x.getValue()); + return false; + } + + @Override + public boolean visit(JsObjectLiteral x, JsContext ctx) { + Assert.assertTrue(other instanceof JsObjectLiteral); + return false; + } + + @Override + public boolean visit(JsParameter x, JsContext ctx) { + Assert.assertTrue(other instanceof JsParameter); + Assert.assertEquals(((JsParameter) other).getName().getIdent(), x.getName().getIdent()); + return false; + } + + @Override + public boolean visit(JsPostfixOperation x, JsContext ctx) { + Assert.assertTrue(other instanceof JsPostfixOperation); + Assert.assertEquals(((JsPostfixOperation) other).getOperator().getSymbol(), + x.getOperator().getSymbol()); + return false; + } + + @Override + public boolean visit(JsPrefixOperation x, JsContext ctx) { + Assert.assertTrue(other instanceof JsPrefixOperation); + Assert.assertEquals(((JsPrefixOperation) other).getOperator().getSymbol(), + x.getOperator().getSymbol()); + return false; + } + + @Override + public boolean visit(JsProgram x, JsContext ctx) { + Assert.assertTrue(other instanceof JsProgram); + return false; + } + + @Override + public boolean visit(JsPropertyInitializer x, JsContext ctx) { + Assert.assertTrue(other instanceof JsPropertyInitializer); + return false; + } + + @Override + public boolean visit(JsRegExp x, JsContext ctx) { + Assert.assertTrue(other instanceof JsRegExp); + Assert.assertEquals(((JsRegExp) other).getFlags(), x.getFlags()); + Assert.assertEquals(((JsRegExp) other).getPattern(), x.getPattern()); + return false; + } + + @Override + public boolean visit(JsReturn x, JsContext ctx) { + Assert.assertTrue(other instanceof JsReturn); + return false; + } + + @Override + public boolean visit(JsStringLiteral x, JsContext ctx) { + Assert.assertTrue(other instanceof JsStringLiteral); + Assert.assertEquals(((JsStringLiteral) other).getValue(), x.getValue()); + return false; + } + + @Override + public boolean visit(JsSwitch x, JsContext ctx) { + Assert.assertTrue(other instanceof JsSwitch); + return false; + } + + @Override + public boolean visit(JsThisRef x, JsContext ctx) { + Assert.assertTrue(other instanceof JsThisRef); + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + Assert.assertTrue(other instanceof JsThrow); + return false; + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + Assert.assertTrue(other instanceof JsTry); + return false; + } + + @Override + public boolean visit(JsVar x, JsContext ctx) { + TestCase.assertTrue(other instanceof JsVar); + TestCase.assertEquals(((JsVar) other).getName().getIdent(), x.getName().getIdent()); + return false; + } + + public boolean visit(JsVars x, JsContext ctx) { + TestCase.assertTrue(other instanceof JsVars); + return false; + } + + public boolean visit(JsWhile x, JsContext ctx) { + TestCase.assertTrue(other instanceof JsWhile); + return false; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/ExprOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/ExprOptTest.java new file mode 100644 index 00000000000..f8075c78725 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/ExprOptTest.java @@ -0,0 +1,30 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +/** + * Tests for binary expression optimizations. + */ +public class ExprOptTest extends SnippetTestCase { + + private JavascriptBackend jsBackend = new JavascriptBackend() { + @Override + protected boolean shouldOptimize() { + // We're testing the optimizer here, so turn it on explicitly. + return true; + } + }; + + @Override + protected AbstractJsBackend getBackend() { + return jsBackend; + } + + @Override + protected void tearDown() throws Exception { + jsBackend = null; + super.tearDown(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/FlatteningVisitor.java b/compiler/javatests/com/google/dart/compiler/backend/js/FlatteningVisitor.java new file mode 100644 index 00000000000..b50515ad024 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/FlatteningVisitor.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.backend.js.ast.JsVisitable; +import com.google.dart.compiler.backend.js.ast.JsVisitor; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +class FlatteningVisitor extends JsVisitor { + + public static TreeNode exec(List statements) { + FlatteningVisitor visitor = new FlatteningVisitor(); + visitor.acceptList(statements); + return visitor.root; + } + + public static class TreeNode { + public final JsVisitable node; + public final List children = new ArrayList(); + + public TreeNode(JsVisitable node) { + this.node = node; + } + } + + private TreeNode root; + + private FlatteningVisitor() { + root = new TreeNode(null); + } + + protected T doAccept(T node) { + TreeNode oldRoot = root; + root = new TreeNode(node); + oldRoot.children.add(root); + super.doAccept(node); + root = oldRoot; + return node; + } + + // @Override + protected void doAcceptList(List collection) { + for (Iterator it = collection.iterator(); it.hasNext();) { + doAccept(it.next()); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JavaScriptStringTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JavaScriptStringTest.java new file mode 100644 index 00000000000..da090f12ee9 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JavaScriptStringTest.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.JsToStringGenerationVisitor; + +import junit.framework.TestCase; + +import org.mozilla.javascript.Node; +import org.mozilla.javascript.Parser; +import org.mozilla.javascript.Token; +import org.mozilla.javascript.ast.AstNode; +import org.mozilla.javascript.ast.ExpressionStatement; +import org.mozilla.javascript.ast.StringLiteral; + +import java.io.IOException; +import java.io.StringReader; + +/** + * Tests {@link JsToStringGenerationVisitor#javaScriptString(String)}. + */ +public class JavaScriptStringTest extends TestCase { + private void test(String original) throws IOException { + String escaped = JsToStringGenerationVisitor.javaScriptString(original); + + // Parse it back + Parser parser = new Parser(); + AstNode node = parser.parse(new StringReader(escaped), "virtual file", 1); + assertEquals(Token.SCRIPT, node.getType()); + Node exprResult = node.getFirstChild(); + assertEquals(Token.EXPR_RESULT, node.getFirstChild().getType()); + ExpressionStatement exprStatement = (ExpressionStatement) node.getFirstChild(); + assertEquals(Token.STRING, exprStatement.getExpression().getType()); + StringLiteral stringLiteral = (StringLiteral) exprStatement.getExpression(); + assertEquals(original, stringLiteral.getValue()); + + // It should be the only token + assertNull(node.getNext()); + } + + public void testBasic() throws IOException { + test("abc"); + test(""); + test("abc\0def"); + test("abc\\def"); + test("\u00CC\u1234\5678\uabcd"); + test("'''"); + test("\"\"\""); + test("\b\f\n\r\t"); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsArrayExprOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsArrayExprOptTest.java new file mode 100644 index 00000000000..571428147ff --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsArrayExprOptTest.java @@ -0,0 +1,93 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * Tests for binary expression optimizations. + */ +public class JsArrayExprOptTest extends ExprOptTest { + + public void testListExprOpt() throws IOException { + String js = compileSingleUnit(getName()); + // base case. + { + String write = findMarkerAtOccurrence(js, "_list0_", 2); + assertEquals(write, "_list0_[$inlineArrayIndexCheck(_list0_, 0)] = 1"); + String read = findMarkerAtOccurrence(js, "_list0_", 3); + assertEquals(read, "_list0_[$inlineArrayIndexCheck(_list0_, 0)]"); + } + // Const array + { + String write = findMarkerAtOccurrence(js, "_list1_", 2); + assertEquals(write, "_list1_.ASSIGN_INDEX$operator(0, tmp$0 = 1) , tmp$0"); + String read = findMarkerAtOccurrence(js, "_list1_", 3); + assertEquals(read, "_list1_[$inlineArrayIndexCheck(_list1_, 0)]"); + } + // custom implementation of []. + { + String write = findMarkerAtOccurrence(js, "_list2_", 2); + assertEquals(write, "_list2_.ASSIGN_INDEX$operator(0, tmp$1 = 'foo') , tmp$1"); + String read = findMarkerAtOccurrence(js, "_list2_", 3); + assertEquals(read, "_list2_.INDEX$operator(0)"); + } + // untyped. + { + String write = findMarkerAtOccurrence(js, "_list3_", 2); + assertEquals(write, "_list3_.ASSIGN_INDEX$operator(0, tmp$2 = 'foo') , tmp$2"); + String read = findMarkerAtOccurrence(js, "_list3_", 3); + assertEquals(read, "_list3_.INDEX$operator(0)"); + } + // index expression. + { + String write = findMarkerAtOccurrence(js, "_list4_", 2); + assertEquals(write, "_list4_[$inlineArrayIndexCheck(_list4_, i_0 + j_0)] = 'foo'"); + String read = findMarkerAtOccurrence(js, "_list4_", 3); + assertEquals(read, "_list4_[$inlineArrayIndexCheck(_list4_, i_0 - j_0)]"); + } + // nested array - 0 dimension + { + String write = findMarkerAtOccurrence(js, "_list5_", 2); + assertEquals(write, "_list5_[$inlineArrayIndexCheck(_list5_, 0)] = $Dart$Null"); + String read = findMarkerAtOccurrence(js, "_list5_", 3); + assertEquals(read, "_list5_[$inlineArrayIndexCheck(_list5_, 0)]"); + } + // nested array - 1 dimension + { + String write = findMarkerAtOccurrence(js, "_list5_", 4); + assertEquals(write, "_list5_[$inlineArrayIndexCheck(_list5_, 0)][$inlineArrayIndexCheck(" + + "_list5_[$inlineArrayIndexCheck(_list5_, 0)], 1)] = $Dart$Null"); + String read = findMarkerAtOccurrence(js, "_list5_", 5); + assertEquals(read, "_list5_[$inlineArrayIndexCheck(_list5_, 0)][$inlineArrayIndexCheck(" + + "_list5_[$inlineArrayIndexCheck(_list5_, 0)], 1)]"); + } + // nested array - 2 dimension + { + String write = findMarkerAtOccurrence(js, "_list5_", 6); + assertEquals(write, "_list5_[$inlineArrayIndexCheck(_list5_, 0)]" + + "[$inlineArrayIndexCheck(_list5_[$inlineArrayIndexCheck(_list5_, 0)], 1)]" + + "[$inlineArrayIndexCheck(_list5_[$inlineArrayIndexCheck(_list5_, 0)]" + + "[$inlineArrayIndexCheck(_list5_[$inlineArrayIndexCheck(_list5_, 0)], 1)]" + + ", 2)] = 1"); + String read = findMarkerAtOccurrence(js, "_list5_", 7); + assertEquals(read, "_list5_[$inlineArrayIndexCheck(_list5_, 0)]" + + "[$inlineArrayIndexCheck(_list5_[$inlineArrayIndexCheck(_list5_, 0)], 1)]" + + "[$inlineArrayIndexCheck(_list5_[$inlineArrayIndexCheck(_list5_, 0)]" + + "[$inlineArrayIndexCheck(_list5_[$inlineArrayIndexCheck(_list5_, 0)], 1)]" + ", 2)]"); + } + } + + public void testListSubTypeExprOpt() throws IOException { + String js = compileSingleUnit(getName()); + // Array subtype. + { + String write = findMarkerAtOccurrence(js, "_list0_", 2); + assertEquals(write, "_list0_.ASSIGN_INDEX$operator(0, tmp$0 = 'foo') , tmp$0"); + String read = findMarkerAtOccurrence(js, "_list0_", 3); + assertEquals(read, "_list0_.INDEX$operator(0)"); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsBackendTests.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsBackendTests.java new file mode 100644 index 00000000000..188d309bbe0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsBackendTests.java @@ -0,0 +1,37 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.common.TypeHeuristicImplementationTest; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class JsBackendTests extends TestSetup { + + public JsBackendTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart Javascript backend test suite."); + + suite.addTestSuite(JavaScriptStringTest.class); +// suite.addTestSuite(JsParserTest.class); + suite.addTestSuite(JsScopeTest.class); +// suite.addTestSuite(JsToStringGenerationVisitorAccuracyTest.class); +// suite.addTestSuite(JsToStringGenerationVisitorConcisenessTest.class); + suite.addTestSuite(ClosureJsCodingConventionTest.class); + suite.addTestSuite(JsBinaryExprOptTest.class); + suite.addTestSuite(JsUnaryExprOptTest.class); + suite.addTestSuite(JsArrayExprOptTest.class); + suite.addTestSuite(JsFieldAccessOptTest.class); + suite.addTestSuite(TypeHeuristicImplementationTest.class); + suite.addTestSuite(JsCompoundBinaryExprOptTest.class); + suite.addTestSuite(JsClosureExprOptTest.class); + return new JsBackendTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsBinaryExprOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsBinaryExprOptTest.java new file mode 100644 index 00000000000..77baa08c00e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsBinaryExprOptTest.java @@ -0,0 +1,192 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; +import java.util.List; + +/** + * Tests for binary expression optimizations. + */ +public class JsBinaryExprOptTest extends ExprOptTest { + + public void testLiteralExpressions() throws IOException { + String js = compileSingleUnit(getName()); + List lines = findMarkerLines(js); + + assertEquals("0 = 1 + 1", lines.get(0)); + assertEquals("1 = 1 - 1", lines.get(1)); + assertEquals("2 = 1 * 1", lines.get(2)); + assertEquals("3 = 1 / 1", lines.get(3)); + // we can't inline % as dart uses euclidean 'module'. Inlining % has the same semantics + // if operands are non-negative, but will have different semantics if operands are negative. + assertEquals("4 = MOD$operator(1, 1)", lines.get(4)); + + assertEquals("5 = 1 > 1", lines.get(5)); + assertEquals("6 = 1 < 1", lines.get(6)); + assertEquals("7 = 1 >= 1", lines.get(7)); + assertEquals("8 = 1 <= 1", lines.get(8)); + + assertEquals("9 = 1 === 1", lines.get(9)); + assertEquals("10 = 1 !== 1", lines.get(10)); + + assertEquals("11 = true === false", lines.get(11)); + assertEquals("12 = true !== false", lines.get(12)); + + assertEquals("13 = TRUNC$operator(1, 2)", lines.get(13)); + + assertEquals("14 = 1 | 1", lines.get(14)); + assertEquals("15 = 1 & 1", lines.get(15)); + assertEquals("16 = 1 << 1", lines.get(16)); + assertEquals("17 = 1 >> 1", lines.get(17)); + + assertEquals("18 = true || false", lines.get(18)); + assertEquals("19 = true && false", lines.get(19)); + + // i == 0 => i === 0 + assertEquals("20 = i === 0", lines.get(20)); + + // i != 0 => i !== 0 + assertEquals("21 = i !== 0", lines.get(21)); + + // str == 'a' => str === 'a' + assertEquals("22 = str === 'a'", lines.get(22)); + + // str != 'a' => str !== 'a' + assertEquals("23 = str !== 'a'", lines.get(23)); + + // a == b => a === b + assertEquals("24 = a === b", lines.get(24)); + + // a != b => a !== b + assertEquals("25 = a !== b", lines.get(25)); + + // b == a => b.Equals(a) - b overrides equals operator. + assertEquals("26 = EQ$operator(b, a)", lines.get(26)); + + // b != a => b.NotEquals(a) - b overrides equals operator. + assertEquals("27 = NE$operator(b, a)", lines.get(27)); + + // a == null => a == null + assertEquals("28 = a == null", lines.get(28)); + + // a != null => a != null + assertEquals("29 = a != null", lines.get(29)); + + // null == a => a == null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("30 = a == null", lines.get(30)); + + // null != a => a != null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("31 = a != null", lines.get(31)); + + // b == null => b.Equals($Dart$Null) + assertEquals("32 = EQ$operator(b, $Dart$Null)", lines.get(32)); + + // b != null => b.NotEquals($Dart$Null) + assertEquals("33 = NE$operator(b, $Dart$Null)", lines.get(33)); + + // null == b => b == null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("34 = b == null", lines.get(34)); + + // null != b => b != null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("35 = b != null", lines.get(35)); + + // i === 0 => i === 0 + assertEquals("36 = i === 0", lines.get(36)); + + // i !== 0 => i !== 0 + assertEquals("37 = i !== 0", lines.get(37)); + + // str === 'a' => str === 'a' + assertEquals("38 = str === 'a'", lines.get(38)); + + // str !== 'a' => str !== 'a' + assertEquals("39 = str !== 'a'", lines.get(39)); + + // a === b => a === b + assertEquals("40 = a === b", lines.get(40)); + + // a !== b => a !== b + assertEquals("41 = a !== b", lines.get(41)); + + // b === a => b === a + assertEquals("42 = b === a", lines.get(42)); + + // b !== a => b !== a + assertEquals("43 = b !== a", lines.get(43)); + + // a === null => a == null + assertEquals("44 = a == null", lines.get(44)); + + // a !== null => a != null + assertEquals("45 = a != null", lines.get(45)); + + // null === a => a == null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("46 = a == null", lines.get(46)); + + // null !== a => a != null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("47 = a != null", lines.get(47)); + + // b === null => b == null + assertEquals("48 = b == null", lines.get(48)); + + // b !== null => b != null + assertEquals("49 = b != null", lines.get(49)); + + // null === b => b == null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("50 = b == null", lines.get(50)); + + // null !== b => b != null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("51 = b != null", lines.get(51)); + + // str === null => str === null + assertEquals("52 = str == null", lines.get(52)); + + // str !== null => str != null + assertEquals("53 = str != null", lines.get(53)); + + // null === str => str == null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("54 = str == null", lines.get(54)); + + // null !== str => str != null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("55 = str != null", lines.get(55)); + + // null == null => null == null + assertEquals("56 = null == null", lines.get(56)); + + // null === null => null === null + assertEquals("57 = null == null", lines.get(57)); + + // false == null => false == null + assertEquals("58 = false == null", lines.get(58)); + + // false === null => false === null + assertEquals("59 = false == null", lines.get(59)); + + // str == null => str == null + assertEquals("60 = str == null", lines.get(60)); + + // str != null => str != null + assertEquals("61 = str != null", lines.get(61)); + + // null == str => str == null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("62 = str == null", lines.get(62)); + + // null != strl => str != null + // We flip lhs and rhs due to v8 issue in which the lhs being null is not as fast as being rhs. + assertEquals("63 = str != null", lines.get(63)); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsClosureExprOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsClosureExprOptTest.java new file mode 100644 index 00000000000..fa6107048ce --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsClosureExprOptTest.java @@ -0,0 +1,97 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * Tests for closure optimizations. + */ +public class JsClosureExprOptTest extends ExprOptTest { + public void testClosureOpt() throws IOException { + String js = compileSingleUnit(getName(), "A"); + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_0", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind0_2")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_1", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind0_3")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_2", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind0_4")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_3", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind0_5")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_4", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind(")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_5", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind1_2")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_6", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind2_2")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_7", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind3_2")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_8", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind(")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_9", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind1_3")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_A", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind2_4")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_B", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind3_5")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_C", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind(")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_D", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind(")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_E", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind1_3")); + assertTrue(findMarkerAtOccurrence.contains("this")); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_fn_F", ";", 1); + assertTrue(findMarkerAtOccurrence.contains("$bind2_3")); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsCompoundBinaryExprOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsCompoundBinaryExprOptTest.java new file mode 100644 index 00000000000..21722616ba8 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsCompoundBinaryExprOptTest.java @@ -0,0 +1,202 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * Tests for binary expression optimizations. + */ +public class JsCompoundBinaryExprOptTest extends ExprOptTest { + private static final String DELIMETERS = "[\\n,;]"; + private static final String FIELD_DELIMETERS = "[\\n,;.]"; + + /** + * Test that compound binary expressions (+=,-=,*=, /=) on NUMBERIMPLEMENTATION generated as + * operator invocations. + */ + public void testCompoundBinaryExprOpt() throws IOException { + // TODO(zundel): The source for this test compiles but does not execute correctly. + String js = compileSingleUnit(getName()); + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_1", DELIMETERS, 2); + assertEquals("_marker_1 += _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_2", DELIMETERS, 2); + assertEquals("_marker_2 -= _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_3", DELIMETERS, 2); + assertEquals("_marker_3 *= _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_4", DELIMETERS, 2); + assertEquals("_marker_4 /= _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_5", DELIMETERS, 2); + assertEquals("_marker_5 += _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_6", DELIMETERS, 2); + assertEquals("_marker_6 -= _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_7", DELIMETERS, 2); + assertEquals("_marker_7 *= _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_8", DELIMETERS, 2); + assertEquals("_marker_8 /= _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_8", DELIMETERS, 2); + assertEquals("_marker_8 /= _marker_0 + 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_a_.Ay_01", DELIMETERS, 1); + assertEquals("_a_.Ay_01$field++", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_a_.Ay_02", DELIMETERS, 1); + assertEquals("_a_.Ay_02$field--", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "Ay_03", DELIMETERS, 1); + assertEquals("_a_.Ay_03$field += 2 * tmp * -123", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "Ay_04", DELIMETERS, 1); + assertEquals("_a_.Ay_04$field -= 2 * tmp * -123", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "Ay_05", DELIMETERS, 1); + assertEquals("_a_.Ay_05$field *= 2 * tmp * -123", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "Ay_06", DELIMETERS, 1); + assertEquals("_a_.Ay_06$field /= 2 * tmp * -123", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "AAAx_01", DELIMETERS, 1); + assertEquals("_a_.aa_$field.aaa_$field.AAAx_01$field += 2 * tmp / -1", + findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "AAAx_02", DELIMETERS, 1); + assertEquals("_a_.aa_$field.aaa_$field.AAAx_02$field -= 2 * tmp / -1", + findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "AAAx_03", DELIMETERS, 1); + assertEquals("_a_.aa_$field.aaa_$field.AAAx_03$field *= 2 * tmp / -1", + findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "AAAx_04", DELIMETERS, 1); + assertEquals("_a_.aa_$field.aaa_$field.AAAx_04$field /= 2 * tmp / -1", + findMarkerAtOccurrence); + } + + { + String getter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_01", FIELD_DELIMETERS, 1)); + assertEquals("AAAz_01$getter()", getter); + String setter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_01", FIELD_DELIMETERS, 2)); + assertEquals("AAAz_01$setter(tmp = ADD$operator(tmp", setter); + } + + { + String getter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_02", FIELD_DELIMETERS, 1)); + assertEquals("AAAz_02$getter()", getter); + String setter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_02", FIELD_DELIMETERS, 2)); + assertEquals("AAAz_02$setter(tmp = ADD$operator(tmp", setter); + } + + { + String setter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_03", FIELD_DELIMETERS, 1)); + assertEquals("AAAz_03$setter(tmp = ADD$operator(tmp", setter); + String getter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_03", FIELD_DELIMETERS, 2)); + assertEquals("AAAz_03$getter()", getter); + } + + { + String setter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_04", FIELD_DELIMETERS, 1)); + assertEquals("AAAz_04$setter(tmp = ADD$operator(tmp", setter); + String getter = replaceTemps(findMarkerAtOccurrence(js, "AAAz_04", FIELD_DELIMETERS, 2)); + assertEquals("AAAz_04$getter()", getter); + } + + { + String setter = replaceTemps(findMarkerAtOccurrence(js, "AAAx_06", FIELD_DELIMETERS, 1)); + assertEquals("AAAx_06$setter(tmp = MOD$operator(tmp", setter); + String getter = replaceTemps(findMarkerAtOccurrence(js, "AAAx_06", FIELD_DELIMETERS, 2)); + assertEquals("AAAx_06$getter()", getter); + } + + { + String setter = replaceTemps(findMarkerAtOccurrence(js, "AAAx_07", FIELD_DELIMETERS, 1)); + assertEquals("AAAx_07$setter(tmp = TRUNC$operator(tmp", setter); + String getter = replaceTemps(findMarkerAtOccurrence(js, "AAAx_07", FIELD_DELIMETERS, 2)); + assertEquals("AAAx_07$getter()", getter); + } + + String classAAA = compileSingleUnit(getName(), "AAA"); + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(classAAA, "AAAw_01", DELIMETERS, 9); + assertEquals("this.AAAw_01$field++", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(classAAA, "AAAu_01", DELIMETERS, 9); + assertEquals("this.AAAu_01$field += a * 123", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_9", DELIMETERS, 2); + assertEquals("_marker_9 |= _marker_0 & 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker__10", DELIMETERS, 2); + assertEquals("_marker__10 &= _marker_0 & 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker__11", DELIMETERS, 2); + assertEquals("_marker__11 ^= _marker_0 & 1", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker__12", "[\\n;]", 2); + assertEquals("_marker__12 |= BIT_AND$operator(_var_marker, 1)", findMarkerAtOccurrence); + } + + { + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_var_marker", "[\\n;]", 3); + assertEquals("_var_marker = BIT_OR$operator(_var_marker, _marker__12 & 1)", + findMarkerAtOccurrence); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsConstExprOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsConstExprOptTest.java new file mode 100644 index 00000000000..9b712bc60a5 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsConstExprOptTest.java @@ -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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * Tests for binary expression optimizations. + */ +public class JsConstExprOptTest extends ExprOptTest { + + public void testConstantExprOpt() throws IOException { + String js = compileSingleUnit(getName()); + + { + String marker = findMarkerAtOccurrence(js, "_marker_0", "[\\n;]", 2); + assertEquals("_marker_0 = 50 * 2", marker); + } + + { + String marker = findMarkerAtOccurrence(js, "_marker_1", "[\\n;]", 2); + assertEquals("_marker_1 = 10 + 5 * 2 + 5", marker); + } + + { + String marker = findMarkerAtOccurrence(js, "_marker_2", "[\\n;]", 2); + assertEquals("_marker_2 = 5 + 5", marker); + } + + // Cant inline array. + { + String marker = findMarkerAtOccurrence(js, "_marker_3", "[\\n;]", 2); + assertEquals("_marker_3 = 5 + Test_app4a54ba$A$Dart.ARRAY$getter()" + + "[$inlineArrayIndexCheck(Test_app4a54ba$A$Dart.ARRAY$getter(), 0)]", marker); + } + + // Cant bind constants that refer to methods + { + String marker = findMarkerAtOccurrence(js, "_marker_4", "[\\n;]", 2); + assertEquals("_marker_4 = Test_app4a54ba$A$Dart.C3$getter()", marker); + } + + // Cant bind constants that instantiate objects. + { + String marker = findMarkerAtOccurrence(js, "_marker_5", "[\\n;]", 2); + assertEquals("_marker_5 = Test_app4a54ba$A$Dart.C4$getter()", marker); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsConstructorOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsConstructorOptTest.java new file mode 100644 index 00000000000..745364f504e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsConstructorOptTest.java @@ -0,0 +1,144 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * Tests for javascript object construction and initialization. + */ +public class JsConstructorOptTest extends ExprOptTest { + + /** + * Test javascript object creation and inlining of field in the factory body. + */ + public void testConstructorOptTest() throws IOException { + + String classAAA = compileSingleUnit(getName(), "AAA"); + // class AAA { + // int a; + // int b = 567; + // AAA(this.a) : this.c = 123 { } + // int c; + // int d; + // } + { + // Ensure arguments to constructor are in the same field order definition. + String params = findMarkerAtOccurrence(classAAA, "p$a$field", "[\\(\\)}]", 1); + assertEquals("p$a$field, p$b$field, p$c$field, p$d$field", params); + + String init_a = findMarkerAtOccurrence(classAAA, "p$a$field", "[;\\n]", 2); + assertEquals("this.a$field = p$a$field", init_a); + + String init_b = findMarkerAtOccurrence(classAAA, "p$b$field", "[;\\n]", 2); + assertEquals("this.b$field = p$b$field", init_b); + + String init_c = findMarkerAtOccurrence(classAAA, "p$c$field", "[;\\n]", 2); + assertEquals("this.c$field = p$c$field", init_c); + + String init_d = findMarkerAtOccurrence(classAAA, "p$d$field", "[;\\n]", 2); + assertEquals("this.d$field = p$d$field", init_d); + + String[] bodyLines = getFunctionBody("AAA$$Factory", classAAA); + + assertTrue(bodyLines.length > 0); + assertEquals(bodyLines.length, 7); + + String tmp_init_a = bodyLines[0].trim(); + assertEquals("var init$a$field = a;", tmp_init_a); + + String tmp_init_b = bodyLines[1].trim(); + assertEquals("var init$b$field = 567;", tmp_init_b); + + String tmp_init_c = bodyLines[2].trim(); + assertEquals("var init$c$field = 123;", tmp_init_c); + + String tmp_init_d = bodyLines[3].trim(); + assertEquals("var init$d$field = $Dart$Null;", tmp_init_d); + + String newCall = replaceTemps(bodyLines[4].trim()); + assertEquals("var tmp = new Test_app4a54ba$AAA$Dart(init$a$field, init$b$field, " + + "init$c$field, init$d$field);", newCall); + + String ctorCall = replaceTemps(bodyLines[5].trim()); + assertTrue(ctorCall.indexOf("$Constructor.call") != -1); + } + + String classBBB = compileSingleUnit(getName(), "BBB"); + + // class BBB { + // int a; + // int b = 567; + // int c; + // BBB(this.a) { } + // } + // + // class CCC extends BBB { + // int d; + // CCC(this.d) : super(this.d) { } + // } + { + String[] bodyLines = getFunctionBody("BBB$$Factory", classBBB); + + assertTrue(bodyLines.length > 0); + assertEquals(bodyLines.length, 4); + + String line1 = replaceTemps(bodyLines[0].trim()); + assertEquals("var tmp = new Test_app4a54ba$BBB$Dart;", line1); + + String initCall = replaceTemps(bodyLines[1].trim()); + assertTrue(initCall.indexOf("$Initializer.call") != -1); + + String ctorCall = replaceTemps(bodyLines[2].trim()); + assertTrue(ctorCall.indexOf("$Constructor.call") != -1); + + String returnStmt = replaceTemps(bodyLines[3].trim()); + assertEquals("return tmp;", returnStmt); + } + + String classCCC = compileSingleUnit(getName(), "CCC"); + { + String[] bodyLines = getFunctionBody("CCC$$Factory", classCCC); + + assertTrue(bodyLines.length > 0); + assertEquals(bodyLines.length, 4); + + String line1 = replaceTemps(bodyLines[0].trim()); + assertEquals("var tmp = new Test_app4a54ba$CCC$Dart;", line1); + + String initCall = replaceTemps(bodyLines[1].trim()); + assertTrue(initCall.indexOf("$Initializer.call") != -1); + + String ctorCall = replaceTemps(bodyLines[2].trim()); + assertTrue(ctorCall.indexOf("$Constructor.call") != -1); + + String returnStmt = replaceTemps(bodyLines[3].trim()); + assertEquals("return tmp;", returnStmt); + } + + // class DDD { + // int x; + // int z; + // DDD(this.x, this.z = 123); + // } + { + String classDDD = compileSingleUnit(getName(), "DDD"); + String[] bodyLines = getFunctionBody("DDD$$Factory", classDDD); + + assertTrue(bodyLines.length > 0); + assertEquals(bodyLines.length, 8); + assertEquals("switch (arguments.length) {", replaceTemps(bodyLines[0].trim())); + assertEquals("case 1:", replaceTemps(bodyLines[1].trim())); + assertEquals("z = 123;", replaceTemps(bodyLines[2].trim())); + assertEquals("}", replaceTemps(bodyLines[3].trim())); + assertEquals("var tmp = new Test_app4a54ba$DDD$Dart;", replaceTemps(bodyLines[4].trim())); + assertEquals("Test_app4a54ba$DDD$Dart.$Initializer.call(tmp, x, z);", + replaceTemps(bodyLines[5].trim())); + assertEquals("Test_app4a54ba$DDD$Dart.$Constructor.call(tmp, x, z);", + replaceTemps(bodyLines[6].trim())); + assertEquals("return tmp;", replaceTemps(bodyLines[7].trim())); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsFieldAccessOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsFieldAccessOptTest.java new file mode 100644 index 00000000000..7bbce2a3b90 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsFieldAccessOptTest.java @@ -0,0 +1,69 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * Tests for binary expression optimizations. + */ +public class JsFieldAccessOptTest extends ExprOptTest { + private static final String DELIMETERS = "[;\\(\\)= ]"; + + private final DollarMangler mangler = new DollarMangler(); + + /** + * Test that unary inc and dec operations on integers are not mangled into operator invocations. + */ + public void testFieldAccessExprOpt() throws IOException { + String js = compileSingleUnit(getName()); + + // Ensure that _marker_0.x remains _marker_0.x for loads + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_0", DELIMETERS, 2); + assertEquals("_marker_0.x$field", findMarkerAtOccurrence); + + // Ensure that _marker_0.x remains _marker_0.x for stores + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_0", DELIMETERS, 3); + assertEquals("_marker_0.x$field", findMarkerAtOccurrence); + + // Ensure that _marker_1.x becomes _marker_1.x$setter(1) for stores + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_1", DELIMETERS, 2); + assertEquals("_marker_1." + mangler.createSetterSyntax("x", null), findMarkerAtOccurrence); + + // Ensure that _marker_1.x becomes _marker_1.x$getter() for loads + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_1", DELIMETERS, 3); + assertEquals("_marker_1." + mangler.createGetterSyntax("x", null), findMarkerAtOccurrence); + + // var _marker_2 = b.x$field; + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_2", "[;\\n]", 1); + String[] parts = findMarkerAtOccurrence.split("[=]"); + assertTrue(parts.length == 2); + assertEquals("b.x$field", parts[1].trim()); + + // var _marker_3 = b.x_Getter_WithSideEffect$getter(); + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_3", "[;\\n]", 1); + parts = findMarkerAtOccurrence.split("[=]"); + assertTrue(parts.length == 2); + assertEquals("b.x_Getter_WithSideEffect$getter()", parts[1].trim()); + + // var _marker_4 = b.x_Getter_WithSideEffect$getter(); + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_4", "[;\\n]", 1); + parts = findMarkerAtOccurrence.split("[=]"); + assertTrue(parts.length == 2); + assertEquals("b.x_Getter_WithSomeExpression$getter()", parts[1].trim()); + + // var _marker_5 = b.A_Getter$getter(); + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_5", "[;\\n]", 1); + parts = findMarkerAtOccurrence.split("[=]"); + assertTrue(parts.length == 2); + assertEquals("b.A_Getter$getter()", parts[1].trim()); + + // var _marker_6 = b.X_Getter$getter(); + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_6", "[;\\n]", 1); + parts = findMarkerAtOccurrence.split("[=]"); + assertTrue(parts.length == 2); + assertEquals("b.X_Getter$getter()", parts[1].trim()); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsScopeTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsScopeTest.java new file mode 100644 index 00000000000..a179b33d4e1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsScopeTest.java @@ -0,0 +1,81 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsRootScope; +import com.google.dart.compiler.backend.js.ast.JsScope; + +import junit.framework.TestCase; + +/** + * Tests {@link JsScope} and {@link JsName}. + */ +public class JsScopeTest extends TestCase { + public void testRootScope() { + JsScope scope = new JsRootScope(null); + JsName name = scope.declareName("foo"); + assertSame(name, scope.findExistingName("foo")); + assertTrue("foo".equals(name.getOriginalName())); + JsName name2 = scope.declareName("foo"); + assertSame(name, name2); + + name = scope.declareName("bar"); + assertSame(name, scope.findExistingName("bar")); + assertTrue("bar".equals(name.getOriginalName())); + name2 = scope.declareName("bar"); + assertSame(name, name2); + + name = scope.declareName("false"); + assertTrue("false".equals(name.getIdent())); + assertTrue("false".equals(name.getShortIdent())); + assertTrue("false".equals(name.getOriginalName())); + } + + public void testJsNames() { + JsScope scope = new JsRootScope(null); + + JsName name = scope.declareName("foobar1"); + assertTrue("foobar1".equals(name.getIdent())); + assertTrue("foobar1".equals(name.getShortIdent())); + assertTrue("foobar1".equals(name.getOriginalName())); + + name = scope.declareName("foobar2", "foobar3"); + assertTrue("foobar2".equals(name.getIdent())); + assertTrue("foobar3".equals(name.getShortIdent())); + assertTrue("foobar2".equals(name.getOriginalName())); + + name = scope.declareName("foobar4", "foobar5", "foobar6"); + assertTrue("foobar4".equals(name.getIdent())); + assertTrue("foobar5".equals(name.getShortIdent())); + assertTrue("foobar6".equals(name.getOriginalName())); + } + + public void testNestedScope() { + JsScope rootScope = new JsRootScope(null); + JsScope scope = new JsScope(rootScope, "nested", "unitid"); + + // First the basic operations. + JsName name = scope.declareName("foo"); + assertSame(name, scope.findExistingName("foo")); + assertTrue("foo".equals(name.getOriginalName())); + JsName name2 = scope.declareName("foo"); + assertSame(name, name2); + name = scope.declareName("bar"); + assertSame(name, scope.findExistingName("bar")); + assertTrue("bar".equals(name.getOriginalName())); + name2 = scope.declareName("bar"); + assertSame(name, name2); + + // Test deep search. + name = rootScope.declareName("fisk"); + name2 = scope.findExistingName("fisk"); + assertSame(name, name2); + // Test shadowing. + name2 = scope.declareName("fisk"); + assertNotSame(name, name2); + assertTrue("fisk".equals(name2.getOriginalName())); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/JsUnaryExprOptTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/JsUnaryExprOptTest.java new file mode 100644 index 00000000000..323085c3507 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/JsUnaryExprOptTest.java @@ -0,0 +1,73 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * Tests for binary expression optimizations. + */ +public class JsUnaryExprOptTest extends ExprOptTest { + private static final String DELIMETERS = "[;\\(\\) ]"; + + /** + * Test that unary operations on integers are not mangled into operator invocations. + */ + public void testUnaryDecIncExprOpt() throws IOException { + String js = compileSingleUnit(getName()); + + // Ensure that ++_marker_0 remains ++_marker_0 on simple assignment + String findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_0", DELIMETERS, 2); + assertEquals("++_marker_0", findMarkerAtOccurrence); + + // Ensure that _marker_1++ remains _marker_1++ when used in a for loop + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_1", DELIMETERS, 3); + assertEquals("_marker_1++", findMarkerAtOccurrence); + + // Ensure that --_marker_0 remains --_marker_0 on simple assignment + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_0", DELIMETERS, 3); + assertEquals("--_marker_0", findMarkerAtOccurrence); + + // Ensure that _marker_2-- remains _marker_2-- when used in a for loop + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_2", DELIMETERS, 3); + assertEquals("_marker_2--", findMarkerAtOccurrence); + + // Ensure that parameter _marker_3 remains as _marker_3++ + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_3", DELIMETERS, 2); + assertEquals("_marker_3++", findMarkerAtOccurrence); + + // Ensure that parameter _marker_3 remains as --_marker_3 + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_3", DELIMETERS, 3); + assertEquals("--_marker_3", findMarkerAtOccurrence); + + // Ensure bit op is inlined (variable). + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_4", DELIMETERS, 2); + assertEquals("~_marker_4", findMarkerAtOccurrence); + + // Ensure bit op is inlined (parameter). + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_3", DELIMETERS, 4); + assertEquals("~_marker_3", findMarkerAtOccurrence); + + // Ensure bit op is inlined (field). + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "field_0", DELIMETERS, 1); + assertEquals("~a.field_0$field", findMarkerAtOccurrence); + + // Ensure bit not op is not inlined if operand is untyped. + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "_marker_5", "[\n;]", 2); + assertEquals("_marker_5 = BIT_NOT$operator(foo)", findMarkerAtOccurrence); + + // Ensure bit not op is not inlined if operand is a field with derived abstract field. + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "field_1", "[\n;]", 1); + assertEquals("i = (tmp$0 = aa , (tmp$0.field_1$setter(tmp$1 = " + + "ADD$operator(tmp$0.field_1$getter(), 1)) , tmp$1))", + findMarkerAtOccurrence); + + // Ensure bit not op is not inlined if operand is a field with derived abstract field. + findMarkerAtOccurrence = findMarkerAtOccurrence(js, "field_2", "[\n;]", 1); + assertEquals("i = (tmp$2 = aaa , (tmp$2.field_2$setter(tmp$3 = " + + "ADD$operator(tmp$2.field_2$getter(), 1)) , tmp$3))", + findMarkerAtOccurrence); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/RttTest.java b/compiler/javatests/com/google/dart/compiler/backend/js/RttTest.java new file mode 100644 index 00000000000..5aded293acf --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/RttTest.java @@ -0,0 +1,78 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import java.io.IOException; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class RttTest extends SnippetTestCase { + private static final String DELIMETERS = "[\\n,;]"; + // private static final String FIELD_DELIMETERS = "[\\n,;.]"; + + public void testRuntimeTypes() throws IOException { + String js = compileSingleUnit(getName()); + + { + String init = findMarkerAtOccurrence(js, "_marker_B1", DELIMETERS, 1); + assertEquals("var _marker_B1 = $intern(Test_app4a54ba$B$Dart.B$$Factory())", init); + + String expr = findMarkerAtOccurrence(js, "_marker_B1", DELIMETERS, 2); + assertEquals("a = _marker_B1 instanceof Test_app4a54ba$B$Dart", expr); + } + + { + String init = findMarkerAtOccurrence(js, "_marker_B2", DELIMETERS, 1); + assertEquals("var _marker_B2 = Test_app4a54ba$B$Dart.B$$Factory()", init); + } + + { + String init = findMarkerAtOccurrence(js, "_marker_C1", DELIMETERS, 1); + assertEquals("var _marker_C1 = $intern(" + + "Test_app4a54ba$C$Dart.C$$Factory(" + + "Test_app4a54ba$C$Dart.$lookupRTT())", init); + + String expr = findMarkerAtOccurrence(js, "_marker_C1", DELIMETERS, 2); + assertEquals("a = _marker_C1 instanceof Test_app4a54ba$C$Dart", expr); + } + + { + String init = findMarkerAtOccurrence(js, "_marker_C2", DELIMETERS, 1); + assertEquals("var _marker_C2 = $intern(Test_app4a54ba$C$Dart.C$$Factory(" + + "Test_app4a54ba$C$Dart.$lookupRTT([String$Dart.$lookupRTT()]))", init); + + String expr = findMarkerAtOccurrence(js, "_marker_C2", DELIMETERS, 2); + assertEquals("a = Test_app4a54ba$C$Dart.$lookupRTT([String$Dart.$lookupRTT()])" + + ".implementedBy(_marker_C2)", expr); + } + + { + String init = findMarkerAtOccurrence(js, "_marker_C3", DELIMETERS, 1); + assertEquals("var _marker_C3 = " + + "Test_app4a54ba$C$Dart.C$$Factory(" + + "Test_app4a54ba$C$Dart.$lookupRTT())", init); + } + + { + String init = findMarkerAtOccurrence(js, "_marker_C4", DELIMETERS, 1); + assertEquals("var _marker_C4 = " + + "Test_app4a54ba$C$Dart.C$$Factory(" + + "Test_app4a54ba$C$Dart.$lookupRTT([Object.$lookupRTT()]))", init); + + String expr = findMarkerAtOccurrence(js, "_marker_C4", DELIMETERS, 2); + assertEquals("a = Test_app4a54ba$C$Dart.$lookupRTT([Object.$lookupRTT()])" + + ".implementedBy(_marker_C4)", expr); + } + + { + String init = findMarkerAtOccurrence(js, "_marker_D1", DELIMETERS, 1); + assertEquals("var _marker_D1 = Test_app4a54ba$D$Dart.D$$Factory([])", init); + + String expr = findMarkerAtOccurrence(js, "_marker_D1", DELIMETERS, 2); + assertEquals("a = _marker_D1 instanceof Test_app4a54ba$D$Dart", expr); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/SnippetTestCase.java b/compiler/javatests/com/google/dart/compiler/backend/js/SnippetTestCase.java new file mode 100644 index 00000000000..9118cbcf90a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/SnippetTestCase.java @@ -0,0 +1,160 @@ +// 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. + +package com.google.dart.compiler.backend.js; + +import com.google.common.base.Strings; +import com.google.dart.compiler.CompilerConfiguration; +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartCompilerListenerTest; +import com.google.dart.compiler.DartSourceTest; +import com.google.dart.compiler.DefaultCompilerConfiguration; +import com.google.dart.compiler.MockArtifactProvider; +import com.google.dart.compiler.MockLibrarySource; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for binary expression optimizations. + */ +public class SnippetTestCase extends CompilerTestCase { + + private static final String MARKER = "_marker_"; + + private MockArtifactProvider provider = new MockArtifactProvider(); + private JavascriptBackend jsBackend = new JavascriptBackend(); + protected AbstractJsBackend getBackend() { + return jsBackend; + } + + @Override + protected void tearDown() throws Exception { + provider = null; + jsBackend = null; + super.tearDown(); + } + + protected String compileSingleUnit(final String filePath) throws IOException { + return compileSingleUnit(filePath, "Main"); + } + + protected String compileSingleUnit(final String filePath, final String part) throws IOException { + URL url = inputUrlFor(getClass(), filePath + ".dart"); + String source = readUrl(url); + MockLibrarySource lib = new MockLibrarySource(); + DartSourceTest src = new DartSourceTest(filePath, source, lib); + lib.addSource(src); + CompilerOptions options = new CompilerOptions(); + CompilerConfiguration config = new DefaultCompilerConfiguration(this.getBackend(), options); + DartCompilerListener listener = new DartCompilerListenerTest(src.getName()); + DartCompiler.compileLib(lib, config, provider, listener); + + return provider.getArtifactString(src, part, JavascriptBackend.EXTENSION_JS); + } + + protected List findMarkerLines(String js) { + assertNotNull(js); + List lines = new ArrayList(); + + int begin = 0; + int end = 0; + while (true) { + begin = js.indexOf(MARKER, begin); + if (begin < 0) { + break; + } + end = js.indexOf(';', begin); + if (end > begin) { + lines.add(js.substring(begin + MARKER.length(), end)); + } + begin = end; + } + + return lines; + } + + /** + * Returns the specified occurence of the marker string inside of the JS code or null if there is + * none. Note that this method may miss the text around the specified occurence of the marker. + */ + protected String findMarkerAtOccurrence(String js, String marker, int occurrence) { + int begin = 0; + int end = 0; + int nOccurrence = 0; + while (true) { + begin = js.indexOf(marker, begin); + if (begin < 0) { + break; + } + nOccurrence++; + end = js.indexOf(';', begin); + if (end > begin && occurrence == nOccurrence) { + return js.substring(begin, end); + } + + begin = end; + } + + return null; + } + + /** + * Returns the specified occurrence of the marker string inside of the JS code by splitting the + * string using the specified regex and then locating the occurrence. This method allows you to + * control how much of the text around the marker occurence you are interested in. + */ + protected String findMarkerAtOccurrence(String js, String marker, String regEx, int occurrence) { + int iOccurrence = 0; + String[] strings = js.split(regEx); + for (int i = 0; i < strings.length; ++i) { + if (strings[i].indexOf(marker) != -1) { + if (++iOccurrence == occurrence) { + return strings[i].trim(); + } + } + } + return null; + } + + protected String replaceTemps(String js) { + return Strings.isNullOrEmpty(js) ? "" : js.replaceAll("tmp\\$[0-9]+", "tmp"); + } + + protected String[] getFunctionBody(String name, String corpus) { + assertTrue(!Strings.isNullOrEmpty(name)); + int start = corpus.indexOf(name); + if (start == -1) { + return new String[0]; + } + int len = corpus.length(); + while (start < len && corpus.charAt(start) != '{') { + start++; + } + int end = start; + int count = 0; + while (end < len) { + if (corpus.charAt(end) == '{') + count++; + if (corpus.charAt(end) == '}') + count--; + end++; + if (count == 0) + break; + } + assert end >= start; + List result = new ArrayList(); + for (String s : corpus.substring(start, end).split("\\n")) { + if (s.equals("{") || s.equals("}")) + continue; + result.add(s.trim()); + } + return result.toArray(new String[0]); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testClosureOpt.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testClosureOpt.dart new file mode 100644 index 00000000000..16f62a6d43b --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testClosureOpt.dart @@ -0,0 +1,66 @@ +// 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. + +class A { + A(this.x); + + int x; + + testMethod(int arg1) { + int s1 = 1; + { + int s2 = 2; + { + int s3 = 3; + { + int s4 = 4; + + var _fn_0 = int () => 0; // hoisted + + var _fn_1 = int (p1) => p1; // hoisted + + var _fn_2 = int (p1, p2) => p1 + p2; // hoisted + + var _fn_3 = int (p1, p2, p3) => p1 + p2 + p3; // hoisted + + var _fn_4 = int (p1, p2, p3, p4) => p1 + p2 + p3 + p4; // hoisted + + + var _fn_5 = int () => s1; // bind 1-0 + + var _fn_6 = int () => s1 + s2; // bind 2-0 + + var _fn_7 = int () => s1 + s2 + s3; // bind 3-0 + + var _fn_8 = int () => s1 + s2 + s3 + s4; // bind + + + var _fn_9 = int (p1) => p1 + s1; // bind 1-1 + + var _fn_A = int (p1, p2) => p1 + p2 + s1 + s2; // bind 2-2 + + var _fn_B = int (p1, p2, p3) => p1 + p2 + p3 + s1 + s2 + s3; // bind 3-3 + + // bind + var _fn_C = int (p1, p2, p3, p4) => p1 + p2 + p3 + p4 + s1 + s2 + s3 + s4; + + + // cannot inline - named args + var _fn_D = int (p1, [n1 = 20]) => p1 + s1 + n1; + + var _fn_E = int (p1) => p1 + s1 + this.x; + + var _fn_F = int (p1) => p1 + s1 + arg1; + } + } + } + } +} + +class Main { + static void main() { + A a = new A(1); + a.testMethod(1); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testCompoundBinaryExprOpt.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testCompoundBinaryExprOpt.dart new file mode 100644 index 00000000000..a39809d615c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testCompoundBinaryExprOpt.dart @@ -0,0 +1,141 @@ +// 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. + +class AAA { + AAA() { } + num AAAx_01; + num AAAx_02; + num AAAx_03; + num AAAx_04; + num AAAx_05; + num AAAx_06; + num AAAx_07; + + num AAAz_01; + num AAAz_02; + num AAAz_03; + num AAAz_04; + + num AAAw_01; + num AAAu_01; + + foo() { + int a = 0; + AAAw_01++; + AAAu_01 += a * 123; + } +} + +class BBB extends AAA { + BBB() : super() { } + num get AAAz_01() { } + num get AAAz_03() { } + set AAAz_02(x) { } + set AAAz_04(x) { } +} + +class AA { + AA() { } + AAA aaa_; +} + +class A { + A() { } + AA aa_; + num Ay_01; + num Ay_02; + num Ay_03; + num Ay_04; + num Ay_05; + num Ay_06; +} + +class Main { + + static void main(num _marker_5, num _marker_6, num _marker_7, num _marker_8) { + num _marker_0, _marker_1, _marker_2, _marker_3, _marker_4; + num _marker_9, _marker__10, _marker__11, _marker__12; + + // Ensure that += is not generated as shim + _marker_1 += _marker_0 + 1; + + // Ensure that -= is not generated as shim + _marker_2 -= _marker_0 + 1; + + // Ensure that *= is not generated as shim + _marker_3 *= _marker_0 + 1; + + // Ensure that /= is not generated as shim + _marker_4 /= _marker_0 + 1; + + // Ensure that += is not generated as shim + _marker_5 += _marker_0 + 1; + + // Ensure that -= is not generated as shim + _marker_6 -= _marker_0 + 1; + + // Ensure that *= is not generated as shim + _marker_7 *= _marker_0 + 1; + + // Ensure that /= is not generated as shim + _marker_8 /= _marker_0 + 1; + + A _a_ = new A(); + + // Should be optimized - simple field case. + _a_.Ay_01++; + _a_.Ay_02--; + + // all 'inline-able' operators + int tmp = 123; + _a_.Ay_03 += 2 * tmp * -123; + _a_.Ay_04 -= 2 * tmp * -123; + _a_.Ay_05 *= 2 * tmp * -123; + _a_.Ay_06 /= 2 * tmp * -123; + + // All 'inline-able' operators with long path expressions. + _a_.aa_.aaa_.AAAx_01 += 2 * tmp / -1; + _a_.aa_.aaa_.AAAx_02 -= 2 * tmp / -1; + _a_.aa_.aaa_.AAAx_03 *= 2 * tmp / -1; + _a_.aa_.aaa_.AAAx_04 /= 2 * tmp / -1; + + // add method to double check we are inlining correctly. + _a_.aa_.aaa_.AAAx_05 += call(_a_.aa_.aaa_.AAAx_05) * tmp / -1; + + // Negative test cases. + + // _AAAz_01 must call shim (derived class has an a getter with same name as parent field). + _a_.aa_.aaa_.AAAz_01++; + + // _AAAz_02 must call shim (derived class has an a setter with same name as parent field). + _a_.aa_.aaa_.AAAz_02++; + + // _AAAz_03 must call shim (derived class has an a getter with same name as parent field). + _a_.aa_.aaa_.AAAz_03 += 22 * tmp / -1; + + // _AAAz_04 must call shim (derived class has an a getter with same name as parent field). + _a_.aa_.aaa_.AAAz_04 += 222 * tmp / -1; + + // Cannot be inlined % and ~ + _a_.aa_.aaa_.AAAx_06 %= 2 * tmp / -1; + _a_.aa_.aaa_.AAAx_07 ~/= 2 * tmp / -1; + + _marker_9 |= _marker_0 & 1; + + _marker__10 &= _marker_0 & 1; + + _marker__11 ^= _marker_0 & 1; + + var _var_marker; + _marker__12 |= _var_marker & 1; + + _var_marker |= _marker__12 & 1; + } + + static double call(x) { return x; } +} + +main() { + Main.main(0, 0, 0, 0); +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testConstantExprOpt.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testConstantExprOpt.dart new file mode 100644 index 00000000000..3091566c84c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testConstantExprOpt.dart @@ -0,0 +1,41 @@ +// 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. + +class Foo { + const Foo(); +} + +class A { + static final int C1 = 10 + C2 * 2; // 20 + static final int C2 = 5; + static final List ARRAY = [1]; + static final int C3 = C2 + foo(); + static final Foo C4 = const Foo(); + static int foo() { return 1; } +} + +class Main { + static void main() { + int _marker_0, _marker_1, _marker_2, _marker_3, _marker_4; + Foo _marker_5; + + final int x = 50; + + _marker_0 = x * 2; + + _marker_1 = A.C1 + 5; + + _marker_2 = 5 + A.C2; + + _marker_3 = 5 + A.ARRAY[0]; + + _marker_4 = A.C3; + + _marker_5 = A.C4; + } +} + +main() { + Main.main(); +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testConstructorOptTest.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testConstructorOptTest.dart new file mode 100644 index 00000000000..4097570fbfc --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testConstructorOptTest.dart @@ -0,0 +1,34 @@ +// 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. + +class AAA { + int a; + int b = 567; + AAA(this.a) : this.c = 123 { } + int c; + int d; +} + +class BBB { + int a; + int b = 567; + int c; + BBB(this.a) { } +} + +class CCC extends BBB { + int d; + CCC(this.d) : super(this.d) { } +} + +class DDD { + int x; + int z; + DDD(this.x, [this.z = 123]); +} + +main() { + AAA _a_marker = new AAA(123); + BBB _b_marker = new BBB(999); +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testFieldAccessExprOpt.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testFieldAccessExprOpt.dart new file mode 100644 index 00000000000..11f43a95922 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testFieldAccessExprOpt.dart @@ -0,0 +1,85 @@ +// 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. + +class A { + int x; + + static final A a = new A(); + + A(){} +} + + +class WillNotOptimizeFieldAccess { + WillNotOptimizeFieldAccess(){} + int x; +} + +class WillNotOptimizeFieldAccessSubclass extends WillNotOptimizeFieldAccess { + WillNotOptimizeFieldAccessSubclass() : super() {} + + int get x() { + return this.x; + } +} + +class B extends A { + B() : super() { } + + // can be inlined. + int get x_Getter_WithoutSideEffect() { + return x; + } + + // cannot be inlined - getter has side effect. + int get x_Getter_WithSideEffect() { + return x + 1; + } + + // cannot be inlined - underlying value is not a field. + int get x_Getter_WithSomeExpression() { + return foo() * 2; + } + + static foo() { return 123; } + + // cannot be inlined - underlying field is static. + A get A_Getter() { + return a; + } + + // cannot be inlined - cycle. + int get X_Getter() { + return this.X_Getter; + } +} + +class Main { + static void main() { + A _marker_0 = new A(); + + _marker_0.x = 1; + + int x = _marker_0.x; + + WillNotOptimizeFieldAccessSubclass _marker_1 = new WillNotOptimizeFieldAccessSubclass(); + _marker_1.x = 1; + int y = _marker_1.x; + + B b = new B(); + int _marker_2 = b.x_Getter_WithoutSideEffect; + + int _marker_3 = b.x_Getter_WithSideEffect; + + int _marker_4 = b.x_Getter_WithSomeExpression; + + A _marker_5 = b.A_Getter; + + int _marker_6 = b.X_Getter; + } +} + +main() { + Main.main(); +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testListExprOpt.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testListExprOpt.dart new file mode 100644 index 00000000000..cb22404acd1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testListExprOpt.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. + +class MyClass { + MyClass(x) { } + T operator [](int index) { + } + void operator []=(int index, int val) { + } +} + +class Main { + static void main() { + // base case (should be inlined). + List _list0_ = new List(10); + _list0_[0] = 1; + int lhs0 = _list0_[0]; + + // final case - only 'reads' can be inlined. + final List _list1_ = new List(10); + _list1_[0] = 1; + int lhs1 = _list1_[0]; + + // operator [] - cannot inline reads or writes. + MyClass _list2_ = new MyClass(2); + _list2_[0] = "foo"; + String lhs2 = _list2_[0]; + + // untyped. + var _list3_ = new List(2); + _list3_[0] = "foo"; + String lhs3 = _list3_[0]; + + // untyped. + List _list4_ = new List(2); + int i_0 = 0; + int j_0 = 0; + _list4_[i_0 + j_0] = "foo"; + String lhs4 = _list4_[i_0 - j_0]; + + // nested list (should be inlined). + List>> _list5_ = new List>>(10); + _list5_[0] = null; + var lhs5 = _list5_[0]; + _list5_[0][1] = null; + lhs5 = _list5_[0][1]; + _list5_[0][1][2] = 1; + lhs5 = _list5_[0][1][2]; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testListSubTypeExprOpt.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testListSubTypeExprOpt.dart new file mode 100644 index 00000000000..53a33b234d3 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testListSubTypeExprOpt.dart @@ -0,0 +1,39 @@ +// 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. + +class MyList implements List { + MyList() { } + T operator[](int index) { return null; } + void operator[]=(int index, T value) {} + int length; + void add(T value) {} + void addLast(T value) {} + void addAll(Collection collection) {} + void sort(int f(T, T)) {} + void copyFrom(List src, int srcStart, int dstStart, int count) {} + int indexOf(T element, int startIndex) { return null; } + int lastIndexOf(T element, int startIndex) { return null; } + void clear() {} + T removeLast() { return null; } + T last() { return null; } + void forEach(void f(T)) {} + Collection filter(bool f(T)) { return null; } + bool every(bool (T)) { return null; } + bool some(bool f(T)) { return null; } + bool isEmpty() { return null; } + Iterator iterator() { return null; } +} + +class Main { + static void main() { + // List subtype (we will do better in the future, for now, take the conservative path). + MyList _list0_ = new MyList(); + _list0_[0] = "foo"; + String lhs0 = _list0_[0]; + } +} + +main() { + Main.main(); +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testLiteralExpressions.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testLiteralExpressions.dart new file mode 100644 index 00000000000..3d1db4ce0f4 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testLiteralExpressions.dart @@ -0,0 +1,95 @@ +// 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. + +class A { + A() { } +} + +class B { + B() { } + bool operator ==(Object other) { + return this === other; + } +} + +class Main { + static void main() { + int _marker_0 = 1 + 1; + int _marker_1 = 1 - 1; + int _marker_2 = 1 * 1; + double _marker_3 = 1 / 1; + int _marker_4 = 1 % 1; + + bool _marker_5 = 1 > 1; + bool _marker_6 = 1 < 1; + bool _marker_7 = 1 >= 1; + bool _marker_8 = 1 <= 1; + + bool _marker_9 = 1 == 1; + bool _marker_10 = 1 != 1; + + bool _marker_11 = true == false; + bool _marker_12 = true != false; + + double _marker_13 = 1.0 ~/ 2.0; + + int _marker_14 = 1 | 1; + int _marker_15 = 1 & 1; + int _marker_16 = 1 << 1; + int _marker_17 = 1 >> 1; + + bool _marker_18 = true || false; + bool _marker_19 = true && false; + + A a = new A(); + B b = new B(); + String str = "foo"; + int i = 0; + + bool _marker_20 = i == 0; + bool _marker_21 = i != 0; + bool _marker_22 = str == "a"; + bool _marker_23 = str != "a"; + bool _marker_24 = a == b; + bool _marker_25 = a != b; + bool _marker_26 = b == a; + bool _marker_27 = b != a; + bool _marker_28 = a == null; + bool _marker_29 = a != null; + bool _marker_30 = null == a; + bool _marker_31 = null != a; + bool _marker_32 = b == null; + bool _marker_33 = b != null; + bool _marker_34 = null == b; + bool _marker_35 = null != b; + bool _marker_36 = i === 0; + bool _marker_37 = i !== 0; + bool _marker_38 = str === "a"; + bool _marker_39 = str !== "a"; + bool _marker_40 = a === b; + bool _marker_41 = a !== b; + bool _marker_42 = b === a; + bool _marker_43 = b !== a; + bool _marker_44 = a === null; + bool _marker_45 = a !== null; + bool _marker_46 = null === a; + bool _marker_47 = null !== a; + bool _marker_48 = b === null; + bool _marker_49 = b !== null; + bool _marker_50 = null === b; + bool _marker_51 = null !== b; + bool _marker_52 = str === null; + bool _marker_53 = str !== null; + bool _marker_54 = null === str; + bool _marker_55 = null !== str; + bool _marker_56 = null == null; + bool _marker_57 = null === null; + bool _marker_58 = false == null; + bool _marker_59 = false === null; + bool _marker_60 = str == null; + bool _marker_61 = str != null; + bool _marker_62 = null == str; + bool _marker_63 = null != str; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testRuntimeTypes.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testRuntimeTypes.dart new file mode 100644 index 00000000000..8059b321972 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testRuntimeTypes.dart @@ -0,0 +1,41 @@ +// 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. + +class B { + const B(); +} + +class C { + const C(); +} + +class D extends C { + factory D() { + return new C(); + } +} + +class Main { + + static void main() { + var a = 0; + var _marker_0 = 1; + var _marker_B1 = const B(); + var _marker_B2 = new B(); + var _marker_C1 = const C(); + var _marker_C2 = const C(); + var _marker_C3 = new C(); + var _marker_C4 = new C(); + var _marker_D1 = new D(); + // var _marker_D2 = new D(); // fails in resolver: wrong number of type args + + a = _marker_B1 is B; + a = _marker_C1 is C; + a = _marker_C2 is C; + a = _marker_C4 is C; + a = _marker_C4 is Object; + a = _marker_D1 is D; + // a = _marker_D2 is D; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/backend/js/testUnaryDecIncExprOpt.dart b/compiler/javatests/com/google/dart/compiler/backend/js/testUnaryDecIncExprOpt.dart new file mode 100644 index 00000000000..b493493479b --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/backend/js/testUnaryDecIncExprOpt.dart @@ -0,0 +1,70 @@ +// 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. + +class Main { + static void main(int _marker_3) { + int _marker_0 = 0; + + // Ensure that ++_marker_0 remains ++_marker_0 on simple assignment + int i = ++_marker_0; + + // Ensure that _marker_1++ remains _marker_1++ when used in a for loop + for (int _marker_1 = 0; _marker_1 < 10; _marker_1++) { + } + + // Ensure that --_marker_0 remains --_marker_0 on simple assignment + int j = --_marker_0; + + // Ensure that _marker_2-- remains _marker_2-- when used in a for loop + for (int _marker_2 = 10; _marker_2 >= 0; _marker_2--) { + } + + // Ensure that parameter _marker_3 remains as _marker_3++ + _marker_3++; + + // Ensure that parameter _marker_3 remains as --_marker_3 + --_marker_3; + + // Ensure binary op is inlined (variable). + int _marker_4; + i = ~_marker_4; + + // Ensure binary op is inlined (parameter). + i = ~_marker_3; + + // Ensure binary op is inlined (field). + A a = new A(); + i = ~a.field_0; + + // Ensure untyped operand is not inlined + var foo; + int _marker_5; + _marker_5 = ~foo; + + // Ensure binary op is not inlined (abstract field). + A aa = new B(); + i = ++aa.field_1; + + // Ensure binary op is not inlined (abstract field). + A aaa = new B(); + i = ++aaa.field_2; + } +} + +class A { + A() { } + int field_0; + int field_1; + int field_2; +} + +class B extends A { + B() : super() { } + int get field_1() { } + set field_2(x) { } +} + +main() { + Main.main(0); +} diff --git a/compiler/javatests/com/google/dart/compiler/common/ApplicationSourceFileTest.dart b/compiler/javatests/com/google/dart/compiler/common/ApplicationSourceFileTest.dart new file mode 100644 index 00000000000..e392f5dcb2b --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/ApplicationSourceFileTest.dart @@ -0,0 +1,8 @@ +// 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. + +#import("somedir/somelib.dart"); +#source("MyFirstApp.dart"); +#source("AnotherSource2.dart"); +#source("subdir2/Source3x.dart"); diff --git a/compiler/javatests/com/google/dart/compiler/common/CommonTests.java b/compiler/javatests/com/google/dart/compiler/common/CommonTests.java new file mode 100644 index 00000000000..4a490cace9f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/CommonTests.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.common; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class CommonTests extends TestSetup { + + public CommonTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart common test suite."); + + suite.addTestSuite(GenerateSourceMapTest.class); + suite.addTestSuite(LibrarySourceFileTest.class); + suite.addTestSuite(NameTest.class); + suite.addTestSuite(NameFactoryTest.class); + return new CommonTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/common/GenerateSourceMapTest.java b/compiler/javatests/com/google/dart/compiler/common/GenerateSourceMapTest.java new file mode 100644 index 00000000000..80dfa51cbda --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/GenerateSourceMapTest.java @@ -0,0 +1,308 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.CharStreams; +import com.google.dart.compiler.Backend; +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartArtifactProvider; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.MockArtifactProvider; +import com.google.dart.compiler.backend.dart.DartBackend; +import com.google.dart.compiler.backend.js.ClosureJsBackend; +import com.google.dart.compiler.backend.js.JavascriptBackend; +import com.google.debugging.sourcemap.FilePosition; +import com.google.debugging.sourcemap.SourceMapConsumerFactory; +import com.google.debugging.sourcemap.SourceMapParseException; +import com.google.debugging.sourcemap.SourceMapSection; +import com.google.debugging.sourcemap.SourceMapping; +import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; +import java.util.Map; + +/** + * Tests for {@link GenerateSourceMap}. + * + * @author jschorr@google.com (Joseph Schorr) + * @author johnlenz@google.com (John Lenz) + */ +public class GenerateSourceMapTest extends CompilerTestCase { + + // TODO(johnlenz): support detail levels + + enum ResultType { + JS, + CLOSURE_JS, + DART + } + + @Override + public void setUp() { + } + + // TODO(johnlenz): fix this + public void disable_testClassMapping() throws Exception { + compileAndCheck("class __CLASS__ { }", "__CLASS__"); + } + + public void testMethodMapping() throws Exception { + compileAndCheck("class myClass { static __MEMBER__() {} }", "myClass"); + } + + // TODO(johnlenz): fix this + public void disable_testFieldMapping1() throws Exception { + compileAndCheck("class myClass { const int __FIELD__; }", "myClass"); + } + + // TODO(johnlenz): fails + public void disable_testFieldMapping2() throws Exception { + compileAndCheck("class myClass { static const int __FIELD__ = 1; }", "myClass"); + } + + public void testMethodParamMapping() throws Exception { + compileAndCheck("class myClass { static member(__PARAM1__, __PARAM2__) {} }", "myClass"); + } + + public void testNamedFunctionMapping() throws Exception { + compileAndCheck( + "class c {\n" + + " void member() {\n" + + " __FN__(__PARAM1__, __PARAM2__) {}\n" + + " }" + + "}", "c"); + } + + public void testLocalMapping() throws Exception { + compileAndCheck( + "class c {\n" + + " void member() {\n" + + " var __VAR__ = '__STR__'; \n" + + " }" + + "}", "c"); + } + + public void testLocalInClosureMapping() throws Exception { + compileAndCheck( + "class c {\n" + + " void member() {\n" + + " fn(p1, p2) {\n" + + " var __VAR__ = '__STR__'; \n" + + " }\n" + + " }" + + "}", "c"); + } + + public void testWriteMetaMap() throws IOException { + StringWriter out = new StringWriter(); + String name = "./app.js"; + List appSections = Lists.newArrayList( + SourceMapSection.forURL("src1", 0, 0), + SourceMapSection.forURL("src2", 100, 10), + SourceMapSection.forURL("src3", 150, 5)); + + new GenerateSourceMap().appendIndexMapTo(out, name, appSections); + + assertEquals( + "{\n" + + "\"version\":3,\n" + + "\"file\":\"./app.js\",\n" + + "\"sections\":[\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":0,\n" + + "\"column\":0\n" + + "},\n" + + "\"url\":\"src1\"\n" + + "},\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":100,\n" + + "\"column\":10\n" + + "},\n" + + "\"url\":\"src2\"\n" + + "},\n" + + "{\n" + + "\"offset\":{\n" + + "\"line\":150,\n" + + "\"column\":5\n" + + "},\n" + + "\"url\":\"src3\"\n" + + "}\n" + + "]\n" + + "}\n", + out.toString()); + } + + + private static class RunResult { + final String generatedSource; + final String sourceMapFileContent; + + RunResult(String source, String sourceMap) { + this.generatedSource = source; + this.sourceMapFileContent = sourceMap; + } + } + + private static class Token { + String tokenName; + FilePosition position; + } + + /** + * Finds the all the __XX__ tokens in the given Javascript + * string. + */ + private Map findTokens(String js) { + Map tokens = Maps.newLinkedHashMap(); + + int currentLine = 0; + int positionOffset = 0; + + for (int i = 0; i < js.length(); ++i) { + char current = js.charAt(i); + + if (current == '\n') { + positionOffset = i + 1; + currentLine++; + continue; + } + + if (current == '_' && (i < js.length() - 5)) { + // Check for the _ token. + if (js.charAt(i + 1) != '_') { + continue; + } + + // Loop until we have another _ token. + String tokenName = ""; + + int j = i + 2; + for (; j < js.length(); ++j) { + if (js.charAt(j) == '_') { + break; + } + + tokenName += js.charAt(j); + } + + if (tokenName.length() > 0) { + Token token = new Token(); + token.tokenName = tokenName; + int currentPosition = i - positionOffset; + token.position = new FilePosition(currentLine, currentPosition); + + // Only use the first instance of a token (parameters can be repeated in trampolines). + if (!tokens.containsKey(tokenName)) { + tokens.put(tokenName, token); + } + } + + i = j; + } + } + + return tokens; + } + + private void compileAndCheck(String dartSource, String part) throws Exception { + compileAndCheck(dartSource, "", ResultType.DART); + compileAndCheck(dartSource, part, ResultType.JS); + // TODO(johnlenz): Use the application map instead of the per file map + // compileAndCheck(dartSource, null, ResultType.CLOSURE_JS); + } + + private void compileAndCheck(String dartSource, String part, ResultType type) throws Exception { + RunResult result = getCompileResult("testcode", dartSource, part, type); + + // Find all instances of the __XXX__ pattern in the original + // source code. + Map originalTokens = findTokens(dartSource); + + // Find all instances of the __XXX__ pattern in the generated + // source code. + Map resultTokens = findTokens(result.generatedSource); + + // Ensure that the generated instances match via the source map + // to the original source code. + + // Ensure the token counts match. + assertEquals(originalTokens.size(), resultTokens.size()); + + SourceMapping sourcemap; + try { + sourcemap = SourceMapConsumerFactory.parse(result.sourceMapFileContent); + } catch (SourceMapParseException e) { + throw new RuntimeException("unexpected exception", e); + } + + // Map the tokens from the generated source back to the + // input source and ensure that the map is correct. + for (Token token : resultTokens.values()) { + OriginalMapping mapping = sourcemap.getMappingForLine( + token.position.getLine() + 1, + token.position.getColumn() + 1); + + assertNotNull(mapping); + + // Find the associated token in the input source. + Token inputToken = originalTokens.get(token.tokenName); + assertNotNull(inputToken); + + // Ensure that the map correctly points to the token (we add 1 + // to normalize versus the Rhino line number indexing scheme). + assertEquals(mapping.getLineNumber(), + inputToken.position.getLine() + 1); + + // Ensure that if the token name does not being with an 'STR' (meaning a + // string) it has an original name. + String originalName = mapping.getIdentifier(); + if (!inputToken.tokenName.startsWith("STR")) { + assertTrue(!originalName.isEmpty()); + } + + // Ensure that if the mapping has a name, it matches the token. + if (!originalName.isEmpty()) { + assertEquals("__" + inputToken.tokenName + "__", originalName); + } + } + } + + protected RunResult getResultForCompile(String fileName, String sourceCode, String part, + Backend backend, String outExt, String mapExt) throws Exception { + DartArtifactProvider provider = new MockArtifactProvider(); + DartSource dart = compileSingleUnit( + fileName, sourceCode, provider, backend); + + StringBuilder src = new StringBuilder(); + StringBuilder map = new StringBuilder(); + CharStreams.copy(provider.getArtifactReader(dart, part, outExt), src); + CharStreams.copy(provider.getArtifactReader(dart, part, mapExt), map); + + return new RunResult(src.toString(), map.toString()); + } + + private RunResult getCompileResult( + String filename, String sourceCode, String part, ResultType type) + throws Exception { + switch (type) { + case DART: + return getResultForCompile(filename, sourceCode, part, new DartBackend(), + DartBackend.EXTENSION_DART, DartBackend.EXTENSION_DART_SRC_MAP); + case JS: + return getResultForCompile(filename, sourceCode, part, new JavascriptBackend(), + JavascriptBackend.EXTENSION_JS, JavascriptBackend.EXTENSION_JS_SRC_MAP); + case CLOSURE_JS: + return getResultForCompile(filename, sourceCode, part, new ClosureJsBackend(), + ClosureJsBackend.EXTENSION_JS, ClosureJsBackend.EXTENSION_JS_SRC_MAP); + } + throw new IllegalStateException(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.dart b/compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.dart new file mode 100644 index 00000000000..7534d1b5cb7 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.dart @@ -0,0 +1,8 @@ +// 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. + +#library("LibrarySourceFileTest"); +#source("OneSourceFile.dart"); +#source("Source2.dart"); +#source("subdir/Source3.dart"); diff --git a/compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.java b/compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.java new file mode 100644 index 00000000000..5832c50e154 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/LibrarySourceFileTest.java @@ -0,0 +1,121 @@ +// 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. + +package com.google.dart.compiler.common; + +import com.google.dart.compiler.AbstractSourceFileTest; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.UrlLibrarySource; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; +import com.google.dart.compiler.parser.DartParser; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +/** + * Tests {@link LibrarySource} + */ +public class LibrarySourceFileTest extends AbstractSourceFileTest { + private LibraryUnit libUnit; + + public void testGetImports() throws IOException { + LibraryUnit libUnit = getLibraryUnit("LibrarySourceFileTest.dart"); + Iterable imports = libUnit.getImportPaths(); + assertFalse(imports.iterator().hasNext()); + } + + public void testGetAppImports() throws IOException { + LibraryUnit appUnit = getLibraryUnit("ApplicationSourceFileTest.dart"); + Iterable imports = appUnit.getImportPaths(); + Iterator iter = imports.iterator(); + assertEquals("somedir/somelib.dart", iter.next().getText()); + assertFalse(iter.hasNext()); + } + + public void testGetSources() throws IOException { + LibraryUnit libUnit = getLibraryUnit("LibrarySourceFileTest.dart"); + Iterable sources = libUnit.getSourcePaths(); + Iterator iter = sources.iterator(); + assertEquals("OneSourceFile.dart", iter.next().getText()); + assertEquals("Source2.dart", iter.next().getText()); + assertEquals("subdir/Source3.dart", iter.next().getText()); + assertEquals(libUnit.getSelfSourcePath(), iter.next()); + assertFalse(iter.hasNext()); + } + + public void testGetAppSources() throws IOException { + LibraryUnit appUnit = getLibraryUnit("ApplicationSourceFileTest.dart"); + Iterable paths = appUnit.getSourcePaths(); + Iterator iter = paths.iterator(); + assertEquals("MyFirstApp.dart", iter.next().getText()); + assertEquals("AnotherSource2.dart", iter.next().getText()); + assertEquals("subdir2/Source3x.dart", iter.next().getText()); + assertEquals(libUnit.getSelfSourcePath(), iter.next()); + assertFalse(iter.hasNext()); + } + + public void testSpacesInPaths() throws IOException { + String sourceName = "Source with Spaces.dart"; + LibraryUnit appUnit = getLibraryUnit("Library Source With Spaces.dart", + "library {\n" + + " import = [\n" + + " ]\n" + + " source = [\n" + + "'Source with Spaces.dart'\n" + + "]\n" + + "}"); + LibrarySource source = appUnit.getSource(); + assertNotNull(source.getSourceFor(sourceName)); + } + + /** + * Answer the {@link LibraryUnit} on which tests are performed + * + * @param filePath the path to the file relative to the test class + * @return the library unit (not null) + */ + protected LibraryUnit getLibraryUnit(String filePath) throws IOException { + File tempFile = createTempFile(filePath); + return getLibraryUnit(tempFile); + } + + protected LibraryUnit getLibraryUnit(String filePath, String source) throws IOException { + File tempFile = createTempFile(filePath, source); + return getLibraryUnit(tempFile); + } + + protected LibraryUnit getLibraryUnit(File file) { + if (libUnit == null) { + UrlLibrarySource lib = new UrlLibrarySource(file); + + DartCompilerListener listener = new DartCompilerListener() { + @Override + public void compilationError(DartCompilationError event) { + throw new RuntimeException(event.getMessage()); + } + + @Override + public void compilationWarning(DartCompilationError event) { + // Ignore warnings when testing. + } + + @Override + public void typeError(DartCompilationError event) { + throw new RuntimeException(event.getMessage()); + } + }; + + try { + libUnit = DartParser.getSourceParser(lib, listener).preProcessLibraryDirectives(lib); + } catch (IOException ioEx) { + libUnit = null; + } + } + return libUnit; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/common/NameFactoryTest.java b/compiler/javatests/com/google/dart/compiler/common/NameFactoryTest.java new file mode 100644 index 00000000000..69239b71cde --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/NameFactoryTest.java @@ -0,0 +1,127 @@ +// 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. + +package com.google.dart.compiler.common; + +import java.lang.ref.WeakReference; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +public class NameFactoryTest extends NameTestCase { + private static final int NUM_RETRIES = 50; + private static final int NUM_THREADS = 10; + + private NameFactory factory; + + @Override + protected void tearDown() { + factory = null; + } + + /** + * I'm told this can be made to work, but it's flaky for me. + */ + public void disabledTestGc() { + factory = new NameFactory(); + insertDefaultNames(); + System.gc(); + System.gc(); + System.gc(); + System.gc(); + factory.cleanUp(); + assertEquals(0, factory.numEntries()); + } + + public void testContention() throws Throwable { + // Run many times to try to trip a concurreny problem. + for (int r = 0; r < NUM_RETRIES; ++r) { + factory = new NameFactory(); + Name[] names = new Name[NUM_INPUTS]; + for (int i = 0; i < NUM_INPUTS; ++i) { + assertEquals(i, factory.numEntries()); + names[i] = testContentionFor(INPUTS[i]); + assertEquals(i + 1, factory.numEntries()); + } + } + } + + public void testCreation() { + factory = new NameFactory(); + Name[] names = insertDefaultNames(); + // Check that the same names come back. + for (int i = 0; i < NUM_INPUTS; ++i) { + assertEquals(NUM_INPUTS, factory.numEntries()); + Name newName = factory.of(INPUTS[i]); + assertEquals(names[i], newName); + assertSame(names[i], newName); + assertEquals(NUM_INPUTS, factory.numEntries()); + } + } + + public void testRemoval() { + factory = new NameFactory(); + Name[] names = insertDefaultNames(); + // Simulate GC removal. + for (int i = NUM_INPUTS; i > 0; --i) { + assertEquals(i, factory.numEntries()); + WeakReference ref = factory.getRefFor(names[NUM_INPUTS - i]); + ref.clear(); + ref.enqueue(); + factory.cleanUp(); + assertEquals(i - 1, factory.numEntries()); + } + // Check that different names come back. + for (int i = 0; i < NUM_INPUTS; ++i) { + assertEquals(i, factory.numEntries()); + Name newName = factory.of(INPUTS[i]); + assertNotSame(names[i], newName); + assertNotEquals(names[i], newName); + assertEquals(i + 1, factory.numEntries()); + } + } + + private Name[] insertDefaultNames() { + Name[] names = new Name[NUM_INPUTS]; + for (int i = 0; i < NUM_INPUTS; ++i) { + assertEquals(i, factory.numEntries()); + names[i] = factory.of(INPUTS[i]); + assertEquals(i + 1, factory.numEntries()); + } + return names; + } + + private Name testContentionFor(final char[] data) throws Throwable { + final CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS); + final CountDownLatch countDown = new CountDownLatch(NUM_THREADS); + final Object[] results = new Name[NUM_THREADS]; + for (int i = 0; i < NUM_THREADS; ++i) { + final int id = i; + new Thread() { + public void run() { + try { + barrier.await(); + results[id] = factory.of(data); + } catch (Throwable e) { + results[id] = e; + } finally { + countDown.countDown(); + } + } + }.start(); + } + countDown.await(); + Name expected = factory.of(data); + for (int i = 0; i < NUM_THREADS; ++i) { + Object result = results[i]; + if (result == null) { + throw new NullPointerException("Missing results from " + i); + } else if (results[i] instanceof Throwable) { + throw (Throwable) results[i]; + } else { + assertSame(expected, results[i]); + } + } + return expected; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/common/NameTest.java b/compiler/javatests/com/google/dart/compiler/common/NameTest.java new file mode 100644 index 00000000000..90f226d7020 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/NameTest.java @@ -0,0 +1,145 @@ +// 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. + +package com.google.dart.compiler.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; + +/** + * Tests for {@link Name}. + */ +public class NameTest extends NameTestCase { + private static final Name AM = Name.of(_AM); + private static final Name EMPTY = Name.of(_EMPTY); + private static final Name HIGHCHARS = Name.of(_HIGHCHARS); + private static final Name NAME = Name.of(_NAME); + + private static final Name[] NAMES = {AM, EMPTY, HIGHCHARS, NAME}; + + public void testEquals() { + for (int i = 0; i < NUM_INPUTS; ++i) { + for (int j = 0; j < NUM_INPUTS; ++j) { + if (i == j) { + assertEquals(NAMES[j], NAMES[i]); + assertEquals(Name.of(INPUTS[j]), NAMES[i]); + } else { + assertNotEquals(NAMES[j], NAMES[i]); + assertNotEquals(Name.of(INPUTS[j]), NAMES[i]); + } + assertNotEquals(null, NAMES[i]); + assertNotEquals(NAMES[i], null); + assertNotEquals(NAMES[i].toString(), NAMES[i]); + assertNotEquals(NAMES[i], NAMES[i].toString()); + } + } + } + + public void testFailureModes() { + try { + Name.of(null); + fail("Expected NullPointerException"); + } catch (NullPointerException expected) { + } + try { + Name.of(_NAME, -1, 3); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException expected) { + } + try { + Name.of(_NAME, 2, 3); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException expected) { + } + } + + public void testHashCode() { + for (int i = 0; i < NUM_INPUTS; ++i) { + assertEquals(computeHashCode(INPUTS[i]), NAMES[i].hashCode()); + } + } + + public void testIdentity() { + for (int i = 0; i < NUM_INPUTS; ++i) { + for (int j = 0; j < NUM_INPUTS; ++j) { + if (i == j) { + assertSame(NAMES[j], NAMES[i]); + assertSame(Name.of(INPUTS[j]), NAMES[i]); + } else { + assertNotSame(NAMES[j], NAMES[i]); + assertNotSame(Name.of(INPUTS[j]), NAMES[i]); + } + assertNotSame(null, NAMES[i]); + assertNotSame(NAMES[i], null); + assertNotSame(NAMES[i].toString(), NAMES[i]); + assertNotSame(NAMES[i], NAMES[i].toString()); + } + } + } + + public void testSerialization() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + for (int i = 0; i < NUM_INPUTS; ++i) { + oos.writeObject(NAMES[i]); + } + oos.close(); + + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream( + baos.toByteArray())); + for (int i = 0; i < NUM_INPUTS; ++i) { + assertSame(NAMES[i], ois.readObject()); + } + } + + public void testSubsequence() { + assertEquals(Name.of("name".toCharArray(), 1, 2), AM); + assertSame(Name.of("name".toCharArray(), 1, 2), AM); + } + + public void testToString() { + for (int i = 0; i < NUM_INPUTS; ++i) { + assertEquals(String.valueOf(INPUTS[i]), NAMES[i].toString()); + } + } + + public void testWriteTo() throws Exception { + for (int i = 0; i < NUM_INPUTS; ++i) { + assertEquals(String.valueOf(INPUTS[i]), writeToOutputStream(NAMES[i])); + assertEquals(String.valueOf(INPUTS[i]), writeToPrintStream(NAMES[i])); + assertEquals(String.valueOf(INPUTS[i]), writeToWriter(NAMES[i])); + } + } + + private int computeHashCode(char[] data) { + return Name.computeHashCode(data, 0, data.length); + } + + private String writeToOutputStream(Name name) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + name.writeBytesTo(baos); + return new String(baos.toByteArray(), Name.CHARSET); + } + + private String writeToPrintStream(Name name) throws UnsupportedEncodingException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos, false, Name.CHARSET.name()); + name.writeCharsTo(ps); + ps.close(); + assertFalse(ps.checkError()); + return new String(baos.toByteArray(), Name.CHARSET); + } + + private String writeToWriter(Name name) throws IOException { + StringWriter writer = new StringWriter(); + name.writeCharsTo(writer); + return writer.toString(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/common/NameTestCase.java b/compiler/javatests/com/google/dart/compiler/common/NameTestCase.java new file mode 100644 index 00000000000..a0da65bdc6d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/common/NameTestCase.java @@ -0,0 +1,37 @@ +// 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. + +package com.google.dart.compiler.common; + +import junit.framework.TestCase; + +abstract class NameTestCase extends TestCase { + protected static final char[] _AM = "am".toCharArray(); + protected static char[] _EMPTY = "".toCharArray(); + + /** + * Google favorite: "Îñţérñåţîöñåļîžåţîờñ". Using modified + * UTF-8 encoding, this 20 character string is 40 bytes long. So it must be + * shrunk by appending a byte array. This string contains characters that + * expand to 1, 2, and 3 bytes, thus covering all the cases in modified UTF-8. + */ + protected static char[] _HIGHCHARS = ("\u00ce\u00f1\u0163\u00e9r" + + "\u00f1\u00e5\u0163\u00ee\u00f6\u00f1\u00e5\u013c\u00ee\u017e" + + "\u00e5\u0163\u00ee\u1edd\u00f1").toCharArray(); + + protected static char[] _NAME = "name".toCharArray(); + + protected static final char[][] INPUTS = {_AM, _EMPTY, _HIGHCHARS, _NAME}; + protected static final int NUM_INPUTS = INPUTS.length; + + protected static void assertNotEquals(Object expected, Object actual) { + if ((expected == null) != (actual == null)) { + return; + } + if (expected != null && !expected.equals(actual)) { + return; + } + fail("expected not equals:<" + expected + "> was:<" + actual + ">"); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/BasicOptTest.java b/compiler/javatests/com/google/dart/compiler/end2end/BasicOptTest.java new file mode 100644 index 00000000000..748b6edd315 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/BasicOptTest.java @@ -0,0 +1,17 @@ +// 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. + +package com.google.dart.compiler.end2end; + +import java.util.List; + +/** + * Optimized version of {@link BasicTest}. + */ +public class BasicOptTest extends BasicTest { + + protected void runTest(List srcs) throws Exception { + runTest(srcs, OptimizationLevel.APP); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/BasicTest.dart b/compiler/javatests/com/google/dart/compiler/end2end/BasicTest.dart new file mode 100644 index 00000000000..1b483b62fc9 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/BasicTest.dart @@ -0,0 +1,6 @@ +// 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. + +main() { +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/BasicTest.java b/compiler/javatests/com/google/dart/compiler/end2end/BasicTest.java new file mode 100644 index 00000000000..75045781480 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/BasicTest.java @@ -0,0 +1,19 @@ +// 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. + +package com.google.dart.compiler.end2end; + +/** + * Basic end-to-end tests, covering expressions, statements, and so forth. + */ +public class BasicTest extends End2EndTestCase { + + public void testNative() throws Exception { + runTest("NativeTestLib.dart"); + } + + public void testRedirectedConstructors() throws Exception { + runTest("RedirectedConstructorTest.dart"); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/BasicTest_app.dart b/compiler/javatests/com/google/dart/compiler/end2end/BasicTest_app.dart new file mode 100644 index 00000000000..48fbe435591 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/BasicTest_app.dart @@ -0,0 +1,5 @@ +// 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. + +#source("BasicTest.dart"); diff --git a/compiler/javatests/com/google/dart/compiler/end2end/End2EndOptTests.java b/compiler/javatests/com/google/dart/compiler/end2end/End2EndOptTests.java new file mode 100644 index 00000000000..9b365374db4 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/End2EndOptTests.java @@ -0,0 +1,24 @@ +// 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. + +package com.google.dart.compiler.end2end; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class End2EndOptTests extends TestSetup { + + public End2EndOptTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart end-to-end test suite (optimized version)."); + + suite.addTestSuite(BasicOptTest.class); + suite.addTestSuite(MainMethodOptTest.class); + return new End2EndOptTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/End2EndTestCase.java b/compiler/javatests/com/google/dart/compiler/end2end/End2EndTestCase.java new file mode 100644 index 00000000000..682e19acdbf --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/End2EndTestCase.java @@ -0,0 +1,122 @@ +// 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. + +package com.google.dart.compiler.end2end; + +import com.google.dart.compiler.CompilerConfiguration; +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartCompilerListenerTest; +import com.google.dart.compiler.DartLibrarySourceTest; +import com.google.dart.compiler.DartSourceTest; +import com.google.dart.compiler.DefaultCompilerConfiguration; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.MockLibrarySource; +import com.google.dart.compiler.backend.js.ClosureJsBackend; +import com.google.dart.compiler.backend.js.JavascriptBackend; +import com.google.dart.runner.DartRunner; +import com.google.dart.runner.RunnerError; +import com.google.dart.runner.RunnerFlag; + +import org.mozilla.javascript.RhinoException; + +import java.util.EnumSet; +import java.util.List; + +/** + * Abstract base for end-to-end tests. Tests are entirely Dart code that are compiled and run + * within Rhino or V8. + */ +public abstract class End2EndTestCase extends CompilerTestCase { + + enum OptimizationLevel { + RAW, + APP + } + + /** + * Creates an ApplicationSource that should be compiled and executed. + * + * @param srcs paths to the input. + * @param mainClass the name of the main class to execute. + * @return an ApplicationSource suitable to be compiled and executed. + */ + protected LibrarySource createApplication(List srcs) { + MockLibrarySource app = new MockLibrarySource(); + for (String src : srcs) { + DartSourceTest dartSource = new DartSourceTest(getClass(), src, app); + app.addSource(dartSource); + } + return app; + } + + /** + * Creates a compiler configuration appropriate for the optimization level. + */ + CompilerConfiguration getCompilerConfiguration(OptimizationLevel opLevel) { + switch (opLevel) { + case RAW: + return new DefaultCompilerConfiguration(new JavascriptBackend()); + case APP: + return new DefaultCompilerConfiguration( + new ClosureJsBackend()); + } + throw new IllegalStateException("unexpected opLevel"); + } + + /** + * Runs an end-to-end Dart test for the given compilation unit. + */ + protected void runTest(LibrarySource app, OptimizationLevel opLevel, + DartCompilerListener listener) { + try { + CompilerConfiguration config = getCompilerConfiguration(opLevel); + DartRunner.compileAndRunApp(app, + EnumSet.of(RunnerFlag.VERBOSE), + config, + listener, + new String[0], + System.out, + System.err); + } catch (RhinoException e) { + // TODO(jgw): This is a hack to dump the translated source when something goes wrong. It can + // be removed as soon as we have a source map we can use to provide source-level errors. + + // TODO(floitsch): clean up the exception handling. Who prints what, and when? + + StringBuffer msg = new StringBuffer(); + msg.append("optimization level: " + opLevel.toString() + "\n"); + msg.append(e.sourceName()); + msg.append(" (" + e.lineNumber() + ":" + e.columnNumber() + ")"); + msg.append(" : " + e.details()); + fail(msg.toString()); + } catch (RunnerError e) { + fail(e.getLocalizedMessage()); + } + } + + /** + * Runs an end-to-end Dart test for the given compilation unit. + */ + protected void runTest(LibrarySource app, OptimizationLevel opLevel) { + DartCompilerListener listener = new DartCompilerListenerTest(null); + runTest(app, opLevel, listener); + } + + /** + * Runs an end-to-end Dart test for the given compilation unit. + * + * @param srcs path to the Dart source files containing the test + * @param opLevel The type of optimization to perform on the test code. + */ + protected void runTest( + List srcs, OptimizationLevel opLevel) + throws SecurityException { + runTest(createApplication(srcs), opLevel); + } + + protected void runTest(String appSrc) throws Exception { + runTest(new DartLibrarySourceTest(getClass(), appSrc), OptimizationLevel.RAW); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/End2EndTests.java b/compiler/javatests/com/google/dart/compiler/end2end/End2EndTests.java new file mode 100644 index 00000000000..08f9e80d5e0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/End2EndTests.java @@ -0,0 +1,27 @@ +// 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. + +package com.google.dart.compiler.end2end; + +import com.google.dart.compiler.end2end.inc.IncrementalCompilationTest; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class End2EndTests extends TestSetup { + + public End2EndTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart end-to-end test suite."); + + suite.addTestSuite(BasicTest.class); + suite.addTestSuite(MainMethodTest.class); + suite.addTestSuite(IncrementalCompilationTest.class); + return new End2EndTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/MainMethodOptTest.java b/compiler/javatests/com/google/dart/compiler/end2end/MainMethodOptTest.java new file mode 100644 index 00000000000..c395dfa3b52 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/MainMethodOptTest.java @@ -0,0 +1,17 @@ +// 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. + +package com.google.dart.compiler.end2end; + +import java.util.List; + +/** + * Optimized version of {@link MainMethodTest}. + */ +public class MainMethodOptTest extends MainMethodTest { + + protected void runTest(List srcs) throws Exception { + runTest(srcs, OptimizationLevel.APP); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/MainMethodTest.java b/compiler/javatests/com/google/dart/compiler/end2end/MainMethodTest.java new file mode 100644 index 00000000000..bb10e85cac1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/MainMethodTest.java @@ -0,0 +1,130 @@ +// 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. + +package com.google.dart.compiler.end2end; + +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DefaultLibrarySource; +import com.google.dart.compiler.LibrarySource; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +/** + * Augment the {@link BasicTests} and {@link End2EndTestCase} by driving the + * {@link DartCompiler#main(String[])} method. + */ +public class MainMethodTest extends End2EndTestCase { + private File tempDir; + + /** + * Sanity check that the {@link DartCompiler#main(String[])} method will + * compile a single dart file with no lib or app file. + */ + public void testDartCompiler_main_dartFileOnly() throws Exception { + runTest("BasicTest.dart"); + } + + @Override + protected LibrarySource createApplication(List srcs) { + assert srcs.size() == 1; + String path = srcs.get(0); + try { + File dartFile = writeTempFile("BasicTest.dart", readResource(path)); + return new DefaultLibrarySource(dartFile, null); + } catch (IOException e) { + String message = "Failed to compile " + path; + System.err.println(message); + e.printStackTrace(); + fail(message + "\n" + e.getMessage()); + return null; + } + } + + /** + * Read the content of a resource stored relative to this class + * + * @param fileName the path to the resource relative to this class + * @return the content + */ + private String readResource(String fileName) throws IOException { + return readStream(getClass().getResourceAsStream(fileName)); + } + + /** + * Read the content of the specified string and close the stream. + * + * @param stream the stream to read (not null) + * @return the content (not null) + */ + private String readStream(InputStream stream) throws IOException { + try { + InputStreamReader reader = new InputStreamReader(stream); + StringBuilder result = new StringBuilder(2000); + char[] buf = new char[100]; + while (true) { + int count = reader.read(buf); + if (count == -1) + break; + result.append(buf, 0, count); + } + return result.toString(); + } finally { + stream.close(); + } + } + + /** + * Write the specified source into a temporary file with the specified name in + * a temporary directory that will be cleaned up at the end of the test. + * + * @param fileName the name of the file to be written (not null) + * @param content the content to be written (not null) + * @return the file that was written (not null) + */ + private File writeTempFile(String fileName, String content) + throws IOException { + File file = new File(getTempDir(), fileName); + FileWriter writer = new FileWriter(file); + try { + writer.write(content); + } finally { + writer.close(); + } + return file; + } + + /** + * Answer a temporary directory for this test that is later cleaned up in + * {@link #tearDown()}. + */ + private File getTempDir() throws IOException { + if (tempDir == null) { + tempDir = File.createTempFile(getClass().getSimpleName(), null); + tempDir.delete(); + tempDir.mkdirs(); + } + return tempDir; + } + + /** + * Delete the temporary directory if it exists + */ + @Override + protected void tearDown() throws Exception { + if (tempDir != null) { + File[] allFiles = tempDir.listFiles(); + for (File file : allFiles) { + file.delete(); + } + tempDir.delete(); + tempDir = null; + } + super.tearDown(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.dart b/compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.dart new file mode 100644 index 00000000000..b57931f4c9a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.dart @@ -0,0 +1,221 @@ +// 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. +// Dart test program for testing named parameters. + +class A { + static Function sfn; + Function fn; + + factory A.make(int a, [int b, int c=3]) { + return new A(a, b, c); + } + + A(int a, [int b, int c=3]) { } + A.named_ctor(int a, [int b, int c=3]) { } + + static int static_method(int a, [int b, int c=3]) { + return (a + b) * c; + } + + static int _private_static_method(int a, [int b, int c=3]) { + return (a + b) * c; + } + + int instance_method(int x, [int y, int z=3]) { + return (x + y) * z; + } + + int _private_instance_method(int x, [int y, int z=3]) { + return (x + y) * z; + } +} + +int global_function(int x, int y, [int z]) { + return (x + y) * z; +} + +Function gfn; + +class C { + int i; + Function fn; + + C(this.i) {} +} + +testCtors() { + A a = new A(1, 2); + A a0 = new A(1, b:2); + A a1 = new A(1, c:3, b:2); + + A a2 = new A.make(1, 2); + A a3 = new A.make(1, 2, 3); + A a4 = new A.make(1, 2, c:3); + A a5 = new A.make(1, b:2, c:3); +} + +testCalls() { + A a = new A(1, 2); + + // global calls + Expect.equals(9, global_function(1, 2, 3)); + Expect.equals(9, global_function(1, 2, z:3)); + + // static calls + Expect.equals(9, A.static_method(1, 2)); + Expect.equals(9, A.static_method(1, 2, 3)); + Expect.equals(9, A.static_method(1, 2, c:3)); + Expect.equals(9, A.static_method(1, c:3, b:2)); + + // private static calls + Expect.equals(9, A._private_static_method(1, 2)); + Expect.equals(9, A._private_static_method(1, 2, 3)); + Expect.equals(9, A._private_static_method(1, 2, c:3)); + Expect.equals(9, A._private_static_method(1, c:3, b:2)); + + // instance calls + Expect.equals(9, a.instance_method(1, 2)); + Expect.equals(9, a.instance_method(1, 2, 3)); + Expect.equals(9, a.instance_method(1, z:3, y:2)); + + // private instance calls + Expect.equals(9, a._private_instance_method(1, 2)); + Expect.equals(9, a._private_instance_method(1, 2, 3)); + Expect.equals(9, a._private_instance_method(1, z:3, y:2)); +} + +testCallsThroughVars() { + A a = new A(1, 2); + + // bound global calls + Function fn0 = global_function; + Expect.equals(9, fn0(1, 2, 3)); + Expect.equals(9, fn0(1, 2, z:3)); + + // bound static calls + Function fn1 = A.static_method; + Expect.equals(9, fn1(1, 2)); + Expect.equals(9, fn1(1, 2, 3)); + Expect.equals(9, fn1(1, 2, c:3)); + Expect.equals(9, fn1(1, c:3, b:2)); + + // bound instance calls + Function fn2 = a.instance_method; + Expect.equals(9, fn2(1, 2)); + Expect.equals(9, fn2(1, 2, 3)); + Expect.equals(9, fn2(1, z:3, y:2)); + + // call to bound method through instance field + a.fn = global_function; + Expect.equals(9, a.fn(1, 2, 3)); + Expect.equals(9, a.fn(1, 2, z:3)); + + // call to bound method through static field + A.sfn = global_function; + Expect.equals(9, A.sfn(1, 2, 3)); + Expect.equals(9, A.sfn(1, 2, z:3)); + + // call to bound method through global field + gfn = global_function; + Expect.equals(9, gfn(1, 2, 3)); + Expect.equals(9, gfn(1, 2, z:3)); +} + +// --------------------------------------------------------------------------- +testClosures() { + // call to hoisted closure + var cfn = int foo(int x, int y, [int z]) { return (x + y) * z; }; + Expect.equals(9, cfn(1, 2, 3)); + Expect.equals(9, cfn(1, 2, z:3)); + + // call to local function + int lfn(int x, int y, [int z]) { return (x + y) * z; } + Expect.equals(9, lfn(1, 2, 3)); + Expect.equals(9, lfn(1, 2, z:3)); + + // fun case with local and this binding + C c = new C(20); + Function refc = () => c.i; + c.fn = refc; + Expect.equals(20, c.fn()); +} + +testMultipleClosureScopes() { + for (int x = 0; x < 1; ++x) { + int i = 6; + for (int y = 0; y < 1; ++y) { + int j = 9; + + var a = new List(1); + a[0] = () => i * j; + Expect.equals(54, a[0]()); + } + } +} + +// --------------------------------------------------------------------------- +class Sup { + Sup() { } + int foo() { return 54; } +} + +class Sub extends Sup { + Sub(): super() { } + int foo() { return 42; } + Function getSuperFoo() { return super.foo; } +} + +testSuperMethodGetter() { + var sup = new Sup(); + var sub = new Sub(); + Expect.equals(sup.foo(), sub.getSuperFoo()()); +} + +// --------------------------------------------------------------------------- +class HasGetter { + HasGetter() { } + var field; + get getter() { return field; } + method() { return field; } +} + +void testGetter() { + HasGetter a = new HasGetter(); + a.field = () => 42; + Expect.equals(42, a.getter()); + Expect.equals(42, (a.getter)()); + + a.field = () => 87; + Expect.equals(87, a.getter()); + Expect.equals(87, (a.getter)()); +} + +// --------------------------------------------------------------------------- +expectNSME(Function fn) { + try { + fn(); + Expect.fail("Expected NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } +} + +int takesNoArgs() => 42; +int takesOneArg(int x) => 42; +int takesOneNamedArg([int x]) => 42; + +testStaticNSM() { + expectNSME(() => takesNoArgs("I'm ignoring static errors!")); + expectNSME(() => takesOneArg()); + expectNSME(() => takesOneNamedArg(y:54)); +} + +// --------------------------------------------------------------------------- +main() { + testCtors(); + testCalls(); + testCallsThroughVars(); + testMultipleClosureScopes(); + testGetter(); + testStaticNSM(); +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.java b/compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.java new file mode 100644 index 00000000000..e6ccb0b5c63 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/NamedParameterTest.java @@ -0,0 +1,51 @@ +// Copyright 2011, the Dart project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.dart.compiler.end2end; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartCompilerListenerTest; +import com.google.dart.compiler.DartLibrarySourceTest; + +/** + * Tests for lots of cases involving named parameters. + */ +public class NamedParameterTest extends End2EndTestCase { + + public void testStuff() throws Exception { + DartCompilerListener listener = new DartCompilerListenerTest(null) { + @Override + public void typeError(DartCompilationError event) { + // Skip type errors -- we trigger some intentionally in order to test that + // NoSuchMethodException gets thrown. + } + }; + DartLibrarySourceTest app = new DartLibrarySourceTest(getClass(), "NamedParameterTest.dart"); + runTest(app, OptimizationLevel.RAW, listener); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/NativeTest.dart b/compiler/javatests/com/google/dart/compiler/end2end/NativeTest.dart new file mode 100644 index 00000000000..e97cdeddfdc --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/NativeTest.dart @@ -0,0 +1,62 @@ +// 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. + +class NativeClass native "FooBar" { + factory NativeClass() { + return _createFooBar(); + } + + int foo(x, y) { return x + y + 499; } + int bar(x, y) native; + static toto(x, y) { return x - y + 499; } + static NativeClass _createFooBar() native; +} + + +interface A { + foo(); +} + +class NativeA implements A native "JSA" { + factory NativeA() { + return _new(); + } + foo(){} + + static _new() native; +} + +class NativeTest { + static int counter; + + static int jsIncrementBy(x, y) native; + + static int dartIncrementBy(int x, int y) native { + counter += x + y; + return counter; + } + + static void testRoundTrip() { + counter = 0; + var passedThrough = jsIncrementBy(3, 4); + assert(passedThrough == 7); + assert(counter == 7); + } + + static void testNativeClass() { + assert(NativeClass.toto(1, 1) == 499); + NativeClass nc = new NativeClass(); + assert(nc is NativeClass); + assert(nc.foo(1, 524) == 1024); + assert(nc.bar(1, 499) == -1); + NativeA na = new NativeA(); + assert(na is NativeA); + assert(na is A); + } +} + +main() { + NativeTest.testRoundTrip(); + NativeTest.testNativeClass(); +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/NativeTest.js b/compiler/javatests/com/google/dart/compiler/end2end/NativeTest.js new file mode 100644 index 00000000000..fd2e3e5c296 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/NativeTest.js @@ -0,0 +1,25 @@ +// 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. + +function native_NativeTest_jsIncrementBy(x, y) { + return native_NativeTest_dartIncrementBy(x, y); +} + +function FooBar() { + this.js_const = 499; +} + +function native_NativeClass_bar(x, y) { + return this.js_const - x - y; +} + +function native_NativeClass__createFooBar() { + return new FooBar(); +} + +function JSA() {} + +function native_NativeA__new() { + return new JSA(); +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/NativeTestLib.dart b/compiler/javatests/com/google/dart/compiler/end2end/NativeTestLib.dart new file mode 100644 index 00000000000..1dc5337b305 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/NativeTestLib.dart @@ -0,0 +1,6 @@ +// 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. + +#source("NativeTest.dart"); +#native("NativeTest.js"); diff --git a/compiler/javatests/com/google/dart/compiler/end2end/RedirectedConstructorTest.dart b/compiler/javatests/com/google/dart/compiler/end2end/RedirectedConstructorTest.dart new file mode 100644 index 00000000000..6129644e50e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/RedirectedConstructorTest.dart @@ -0,0 +1,26 @@ +// 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. + +class A { + var x; + A(this.x) {} + A.named1(x, y) : this(x + y); + A.named2(x, y, z) : this.named1(x + y, z); +} + +class B extends A { + B(y) : super.named2(y, y + 1, y + 2) { } + B.named(x) : this(x); +} + +main() { + var a1 = new A.named1(1, 2); + assert(a1.x == 3); + + var a2 = new A.named2(1, 2, 3); + assert( a2.x == 6); + + var b = new B.named(-1); + assert(b.x == 0); +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/IncrementalCompilationTest.java b/compiler/javatests/com/google/dart/compiler/end2end/inc/IncrementalCompilationTest.java new file mode 100644 index 00000000000..8c71ebb16b0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/IncrementalCompilationTest.java @@ -0,0 +1,725 @@ +// 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. + +package com.google.dart.compiler.end2end.inc; + +import static com.google.dart.compiler.DartCompiler.EXTENSION_API; +import static com.google.dart.compiler.DartCompiler.EXTENSION_DEPS; +import static com.google.dart.compiler.backend.js.JavascriptBackend.EXTENSION_APP_JS; +import static com.google.dart.compiler.backend.js.JavascriptBackend.EXTENSION_JS; + +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartCompilerListenerTest; +import com.google.dart.compiler.DefaultCompilerConfiguration; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.MockArtifactProvider; +import com.google.dart.compiler.MockBundleLibrarySource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.backend.js.JavascriptBackend; + +import junit.framework.AssertionFailedError; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URISyntaxException; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +public class IncrementalCompilationTest extends CompilerTestCase { + + private static final String TEST_BASE_PATH = "com/google/dart/compiler/end2end/inc/"; + private static final String TEST_APP = "my.app.dart"; + + static class IncMockArtifactProvider extends MockArtifactProvider { + Set reads = new ConcurrentSkipListSet(); + Set writes = new ConcurrentSkipListSet(); + + @Override + public Reader getArtifactReader(Source source, String part, String extension) { + reads.add(source.getName() + "/" + extension); + return super.getArtifactReader(source, part, extension); + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) { + writes.add(source.getName() + "/" + extension); + return super.getArtifactWriter(source, part, extension); + } + + void resetReadsAndWrites() { + reads.clear(); + writes.clear(); + } + } + + private DefaultCompilerConfiguration config; + private IncMockArtifactProvider provider; + + private MockBundleLibrarySource myAppSource; + private MockBundleLibrarySource someLibSource; + private MockBundleLibrarySource someImplLibSource; + + @Override + protected void setUp() throws Exception { + config = new DefaultCompilerConfiguration(new JavascriptBackend()) { + @Override + public boolean incremental() { + return true; + } + }; + provider = new IncMockArtifactProvider(); + + myAppSource = new MockBundleLibrarySource(IncrementalCompilationTest.class.getClassLoader(), + TEST_BASE_PATH, TEST_APP); + someLibSource = myAppSource.getImportFor("some.lib.dart"); + someImplLibSource = someLibSource.getImportFor("someimpl.lib.dart"); + } + + @Override + protected void tearDown() { + config = null; + provider = null; + myAppSource = null; + someLibSource = null; + someImplLibSource = null; + } + + public void testRemoveDeps() throws URISyntaxException { + compile(); + + MockBundleLibrarySource myNuke5AppSource = new MockBundleLibrarySource( + IncrementalCompilationTest.class.getClassLoader(), + TEST_BASE_PATH, "my.nuke5.app.dart", "my.app.dart"); + myNuke5AppSource.remapSource("mybase.dart", "mybase.no5ref.dart"); + myNuke5AppSource.remapSource("my.dart", "my.no5ref.dart"); + myNuke5AppSource.removeSource("myother5.dart"); + + compile(myNuke5AppSource, null); + } + + public void testFullCompile() { + compile(); + + // Assert that all artifacts are written. + didWrite("someimpl.dart", EXTENSION_JS, provider); + didWrite("someimpl.lib.dart", EXTENSION_API, provider); + didWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didWrite("some.dart", EXTENSION_JS, provider); + didWrite("some.lib.dart", EXTENSION_API, provider); + didWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("myother1.dart", EXTENSION_JS, provider); + didWrite("myother2.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_JS, provider); + } + + public void testNoOpRecompile() { + compile(); + + provider.resetReadsAndWrites(); + compile(); + + // Assert we didn't write anything. + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("my.dart", EXTENSION_JS, provider); + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + didNotWrite("my.app.dart", EXTENSION_API, provider); + didNotWrite("my.app.dart", EXTENSION_DEPS, provider); + didNotWrite("my.app.dart", EXTENSION_JS, provider); + } + + public void testTouchOneSource() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("my.dart"); + compile(); + + // We just bumped the timestamp on my.dart, so only my.dart.js and my.app.js should be changed. + // At present, the app's deps and api will be rewritten. This might be optimized later. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + + // Nothing else should have changed. + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testKnockout_jsArtifact() { + compile(); + + provider.resetReadsAndWrites(); + provider.removeArtifact("my.dart", "", EXTENSION_JS); + compile(); + + // At present, knocking out a js artifact will force an update of the library's api and + // deps. This could be optimized. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + + // Assert that everything else was left alone. + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testKnockout_intfArtifact() { + compile(); + + provider.resetReadsAndWrites(); + provider.removeArtifact("my.app.dart", "", EXTENSION_API); + compile(); + + // At present, knocking out an api artifact will force an update of the library's units + // and deps. This could be optimized. + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("myother1.dart", EXTENSION_JS, provider); + didWrite("myother2.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + + // Assert that everything else was left alone. + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + } + + public void testChangeImplementation_methodBody() { + compile(); + + provider.resetReadsAndWrites(); + someImplLibSource.touchSource("someimpl.dart"); + someImplLibSource.remapSource("someimpl.dart", "someimpl.bodychange.dart"); + compile(); + + // Changed someimpl.dart, so it, its library, and the compiled app should be written, but not + // units that depend upon it. + didWrite("someimpl.dart", EXTENSION_JS, provider); + didWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didWrite("someimpl.lib.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("my.dart", EXTENSION_JS, provider); + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + didNotWrite("my.app.dart", EXTENSION_API, provider); + didNotWrite("my.app.dart", EXTENSION_DEPS, provider); + } + + public void testChangeApi_newStaticMethod() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother0.dart"); + myAppSource.remapSource("myother0.dart", "myother0.newstaticmethod.dart"); + compile(); + + // Added a new static method to Other0, which should force a recompile of my.dart, because the + // latter contains a reference to one of its static methods. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testChangeApi_staticFieldRef() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother3.dart"); + myAppSource.remapSource("myother3.dart", "myother3.newstaticfield.dart"); + compile(); + + // Added a new static method to Other0, which should force a recompile of my.dart, because the + // latter contains a reference to one of its static methods. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother3.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testChangeApi_viaTypeParamBound() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother4.dart"); + myAppSource.remapSource("myother4.dart", "myother4.newstaticfield.dart"); + compile(); + + // Added a new static method to Other0, which should force a recompile of my.dart, because the + // latter contains a reference to one of its static methods. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother4.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testChangeApi_returnTypeChange() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother0.dart"); + myAppSource.remapSource("myother0.dart", "myother0.returntypechange.dart"); + compile(); + + // Changed a return type in Other0, which should force a recompile of my.dart, because + // the latter contains a reference to one of its static methods. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testChangeApi_globalVarChange() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother0.dart"); + myAppSource.remapSource("myother0.dart", "myother0.globalvarchange.dart"); + compile(); + + // Changed a return type in Other0, which should force a recompile of my.dart, because + // the latter contains a reference to one of its static methods. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + } + + public void testChangeApi_globalFunctionChange() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother0.dart"); + myAppSource.remapSource("myother0.dart", "myother0.globalfunctionchange.dart"); + compile(); + + // Changed a return type in Other0, which should force a recompile of my.dart, because + // the latter contains a reference to one of its static methods. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + } + + public void testChangeApi_viaNew() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother1.dart"); + myAppSource.remapSource("myother1.dart", "myother1.change.dart"); + compile(); + + // Changed the api of Other1, which should force a recompile of my.dart, because the + // latter intantiates one of its classes. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother1.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testChangeApi_viaSubclassing() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother2.dart"); + myAppSource.remapSource("myother2.dart", "myother2.change.dart"); + compile(); + + // Changed the api of Other2, which should force a recompile of my.dart, because the + // latter subclasses one of its classes. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother2.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + } + + public void testChangeApi_inLibrary() { + compile(); + + provider.resetReadsAndWrites(); + someLibSource.touchSource("some.dart"); + someLibSource.remapSource("some.dart", "some.newmethod.dart"); + someImplLibSource.touchSource("someimpl.dart"); + someImplLibSource.remapSource("someimpl.dart", "someimpl.change.dart"); + compile(); + + // We changed both the interface and implementation libraries, so almost everything should have + // been recompiled. + didWrite("someimpl.dart", EXTENSION_JS, provider); + didWrite("someimpl.lib.dart", EXTENSION_API, provider); + didWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didWrite("some.dart", EXTENSION_JS, provider); + didWrite("some.lib.dart", EXTENSION_API, provider); + didWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + // Except the "others", which have no dependency on the library. + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + + // And the app's api, which also hasn't changed. + didNotWrite("my.app.dart", EXTENSION_API, provider); + } + + public void testChangeApi_inImplLibrary() { + compile(); + + provider.resetReadsAndWrites(); + someImplLibSource.touchSource("someimpl.dart"); + someImplLibSource.remapSource("someimpl.dart", "someimpl.change.dart"); + compile(); + + // Assert that only the interface and implementation library were recompiled. + didWrite("someimpl.dart", EXTENSION_JS, provider); + didWrite("someimpl.lib.dart", EXTENSION_API, provider); + didWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didWrite("some.dart", EXTENSION_JS, provider); + didWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + // The app should remain untouched. + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + didNotWrite("my.dart", EXTENSION_JS, provider); + didNotWrite("my.app.dart", EXTENSION_API, provider); + didNotWrite("my.app.dart", EXTENSION_DEPS, provider); + + // As should the api of some.lib. + didNotWrite("some.lib.dart", EXTENSION_API, provider); + } + + public void testChangeApi_inInterface() { + compile(); + + provider.resetReadsAndWrites(); + someLibSource.touchSource("some.dart"); + someLibSource.remapSource("some.dart", "some.intfchange.dart"); + compile(); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + didNotWrite("my.app.dart", EXTENSION_API, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + // Assert we recompiled both some.dart and someimpl.dart, as well as my.dart. + // (someimpl.dart is recompiled because its interface in some.dart changed) + didWrite("someimpl.dart", EXTENSION_JS, provider); + didWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didWrite("some.dart", EXTENSION_JS, provider); + didWrite("some.lib.dart", EXTENSION_API, provider); + didWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + } + + // TODO(jgw): Bug 5319907. + public void disabled_testFieldHole() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother0.dart"); + myAppSource.remapSource("myother0.dart", "myother0.fillthehole.dart"); + compile(); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + + // Both myother0.dart and my.dart should be recompiled. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + } + + public void testMethodHole() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother0.dart"); + myAppSource.remapSource("myother0.dart", "myother0.fillthemethodhole.dart"); + compile("my.dart", "methodHole is a class. Did you mean (new methodHole)?", 48, 5); + } + + public void testTheNotHole() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother0.dart"); + myAppSource.remapSource("myother0.dart", "myother0.fillthenothole.dart"); + compile(); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("my.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + + // Only myother0.dart should be recompiled. + didWrite("myother0.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + } + + public void testQualifiedFieldRef() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother5.dart"); + myAppSource.remapSource("myother5.dart", "myother5.change.dart"); + compile(); + + // Changed the api of Other5, which should force a recompile of my.dart, because the + // latter includes a qualified reference to one of its instance fields. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother5.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testQualifiedMethodRef() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother6.dart"); + myAppSource.remapSource("myother6.dart", "myother6.change.dart"); + compile(); + + // Changed the api of Other6, which should force a recompile of my.dart, because the + // latter includes a qualified reference to one of its instance methods. + didWrite("my.dart", EXTENSION_JS, provider); + didWrite("myother6.dart", EXTENSION_JS, provider); + didWrite("my.app.dart", EXTENSION_API, provider); + didWrite("my.app.dart", EXTENSION_DEPS, provider); + didWrite("my.app.dart", EXTENSION_APP_JS, provider); + + didNotWrite("someimpl.dart", EXTENSION_JS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_DEPS, provider); + didNotWrite("someimpl.lib.dart", EXTENSION_API, provider); + + didNotWrite("some.dart", EXTENSION_JS, provider); + didNotWrite("some.lib.dart", EXTENSION_API, provider); + didNotWrite("some.lib.dart", EXTENSION_DEPS, provider); + + didNotWrite("myother0.dart", EXTENSION_JS, provider); + didNotWrite("myother1.dart", EXTENSION_JS, provider); + didNotWrite("myother2.dart", EXTENSION_JS, provider); + } + + public void testRemoveDepClass() { + compile(); + + provider.resetReadsAndWrites(); + myAppSource.touchSource("myother5.dart"); + myAppSource.remapSource("myother5.dart", "myother5.change.dart"); + myAppSource.touchSource("myother6.dart"); + myAppSource.remapSource("myother6.dart", "myother6.removeclass.dart"); + compile(); + + // TODO + } + + public void testMergeFiles() throws URISyntaxException { + compile(); + + MockBundleLibrarySource myMergedAppSource = new MockBundleLibrarySource( + IncrementalCompilationTest.class.getClassLoader(), + TEST_BASE_PATH, "my.merged.app.dart", "my.app.dart"); + + compile(myMergedAppSource, null); + } + + private void compile() { + compile(null); + } + + private void compile(String srcName, Object... errors) { + compile(myAppSource, srcName, errors); + } + + private void compile(LibrarySource lib, String srcName, Object... errors) { + try { + DartCompilerListener listener = new DartCompilerListenerTest(srcName, errors); + DartCompiler.compileLib(lib, config, provider, listener); + } catch (IOException e) { + throw new AssertionFailedError("Unexpected IOException: " + e.getMessage()); + } + } + + private void didWrite(String sourceName, String extension, IncMockArtifactProvider provider) { + String spec = sourceName + "/" + extension; + assertTrue("Expected write: " + spec, provider.writes.contains(spec)); + } + + private void didNotWrite(String sourceName, String extension, IncMockArtifactProvider provider) { + String spec = sourceName + "/" + extension; + assertFalse("Didn't expect write: " + spec, provider.writes.contains(spec)); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/my.app.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.app.dart new file mode 100644 index 00000000000..4a7e4bddcc2 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.app.dart @@ -0,0 +1,14 @@ +// 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. + +#import("some.lib.dart"); +#source("my.dart"); +#source("mybase.dart"); +#source("myother0.dart"); +#source("myother1.dart"); +#source("myother2.dart"); +#source("myother3.dart"); +#source("myother4.dart"); +#source("myother5.dart"); +#source("myother6.dart"); \ No newline at end of file diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/my.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.dart new file mode 100644 index 00000000000..62ea0d17336 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.dart @@ -0,0 +1,62 @@ +// 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. + +var x = 0, y = 1; +void fn() { /* ... */ } + +class Spoo { + Spoo() { } + Spoo.other() { } +} + +main() { + // Static method reference to Other0. + var v = Other0.value(); + + // Reference Other1 via new. + var o1 = new Other1(); + + // Static field reference to Other3. + var f = Other3.field; + + // Reference SomeClass via new, and SomeClassImpl transitively. + var sc = new SomeClass(); + var msg = sc.message; + + // Reference global var defined in myother0.dart + var gv = globalVar; + + // Reference global function defined in myother0.dart + var gf = globalFunction(); +} + +class Qualifiers extends QualifierBase { + void fn() { + // Qualified reference to Other5's field. + var field = other5.field; + + // Qualified reference to Other6's method. + var result = other6.method(); + } +} + +// Reference Other2 by subclassing it. +class Foo extends Other2 { + foo() { + // unqualified reference to superclass method. + methodHole(); + + // unqualified reference to superclass field. + return hole; + } + + int bar() { + // qualified reference + return super.not_hole.contents; + } +} + +// Reference Other4 using it as a type parameter bound. +class Bar { +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/my.merged.app.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.merged.app.dart new file mode 100644 index 00000000000..62b901bce8d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.merged.app.dart @@ -0,0 +1,13 @@ +// 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. + +#import("some.lib.dart"); +#source("my.dart"); +#source("mybase.dart"); +#source("myother0.dart"); +#source("myother1.dart"); +#source("myother2.dart"); +#source("myother34.dart"); +#source("myother5.dart"); +#source("myother6.dart"); \ No newline at end of file diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/my.no5ref.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.no5ref.dart new file mode 100644 index 00000000000..e255a53ceab --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.no5ref.dart @@ -0,0 +1,50 @@ +// 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. + +class Spoo { + Spoo() { } + Spoo.other() { } +} + +main() { + // Static method reference to Other0. + var v = Other0.value(); + + // Reference Other1 via new. + var o1 = new Other1(); + + // Static field reference to Other3. + var f = Other3.field; + + // Reference SomeClass via new, and SomeClassImpl transitively. + var sc = new SomeClass(); + var msg = sc.message; +} + +class Qualifiers extends QualifierBase { + void fn() { + // Qualified reference to Other6's method. + var result = other6.method(); + } +} + +// Reference Other2 by subclassing it. +class Foo extends Other2 { + foo() { + // unqualified reference to superclass method. + methodHole(); + + // unqualified reference to superclass field. + return hole; + } + + int bar() { + // qualified reference + return super.not_hole.contents; + } +} + +// Reference Other4 using it as a type parameter bound. +class Bar { +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/my.nuke5.app.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.nuke5.app.dart new file mode 100644 index 00000000000..144f922f6d5 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/my.nuke5.app.dart @@ -0,0 +1,13 @@ +// 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. + +#import("some.lib.dart"); +#source("my.dart"); +#source("mybase.dart"); +#source("myother0.dart"); +#source("myother1.dart"); +#source("myother2.dart"); +#source("myother3.dart"); +#source("myother4.dart"); +#source("myother6.dart"); \ No newline at end of file diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.dart new file mode 100644 index 00000000000..80a30459191 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.dart @@ -0,0 +1,8 @@ +// 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. + +class QualifierBase { + Other5 other5; + Other6 other6; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.no5ref.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.no5ref.dart new file mode 100644 index 00000000000..53d5a3af975 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/mybase.no5ref.dart @@ -0,0 +1,7 @@ +// 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. + +class QualifierBase { + Other6 other6; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.dart new file mode 100644 index 00000000000..d2192a15f43 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.dart @@ -0,0 +1,18 @@ +// 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. + +class Other0 { + static int value() { return 42; } + + int field_; + + Other0() : this.field_ = 42 { } + int get field() { return field_; } +} + +int globalVar = 42; + +int globalFunction() { + return 42; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthehole.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthehole.dart new file mode 100644 index 00000000000..99476b9d5d9 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthehole.dart @@ -0,0 +1,24 @@ +// 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. + +class Other0 { + static int value() { return 42; } + + int field_; + + Other0() : this.field_ = 42 { } + int get field() { return field_; } +} + +int globalVar = 42; + +int globalFunction() { + return 42; +} + +// The definition of this var should force a recompile of my.dart/Foo, because +// it ends up binding to an unqualified reference of one of Foo's superclass +// fields (hole). +int hole = 42; + diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthemethodhole.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthemethodhole.dart new file mode 100644 index 00000000000..53396f08f71 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthemethodhole.dart @@ -0,0 +1,24 @@ +// 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. + +class Other0 { + static int value() { return 42; } + + int field_; + + Other0() : this.field_ = 42 { } + int get field() { return field_; } +} + +int globalVar = 42; + +int globalFunction() { + return 42; +} + +// The definition of this class should force a recompile of my.dart/Foo, because +// it ends up binding to an unqualified reference of one of Foo's superclass +// methods (methodHole). +class methodHole { +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthenothole.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthenothole.dart new file mode 100644 index 00000000000..1f9e9d49f6d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.fillthenothole.dart @@ -0,0 +1,24 @@ +// 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. + +class Other0 { + static int value() { return 42; } + + int field_; + + Other0() : this.field_ = 42 { } + int get field() { return field_; } +} + +int globalVar = 42; + +int globalFunction() { + return 42; +} + +// The definition of this class should not cause a recompile of my.dart, because +// my.dart/Foo's reference to 'not_hole' is super-qualified. +class not_hole { + static final int contents = 42; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalfunctionchange.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalfunctionchange.dart new file mode 100644 index 00000000000..f9fc875aa76 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalfunctionchange.dart @@ -0,0 +1,19 @@ +// 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. + +class Other0 { + static int value() { return 42; } + + int field_; + + Other0() : this.field_ = 42 { } + int get field() { return field_; } +} + +int globalVar = 42; + +// changed return type from int to num +num globalFunction() { + return 42; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalvarchange.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalvarchange.dart new file mode 100644 index 00000000000..7698a9616bd --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.globalvarchange.dart @@ -0,0 +1,18 @@ +// 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. + +class Other0 { + static int value() { return 42; } + + int field_; + + Other0() : this.field_ = 42 { } + int get field() { return field_; } +} + +num globalVar = 42; // changed return type from int to num + +int globalFunction() { + return 42; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.newstaticmethod.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.newstaticmethod.dart new file mode 100644 index 00000000000..ca73d6d7937 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.newstaticmethod.dart @@ -0,0 +1,19 @@ +// 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. + +class Other0 { + static int value() { return 42; } + static String strValue() { return "42"; } + + int field_; + + Other0() : this.field_ = 42 { } + int get field() { return field_; } +} + +int globalVar = 42; + +int globalFunction() { + return 42; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.returntypechange.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.returntypechange.dart new file mode 100644 index 00000000000..8f6112798cd --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother0.returntypechange.dart @@ -0,0 +1,18 @@ +// 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. + +class Other0 { + static int value() { return 42; } + + int field_; + + Other0() : this.field_ = 42 { } + num get field() { return field_; } +} + +int globalVar = 42; + +int globalFunction() { + return 42; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.change.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.change.dart new file mode 100644 index 00000000000..7ee57d86f8e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.change.dart @@ -0,0 +1,10 @@ +// 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. + +class Other1 { + // The existence of this final triggers b/5078969. + static final Function FN = () { return 42; }; + + Other1() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.dart new file mode 100644 index 00000000000..98c9f1b688f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother1.dart @@ -0,0 +1,11 @@ +// 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. + +class Other1 { + // The existence of this final triggers b/5078969. + static final Function FN = () { return 42; }; + + Other1() { } + void newMethod() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.change.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.change.dart new file mode 100644 index 00000000000..287a13b17cb --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.change.dart @@ -0,0 +1,14 @@ +// 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. + +class IntBag { + int contents; +} + +class Other2 { + IntBag hole, not_hole; + void methodHole() { } + + Other2() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.dart new file mode 100644 index 00000000000..25fa3062bde --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother2.dart @@ -0,0 +1,15 @@ +// 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. + +class IntBag { + int contents; +} + +class Other2 { + IntBag hole, not_hole; + void methodHole() { } + + Other2() { } + newMethod() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.dart new file mode 100644 index 00000000000..d36fbb85d1f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.dart @@ -0,0 +1,7 @@ +// 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. + +class Other3 { + static int field; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.newstaticfield.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.newstaticfield.dart new file mode 100644 index 00000000000..d502c431359 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother3.newstaticfield.dart @@ -0,0 +1,8 @@ +// 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. + +class Other3 { + static int field; + static int newField; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother34.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother34.dart new file mode 100644 index 00000000000..cf252435e1c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother34.dart @@ -0,0 +1,10 @@ +// 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. + +class Other3 { + static int field; +} +class Other4 { + static int field; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.change.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.change.dart new file mode 100644 index 00000000000..7433e9923dd --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.change.dart @@ -0,0 +1,8 @@ +// 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. + +class Other4 { + static int field; + static int newField; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.dart new file mode 100644 index 00000000000..1b71811f17d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.dart @@ -0,0 +1,7 @@ +// 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. + +class Other4 { + static int field; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.newstaticfield.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.newstaticfield.dart new file mode 100644 index 00000000000..7433e9923dd --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother4.newstaticfield.dart @@ -0,0 +1,8 @@ +// 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. + +class Other4 { + static int field; + static int newField; +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.change.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.change.dart new file mode 100644 index 00000000000..54da0a00191 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.change.dart @@ -0,0 +1,10 @@ +// 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. + +class Other5 { + int field; + + Other5() { field = 42; } + void newMethod() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.dart new file mode 100644 index 00000000000..00011c40a8f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother5.dart @@ -0,0 +1,9 @@ +// 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. + +class Other5 { + int field; + + Other5() { field = 42; Unused.unused(); } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.change.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.change.dart new file mode 100644 index 00000000000..989b78e14e0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.change.dart @@ -0,0 +1,13 @@ +// 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. + +class Other6 { + Other6() { } + method() { } + void newMethod() { } +} + +class Unused { + static void unused() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.dart new file mode 100644 index 00000000000..b89b75d8190 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.dart @@ -0,0 +1,12 @@ +// 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. + +class Other6 { + Other6() { } + method() { } +} + +class Unused { + static void unused() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.removeclass.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.removeclass.dart new file mode 100644 index 00000000000..1885bdbcad0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/myother6.removeclass.dart @@ -0,0 +1,8 @@ +// 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. + +class Other6 { + Other6() { } + method() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/some.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.dart new file mode 100644 index 00000000000..24e3091169e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.dart @@ -0,0 +1,8 @@ +// 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. + +interface SomeClass factory SomeClassImpl { + SomeClass(); + get message(); +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/some.intfchange.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.intfchange.dart new file mode 100644 index 00000000000..cbc856c0997 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.intfchange.dart @@ -0,0 +1,8 @@ +// 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. + +interface SomeClass factory SomeClassImpl { + SomeClass(); + String get message(); // Added return type +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/some.lib.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.lib.dart new file mode 100644 index 00000000000..1c5b91586b0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.lib.dart @@ -0,0 +1,7 @@ +// 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. + +#library("some_lib"); +#import("someimpl.lib.dart"); +#source("some.dart"); diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/some.newmethod.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.newmethod.dart new file mode 100644 index 00000000000..9cc813f9c4a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/some.newmethod.dart @@ -0,0 +1,9 @@ +// 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. + +interface SomeClass factory SomeClassImpl { + SomeClass(); + String get message(); + newMethod(); +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.bodychange.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.bodychange.dart new file mode 100644 index 00000000000..ee72437285a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.bodychange.dart @@ -0,0 +1,10 @@ +// 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. + +class SomeClassImpl implements SomeClass { + String message_; + + SomeClassImpl() : this.message_ = "what?!" { } + String get message() { return message_; } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.change.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.change.dart new file mode 100644 index 00000000000..d0ba21f0d50 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.change.dart @@ -0,0 +1,11 @@ +// 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. + +class SomeClassImpl implements SomeClass { + String message_; + + SomeClassImpl() : this.message_ = "w00t!" { } + String get message() { return message_; } + newMethod() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.dart new file mode 100644 index 00000000000..93c4038f83e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.dart @@ -0,0 +1,10 @@ +// 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. + +class SomeClassImpl implements SomeClass { + String message_; + + SomeClassImpl() : message_ = "w00t!" { } + String get message() { return message_; } +} diff --git a/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.lib.dart b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.lib.dart new file mode 100644 index 00000000000..9cefaab8a9c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/end2end/inc/someimpl.lib.dart @@ -0,0 +1,7 @@ +// 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. + +#library("someimpl_dart"); +#import("some.lib.dart"); +#source("someimpl.dart"); diff --git a/compiler/javatests/com/google/dart/compiler/parser/AbstractParserTest.java b/compiler/javatests/com/google/dart/compiler/parser/AbstractParserTest.java new file mode 100644 index 00000000000..cb49d74204c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/AbstractParserTest.java @@ -0,0 +1,165 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.ast.DartDirective; +import com.google.dart.compiler.ast.DartImportDirective; +import com.google.dart.compiler.ast.DartLibraryDirective; +import com.google.dart.compiler.ast.DartResourceDirective; +import com.google.dart.compiler.ast.DartSourceDirective; +import com.google.dart.compiler.ast.DartUnit; + +import java.net.URL; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * Tests for the parser, which simply assert that valid source units parse + * correctly. All tests invoking {@code parseUnit} are designed such that + * they will throw an exception if anything goes wrong in the parser. + */ +public abstract class AbstractParserTest extends CompilerTestCase { + + public void testClasses() { + parseUnit("ClassesInterfaces.dart"); + } + + public void testMethodSignatures() { + parseUnit("MethodSignatures.dart"); + } + + public void testFunctionTypes() { + parseUnit("FunctionTypes.dart"); + } + + public void testFormalParameters() { + parseUnit("FormalParameters.dart"); + } + + public void testSuperCalls() { + parseUnit("SuperCalls.dart"); + } + + public void testGenericTypedef() { + parseUnit("GenericTypedef.dart"); + } + + public void testGenericTypes() { + parseUnit("GenericTypes.dart"); + } + + public void testShifting() { + parseUnit("Shifting.dart"); + } + + public void testFunctionInterfaces() { + parseUnit("FunctionInterfaces.dart"); + } + + public void testStringBuffer() { + parseUnit("StringBuffer.dart"); + } + + public void testListObjectLiterals() { + parseUnit("ListObjectLiterals.dart"); + } + + public void testCatchFinally() { + parseUnit("CatchFinally.dart"); + } + + public void testStrings() { + parseUnit("Strings.dart"); + } + + public void testNewWithPrefix() { + parseUnit("NewWithPrefix.dart"); + } + + public void testRedirectedConstructor() { + parseUnit("RedirectedConstructor.dart"); + } + + public void testTopLevel() { + parseUnit("TopLevel.dart"); + } + + public void testDirectives() { + DartUnit unit = parseUnit("Directives.dart"); + Iterator iter = unit.getDirectives().iterator(); + + DartDirective directive = iter.next(); + assertEquals(DartLibraryDirective.class, directive.getClass()); + assertEquals("a-directives-test", ((DartLibraryDirective) directive).getName().getValue()); + + directive = iter.next(); + assertEquals(DartImportDirective.class, directive.getClass()); + assertEquals("dart:core", ((DartImportDirective) directive).getLibraryUri().getValue()); + assertEquals(null, ((DartImportDirective) directive).getPrefix()); + + directive = iter.next(); + assertEquals(DartSourceDirective.class, directive.getClass()); + assertEquals("ListObjectLiterals.dart", ((DartSourceDirective) directive).getSourceUri().getValue()); + + directive = iter.next(); + assertEquals(DartResourceDirective.class, directive.getClass()); + assertEquals("myimage.gif", ((DartResourceDirective) directive).getResourceUri().getValue()); + } + + public void testDirectives2() { + DartUnit unit = parseUnit("Directives2.dart"); + Iterator iter = unit.getDirectives().iterator(); + + DartDirective directive = iter.next(); + assertEquals(DartLibraryDirective.class, directive.getClass()); + assertEquals("b-directives-test", ((DartLibraryDirective) directive).getName().getValue()); + + directive = iter.next(); + assertEquals(DartImportDirective.class, directive.getClass()); + assertEquals("dart:core", ((DartImportDirective) directive).getLibraryUri().getValue()); + assertEquals(null, ((DartImportDirective) directive).getPrefix()); + + directive = iter.next(); + assertEquals(DartSourceDirective.class, directive.getClass()); + assertEquals("SomeClass.dart", ((DartSourceDirective) directive).getSourceUri().getValue()); + + directive = iter.next(); + assertEquals(DartResourceDirective.class, directive.getClass()); + assertEquals("myimage2.gif", ((DartResourceDirective) directive).getResourceUri().getValue()); + } + + public abstract void testStringsErrors(); + + public void testTiming() { + String[] inputs = new String[]{ + "ClassesInterfaces.dart", "MethodSignatures.dart", + "FunctionTypes.dart", "FormalParameters.dart", "SuperCalls.dart", + "GenericTypes.dart", "Shifting.dart", "FunctionInterfaces.dart", + "StringBuffer.dart", "ListObjectLiterals.dart", "CatchFinally.dart", + "Strings.dart",}; + StringBuilder out = new StringBuilder(); + for (String input : inputs) { + URL url = inputUrlFor(getClass(), input); + String source = readUrl(url); + for (int i = 0; i < 50; ++i) { + out.append(source); + } + } + String megaSource = out.toString(); + long start = System.currentTimeMillis(); + parseUnit("Mega.dart", megaSource); + System.out.format("%s ms for '%s.%s()'%n", System.currentTimeMillis() - start, + getClass().getName(), getName()); + } + + @Override + protected DartParser makeParser(ParserContext context) { + Set set = new HashSet(); + set.add("prefix"); + return new DartParser(context, set); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/BadCommentNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/parser/BadCommentNegativeTest.dart new file mode 100644 index 00000000000..6f5eae1dbdb --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/BadCommentNegativeTest.dart @@ -0,0 +1,4 @@ +/* +* + //comment +X Y \ No newline at end of file diff --git a/compiler/javatests/com/google/dart/compiler/parser/CPParserTest.java b/compiler/javatests/com/google/dart/compiler/parser/CPParserTest.java new file mode 100644 index 00000000000..ade74e318e5 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/CPParserTest.java @@ -0,0 +1,71 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.ast.DartComment; +import com.google.dart.compiler.ast.DartUnit; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for the parser, which simply assert that valid source units parse + * correctly. All tests invoking {@code parseUnit} are designed such that + * they will throw an exception if anything goes wrong in the parser. + */ +public class CPParserTest extends CompilerTestCase { + + private static String[] EXPECTED001 = {"/*\n * Beginning comment\n */", + "// line comment", "// another", "/**/", "//", + }; + private static String[] EXPECTED002 = {"/*\n*\n //comment\nX Y"}; + + private String source; + private CommentPreservingParser parser; + + public void test001() { + DartUnit unit = parseUnit("Comments.dart"); + compareComments(unit.getComments(), EXPECTED001); + } + + public void test002() { + DartUnit unit = parseUnitErrors("BadCommentNegativeTest.dart", + "Unexpected token 'ILLEGAL' (expected end of file)", 1, 1); + compareComments(unit.getComments(), EXPECTED002); + } + + @Override + protected DartParser makeParser(ParserContext context) { + parser = new CommentPreservingParser(context, false); + return parser; + } + + @Override + protected ParserContext makeParserContext(Source src, String sourceCode, + DartCompilerListener listener) { + this.source = sourceCode; + return CommentPreservingParser.createContext(src, sourceCode, listener); + } + + private List extractComments(List cms) { + List comments = new ArrayList(); + for (DartComment cm : cms) { + comments.add(source.substring(cm.getSourceStart(), cm.getSourceStart()+cm.getSourceLength())); + } + return comments; + } + + private void compareComments(List cms, String[] expected) { + List comments = extractComments(cms); + assertEquals(expected.length, comments.size()); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], comments.get(i)); + } + } + +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/CatchFinally.dart b/compiler/javatests/com/google/dart/compiler/parser/CatchFinally.dart new file mode 100644 index 00000000000..8d32f569268 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/CatchFinally.dart @@ -0,0 +1,119 @@ +// 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. + +class Foo { + static main(arguments) { + testCatch(); + testFinally(); + testCatchFinally(); + testMultipleCatch(); + testMultipleCatchFinally(); + testRethrow(); + } + + static testRethrow() { + try { + throw new Foo(); + } catch(var e, var st) { + throw; + } + } + + static testCatch() { + var exception; + try { + throw new Foo(); + } catch(var e, var st) { + exception = e; + } + } + + static testFinally() { + var exception; + try { + throw new Foo(); + } finally { + exception = null; + } + } + + static testCatchFinally() { + var exception; + try { + throw new Foo(); + } catch(var e, var st) { + exception = e; + } finally { + exception = null; + } + } + + static testMultipleCatchFinally() { + var exception; + try { + throw new Foo(); + } catch(Foo e, var st) { + exception = e; + } catch(Bar e) { + exception = e; + } finally { + exception = null; + } + } + + static testMultipleCatch() { + var exception; + try { + throw new Foo(); + } catch (final e) { + exception = e; + } catch (var e) { + exception = e; + } catch (Map e) { + exception = e; + } catch (final Map e) { + exception = e; + } catch (int e) { + exception = e; + } catch (final int e) { + exception = e; + } catch (final e, final st) { + exception = e; + } catch (var e, final st) { + exception = e; + } catch (Map e, final st) { + exception = e; + } catch (int e, final st) { + exception = e; + } catch (int e, final int st) { + exception = e; + } catch (final e, var st) { + exception = e; + } catch (var e, var st) { + exception = e; + } catch (Map e, var st) { + exception = e; + } catch (int e, var st) { + exception = e; + } catch (final e, Map st) { + exception = e; + } catch (var e, Map st) { + exception = e; + } catch (Map e, Map st) { + exception = e; + } catch (int e, Map st) { + exception = e; + } catch (final e, int st) { + exception = e; + } catch (var e, int st) { + exception = e; + } catch (Map e, int st) { + exception = e; + } catch (int e, int st) { + exception = e; + } finally { + exception = e; + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart b/compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart new file mode 100644 index 00000000000..9fbdcec6bb5 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/ClassesInterfaces.dart @@ -0,0 +1,127 @@ +// 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. + +class Object { + var x; + int foo() { + return 42; + } + bar(int x, int y, z) { } +} + +class Baz extends Kuk implements A, B, C { + static final y = 12, z = 42; + static final Foo moms = 42, kuks = 42; + final Kuk hest; + static var foo; + + const Baz(); + const Baz.named() : this.foo = 1; + factory prefix.A.foo() { + } + + /* Try a few + * syntactic constructs. */ + void baz() { + if (42) if (42) 42; else throw 42; + switch (42) { case 42: return 42; default: break;} + switch (42) { + L1: case 42: + L2: for(;;) {} + return 42; + default: + break; + } + switch (42) { + case 42: + L2: for(;;) {} + return 42; + L3: case 43: + case 44: + case 45: + default: + break; + } + switch (42) { + case 42: + L2: for(;;) {} + return 42; + L3: case 43: + break; + L4: case 44: + continue L3; + case 45: + break; + } + try { } catch (var e) { } + L0: while (false) try { } catch (int e) { } finally { break L0; } + L1: if (false) { continue L1; } + int kongy(x,y) { return 42; } // This is a comment. + + 42 is Baz; + 42 is Bar; + 42 is !Baz; + 42 is !Bar; + } + + int bar(args) { + kongy(args); + kongy(1, args); + } + + int hest(a) { + for (var i = 0; i < a.length; i++) { + a.b.c.f().g[i] += foo(i); + int kuk = 42; + (kuk); + id(x) { return x; } + int id(x) { return x; } + Box id(x) { return x; } + var f = hest() { }; + var f = int horse() { }; + var f = Box llama() { }; + var f = Box llama() { }; + assert(x == 12); + id(void foo(x) {}); + id(foo(x) {}); + id(Box foo(x) {}); + id(Box foo(x) {}); + id(Box foo); + a < b; + int x = a < b; + id(() {}); + id(void _() {}); + } + } + + Baz.superOnly(x, y, z) : super(x, y, z) {} + Baz.superAndInit(x, y, z) : super(x, y, z), this.y = 2 {} + Baz.superAndInits(x, y, z) : super(x, y, z), this.y = 2, this.x = 4 {} + + // Try all kinds of formal parameters. + void fisk(final a, + b, + var c, + int d, + e(), + void f(), + Map g, + Map h()) {} + + Baz(x, y, z) : super(x, y, z) {} +} + +interface Foo extends D, E { + bar(); +} + +// Test bounds on type parameters +interface Bar extends Foo { +} + +interface Bar extends Foo { +} + +interface Bar extends Foo { +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/CommentTest.java b/compiler/javatests/com/google/dart/compiler/parser/CommentTest.java new file mode 100644 index 00000000000..0a358653966 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/CommentTest.java @@ -0,0 +1,105 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.Source; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests to ensure the scanner is correctly recording comments, as defined + * in the javadoc for DartScanner.recordCommentLocation(). + */ +public class CommentTest extends CompilerTestCase { + + /** + * A parser context that uses its own scanner. + */ + class CommentParserContext extends DartScannerParserContext { + + CommentParserContext(Source source, String sourceCode, + DartCompilerListener listener) { + super(source, sourceCode, listener); + } + + protected DartScanner createScanner(String sourceCode) { + return new CommentScanner(sourceCode); + } + } + + /** + * A specialized scanner that records comment locations. It would have been + * more natural to use the parser context to record comments, but the + * scanner doesn't know about the context. + */ + class CommentScanner extends DartScanner { + + CommentScanner(String sourceCode) { + super(sourceCode); + } + + @Override + protected void recordCommentLocation(int start, int stop, int line, int col) { + int size = commentLocs.size(); + if (size > 0) { + // check for duplicates + int[] loc = commentLocs.get(size - 1); + // use <= to allow parser to back up more than one token + if (start <= loc[0] && stop <= loc[1]) { + return; + } + } + commentLocs.add(new int[]{start, stop}); + } + } + + private List commentLocs = new ArrayList(); + private String source; + + private static String[] EXPECTED001 = {"/*\n * Beginning comment\n */", + "// line comment", "// another", "/**/", "//", + }; + private static String[] EXPECTED002 = {"/*\n*\n //comment\nX Y"}; + + public void test001() { + parseUnit("Comments.dart"); + compareComments(EXPECTED001); + } + + public void test002() { + parseUnitErrors("BadCommentNegativeTest.dart", + "Unexpected token 'ILLEGAL' (expected end of file)", 1, 1); + compareComments(EXPECTED002); + } + + @Override + protected ParserContext makeParserContext(Source src, String sourceCode, + DartCompilerListener listener) { + // initializing source and commentLocs here is a bit of a hack but it + // means parseUnit() and parseUnitErrors() do not have to be overridden + source = sourceCode; + commentLocs.clear(); + return new CommentParserContext(src, sourceCode, listener); + } + + private List extractComments() { + List comments = new ArrayList(); + for (int[] loc : commentLocs) { + comments.add(source.substring(loc[0], loc[1])); + } + return comments; + } + + private void compareComments(String[] expected) { + List comments = extractComments(); + assertEquals(expected.length, comments.size()); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], comments.get(i)); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/Comments.dart b/compiler/javatests/com/google/dart/compiler/parser/Comments.dart new file mode 100644 index 00000000000..447d25bb39d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/Comments.dart @@ -0,0 +1,7 @@ +/* + * Beginning comment + */ +// line comment +// another +class X/**/{// +} \ No newline at end of file diff --git a/compiler/javatests/com/google/dart/compiler/parser/DartASTValidator.java b/compiler/javatests/com/google/dart/compiler/parser/DartASTValidator.java new file mode 100755 index 00000000000..cc10c69773e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/DartASTValidator.java @@ -0,0 +1,637 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.ast.DartArrayAccess; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartAssertion; +import com.google.dart.compiler.ast.DartBinaryExpression; +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.DartConditional; +import com.google.dart.compiler.ast.DartContinueStatement; +import com.google.dart.compiler.ast.DartDefault; +import com.google.dart.compiler.ast.DartDoWhileStatement; +import com.google.dart.compiler.ast.DartDoubleLiteral; +import com.google.dart.compiler.ast.DartEmptyStatement; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartForInStatement; +import com.google.dart.compiler.ast.DartForStatement; +import com.google.dart.compiler.ast.DartFunction; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionObjectInvocation; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIfStatement; +import com.google.dart.compiler.ast.DartImportDirective; +import com.google.dart.compiler.ast.DartInitializer; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartLabel; +import com.google.dart.compiler.ast.DartLibraryDirective; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMapLiteralEntry; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartMethodInvocation; +import com.google.dart.compiler.ast.DartNamedExpression; +import com.google.dart.compiler.ast.DartNativeBlock; +import com.google.dart.compiler.ast.DartNativeDirective; +import com.google.dart.compiler.ast.DartNewExpression; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartNullLiteral; +import com.google.dart.compiler.ast.DartParameter; +import com.google.dart.compiler.ast.DartParameterizedNode; +import com.google.dart.compiler.ast.DartParenthesizedExpression; +import com.google.dart.compiler.ast.DartPlainVisitor; +import com.google.dart.compiler.ast.DartPropertyAccess; +import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; +import com.google.dart.compiler.ast.DartResourceDirective; +import com.google.dart.compiler.ast.DartReturnStatement; +import com.google.dart.compiler.ast.DartSourceDirective; +import com.google.dart.compiler.ast.DartStringInterpolation; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartSuperConstructorInvocation; +import com.google.dart.compiler.ast.DartSuperExpression; +import com.google.dart.compiler.ast.DartSwitchStatement; +import com.google.dart.compiler.ast.DartSyntheticErrorExpression; +import com.google.dart.compiler.ast.DartSyntheticErrorStatement; +import com.google.dart.compiler.ast.DartThisExpression; +import com.google.dart.compiler.ast.DartThrowStatement; +import com.google.dart.compiler.ast.DartTryStatement; +import com.google.dart.compiler.ast.DartTypeExpression; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnaryExpression; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartUnqualifiedInvocation; +import com.google.dart.compiler.ast.DartVariable; +import com.google.dart.compiler.ast.DartVariableStatement; +import com.google.dart.compiler.ast.DartWhileStatement; +import com.google.dart.compiler.type.Type; + +import junit.framework.Assert; + +import java.util.ArrayList; +import java.util.List; + +public class DartASTValidator implements DartPlainVisitor { + + private ArrayList errors = new ArrayList(); + + public void assertValid() { + if (!errors.isEmpty()) { + StringBuilder builder = new StringBuilder(); + builder.append("Invalid AST structure:"); + for (String message : errors) { + builder.append("\r\n "); + builder.append(message); + } + Assert.fail(builder.toString()); + } + } + + @Override + public void visit(List nodes) { + if (nodes != null) { + int previousEnd = -1; + for (DartNode node : nodes) { + int start = node.getSourceStart(); + if (start <= previousEnd) { + errors.add("Node starts (" + start + ") before previous sibling's end (" + previousEnd + + ") or nodes are not in source order"); + } + node.accept(this); + previousEnd = start + node.getSourceLength() - 1; + } + } + } + + @Override + public Object visitArrayAccess(DartArrayAccess node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitArrayLiteral(DartArrayLiteral node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitAssertion(DartAssertion node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitBinaryExpression(DartBinaryExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitBlock(DartBlock node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitBooleanLiteral(DartBooleanLiteral node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitBreakStatement(DartBreakStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitCase(DartCase node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitCatchBlock(DartCatchBlock node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitClass(DartClass node) { + validate(node); + node.getName().accept(this); + node.visitChildren(this); + return null; + } + + @Override + public Object visitConditional(DartConditional node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitContinueStatement(DartContinueStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitDefault(DartDefault node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitDoubleLiteral(DartDoubleLiteral node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitDoWhileStatement(DartDoWhileStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitEmptyStatement(DartEmptyStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitExprStmt(DartExprStmt node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitFieldDefinition(DartFieldDefinition node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitField(DartField node) { + validate(node); + node.visitChildren(this); + node.getName().accept(this); + return null; + } + + @Override + public Object visitForInStatement(DartForInStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitForStatement(DartForStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitFunction(DartFunction node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitFunctionExpression(DartFunctionExpression node) { + validate(node); + DartIdentifier name = node.getName(); + if (name != null) { + name.accept(this); + } + node.visitChildren(this); + return null; + } + + @Override + public Object visitFunctionObjectInvocation(DartFunctionObjectInvocation node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitFunctionTypeAlias(DartFunctionTypeAlias node) { + validate(node); + node.getName().accept(this); + node.visitChildren(this); + return null; + } + + @Override + public Object visitIdentifier(DartIdentifier node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitIfStatement(DartIfStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitImportDirective(DartImportDirective node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitInitializer(DartInitializer node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitIntegerLiteral(DartIntegerLiteral node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitLabel(DartLabel node) { + validate(node); + node.getLabel().accept(this); + node.visitChildren(this); + return null; + } + + @Override + public Object visitLibraryDirective(DartLibraryDirective node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitMapLiteral(DartMapLiteral node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitMapLiteralEntry(DartMapLiteralEntry node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitMethodDefinition(DartMethodDefinition node) { + validate(node); + node.getName().accept(this); + node.visitChildren(this); + return null; + } + + @Override + public Object visitMethodInvocation(DartMethodInvocation node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitNativeBlock(DartNativeBlock node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitNativeDirective(DartNativeDirective node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitNewExpression(DartNewExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitNullLiteral(DartNullLiteral node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitParameter(DartParameter node) { + validate(node); + node.getName().accept(this); + node.visitChildren(this); + return null; + } + + @Override + public Object visitParenthesizedExpression(DartParenthesizedExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitPropertyAccess(DartPropertyAccess node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitResourceDirective(DartResourceDirective node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitReturnStatement(DartReturnStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitSourceDirective(DartSourceDirective node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitNamedExpression(DartNamedExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitStringInterpolation(DartStringInterpolation node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitStringLiteral(DartStringLiteral node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitSuperConstructorInvocation( + DartSuperConstructorInvocation node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitSuperExpression(DartSuperExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitSwitchStatement(DartSwitchStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Type visitSyntheticErrorExpression(DartSyntheticErrorExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Type visitSyntheticErrorStatement(DartSyntheticErrorStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitThisExpression(DartThisExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitThrowStatement(DartThrowStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitTryStatement(DartTryStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitTypeExpression(DartTypeExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitTypeNode(DartTypeNode node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitTypeParameter(DartTypeParameter node) { + validate(node); + node.getName().accept(this); + node.visitChildren(this); + return null; + } + + @Override + public Object visitUnaryExpression(DartUnaryExpression node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitUnit(DartUnit node) { + node.visitChildren(this); + return null; + } + + @Override + public Object visitUnqualifiedInvocation(DartUnqualifiedInvocation node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitVariable(DartVariable node) { + validate(node); + node.getName().accept(this); + node.visitChildren(this); + return null; + } + + @Override + public Object visitVariableStatement(DartVariableStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitWhileStatement(DartWhileStatement node) { + validate(node); + node.visitChildren(this); + return null; + } + + @Override + public Object visitRedirectConstructorInvocation(DartRedirectConstructorInvocation node) { + validate(node); + node.visitChildren(this); + return null; + } + + private void validate(DartNode node) { + DartNode parent = node.getParent(); + if (parent == null) { + errors.add("No parent for " + node.getClass().getName()); + } + + int nodeStart = node.getSourceStart(); + int nodeLength = node.getSourceLength(); + if (nodeStart < 0 || nodeLength < 0) { + errors.add("No source info for " + node.getClass().getName()); + } + + if (parent != null) { + int nodeEnd = nodeStart + nodeLength; + int parentStart = parent.getSourceStart(); + int parentEnd = parentStart + parent.getSourceLength(); + if (parentStart > nodeStart) { + errors.add("Invalid source start (" + nodeStart + ") for " + + node.getClass().getName() + " inside " + + parent.getClass().getName() + " (" + parentStart + ")"); + } + if (nodeEnd > parentEnd) { + errors.add("Invalid source end (" + nodeEnd + ") for " + + node.getClass().getName() + " inside " + + parent.getClass().getName() + " (" + parentStart + ")"); + } + } + + if (node instanceof DartSyntheticErrorExpression + || node instanceof DartSyntheticErrorExpression) { + errors.add("Parser error at (" + nodeStart + ")"); + } + } + + @Override + public Object visitParameterizedNode(DartParameterizedNode node) { + validate(node); + node.visitChildren(this); + return null; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/DartParserRunner.java b/compiler/javatests/com/google/dart/compiler/parser/DartParserRunner.java new file mode 100644 index 00000000000..adae6e4c1e3 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/DartParserRunner.java @@ -0,0 +1,198 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSourceTest; +import com.google.dart.compiler.ast.DartUnit; + +import junit.framework.TestCase; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +public class DartParserRunner extends DartCompilerListener implements Runnable { + + public static final int DEFAULT_TIMEOUT_IN_MILISECONDS = 10 * 1000; + + public static void main(String[] args) { + for (String fileName : args) { + String source = readSource(fileName); + if (source == null) { + System.err.println("Unable to read " + fileName); + continue; + } + System.out.println("Parsing " + fileName); + DartParserRunner parser = DartParserRunner.parse(fileName, source, Integer.MAX_VALUE, true); + for (DartCompilationError error : parser.getErrors()) { + System.err.println(error.toString()); + } + } + } + + /* + * Parses @sourceCode ensuring explicit failure in case of non-termination. + */ + public static DartParserRunner parse(String name, String sourceCode) { + return DartParserRunner.parse(name, sourceCode, false); + } + + /* + * Parses @sourceCode ensuring explicit failure in case of non-termination. + */ + public static DartParserRunner parse(String name, String sourceCode, int timeoutInMs, + boolean wantWarnings) { + return DartParserRunner.parse(name, sourceCode, false, timeoutInMs, wantWarnings); + } + + /* + * Parses @sourceCode ensuring explicit failure in case of non-termination. + */ + public static DartParserRunner parse(String name, String sourceCode, boolean apiParsing) { + return parse(name, sourceCode, apiParsing, 0, false); + } + + /* + * Parses @sourceCode ensuring explicit failure in case of non-termination. + */ + public static DartParserRunner parse(String name, String sourceCode, boolean apiParsing, + int timeoutInMs, boolean wantWarnings) { + DartParserRunner parser = null; + try { + parser = new DartParserRunner(name, sourceCode, apiParsing); + if (timeoutInMs != 0) { + parser.setTimeout(timeoutInMs); + } + parser.setWantWarnings(wantWarnings); + parser.doWork(); + TestCase.assertFalse("Dart parser failed to terminate.", parser.isAlive()); + Throwable t = parser.workerException.get(); + if (t != null) { + throw new AssertionError(t); + } + } catch(Exception e) { + throw new Error(e.toString()); + } + return parser; + } + + /** + * Reads a text file and returns the contents as a String. + * + * @param file + * @return file contents as a String + */ + private static String readSource(String file) { + StringBuilder buf = new StringBuilder(); + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(file)); + String line; + while ((line = reader.readLine()) != null) { + buf.append(line).append("\n"); + } + return buf.toString(); + } catch (IOException e) { + return null; + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + } + } + } + } + + boolean apiParsing; + DartUnit dartUnit; + List errors = new ArrayList(); + String name; + + Thread parserWorker; + + String sourceCode; + + int timeoutInMillis = DEFAULT_TIMEOUT_IN_MILISECONDS; + + AtomicReference workerException = new AtomicReference(); + + private boolean wantWarnings; + + private DartParserRunner(String name, String sourceCode) { + this(name, sourceCode, false); + } + + private DartParserRunner(String name, String sourceCode, boolean apiParsing) { + this.name = name; + this.sourceCode = sourceCode; + this.parserWorker = new Thread(this); + this.apiParsing = apiParsing; + } + + @Override + public void compilationError(DartCompilationError event) { + errors.add(event); + } + + @Override + public void compilationWarning(DartCompilationError event) { + if (wantWarnings) { + errors.add(event); + } + } + + public int getErrorCount() { + return errors.size(); + } + + public List getErrors() { + return errors; + } + + public boolean hasErrors() { + return getErrors().size() > 0; + } + + @Override + public void run() { + try { + DartSourceTest dartSrc = new DartSourceTest(name, sourceCode, null); + ParserContext context = new DartScannerParserContext(dartSrc, sourceCode, this); + dartUnit = (new DartParser(context, apiParsing)).parseUnit(dartSrc); + } catch (Throwable t) { + workerException.set(t); + } + } + + public DartParserRunner setTimeout(int timeoutInMillis) { + this.timeoutInMillis = timeoutInMillis; + return this; + } + + public DartParserRunner setWantWarnings(boolean wantWarnings) { + this.wantWarnings = wantWarnings; + return this; + } + + @Override + public void typeError(DartCompilationError event) { + errors.add(event); + } + + private void doWork() throws InterruptedException { + parserWorker.start(); + parserWorker.join(timeoutInMillis); + } + + private boolean isAlive() { + return parserWorker.isAlive(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/DietParserTest.java b/compiler/javatests/com/google/dart/compiler/parser/DietParserTest.java new file mode 100644 index 00000000000..5235c4a6527 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/DietParserTest.java @@ -0,0 +1,22 @@ +// 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. + +package com.google.dart.compiler.parser; + +/** + * Tests for the parser, which simply assert that valid source units parse + * correctly. All tests invoking {@code parseUnit} are designed such that + * they will throw an exception if anything goes wrong in the parser. + */ +public class DietParserTest extends AbstractParserTest { + + public void testStringsErrors() { + parseUnit("StringsErrorsNegativeTest.dart"); + } + + @Override + protected DartParser makeParser(ParserContext context) { + return new DartParser(context, /* isDietParse */true); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/Directives.dart b/compiler/javatests/com/google/dart/compiler/parser/Directives.dart new file mode 100644 index 00000000000..a95d96a8c3a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/Directives.dart @@ -0,0 +1,16 @@ +#!/bin/dart +/* + * Beginning comment + */ + +#library("a-directives-test"); + +#import("dart:core"); + +#source("ListObjectLiterals.dart"); +#resource("myimage.gif"); + + main() { + // something to do here + } + \ No newline at end of file diff --git a/compiler/javatests/com/google/dart/compiler/parser/Directives2.dart b/compiler/javatests/com/google/dart/compiler/parser/Directives2.dart new file mode 100644 index 00000000000..e8927a942d1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/Directives2.dart @@ -0,0 +1,11 @@ +#library("b-directives-test"); + +#import("dart:core"); +#source("SomeClass.dart"); + +#resource("myimage2.gif"); + + main() { + // something to do here + } + \ No newline at end of file diff --git a/compiler/javatests/com/google/dart/compiler/parser/ErrorMessageLocationTest.java b/compiler/javatests/com/google/dart/compiler/parser/ErrorMessageLocationTest.java new file mode 100644 index 00000000000..dc8995a1ec9 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/ErrorMessageLocationTest.java @@ -0,0 +1,38 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilationError; + +import junit.framework.TestCase; + +import java.util.List; + +/** + * Test that error messages cover the correct locations in the source code. + */ +public class ErrorMessageLocationTest extends TestCase { + /** + * Test that unexpected token highlights the correct location in the file. + */ + public void testUnexpectedTokenErrorMessage() { + String sourceCode = + "// Empty comment\n" + + "interface foo default Bar {\n" + + "}"; + + DartParserRunner runner = DartParserRunner.parse(getName(), sourceCode); + List actualErrors = runner.getErrors(); + + // Due to error recovery more than a single error is generated + DartCompilationError actualError = actualErrors.get(0); + + String errorTokenString = "default"; + assertEquals(15, actualError.getColumnNumber()); + assertEquals(errorTokenString.length(), actualError.getLength()); + assertEquals(2, actualError.getLineNumber()); + assertEquals(sourceCode.indexOf(errorTokenString), actualError.getStartPosition()); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/FactoryInitializersNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/parser/FactoryInitializersNegativeTest.dart new file mode 100644 index 00000000000..6fcf52cca99 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/FactoryInitializersNegativeTest.dart @@ -0,0 +1,12 @@ +// 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. + +// expect syntax errors: Factory constructors cannot have initializers. + +class A { + int x; + int y; + factory A.foo(x,y) : this.x = 1 { + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/FormalParameters.dart b/compiler/javatests/com/google/dart/compiler/parser/FormalParameters.dart new file mode 100644 index 00000000000..c650b868c5d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/FormalParameters.dart @@ -0,0 +1,10 @@ +// 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. + +class FormalParameterSyntax { + a([x = 42]) { } + b([int x = 42]) { } + c(x, [y = 42]) { } + d(x, [int y = 42]) { } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/FunctionInterfaces.dart b/compiler/javatests/com/google/dart/compiler/parser/FunctionInterfaces.dart new file mode 100644 index 00000000000..ff4f879b37c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/FunctionInterfaces.dart @@ -0,0 +1,28 @@ +// 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. + +typedef void VoidCallback1(Event event); +typedef void VoidCallback2(Event event, int x); +typedef void VoidCallback3(Event event, int x, y); +typedef void VoidCallback4(Event event, int x, var y); + +typedef Callback1(Event event); +typedef Callback2(Event event, int x); +typedef Callback3(Event event, int x, y); +typedef Callback4(Event event, int x, var y); + +typedef int IntCallback1(Event event); +typedef int IntCallback2(Event event, int x); +typedef int IntCallback3(Event event, int x, y); +typedef int IntCallback4(Event event, int x, var y); + +typedef Box BoxCallback1(Event event); +typedef Box BoxCallback2(Event event, int x); +typedef Box BoxCallback3(Event event, int x, y); +typedef Box BoxCallback4(Event event, int x, var y); + +typedef Box> BoxBoxCallback1(Event event); +typedef Box> BoxBoxCallback2(Event event, int x); +typedef Box> BoxBoxCallback3(Event event, int x, y); +typedef Box> BoxBoxCallback4(Event event, int x, var y); diff --git a/compiler/javatests/com/google/dart/compiler/parser/FunctionTypes.dart b/compiler/javatests/com/google/dart/compiler/parser/FunctionTypes.dart new file mode 100644 index 00000000000..0bfdcc8af8b --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/FunctionTypes.dart @@ -0,0 +1,21 @@ +// 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. + +class FunctionTypeSyntax { + Function a; + static Function b; + + Function c() { } + static Function d() { } + + e(Function f) { } + static f(Function f) { } + + g(f()) { } + h(void f()) { } + j(f(x)) { } + k(f(x, y)) { } + l(int f(int x, int y)) { } + m(int x, int f(x), int y) { } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/GenericTypedef.dart b/compiler/javatests/com/google/dart/compiler/parser/GenericTypedef.dart new file mode 100644 index 00000000000..de237285b85 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/GenericTypedef.dart @@ -0,0 +1,5 @@ +// 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. + +typedef T Deserializer(InputDataStream input); diff --git a/compiler/javatests/com/google/dart/compiler/parser/GenericTypes.dart b/compiler/javatests/com/google/dart/compiler/parser/GenericTypes.dart new file mode 100644 index 00000000000..ba1ca8d6003 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/GenericTypes.dart @@ -0,0 +1,15 @@ +// 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. + +class Box { + T t; + getT() { return t; } + setT(T t) { this.t = t; } +} + +class UseBox { + Box>> boxIt(Box> box) { + return new Box>>(box); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/LibraryParserTest.java b/compiler/javatests/com/google/dart/compiler/parser/LibraryParserTest.java new file mode 100644 index 00000000000..dbd1d67551d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/LibraryParserTest.java @@ -0,0 +1,190 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilerListenerTest; +import com.google.dart.compiler.DartSource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.ast.LibraryNode; +import com.google.dart.compiler.ast.LibraryUnit; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * Tests for parsing library directives from a dart file. + */ +public class LibraryParserTest extends TestCase { + + static class TestLibrarySource implements LibrarySource { + private final String source; + + public TestLibrarySource(String source) { + this.source = source; + } + + @Override + public URI getUri() { + try { + return new URI(getName()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @Override + public Reader getSourceReader() { + return new StringReader(source); + } + + @Override + public String getName() { + return "test.dart"; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public long getLastModified() { + return 0; + } + + @Override + public DartSource getSourceFor(String relPath) { + return null; + } + + @Override + public LibrarySource getImportFor(String relPath) { + return null; + } + } + + public void testLibrary() { + // "library { import = ['foo.lib', 'bar.lib'] source = ['this.dart', 'that.dart'] }"; + String text = + "#library(\"testLibrary\");\n" + + "#import(\"foo.dart\");\n" + + "#import(\"bar.dart\");\n" + + "#source(\"this.dart\");\n" + + "#source(\"that.dart\");\n"; + + LibraryUnit unit = parse(text); + + assertHasImport(unit, "foo.dart"); + assertHasImport(unit, "bar.dart"); + assertHasSource(unit, "this.dart"); + assertHasSource(unit, "that.dart"); + } + + public void testNative() { + // "library { import = ['foo.lib'] source = ['this.dart'] native = ['impl.js'] }"; + String text = + "#library(\"testLibrary\");\n" + + "#import(\"foo.dart\");\n" + + "#source(\"this.dart\");\n" + + "#native(\"impl.js\");\n"; + + LibraryUnit unit = parse(text); + + assertHasImport(unit, "foo.dart"); + assertHasSource(unit, "this.dart"); + assertHasNative(unit, "impl.js"); + } + + public void testImportPrefix() { +// "library { import = [foo:'foo.lib', 'bar.lib'] source = ['this.dart', 'that.dart'] }"; + String text = + "#library(\"testLibrary\");\n" + + "#import(\"foo.dart\", prefix:\"foo\");\n" + + "#import(\"bar.dart\");\n" + + "#source(\"this.dart\");\n" + + "#native(\"impl.js\");\n"; + LibraryUnit unit = parse(text); + + assertHasImport(unit, "foo.dart", "foo"); + assertHasImport(unit, "bar.dart"); + assertHasSource(unit, "this.dart"); + assertHasNative(unit, "impl.js"); + } + + public void testResource() { + //"library { import = ['foo.lib', 'bar.lib'] source = ['this.dart', 'that.dart'] resource = [ 'some.html', 'myimage.gif' ] }"; + String text = + "#library(\"testLibrary\");\n" + + "#import(\"foo.dart\");\n" + + "#import(\"bar.dart\");\n" + + "#source(\"this.dart\");\n" + + "#source(\"that.dart\");\n" + + "#resource(\"some.html\");\n" + + "#resource(\"myimage.gif\");\n"; + + LibraryUnit unit = parse(text); + + assertHasImport(unit, "foo.dart"); + assertHasImport(unit, "bar.dart"); + assertHasSource(unit, "this.dart"); + assertHasSource(unit, "that.dart"); + assertHasResource(unit, "some.html"); + assertHasResource(unit, "myimage.gif"); + } + + private void assertHasImport(LibraryUnit unit, String name) { + assertHas(unit.getImportPaths(), name); + } + + private void assertHasImport(LibraryUnit unit, String name, String prefix) { + assertHas(unit.getImportPaths(), name, prefix); + } + + private void assertHasSource(LibraryUnit unit, String name) { + assertHas(unit.getSourcePaths(), name); + } + + private void assertHasResource(LibraryUnit unit, String name) { + assertHas(unit.getResourcePaths(), name); + } + + private void assertHasNative(LibraryUnit unit, String name) { + assertHas(unit.getNativePaths(), name); + } + + private void assertHas(Iterable nodes, String name) { + assertHas(nodes, name, null); + } + + private void assertHas(Iterable nodes, String name, String prefix) { + for (LibraryNode node : nodes) { + if (node.getText().equals(name)) { + if ((prefix != null) && !node.getPrefix().equals(prefix)) { + break; + } + return; + } + } + fail("Missing " + ((prefix != null) ? (prefix + " : ") : "") + name); + } + + private LibraryUnit parse(String text, Object... errors) { + TestLibrarySource source = new TestLibrarySource(text); + DartCompilerListenerTest listener = new DartCompilerListenerTest(source.getName(), errors); + try { + LibraryUnit unit = + DartParser.getSourceParser(source, listener).preProcessLibraryDirectives(source); + listener.checkAllErrorsReported(); + return unit; + } catch (IOException ioEx) { + throw new AssertionError(ioEx); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/ListObjectLiterals.dart b/compiler/javatests/com/google/dart/compiler/parser/ListObjectLiterals.dart new file mode 100644 index 00000000000..58a1b4a0a58 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/ListObjectLiterals.dart @@ -0,0 +1,32 @@ +// 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. + +class ListObjectLiterals { + foo() { + var a = [a, b, [c + 42, x], 1, 2, 3]; + var b = { 'a':1, 'b':c + 42, 'd':[0, 1, 2], 'e':{ 'foo':42, 'bar':49 }}; + + var c = [a, b, ]; + var d = {'a':1, 'b':2, 'c':3}; + + var e = [ ]; + var f = { }; + var g = []; + var h = {}; + + var i = const []; + var j = const []; + var k = []; + var l = const [1,2,3]; + var m = const [1,2,3]; + var n = [1,2,3]; + + var o = const {}; + var p = const {}; + var q = {}; + var r = const {'a':1, 'b':2, 'c':3}; + var s = const {'a':1, 'b':2, 'c':3 }; + var t = {'a':1, 'b':2, 'c':3}; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/MethodSignatures.dart b/compiler/javatests/com/google/dart/compiler/parser/MethodSignatures.dart new file mode 100644 index 00000000000..bceecd6c3ff --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/MethodSignatures.dart @@ -0,0 +1,20 @@ +// 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. + +interface MethodSignatureSyntax { + a(); + b(x); + c(int x); + d(var x); + e(final x); + + f(x, y); + g(var x, y); + h(final x, y); + j(var x, var y); + k(final x, final y); + + l(int x, y); + m(int x, int y); +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java b/compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java new file mode 100644 index 00000000000..2af6784d1e8 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/NegativeParserTest.java @@ -0,0 +1,45 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.CompilerTestCase; + +/** + * Negative Parser/Syntax tests. + */ +public class NegativeParserTest extends CompilerTestCase { + + private void parseExpectErrors(String code, int expectedErrorCount) { + assertEquals(expectedErrorCount, DartParserRunner.parse(getName(), code).getErrorCount()); + } + + private void parseExpectErrors(String code) { + assertTrue("expected errors.", DartParserRunner.parse(getName(), code).hasErrors()); + } + + public void testFieldInitializerInRedirectionConstructor1() { + parseExpectErrors("class A { A(x) { } A.foo() : this(5), y = 5; var y; }"); + } + + public void testFieldInitializerInRedirectionConstructor2() { + parseExpectErrors("class A { A(x) { } A.foo() : y = 5, this(5); var y; }"); + } + + public void testFieldInitializerInRedirectionConstructor3() { + parseExpectErrors("class A { A(x) { } A.foo(this.y) : this(5); var y; }", 1); + } + + public void testSuperInRedirectionConstructor1() { + parseExpectErrors("class A { A(x) { } A.foo(this.y) : this(5), super(); var y; }"); + } + + public void testSuperInRedirectionConstructor2() { + parseExpectErrors("class A { A(x) { } A.foo(this.y) : super(), this(5); var y; }", 1); + } + + public void testMultipleRedirectionConstructors() { + parseExpectErrors("class A { A(x) { } A.foo(this.y) : this(1), this(2); }", 1); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/NewWithPrefix.dart b/compiler/javatests/com/google/dart/compiler/parser/NewWithPrefix.dart new file mode 100644 index 00000000000..a2faa2abcc5 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/NewWithPrefix.dart @@ -0,0 +1,27 @@ +// 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. + +class NewWithPrefix { + foo() { + var a = new prefix.Set.named(); + a = new prefix.Set(); + a = const prefix.Set.named(); + a = const prefix.Set(); + + a = new prefix.Set.named(); + a = new prefix.Set(); + a = const prefix.Set.named(); + a = const prefix.Set(); + + a = new Set.named(); + a = new Set(); + a = const Set.named(); + a = const Set(); + + a = new Set.named(); + a = new Set(); + a = const Set.named(); + a = const Set(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/ParserEventsTest.java b/compiler/javatests/com/google/dart/compiler/parser/ParserEventsTest.java new file mode 100644 index 00000000000..48528523b40 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/ParserEventsTest.java @@ -0,0 +1,756 @@ +// 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. + +package com.google.dart.compiler.parser; + +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ArrayLiteral; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.BinaryExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.Block; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.BreakStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.CatchClause; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.CatchParameter; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ClassBody; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ClassMember; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.CompilationUnit; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ConditionalExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ConstExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ConstructorName; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ContinueStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.DoStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.EmptyStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.Expression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ExpressionList; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ExpressionStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.FieldInitializerOrRedirectedConstructor; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.FinalDeclaration; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ForInitialization; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ForStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.FormalParameterList; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.FunctionDeclaration; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.FunctionLiteral; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.FunctionTypeInterface; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.Identifier; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.IfStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.Initializer; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.TypeExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.Label; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.Literal; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.MapLiteral; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.MapLiteralEntry; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.MethodName; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.NativeBody; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.NewExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.OperatorName; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ParenthesizedExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.PostfixExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.QualifiedIdentifier; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ReturnStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.SelectorExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.SpreadExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.StringInterpolation; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.StringSegment; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.SuperExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.SuperInitializer; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.SwitchMember; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.SwitchStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ThisExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.ThrowStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.TopLevelElement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.TryStatement; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.TypeAnnotation; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.TypeArguments; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.TypeFunctionOrVarable; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.TypeParameter; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.UnaryExpression; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.VarDeclaration; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.VariableDeclaration; +import static com.google.dart.compiler.parser.ParserEventsTest.Mark.WhileStatement; + +import java.util.HashSet; + +public class ParserEventsTest extends AbstractParserTest { + + /** + * A collection of marks representing interesting parser states. Copied from + * com.google.dart.tools.core.internal.completion. + *

    + * TODO(messick) Find a way to share the implementation of Mark. + */ + static enum Mark { + // some elements are roots of the kind-of relationships, they must be first + Block, + Expression, + Literal(Expression), + FormalParameterList, + Statement, + // all others are alphabetical + ArrayLiteral(Literal), + BinaryExpression(Expression), + BreakStatement(Statement), + CatchClause, + CatchParameter, + ClassBody(Block), + ClassMember, + CompilationUnit, + ConditionalExpression(Expression), + FinalDeclaration, + ConstExpression(Expression), + ConstructorName, + ContinueStatement(Statement), + DoStatement(Statement), + EmptyStatement(Statement), + ExpressionList, + ExpressionStatement(Statement), + FieldInitializerOrRedirectedConstructor, + ForInitialization, + ForStatement(Statement), + FunctionDeclaration, + FunctionLiteral(Literal), + FunctionTypeInterface, + Identifier(Expression), + IfStatement(Statement), + Initializer, + TypeExpression(Expression), + Label, + MapLiteral(Literal), + MapLiteralEntry, + MethodName, + NativeBody, + NewExpression(Expression), + OperatorName, + ParenthesizedExpression(Expression), + PostfixExpression(Expression), + QualifiedIdentifier, + ReturnStatement(Statement), + SelectorExpression(Expression), + SpreadExpression(Expression), + StringInterpolation, + StringSegment, + SuperExpression(Expression), + SuperInitializer, + SwitchMember, + SwitchStatement(Statement), + ThisExpression(Expression), + ThrowStatement(Statement), + TopLevelElement, + TryStatement(Statement), + TypeAnnotation, + TypeArguments, + TypeFunctionOrVarable, + TypeParameter, + UnaryExpression(Expression), + VarDeclaration, + VariableDeclaration, + WhileStatement(Statement); + + public final Mark kind; + + private Mark() { + kind = null; + } + + private Mark(Mark kind) { + this.kind = kind; + } + + /** + * Return true if this Mark has a kind-of relation to the given Mark. + * + * @param other the Mark to test for a kind-of relation + * @return true if the test succeeds + */ + public boolean isKindOf(Mark other) { + if (this == other) { + return true; + } + if (kind == null) { + return false; + } + return kind.isKindOf(other); + } + } + + private static class ParserEventRecorder extends DartParser { + private HashSet marks; + + public ParserEventRecorder(ParserContext ctx) { + super(ctx); + marks = new HashSet(); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public HashSet copyMarks() { + return (HashSet) marks.clone(); + } + + @Override + protected void beginArrayLiteral() { + super.beginArrayLiteral(); + recordMark(ArrayLiteral); + } + + @Override + protected void beginBinaryExpression() { + super.beginBinaryExpression(); + recordMark(BinaryExpression); + } + + @Override + protected void beginBlock() { + super.beginBlock(); + recordMark(Block); + } + + @Override + protected void beginBreakStatement() { + super.beginBreakStatement(); + recordMark(BreakStatement); + } + + @Override + protected void beginCatchClause() { + super.beginCatchClause(); + recordMark(CatchClause); + } + + @Override + protected void beginCatchParameter() { + super.beginFormalParameter(); + recordMark(CatchParameter); + } + + @Override + protected void beginClassBody() { + super.beginClassBody(); + recordMark(ClassBody); + } + + @Override + protected void beginClassMember() { + super.beginClassMember(); + recordMark(ClassMember); + } + + @Override + protected void beginCompilationUnit() { + super.beginCompilationUnit(); + recordMark(CompilationUnit); + } + + @Override + protected void beginConditionalExpression() { + super.beginConditionalExpression(); + recordMark(ConditionalExpression); + } + + @Override + protected void beginConstExpression() { + super.beginConstExpression(); + recordMark(ConstExpression); + } + + @Override + protected void beginConstructor() { + super.beginConstructor(); + recordMark(ConstructorName); + } + + @Override + protected void beginContinueStatement() { + super.beginContinueStatement(); + recordMark(ContinueStatement); + } + + @Override + protected void beginDoStatement() { + super.beginDoStatement(); + recordMark(DoStatement); + } + + @Override + protected void beginEmptyStatement() { + super.beginEmptyStatement(); + recordMark(EmptyStatement); + } + + @Override + protected void beginEntryPoint() { + super.beginEntryPoint(); + // TODO(messick): add recording + } + + @Override + protected void beginExpression() { + super.beginExpression(); + recordMark(Expression); + } + + @Override + protected void beginExpressionList() { + super.beginExpressionList(); + recordMark(ExpressionList); + } + + @Override + protected void beginExpressionStatement() { + super.beginExpressionStatement(); + recordMark(ExpressionStatement); + } + + @Override + protected void beginFieldInitializerOrRedirectedConstructor() { + super.beginFieldInitializerOrRedirectedConstructor(); + recordMark(FieldInitializerOrRedirectedConstructor); + } + + @Override + protected void beginFinalDeclaration() { + super.beginFinalDeclaration(); + recordMark(FinalDeclaration); + } + + @Override + protected void beginForInitialization() { + super.beginForInitialization(); + recordMark(ForInitialization); + } + + @Override + protected void beginFormalParameterList() { + super.beginFormalParameterList(); + recordMark(FormalParameterList); + } + + @Override + protected void beginForStatement() { + super.beginForStatement(); + recordMark(ForStatement); + } + + @Override + protected void beginFunctionDeclaration() { + super.beginFunctionDeclaration(); + recordMark(FunctionDeclaration); + } + + @Override + protected void beginFunctionLiteral() { + super.beginFunctionLiteral(); + recordMark(FunctionLiteral); + } + + @Override + protected void beginFunctionTypeInterface() { + super.beginFunctionTypeInterface(); + recordMark(FunctionTypeInterface); + } + + @Override + protected void beginIdentifier() { + super.beginIdentifier(); + recordMark(Identifier); + } + + @Override + protected void beginIfStatement() { + super.beginIfStatement(); + recordMark(IfStatement); + } + + @Override + protected void beginInitializer() { + super.beginInitializer(); + recordMark(Initializer); + } + + @Override + protected void beginTypeExpression() { + super.beginTypeExpression(); + recordMark(TypeExpression); + } + + @Override + protected void beginLabel() { + super.beginLabel(); + recordMark(Label); + } + + @Override + protected void beginLiteral() { + super.beginLiteral(); + recordMark(Literal); + } + + @Override + protected void beginMapLiteral() { + super.beginMapLiteral(); + recordMark(MapLiteral); + } + + @Override + protected void beginMapLiteralEntry() { + super.beginMapLiteralEntry(); + recordMark(MapLiteralEntry); + } + + @Override + protected void beginMethodName() { + super.beginMethodName(); + recordMark(MethodName); + } + + @Override + protected void beginNativeBody() { + super.beginNativeBody(); + recordMark(NativeBody); + } + + @Override + protected void beginNewExpression() { + super.beginNewExpression(); + recordMark(NewExpression); + } + + @Override + protected void beginOperatorName() { + super.beginOperatorName(); + recordMark(OperatorName); + } + + @Override + protected void beginParameter() { + super.beginParameter(); + // TODO(messick): add recording + } + + @Override + protected void beginParameterName() { + super.beginParameterName(); + // TODO(messick): add recording + } + + @Override + protected void beginParenthesizedExpression() { + super.beginParenthesizedExpression(); + recordMark(ParenthesizedExpression); + } + + @Override + protected void beginPostfixExpression() { + super.beginPostfixExpression(); + recordMark(PostfixExpression); + } + + @Override + protected void beginQualifiedIdentifier() { + super.beginQualifiedIdentifier(); + recordMark(QualifiedIdentifier); + } + + @Override + protected void beginReturnStatement() { + super.beginReturnStatement(); + recordMark(ReturnStatement); + } + + @Override + protected void beginReturnType() { + super.beginReturnType(); + // TODO(messick): add recording + } + + @Override + protected void beginSelectorExpression() { + super.beginSelectorExpression(); + recordMark(SelectorExpression); + } + + @Override + protected void beginSpreadExpression() { + super.beginSpreadExpression(); + recordMark(SpreadExpression); + } + + @Override + protected void beginStringInterpolation() { + super.beginStringInterpolation(); + recordMark(StringInterpolation); + } + + @Override + protected void beginStringSegment() { + super.beginStringSegment(); + recordMark(StringSegment); + } + + @Override + protected void beginSuperExpression() { + super.beginSuperExpression(); + recordMark(SuperExpression); + } + + @Override + protected void beginSuperInitializer() { + super.beginSuperInitializer(); + recordMark(SuperInitializer); + } + + @Override + protected void beginSwitchMember() { + super.beginSwitchMember(); + recordMark(SwitchMember); + } + + @Override + protected void beginSwitchStatement() { + super.beginSwitchStatement(); + recordMark(SwitchStatement); + } + + @Override + protected void beginThisExpression() { + super.beginThisExpression(); + recordMark(ThisExpression); + } + + @Override + protected void beginThrowStatement() { + super.beginThrowStatement(); + recordMark(ThrowStatement); + } + + @Override + protected void beginTopLevelElement() { + super.beginTopLevelElement(); + recordMark(TopLevelElement); + } + + @Override + protected void beginTryStatement() { + super.beginTryStatement(); + recordMark(TryStatement); + } + + @Override + protected void beginTypeAnnotation() { + super.beginTypeAnnotation(); + recordMark(TypeAnnotation); + } + + @Override + protected void beginTypeArguments() { + super.beginTypeArguments(); + recordMark(TypeArguments); + } + + @Override + protected void beginTypeFunctionOrVariable() { + super.beginTypeFunctionOrVariable(); + recordMark(TypeFunctionOrVarable); + } + + @Override + protected void beginTypeParameter() { + super.beginTypeParameter(); + recordMark(TypeParameter); + } + + @Override + protected void beginUnaryExpression() { + super.beginUnaryExpression(); + recordMark(UnaryExpression); + } + + @Override + protected void beginVarDeclaration() { + super.beginVarDeclaration(); + recordMark(VarDeclaration); + } + + @Override + protected void beginVariableDeclaration() { + super.beginVariableDeclaration(); + recordMark(VariableDeclaration); + } + + @Override + protected void beginWhileStatement() { + super.beginWhileStatement(); + recordMark(WhileStatement); + } + + private void recordMark(Mark mark) { + marks.add(mark); + } + } + + private ParserEventRecorder recorder = null; + + @Override + public void testListObjectLiterals() { + parseUnit("ListObjectLiterals.dart"); + } + + @Override + public void testCatchFinally() { + parseUnit("CatchFinally.dart"); + } + + @Override + public void testClasses() { + parseUnit("ClassesInterfaces.dart"); + compareMarks(ReturnStatement, TopLevelElement, Block, + ForStatement, ClassBody, FunctionLiteral, ParenthesizedExpression, TypeExpression, + MethodName, ConditionalExpression, BinaryExpression, FormalParameterList, + FunctionDeclaration, BreakStatement, PostfixExpression, SuperInitializer, TypeAnnotation, + ClassMember, VarDeclaration, SwitchMember, CompilationUnit, Expression, + TypeFunctionOrVarable, TypeParameter, ExpressionList, Identifier, + IfStatement, QualifiedIdentifier, SwitchStatement, + SelectorExpression, ForInitialization, CatchClause, CatchParameter, + FieldInitializerOrRedirectedConstructor, ContinueStatement, Label, TryStatement, Literal, + SpreadExpression, VariableDeclaration, TypeArguments, ExpressionStatement, ThrowStatement, + WhileStatement, Initializer); + } + + @Override + public void testFormalParameters() { + parseUnit("FormalParameters.dart"); + compareMarks(TopLevelElement, ClassMember, CompilationUnit, + BinaryExpression, NativeBody, Identifier, TypeAnnotation, ClassBody, + Literal, Block, PostfixExpression, MethodName, Expression, QualifiedIdentifier, + FormalParameterList, ConditionalExpression); + } + + @Override + public void testFunctionInterfaces() { + parseUnit("FunctionInterfaces.dart"); + compareMarks(TypeAnnotation, TopLevelElement, CompilationUnit, + FormalParameterList, QualifiedIdentifier, Identifier, FunctionTypeInterface); + } + + @Override + public void testFunctionTypes() { + parseUnit("FunctionTypes.dart"); + compareMarks(Identifier, TypeAnnotation, MethodName, CompilationUnit, ClassMember, + TopLevelElement, NativeBody, QualifiedIdentifier, FormalParameterList, ClassBody, Block, + VariableDeclaration); + } + + @Override + public void testGenericTypes() { + parseUnit("GenericTypes.dart"); + compareMarks(NativeBody, MethodName, ClassBody, ExpressionStatement, FormalParameterList, + Literal, ReturnStatement, Expression, TypeParameter, + PostfixExpression, TopLevelElement, ConditionalExpression, ThisExpression, ConstructorName, + NewExpression, QualifiedIdentifier, CompilationUnit, Block, + Identifier, VariableDeclaration, BinaryExpression, ClassMember, TypeAnnotation); + } + + @Override + public void testMethodSignatures() { + parseUnit("MethodSignatures.dart"); + compareMarks(TopLevelElement, ClassMember, TypeAnnotation, + CompilationUnit, MethodName, QualifiedIdentifier, Identifier, + ClassBody, FormalParameterList); + } + + @Override + public void testNewWithPrefix() { + parseUnit("NewWithPrefix.dart"); + compareMarks(VarDeclaration, ClassMember, ConstructorName, + ConditionalExpression, ConstExpression, Literal, NewExpression, Identifier, TypeAnnotation, + PostfixExpression, FormalParameterList, QualifiedIdentifier, TopLevelElement, + BinaryExpression, NativeBody, ClassBody, ExpressionStatement, MethodName, CompilationUnit, + VariableDeclaration, Block, Expression); + } + + @Override + public void testRedirectedConstructor() { + parseUnit("RedirectedConstructor.dart"); + compareMarks(BinaryExpression, CompilationUnit, FormalParameterList, ConditionalExpression, + Expression, PostfixExpression, ClassBody, ThisExpression, + Initializer, TopLevelElement, NativeBody, Block, MethodName, SuperInitializer, Literal, + ClassMember, Identifier, QualifiedIdentifier, FieldInitializerOrRedirectedConstructor, + TypeAnnotation, VariableDeclaration); + } + + @Override + public void testShifting() { + parseUnit("Shifting.dart"); + compareMarks(Expression, ClassBody, TypeFunctionOrVarable, VariableDeclaration, + CompilationUnit, ConditionalExpression, Identifier, ClassMember, + Block, QualifiedIdentifier, NativeBody, OperatorName, TypeAnnotation, TypeArguments, + PostfixExpression, BinaryExpression, FormalParameterList, TopLevelElement, Literal, + ReturnStatement); + } + + @Override + public void testStringBuffer() { + parseUnit("StringBuffer.dart"); + compareMarks(ClassBody, ConstructorName, TypeAnnotation, + TypeFunctionOrVarable, NativeBody, SelectorExpression, TopLevelElement, Block, + NewExpression, MethodName, Literal, Expression, CompilationUnit, Identifier, + ThrowStatement, PostfixExpression, BinaryExpression, ClassMember, VariableDeclaration, + ConditionalExpression, ExpressionStatement, ReturnStatement, IfStatement, + QualifiedIdentifier, FormalParameterList, FunctionLiteral); + } + + @Override + public void testStrings() { + parseUnit("Strings.dart"); + compareMarks(Expression, MethodName, ExpressionStatement, Block, ConditionalExpression, + ClassBody, TopLevelElement, VariableDeclaration, CompilationUnit, ClassMember, + PostfixExpression, BinaryExpression, VarDeclaration, Identifier, Literal, + FormalParameterList, NativeBody); + } + + @Override + public void testStringsErrors() { + parseUnitErrors("StringsErrorsNegativeTest.dart", "Unexpected token 'ILLEGAL'", 7, 13, + "Unexpected token 'ILLEGAL'", 9, 9); + compareMarks(NativeBody, TopLevelElement, PostfixExpression, ClassMember, CompilationUnit, + VariableDeclaration, Identifier, BinaryExpression, ClassBody, + ConditionalExpression, Literal, Expression, MethodName, ExpressionStatement, Block, + FormalParameterList, VarDeclaration); + } + + @Override + public void testSuperCalls() { + parseUnit("SuperCalls.dart"); + compareMarks(ExpressionStatement, ClassMember, SelectorExpression, + VariableDeclaration, BinaryExpression, Literal, Identifier, SuperExpression, + FormalParameterList, TopLevelElement, PostfixExpression, NativeBody, ClassBody, MethodName, + CompilationUnit, Expression, VarDeclaration, Block, ConditionalExpression); + } + + @Override + public void testTiming() { + // do nothing except stop broken superclass method from printing to console + } + + @Override + public void testTopLevel() { + parseUnit("TopLevel.dart"); + compareMarks(ArrayLiteral, CompilationUnit, PostfixExpression, NativeBody, Literal, Identifier, + ClassMember, Block, MethodName, TopLevelElement, TypeAnnotation, Expression, + QualifiedIdentifier, ConditionalExpression, BinaryExpression, VariableDeclaration, + FormalParameterList); + } + + @Override + protected DartParser makeParser(ParserContext context) { + recorder = new ParserEventRecorder(context); + return recorder; + } + + private void compareMarks(Mark... expectedMarks) { + HashSet recordedMarks = recorder.copyMarks(); + for (Mark m : expectedMarks) { + assertNotNull("Missing mark: " + m.name(), recordedMarks.remove(m)); + } + for (Mark m : recordedMarks) { + fail("Unexpected mark: " + m.name()); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/ParserRoundTripTest.java b/compiler/javatests/com/google/dart/compiler/parser/ParserRoundTripTest.java new file mode 100644 index 00000000000..f791fe17c9d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/ParserRoundTripTest.java @@ -0,0 +1,78 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.ast.DartToSourceVisitor; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.util.DefaultTextOutput; + +/** + * Tests, for each of the parser test examples, that {@link DartToSourceVisitor} produces + * something parseable. It's an imperfect test, as it doesn't account for semantic changes, + * but it catches a lot of common problems. + */ +public class ParserRoundTripTest extends CompilerTestCase { + + public void testClasses() { + roundTrip("ClassesInterfaces.dart"); + } + + public void testMethodSignatures() { + roundTrip("MethodSignatures.dart"); + } + + public void testFunctionTypes() { + roundTrip("FunctionTypes.dart"); + } + + public void testFormalParameters() { + roundTrip("FormalParameters.dart"); + } + + public void testSuperCalls() { + roundTrip("SuperCalls.dart"); + } + + public void testGenericTypes() { + roundTrip("GenericTypes.dart"); + } + + public void testShifting() { + roundTrip("Shifting.dart"); + } + + public void testFunctionInterfaces() { + roundTrip("FunctionInterfaces.dart"); + } + + public void testStringBuffer() { + roundTrip("StringBuffer.dart"); + } + + public void testListObjectLiterals() { + roundTrip("ListObjectLiterals.dart"); + } + + public void testCatchFinally() { + roundTrip("CatchFinally.dart"); + } + + public void testStrings() { + roundTrip("Strings.dart"); + } + + /** + * Ensures that a unit can be parsed, re-serialized, and re-parsed without error. + */ + private void roundTrip(String path) { + DartUnit unit = parseUnit(path); + + DefaultTextOutput out = new DefaultTextOutput(false); + new DartToSourceVisitor(out, false).accept(unit); + String src = out.toString(); + parseUnit(path, src); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/ParserTests.java b/compiler/javatests/com/google/dart/compiler/parser/ParserTests.java new file mode 100644 index 00000000000..c7e7548a43c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/ParserTests.java @@ -0,0 +1,31 @@ +// 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. + +package com.google.dart.compiler.parser; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ParserTests extends TestSetup { + + public ParserTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart parser test suite."); + + suite.addTestSuite(SyntaxTest.class); + suite.addTestSuite(DietParserTest.class); + suite.addTestSuite(CPParserTest.class); + suite.addTestSuite(ParserRoundTripTest.class); + suite.addTestSuite(LibraryParserTest.class); + suite.addTestSuite(ValidatingSyntaxTest.class); + suite.addTestSuite(CommentTest.class); + suite.addTestSuite(ErrorMessageLocationTest.class); + suite.addTestSuite(ParserEventsTest.class); + return new ParserTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/RedirectedConstructor.dart b/compiler/javatests/com/google/dart/compiler/parser/RedirectedConstructor.dart new file mode 100644 index 00000000000..5095a5c77c3 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/RedirectedConstructor.dart @@ -0,0 +1,34 @@ +// 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. + +class Simple { + int x; + Simple(this.x) { } + Simple.foo(x,y) : this(x + y); + Simple.bar(x,y,z) : this.foo(x + y, z); +} + +class Point { + final num x; + final num y; + Point() : this.coord(0, 0); // Redirects to Point.coord. + Point.coord(this.x, this.y) {} +} + +class A { + var x; + A() : this.named(499); + A.named(this.x) {} +} + +class B extends A { + B() : super() {} +} + +class C { + int x; + const C() : this.x = 123; + C.foo() : this(); +} + diff --git a/compiler/javatests/com/google/dart/compiler/parser/Shifting.dart b/compiler/javatests/com/google/dart/compiler/parser/Shifting.dart new file mode 100644 index 00000000000..5400639eb4a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/Shifting.dart @@ -0,0 +1,15 @@ +// 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. + +class Shifting { + operator >>>(other) { + Box>> foo = null; + return other >>> 1; + } + + operator >>(other) { + Box> foo = null; + return other >> 1; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/StringBuffer.dart b/compiler/javatests/com/google/dart/compiler/parser/StringBuffer.dart new file mode 100644 index 00000000000..5a76db5b020 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/StringBuffer.dart @@ -0,0 +1,88 @@ +// 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. + +/** + * The StringBuffer class is useful for concatenating strings + * efficiently. Only on a call to [toString] are the strings + * concatenated to a single String. + */ +class StringBuffer implements OutputStream { + /** + * Creates the string buffer with an initial content. + */ + StringBuffer([String content = ""]) { + clear(); + append(content); + } + + /// From OutputStream. Appends [str] to the buffer. + void writeString(String str) { + append(str); + } + + /// From OutputStream. Appends the [charCode] to the buffer. + void writeCharCode(int charCode) { + throw "StringBuffer.writeCharCode Unimplemented"; + } + + void writeByte(int value) { + throw "StringBuffer.writeByte unimplemented"; + } + + void writeByteArray(Array buffer, int offset, int length) { + throw "StringBuffer.writeByteArray unimplemented"; + } + + void close() {} + void flush() {} + + + /** + * Returns the length of the buffer. + */ + int get length() { + return length_; + } + + /** + * Appends [str] to the buffer. + */ + void append(String str) { + if (str === null || str.isEmpty()) return; + buffer_.add(str); + length_ += str.length; + } + + /** + * Appends all items in [strings] to the buffer. + */ + void appendAll(Collection strings) { + strings.forEach((str) { append(str); }); + } + + /** + * Clears the string buffer. + */ + void clear() { + buffer_ = new GrowableArray(4); + length_ = 0; + } + + /** + * Returns the contents of buffer as a concatenated string. + */ + String toString() { + if (buffer_.length == 0) return ""; + if (buffer_.length == 1) return buffer_[0]; + String result = StringBase.concatAll(_buffer); + buffer_.clear(); + buffer_.add(result); + // Since we track the length at each append operation, there is no + // need to update it in this function. + return result; + } + + GrowableArray buffer_; + int length_; +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/Strings.dart b/compiler/javatests/com/google/dart/compiler/parser/Strings.dart new file mode 100644 index 00000000000..9d2b19d213f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/Strings.dart @@ -0,0 +1,33 @@ +// 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. + +class StringsTests { + method() { + var x; + x = "a simple constant"; + x = 'a simple constant'; + + x = "an escaped quote \"."; + x = 'an escaped quote \'.'; + + x = "a new \n line"; + x = 'a new \n line'; + + x = """ + multiline 1 + multiline 2 + """; + x = ''' + multiline 1 + multiline 2 + '''; + + x = """multiline 1 + multiline 2 + """; + x = '''multiline 1 + multiline 2 + '''; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/StringsErrorsNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/parser/StringsErrorsNegativeTest.dart new file mode 100644 index 00000000000..9a40bbcf531 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/StringsErrorsNegativeTest.dart @@ -0,0 +1,12 @@ +// 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. + +class StringsWithErrorsTest { + errors() { + var x = "unterminated string + ; // added to avoid a cascading failure + x = 'another unterminated string + ; // added to avoid a cascading failure + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/SuperCalls.dart b/compiler/javatests/com/google/dart/compiler/parser/SuperCalls.dart new file mode 100644 index 00000000000..d5f7fdfd36a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/SuperCalls.dart @@ -0,0 +1,29 @@ +// 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. + +class SuperCallSyntax { + method() { + super.foo(); + super.foo(1); + super.foo(1, 2); + + super.foo().x; + super.foo()[42]; + super.foo().x++; + + super.foo()(); + super.foo(1, 2)(3, 4); + + var v1 = super.foo(); + var v2 = super.foo(1); + var v3 = super.foo(1, 2); + + var v4 = super.foo().x; + var v5 = super.foo()[42]; + var v6 = super.foo().x++; + + var v7 = super.foo()(); + var v8 = super.foo(1, 2)(3, 4); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java b/compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java new file mode 100644 index 00000000000..fa39da138d7 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/SyntaxTest.java @@ -0,0 +1,175 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DartSourceTest; +import com.google.dart.compiler.ast.DartArrayLiteral; +import com.google.dart.compiler.ast.DartBinaryExpression; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartField; +import com.google.dart.compiler.ast.DartFieldDefinition; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartIntegerLiteral; +import com.google.dart.compiler.ast.DartMapLiteral; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartTryStatement; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartVariableStatement; + +import java.util.List; + +public class SyntaxTest extends AbstractParserTest { + + @Override + public void testStrings() { + DartUnit unit = parseUnit("Strings.dart"); + + // Inspect the first method and check that the strings were + // parsed correctly + List nodes = unit.getTopLevelNodes(); + assertEquals(1, nodes.size()); + DartClass clazz = (DartClass) nodes.get(0); + List members = clazz.getMembers(); + assertEquals(1, members.size()); + DartMethodDefinition m = (DartMethodDefinition) members.get(0); + assertEquals("method", m.getName().toString()); + List body = m.getFunction().getBody().getStatements(); + + String[] expectedStrings = new String[] { + "a simple constant", + "a simple constant", + "an escaped quote \".", + "an escaped quote \'.", + "a new \n line", + "a new \n line", + " multiline 1\n multiline 2\n ", + " multiline 1\n multiline 2\n ", + "multiline 1\n multiline 2\n ", + "multiline 1\n multiline 2\n "}; + assertEquals(expectedStrings.length + 1, body.size()); + assertTrue(body.get(0) instanceof DartVariableStatement); + for (int i = 0; i < expectedStrings.length; i++) { + DartStatement s = body.get(i + 1); + assertTrue(s instanceof DartExprStmt); + DartExprStmt es = (DartExprStmt) s; + DartExpression e = es.getExpression(); + assertTrue(e instanceof DartBinaryExpression); + e = ((DartBinaryExpression) e).getArg2(); + assertTrue(e instanceof DartStringLiteral); + assertEquals(expectedStrings[i], ((DartStringLiteral) e).getValue()); + } + } + + @Override + public void testStringsErrors() { + parseUnitErrors("StringsErrorsNegativeTest.dart", + "Unexpected token 'ILLEGAL'", 7, 13, + "Unexpected token 'ILLEGAL'", 9, 9); + } + + public void testNullAssign() { + String sourceCode = "= 123;"; + try { + DartSourceTest dartSrc = new DartSourceTest(getName(), sourceCode, null); + DartScannerParserContext context = + new DartScannerParserContext(dartSrc, sourceCode, new DartCompilerListener() { + @Override + public void typeError(DartCompilationError event) { + } + @Override + public void compilationWarning(DartCompilationError event) { + } + @Override + public void compilationError(DartCompilationError event) { + } + }); + DartParser parser = new DartParser(context); + parser.parseExpression(); + } + catch(Exception e) { + fail("unexpected exception " + e); + } + } + + public void testFactoryInitializerError() { + parseUnitErrors("FactoryInitializersNegativeTest.dart", + "Unexpected token ':' (expected '{')", 10, 22, + "Unexpected token '{' (expected ';')", 10, 35); + } + + public void testTryCatch () { + DartUnit unit = parseUnit("TryCatch.dart"); + + List nodes = unit.getTopLevelNodes(); + assertEquals(7, nodes.size()); + + DartTryStatement tryCatch; + DartMethodDefinition a = (DartMethodDefinition) nodes.get(2); + assertEquals("a", ((DartIdentifier)a.getName()).getTargetName()); + tryCatch = (DartTryStatement) a.getFunction().getBody().getStatements().get(0); + assertEquals(1, tryCatch.getCatchBlocks().size()); + assertNotNull(tryCatch.getFinallyBlock()); + + DartMethodDefinition b = (DartMethodDefinition) nodes.get(3); + assertEquals("b", ((DartIdentifier)b.getName()).getTargetName()); + tryCatch = (DartTryStatement) b.getFunction().getBody().getStatements().get(0); + assertEquals(1, tryCatch.getCatchBlocks().size()); + assertNull(tryCatch.getFinallyBlock()); + + DartMethodDefinition c = (DartMethodDefinition) nodes.get(4); + assertEquals("c", ((DartIdentifier)c.getName()).getTargetName()); + tryCatch = (DartTryStatement) c.getFunction().getBody().getStatements().get(0); + assertEquals(0, tryCatch.getCatchBlocks().size()); + assertNotNull(tryCatch.getFinallyBlock()); + + DartMethodDefinition d = (DartMethodDefinition) nodes.get(5); + assertEquals("d", ((DartIdentifier)d.getName()).getTargetName()); + tryCatch = (DartTryStatement) d.getFunction().getBody().getStatements().get(0); + assertEquals(2, tryCatch.getCatchBlocks().size()); + assertNull(tryCatch.getFinallyBlock()); + + DartMethodDefinition e = (DartMethodDefinition) nodes.get(6); + assertEquals("e", ((DartIdentifier)e.getName()).getTargetName()); + tryCatch = (DartTryStatement) e.getFunction().getBody().getStatements().get(0); + assertEquals(2, tryCatch.getCatchBlocks().size()); + assertNotNull(tryCatch.getFinallyBlock()); + + parseUnitErrors("TryCatchNegative.dart", + DartCompilerErrorCode.CATCH_OR_FINALLY_EXPECTED, 8, 3); + } + + public void testArrayLiteral() { + DartUnit unit = parseUnit("phony_array_literal.dart", "var x = [1,2,3];"); + List nodes = unit.getTopLevelNodes(); + assertEquals(1, nodes.size()); + DartFieldDefinition f = (DartFieldDefinition)nodes.get(0); + DartField fieldX = (DartField)f.getFields().get(0); + DartArrayLiteral array = (DartArrayLiteral) fieldX.getValue(); + assertEquals(3, array.getExpressions().size()); + assertEquals(1, ((DartIntegerLiteral)array.getExpressions().get(0)).getValue().intValue()); + assertEquals(2, ((DartIntegerLiteral)array.getExpressions().get(1)).getValue().intValue()); + assertEquals(3, ((DartIntegerLiteral)array.getExpressions().get(2)).getValue().intValue()); + } + + public void testMapLiteral() { + DartUnit unit = parseUnit("phony_map_literal.dart", "var x = {'a':1,'b':2,'c':3};"); + List nodes = unit.getTopLevelNodes(); + assertEquals(1, nodes.size()); + DartFieldDefinition f = (DartFieldDefinition)nodes.get(0); + DartField fieldX = (DartField)f.getFields().get(0); + DartMapLiteral map = (DartMapLiteral) fieldX.getValue(); + assertEquals(3, map.getEntries().size()); + assertEquals(1, ((DartIntegerLiteral) (map.getEntries().get(0)).getValue()).getValue() + .intValue()); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/TerminationTest.java b/compiler/javatests/com/google/dart/compiler/parser/TerminationTest.java new file mode 100644 index 00000000000..df093bdfe62 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/TerminationTest.java @@ -0,0 +1,125 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.CompilerTestCase; + +public class TerminationTest extends CompilerTestCase { + + public void testNestedStatement() { + assertTrue(DartParserRunner.parse("testNestedStatement", + "class A { String foo foo; }").hasErrors()); + } + + public void testTypeParameterList() { + assertTrue(DartParserRunner.parse("testTypeParameterList", + "class A { }").hasErrors()); + } + + public void testTypeParameterList2() { + assertTrue(DartParserRunner.parse("testTypeParameterList2", + "class A c; +final List bar = [1, 2]; + +void foo() {} + +List bar() {} + +foobar() {} diff --git a/compiler/javatests/com/google/dart/compiler/parser/TryCatch.dart b/compiler/javatests/com/google/dart/compiler/parser/TryCatch.dart new file mode 100644 index 00000000000..dda83b1a5f7 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/TryCatch.dart @@ -0,0 +1,46 @@ +// 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. + +interface TestException1 { + int foo(); +} + +interface TestException2 { + int bar(); +} + +a() { + try { + } catch (var e) { + } finally { + } +} + +b() { + try { + } catch (var e) { + } +} + +c() { + try { + } finally { + } +} + +d() { + try { + } catch (TestException1 e) { + } catch (TestException2 e) { + } +} + +e() { + try { + } catch (TestException1 e) { + } catch (TestException2 e) { + } finally { + } +} + diff --git a/compiler/javatests/com/google/dart/compiler/parser/TryCatchNegative.dart b/compiler/javatests/com/google/dart/compiler/parser/TryCatchNegative.dart new file mode 100644 index 00000000000..9e3eceea254 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/TryCatchNegative.dart @@ -0,0 +1,9 @@ +// 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. + +a() { + try { + var i = 1; + } // expect catch or finally here. +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/ValidatingSyntaxTest.java b/compiler/javatests/com/google/dart/compiler/parser/ValidatingSyntaxTest.java new file mode 100755 index 00000000000..1d59546c670 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/ValidatingSyntaxTest.java @@ -0,0 +1,90 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.ast.DartBinaryExpression; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartMethodDefinition; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartStringLiteral; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.ast.DartVariableStatement; + +import java.util.List; + +/** + * Tests for the parser, which simply assert that valid source units parse + * correctly. All tests invoking {@link #parseUnit} are designed such that they + * will throw an exception if anything goes wrong in the parser. + */ +public class ValidatingSyntaxTest extends AbstractParserTest { + + @Override + public void testStrings() { + DartUnit unit = parseUnit("Strings.dart"); + + // Inspect the first method and check that the strings were + // parsed correctly + List nodes = unit.getTopLevelNodes(); + assertEquals(1, nodes.size()); + DartClass clazz = (DartClass) nodes.get(0); + List members = clazz.getMembers(); + assertEquals(1, members.size()); + DartMethodDefinition m = (DartMethodDefinition) members.get(0); + assertEquals("method", m.getName().toString()); + List body = m.getFunction().getBody().getStatements(); + + String[] expectedStrings = new String[] { + "a simple constant", + "a simple constant", + "an escaped quote \".", + "an escaped quote \'.", + "a new \n line", + "a new \n line", + " multiline 1\n multiline 2\n ", + " multiline 1\n multiline 2\n ", + "multiline 1\n multiline 2\n ", + "multiline 1\n multiline 2\n "}; + assertEquals(expectedStrings.length + 1, body.size()); + assertTrue(body.get(0) instanceof DartVariableStatement); + for (int i = 0; i < expectedStrings.length; i++) { + DartStatement s = body.get(i + 1); + assertTrue(s instanceof DartExprStmt); + DartExprStmt es = (DartExprStmt) s; + DartExpression e = es.getExpression(); + assertTrue(e instanceof DartBinaryExpression); + e = ((DartBinaryExpression) e).getArg2(); + assertTrue(e instanceof DartStringLiteral); + assertEquals(expectedStrings[i], ((DartStringLiteral) e).getValue()); + } + } + + @Override + public void testStringsErrors() { + parseUnitErrors("StringsErrorsNegativeTest.dart", + "Unexpected token 'ILLEGAL'", 7, 13, + "Unexpected token 'ILLEGAL'", 9, 9); + } + + @Override + protected DartUnit parseUnit(String srcName, String sourceCode) { + return validateUnit(super.parseUnit(srcName, sourceCode)); + } + + private DartUnit validateUnit(DartUnit unit) { + DartASTValidator validator = new DartASTValidator(); + unit.accept(validator); + validator.assertValid(); + return unit; + } + + @Override + public void testTiming() { + // Ignored. + } +} diff --git a/compiler/javatests/com/google/dart/compiler/parser/VoidTest.java b/compiler/javatests/com/google/dart/compiler/parser/VoidTest.java new file mode 100644 index 00000000000..67f2351fbb7 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/parser/VoidTest.java @@ -0,0 +1,82 @@ +// 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. + +package com.google.dart.compiler.parser; + +import com.google.dart.compiler.CompilerTestCase; + +/** + * Tests for void-as-a-keyword. This could be extended to be a general keyword test. + */ +public class VoidTest extends CompilerTestCase { + private void assertHasErrorsEquals(boolean expected, String code) { + DartParserRunner runner = DartParserRunner.parse(getName(), code); + assertEquals(expected, runner.hasErrors()); + } + + private void assertHasErrorsIsFalse(String code) { + assertHasErrorsEquals(false, code); + } + + private void assertHasErrorsIsTrue(String code) { + assertHasErrorsEquals(true, code); + } + + public void testExtendVoid() { + assertHasErrorsIsTrue("class A extends void { }"); + } + + public void testImplementsVoid() { + assertHasErrorsIsTrue("class A implements void { }"); + } + + public void testVoidClass() { + assertHasErrorsIsTrue("class void { }"); + assertHasErrorsIsTrue("class void { }"); + } + + public void testVoidFactory() { + assertHasErrorsIsTrue("interface A factory void { }"); + } + + public void testVoidFieldType() { + assertHasErrorsIsTrue("class A { void x; }"); + } + + public void testVoidFunctionAlias() { + assertHasErrorsIsFalse("typedef void a();"); + } + + public void testVoidFunctionParamName() { + assertHasErrorsIsTrue("class A { int x( int void ) {} }"); + } + + public void testVoidFunctionParamType() { + assertHasErrorsIsFalse("class A { int x( void y() ) {} }"); + } + + public void testVoidFunctionLiteral() { + assertHasErrorsIsTrue("class A { int x() { function void(){} } }"); + } + + public void testVoidLocalVar() { + assertHasErrorsIsTrue("class A { void x() { void y; } }"); + } + + public void testVoidLocalVarName() { + assertHasErrorsIsTrue("class A { void x() { int void; } }"); + } + + public void testVoidMethodName() { + assertHasErrorsIsTrue("class A { int void() {} }"); + } + + public void testVoidParamType() { + assertHasErrorsIsTrue("class A { int x( void y ) {} }"); + } + + public void testVoidTypeParameter() { + assertHasErrorsIsTrue("class a { }"); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/BadNamedConstructorNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/BadNamedConstructorNegativeTest.dart new file mode 100644 index 00000000000..072f2060fff --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/BadNamedConstructorNegativeTest.dart @@ -0,0 +1,7 @@ +// 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. + +class BadNamedConstructorNegativeTest { + A.foo() {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ClassExtendsInterfaceNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/ClassExtendsInterfaceNegativeTest.dart new file mode 100644 index 00000000000..2a1c37be374 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ClassExtendsInterfaceNegativeTest.dart @@ -0,0 +1,12 @@ +// 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. + +// Expect error - I is not a class. + +interface I {} + +class Base extends I { +} + + diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsClassNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsClassNegativeTest.dart new file mode 100644 index 00000000000..146dcd6da31 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsClassNegativeTest.dart @@ -0,0 +1,12 @@ +// 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. + +// Expect error - Base is not an interface. + +class Base { +} + +class Subclass implements Base {} + + diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsUnknownInterfaceNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsUnknownInterfaceNegativeTest.dart new file mode 100644 index 00000000000..94ed0c82294 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ClassImplementsUnknownInterfaceNegativeTest.dart @@ -0,0 +1,10 @@ +// 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. + +// Expect error - Base is not an interface. + +interface I { +} + +interface I extends UNKNOWN; diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ConstRedirectedConstructorNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/ConstRedirectedConstructorNegativeTest.dart new file mode 100644 index 00000000000..7132c972efd --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ConstRedirectedConstructorNegativeTest.dart @@ -0,0 +1,11 @@ +// 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. + +// expect failure - final constructor redirects to non-const constructor. + +class A { + const A(x) : this.foo(x); + A.foo(this.x) { } + var x; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest1.dart b/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest1.dart new file mode 100644 index 00000000000..f8cc9dcd200 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest1.dart @@ -0,0 +1,14 @@ +// 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. + +// Expect error - Sub omits call to Base final ctor in the init list. + +class Base { + const Base(); +} + +class Sub extends Base { + const Sub(a) : this.a_ = a; + final a_; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest2.dart b/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest2.dart new file mode 100644 index 00000000000..e273f121135 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest2.dart @@ -0,0 +1,13 @@ +// 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. + +// expect error - Sub calls a non-const super. +class Base { + Base() { } +} + +class Sub extends Base { + const Sub(a) : super(1), this.a_ = a; + final a_; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest3.dart b/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest3.dart new file mode 100644 index 00000000000..617e4cfb0c1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ConstSuperNegativeTest3.dart @@ -0,0 +1,17 @@ +// 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. + +// expect one failure - Sub.A omits call to const super. + +class Base { + const Base(a); + Base.A(a,b) { } +} + +class Sub extends Base { + const Sub(a) : super(a), this.a_ = a; + const Sub.A(a) : this.a_ = a; + const Sub.B(a) : super(a), this.a_ = a; + final a_; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest1.dart b/compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest1.dart new file mode 100644 index 00000000000..8f8ff13296d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest1.dart @@ -0,0 +1,12 @@ +// 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. + +// case 1 - const variable must be initialized. + +class A { + static foo() { + final Object x; + } +} + diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest2.dart b/compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest2.dart new file mode 100644 index 00000000000..d8731f021a2 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ConstVariableInitializationNegativeTest2.dart @@ -0,0 +1,12 @@ +// 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. + +// case 2 - const variable must be initialized (variable list). + +class A { + static foo() { + final Object x = 1, y, z = 3; + } +} + diff --git a/compiler/javatests/com/google/dart/compiler/resolver/CyclicRedirectedConstructorNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/CyclicRedirectedConstructorNegativeTest.dart new file mode 100644 index 00000000000..b2a2ee3b706 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/CyclicRedirectedConstructorNegativeTest.dart @@ -0,0 +1,9 @@ +// 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. + +class A { + A(x) : this.foo(x); + A.foo(x) : this.bar(x, x * 2); + A.bar(x,y) : this(x + y); +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/Initializer1NegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/Initializer1NegativeTest.dart new file mode 100644 index 00000000000..ed326a0e498 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/Initializer1NegativeTest.dart @@ -0,0 +1,9 @@ +// 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. + +// Check that an unresolved field in an initializer is an error. + +class A { + A() : this.a = 1 {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/Initializer2NegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/Initializer2NegativeTest.dart new file mode 100644 index 00000000000..e0bea1c56d8 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/Initializer2NegativeTest.dart @@ -0,0 +1,10 @@ +// 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. + +// Check that a static field in an initializer is an error. + +class A { + static var a; + A() : this.a = 1 {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/Initializer3NegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/Initializer3NegativeTest.dart new file mode 100644 index 00000000000..01598fe2f93 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/Initializer3NegativeTest.dart @@ -0,0 +1,14 @@ +// 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. + +// Check that a super field in an initializer is an error. + +class B { + var a; + B() : this.a = 1 {} +} + +class A extends B { + A() : super(), this.a = 1 {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/Initializer4NegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/Initializer4NegativeTest.dart new file mode 100644 index 00000000000..00f88d17449 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/Initializer4NegativeTest.dart @@ -0,0 +1,11 @@ +// 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. + +// Check that using an instance field in an initializer expression is an error. + +class A { + var a; + var b; + A() : this.a = b {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/Initializer5NegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/Initializer5NegativeTest.dart new file mode 100644 index 00000000000..27cd0e37da1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/Initializer5NegativeTest.dart @@ -0,0 +1,10 @@ +// 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. + +// Check that using an undefined variable in an initializer expression is an error. + +class A { + final aa; + A(var a) : this.aa = c {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/Initializer6NegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/Initializer6NegativeTest.dart new file mode 100644 index 00000000000..b0c1c76f7e8 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/Initializer6NegativeTest.dart @@ -0,0 +1,10 @@ +// 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. + +// Check that in a class without a supertype referencing super in the initializer expression is an +// error. + +class A { + A() : super(1) {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest1.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest1.dart new file mode 100644 index 00000000000..93dbc78f163 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest1.dart @@ -0,0 +1,10 @@ +// 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. + +// expect failure - multiple unnamed constructor definitions. + +class A { + A(x) { } + A(x,y) { } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest10.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest10.dart new file mode 100644 index 00000000000..245dc098b62 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest10.dart @@ -0,0 +1,11 @@ +// 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. + +// expect failure - field shadows another field. + +class A { + var _a; + static foo(a,b) { } + var _a; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest11.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest11.dart new file mode 100644 index 00000000000..27a494b6789 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest11.dart @@ -0,0 +1,10 @@ +// 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. + +// expect failure - setter shadows method. + +class Foo { + m() {} + set m(x) {} +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest2.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest2.dart new file mode 100644 index 00000000000..0488d943f16 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest2.dart @@ -0,0 +1,11 @@ +// 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. + +// expect failure - multiple unnamed constructor definitions. +// make sure modifiers works as expected. + +class A { + A(x) { } + const A(x,y); +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest4.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest4.dart new file mode 100644 index 00000000000..0635d127bf2 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest4.dart @@ -0,0 +1,10 @@ +// 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. + +// expect failure - method shadows another method. + +class A { + foo() { } + foo() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest5.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest5.dart new file mode 100644 index 00000000000..fa66460d622 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest5.dart @@ -0,0 +1,10 @@ +// 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. + +// expect failure - named constructor shadows another named constructor. + +class A { + A.foo() { } + A.foo() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest6.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest6.dart new file mode 100644 index 00000000000..b8b55d0c5ab --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest6.dart @@ -0,0 +1,10 @@ +// 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. + +// expect failure - field shadows method. + +class A { + foo() { } + var foo; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest7.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest7.dart new file mode 100644 index 00000000000..1641e4ba5b6 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest7.dart @@ -0,0 +1,11 @@ +// 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. + +// expect failure - field shadows setter/getter (expect only 1 error). + +class A { + set foo(x) { } + get foo() { } + var foo; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest8.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest8.dart new file mode 100644 index 00000000000..7ef741dbfe1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest8.dart @@ -0,0 +1,11 @@ +// 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. + +// expect failure - setter shadows another setter. + +class A { + set foo(x) { } + get foo() { } + set foo(x) { } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest9.dart b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest9.dart new file mode 100644 index 00000000000..931b8ab89b0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NameShadowNegativeTest9.dart @@ -0,0 +1,10 @@ +// 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. + +// expect failure - static method shadows instance method. + +class A { + foo(x) { } + static foo(a,b) { } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/NegativeResolverTest.java b/compiler/javatests/com/google/dart/compiler/resolver/NegativeResolverTest.java new file mode 100644 index 00000000000..bbcc0ba2caa --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/NegativeResolverTest.java @@ -0,0 +1,189 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.CompilerTestCase; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.testing.TestCompilerContext; + +import java.util.ArrayList; +import java.util.List; + +// TODO(ngeoffray): Move these tests to the VM tests once we can run VM tests. +public class NegativeResolverTest extends CompilerTestCase { + List errors = new ArrayList(); + List typeErrors = new ArrayList(); + + public void checkNumErrors(String fileName, int expectedErrorCount) { + DartUnit unit = parseUnit(fileName); + unit.addTopLevelNode(ResolverTestCase.makeClass("Object", null)); + unit.addTopLevelNode(ResolverTestCase.makeClass("Function", null)); + ResolverTestCase.resolve(unit, getContext()); + assertEquals(new ArrayList(), typeErrors); + if (errors.size() != expectedErrorCount) { + fail(String.format("Expected %s errors, but got %s: %s", + expectedErrorCount, errors.size(), errors)); + } + } + + public void testInitializer1() { + checkNumErrors("Initializer1NegativeTest.dart", 1); + } + + public void testInitializer2() { + checkNumErrors("Initializer2NegativeTest.dart", 1); + } + + public void testInitializer3() { + checkNumErrors("Initializer3NegativeTest.dart", 1); + } + + public void testInitializer4() { + checkNumErrors("Initializer4NegativeTest.dart", 1); + } + + public void testInitializer5() { + checkNumErrors("Initializer5NegativeTest.dart", 1); + } + + public void testInitializer6() { + checkNumErrors("Initializer6NegativeTest.dart", 1); + } + + public void testCall1() { + checkNumErrors("StaticInstanceCallNegativeTest.dart", 1); + } + + public void testClassExtendsInterfaceNegativeTest() { + checkNumErrors("ClassExtendsInterfaceNegativeTest.dart", 1); + } + + public void tesClassImplementsUnknownInterfaceNegativeTest() { + checkNumErrors("ClassImplementsUnknownInterfaceNegativeTest.dart", 1); + } + + public void testConstSuperNegativeTest1() { + checkNumErrors("ConstSuperNegativeTest1.dart", 1); + } + + public void testConstSuperNegativeTest2() { + checkNumErrors("ConstSuperNegativeTest2.dart", 1); + } + + public void testConstSuperNegativeTest3() { + checkNumErrors("ConstSuperNegativeTest3.dart", 1); + } + + public void testParameterInitializerNegativeTest1() { + checkNumErrors("ParameterInitializerNegativeTest1.dart", 1); + } + + public void testParameterInitializerNegativeTest2() { + checkNumErrors("ParameterInitializerNegativeTest2.dart", 1); + } + + public void testParameterInitializerNegativeTest3() { + checkNumErrors("ParameterInitializerNegativeTest3.dart", 1); + } + + public void testStaticToInstanceInvocationNegativeTest1() { + checkNumErrors("StaticToInstanceInvocationNegativeTest1.dart", 1); + } + + public void testStaticToInstanceInvocationNegativeTest2() { + checkNumErrors("StaticToInstanceInvocationNegativeTest2.dart", 1); + } + + public void testConstVariableInitializationNegativeTest1() { + checkNumErrors("ConstVariableInitializationNegativeTest1.dart", 1); + } + + public void testConstVariableInitializationNegativeTest2() { + checkNumErrors("ConstVariableInitializationNegativeTest2.dart", 1); + } + + public void testNameShadowNegativeTest1() { + checkNumErrors("NameShadowNegativeTest1.dart", 1); + } + + public void testNameShadowNegativeTest2() { + checkNumErrors("NameShadowNegativeTest2.dart", 1); + } + + public void testNameShadowNegativeTest4() { + checkNumErrors("NameShadowNegativeTest4.dart", 1); + } + + public void testNameShadowNegativeTest5() { + checkNumErrors("NameShadowNegativeTest5.dart", 1); + } + + public void testNameShadowNegativeTest6() { + checkNumErrors("NameShadowNegativeTest6.dart", 1); + } + + public void testNameShadowNegativeTest7() { + checkNumErrors("NameShadowNegativeTest7.dart", 1); + } + + public void testNameShadowNegativeTest8() { + checkNumErrors("NameShadowNegativeTest8.dart", 1); + } + + public void testNameShadowNegativeTest9() { + checkNumErrors("NameShadowNegativeTest9.dart", 1); + } + + public void testNameShadowNegativeTest10() { + checkNumErrors("NameShadowNegativeTest10.dart", 1); + } + + public void testNameShadowNegativeTest11() { + checkNumErrors("NameShadowNegativeTest11.dart", 1); + } + + public void testUnresolvedSuperFieldNegativeTest() { + checkNumErrors("UnresolvedSuperFieldNegativeTest.dart", 1); + } + + public void testStaticSuperFieldNegativeTest() { + checkNumErrors("StaticSuperFieldNegativeTest.dart", 1); + } + + public void testStaticSuperGetterNegativeTest() { + checkNumErrors("StaticSuperGetterNegativeTest.dart", 1); + } + + public void testStaticSuperMethodNegativeTest() { + checkNumErrors("StaticSuperMethodNegativeTest.dart", 1); + } + + public void testBadNamedConstructorNegativeTest() { + checkNumErrors("BadNamedConstructorNegativeTest.dart", 1); + } + + public void testCyclicRedirectedConstructorNegativeTest() { + checkNumErrors("CyclicRedirectedConstructorNegativeTest.dart", 3); + } + + public void testConstRedirectedConstructorNegativeTest() { + checkNumErrors("ConstRedirectedConstructorNegativeTest.dart", 1); + } + + private TestCompilerContext getContext() { + return new TestCompilerContext() { + @Override + public void compilationError(DartCompilationError event) { + errors.add(event); + } + + @Override + public void typeError(DartCompilationError event) { + typeErrors.add(event); + } + }; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest1.dart b/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest1.dart new file mode 100644 index 00000000000..8c1071753bc --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest1.dart @@ -0,0 +1,10 @@ +// 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. + +// expect error - parameter initializer does not resolve to a field. + +class A { + A(this.a) { } + Object b; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest2.dart b/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest2.dart new file mode 100644 index 00000000000..423311593c7 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest2.dart @@ -0,0 +1,12 @@ +// 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. + +// expect error - parameter initializer is not valid in ordinary methods. + +class A { + A(this.x) { } + A.myctor(this.x) { } + foo(Object y, this.x) { } // expect to fail. + Object x; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest3.dart b/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest3.dart new file mode 100644 index 00000000000..4677564c677 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ParameterInitializerNegativeTest3.dart @@ -0,0 +1,10 @@ +// 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. + +// expect error - parameter initializer cannot initialize static fields. + +class A { + A(this.x) { } // expect error - cannot use param initializer to initialize a static field. + static Object x; +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java new file mode 100644 index 00000000000..30acde448a4 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTest.java @@ -0,0 +1,360 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.common.base.Joiner; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.parser.DartParser; +import com.google.dart.compiler.parser.DartScannerParserContext; +import com.google.dart.compiler.testing.TestCompilerContext; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.Types; +import com.google.dart.compiler.util.DartSourceString; + +import junit.framework.Assert; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * Basic tests of the resolver. + */ +public class ResolverTest extends ResolverTestCase { + private final DartClass object = makeClass("Object", null); + private final DartClass array = makeClass("Array", makeType("Object"), "E"); + private final DartClass growableArray = makeClass("GrowableArray", makeType("Array", "S"), "S"); + private final Types types = Types.getInstance(null); + private int expectedErrors = 0; + + private void setExpectedErrors(int count) { + expectedErrors = count; + } + + private void checkExpectedErrors() { + Assert.assertEquals(0, expectedErrors); + } + + private ClassElement findElementOrFail(Scope libScope, String elementName) { + Element element = libScope.findElement(elementName); + assertEquals(ElementKind.CLASS, ElementKind.of(element)); + return (ClassElement) element; + } + + private void assertHasSubtypes(ClassElement superElement, ClassElement...expectedSubtypes) { + Set expectedInterfaceTypes = new LinkedHashSet(); + for (ClassElement expectedSubtype : expectedSubtypes) { + expectedInterfaceTypes.add(expectedSubtype.getType()); + } + + Set actualSubtypes = superElement.getSubtypes(); + assertEquals(expectedInterfaceTypes, actualSubtypes); + } + + public void testToString() { + Assert.assertEquals("class Object {\n}", object.toString().trim()); + Assert.assertEquals("class Array extends Object {\n}", array.toString().trim()); + } + + public void testResolve() { + Scope libScope = resolve(makeUnit(object, array, growableArray), getContext()); + ClassElement objectElement = (ClassElement) libScope.findElement("Object"); + Assert.assertNotNull(objectElement); + ClassElement arrayElement = (ClassElement) libScope.findElement("Array"); + Assert.assertNotNull(arrayElement); + ClassElement growableArrayElement = (ClassElement) libScope.findElement("GrowableArray"); + Assert.assertNotNull(growableArrayElement); + + Type objectType = objectElement.getType(); + Type arrayType = arrayElement.getType(); + Type growableArrayType = growableArrayElement.getType(); + Assert.assertNotNull(objectType); + Assert.assertNotNull(arrayType); + Assert.assertNotNull(growableArrayType); + + Assert.assertTrue(types.isSubtype(arrayType, objectType)); + Assert.assertFalse(types.isSubtype(objectType, arrayType)); + + Assert.assertTrue(types.isSubtype(growableArrayType, objectType)); + + // GrowableArray is not a subtype of Array because S and E aren't + // related. + Assert.assertFalse(types.isSubtype(growableArrayType, arrayType)); + Assert.assertFalse(types.isSubtype(objectType, growableArrayType)); + Assert.assertFalse(types.isSubtype(arrayType, growableArrayType)); + } + + /** + * class A {} + * class B extends A {} + * class C extends A {} + * class E extends C {} + * class D extends C {} + */ + public void testGetSubtypes() { + DartClass a = makeClass("A", makeType("Object")); + DartClass b = makeClass("B", makeType("A")); + DartClass c = makeClass("C", makeType("A")); + DartClass e = makeClass("E", makeType("C")); + DartClass d = makeClass("D", makeType("C")); + + Scope libScope = resolve(makeUnit(object, a, b, c, d, e), getContext()); + + ClassElement elementA = findElementOrFail(libScope, "A"); + ClassElement elementB = findElementOrFail(libScope, "B"); + ClassElement elementC = findElementOrFail(libScope, "C"); + ClassElement elementD = findElementOrFail(libScope, "D"); + ClassElement elementE = findElementOrFail(libScope, "E"); + + assertHasSubtypes(elementA, elementA, elementB, elementC, elementD, elementE); + assertHasSubtypes(elementB, elementB); + assertHasSubtypes(elementC, elementC, elementD, elementE); + assertHasSubtypes(elementD, elementD); + assertHasSubtypes(elementE, elementE); + } + + /** + * interface IA extends ID factory B {} + * interface IB extends IA {} + * interface IC extends IA, IB {} + * interface ID extends IB {} + * class A extends IA {} + * class B {} + */ + public void testGetSubtypesWithInterfaceCycles() { + DartClass ia = makeInterface("IA", makeTypes("ID"), makeType("B")); + DartClass ib = makeInterface("IB", makeTypes("IA"), null); + DartClass ic = makeInterface("IC", makeTypes("IA", "IB"), null); + DartClass id = makeInterface("ID", makeTypes("IB"), null); + + DartClass a = makeClass("A", null, makeTypes("IA")); + DartClass b = makeClass("B", null); + + setExpectedErrors(5); + Scope libScope = resolve(makeUnit(object, ia, ib, ic, id, a, b), getContext()); + checkExpectedErrors(); + + ClassElement elementIA = findElementOrFail(libScope, "IA"); + ClassElement elementIB = findElementOrFail(libScope, "IB"); + ClassElement elementIC = findElementOrFail(libScope, "IC"); + ClassElement elementID = findElementOrFail(libScope, "ID"); + ClassElement elementA = findElementOrFail(libScope, "A"); + ClassElement elementB = findElementOrFail(libScope, "B"); + + assertHasSubtypes(elementIA, elementIA, elementIB, elementIC, elementID, elementA); + assertHasSubtypes(elementIB, elementIA, elementIB, elementIC, elementID, elementA); + assertHasSubtypes(elementIC, elementIC); + assertHasSubtypes(elementID, elementIA, elementIB, elementIC, elementID, elementA); + assertHasSubtypes(elementA, elementA); + assertHasSubtypes(elementB, elementB); + } + + /** + * interface IA extends IB {} + * interface IB extends IA {} + */ + public void testGetSubtypesWithSimpleInterfaceCycle() { + DartClass ia = makeInterface("IA", makeTypes("IB"), null); + DartClass ib = makeInterface("IB", makeTypes("IA"), null); + + setExpectedErrors(2); + Scope libScope = resolve(makeUnit(object, ia, ib), getContext()); + checkExpectedErrors(); + + ClassElement elementIA = findElementOrFail(libScope, "IA"); + ClassElement elementIB = findElementOrFail(libScope, "IB"); + assertHasSubtypes(elementIA, elementIA, elementIB); + assertHasSubtypes(elementIB, elementIA, elementIB); + } + + /** + * class A {} + * class B extends A {} + * class C {} + */ + public void testGetSubtypesWithParemeterizedSupertypes() { + DartClass a = makeClass("A", null, "T"); + DartClass b = makeClass("B", makeType("A", "C")); + DartClass c = makeClass("C", null); + + Scope libScope = resolve(makeUnit(object, a, b, c), getContext()); + + ClassElement elementA = findElementOrFail(libScope, "A"); + ClassElement elementB = findElementOrFail(libScope, "B"); + ClassElement elementC = findElementOrFail(libScope, "C"); + + assertHasSubtypes(elementA, elementA, elementB); + assertHasSubtypes(elementC, elementC); + } + + public void testDuplicatedInterfaces() { + setExpectedErrors(1); + resolve(parseUnit( + "class Object {}", + "interface int {}", + "interface bool {}", + "interface I {", + "}", + "class A extends C implements I {", + "}", + "class B extends C implements I {", + "}", + "class C implements I {", + "}"), getContext()); + checkExpectedErrors(); + } + + public void testCyclicSupertype() { + setExpectedErrors(8); + resolve(parseUnit( + "class Object {}", + "interface int {}", + "interface bool {}", + "class Cyclic extends Cyclic {", + "}", + "class A extends B {", + "}", + "class B extends A {", + "}", + "interface I extends I {", + "}", + "class C implements I1, I {", + "}", + "interface I1 {", + "}", + "class D implements I1, I2 {", + "}", + "interface I2 extends I3 {", + "}", + "interface I3 extends I2 {", + "}"), getContext()); + checkExpectedErrors(); + } + + public void testBadFactory() { + setExpectedErrors(1); + resolve(parseUnit("class Object {}", + "class Zebra {", + " factory foo() {}", + "}"), getContext()); + checkExpectedErrors(); + } + + /** + * Test that a class may implement the implied interface of another class and that interfaces may + * extend the implied interface of a class. + * + * @throws DuplicatedInterfaceException + * @throws CyclicDeclarationException + */ + public void testImpliedInterfaces() throws CyclicDeclarationException, + DuplicatedInterfaceException { + DartClass a = makeClass("A", null); + DartClass b = makeClass("B", null, makeTypes("A")); + DartClass ia = makeInterface("IA", makeTypes("B"), null); + setExpectedErrors(0); + Scope libScope = resolve(makeUnit(object, a, b, ia), getContext()); + checkExpectedErrors(); + + ClassElement elementA = findElementOrFail(libScope, "A"); + ClassElement elementB = findElementOrFail(libScope, "B"); + ClassElement elementIA = findElementOrFail(libScope, "IA"); + List superTypes = elementB.getAllSupertypes(); + assertEquals(2, superTypes.size()); // Object and A + superTypes = elementIA.getAllSupertypes(); + assertEquals(3, superTypes.size()); // Object, A, and B + assertHasSubtypes(elementA, elementA, elementB, elementIA); + } + + public void testUnresolvedSuper() { + setExpectedErrors(0); + resolve(parseUnit( + "class Object {}", + "class Foo {", + " foo() { super.foo(); }", + "}"), getContext()); + checkExpectedErrors(); + } + + private static DartUnit makeUnit(DartNode... topLevelElements) { + DartUnit unit = new DartUnit(null); + for (DartNode topLevelElement : topLevelElements) { + unit.addTopLevelNode(topLevelElement); + } + return unit; + } + + private static DartTypeNode makeType(String name, String... arguments) { + List argumentNodes = makeTypes(arguments); + return new DartTypeNode(new DartIdentifier(name), argumentNodes); + } + + static List makeTypes(String... typeNames) { + List types = new ArrayList(); + for (String typeName : typeNames) { + types.add(makeType(typeName)); + } + return types; + } + + private DartUnit parseUnit(String firstLine, String secondLine, String... rest) { + return parseUnit(Joiner.on('\n').join(firstLine, secondLine, (Object[]) rest).toString()); + } + + private DartUnit parseUnit(String string) { + DartSourceString source = new DartSourceString("", string); + return getParser(string).parseUnit(source); + } + + private DartParser getParser(String string) { + return new DartParser(new DartScannerParserContext(null, string, getListener())); + } + + private DartCompilerListener getListener() { + return new DartCompilerListener() { + @Override + public void compilationError(DartCompilationError event) { + expectedErrors--; + if (expectedErrors < 0) { + AssertionError error = new AssertionError(event.getMessage()); + error.initCause(event.getException()); + throw error; + } + } + + @Override + public void compilationWarning(DartCompilationError event) { + compilationError(event); + } + + @Override + public void typeError(DartCompilationError event) { + compilationError(event); + } + }; + } + + private TestCompilerContext getContext() { + return new TestCompilerContext() { + @Override + public void compilationError(DartCompilationError event) { + expectedErrors--; + if (expectedErrors < 0) { + AssertionError error = new AssertionError(event.getMessage()); + error.initCause(event.getException()); + throw error; + } + } + }; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ResolverTestCase.java b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTestCase.java new file mode 100644 index 00000000000..799d13c0774 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTestCase.java @@ -0,0 +1,193 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartIdentifier; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartTypeNode; +import com.google.dart.compiler.ast.DartTypeParameter; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.testing.TestCompilerContext; +import com.google.dart.compiler.type.DynamicType; +import com.google.dart.compiler.type.InterfaceType; +import com.google.dart.compiler.type.Type; +import com.google.dart.compiler.type.Types; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Utility methods for resolver tests. + */ +abstract class ResolverTestCase extends TestCase { + + static Scope resolve(DartUnit unit, TestCompilerContext context) { + Scope scope = new Scope("library"); + new TopLevelElementBuilder().exec(unit, context); + new TopLevelElementBuilder().fillInUnitScope(unit, context, scope); + ClassElement object = (ClassElement) scope.findElement("Object"); + assertNotNull("Cannot resolve Object", object); + CoreTypeProvider typeProvider = new MockCoreTypeProvider(object); + new SupertypeResolver().exec(unit, context, scope, typeProvider); + new MemberBuilder().exec(unit, context, scope, typeProvider); + new Resolver(context, scope, typeProvider).exec(unit); + return scope; + } + + static DartClass makeClass(String name, DartTypeNode supertype, String... typeParameters) { + return makeClass(name, supertype, Collections.emptyList(), typeParameters); + } + + static DartClass makeClass(String name, DartTypeNode supertype, List interfaces, + String... typeParameters) { + List parameterNodes = new ArrayList(); + for (String parameter : typeParameters) { + parameterNodes.add(makeTypeVariable(parameter)); + } + List members = Arrays.asList(); + return new DartClass(new DartIdentifier(name), null, supertype, + interfaces, members, parameterNodes); + } + + static DartClass makeInterface(String name, List interfaces, + DartTypeNode defaultClass, String... typeParameters) { + List parameterNodes = new ArrayList(); + for (String parameter : typeParameters) { + parameterNodes.add(makeTypeVariable(parameter)); + } + List members = Arrays.asList(); + return new DartClass(new DartIdentifier(name), null, null, + interfaces, members, parameterNodes, defaultClass, true); + } + + private static DartTypeParameter makeTypeVariable(String name) { + return new DartTypeParameter(new DartIdentifier(name), null); + } + + static class MockCoreTypeProvider implements CoreTypeProvider { + + private final InterfaceType intType; + private final InterfaceType stringType; + private final InterfaceType functionType; + private final InterfaceType mapType; + private final InterfaceType arrayType; + private final ClassElement objectElement; + + + { + ClassElement intElement = Elements.classNamed("int"); + intType = Types.interfaceType(intElement, Collections.emptyList()); + ClassElement stringElement = Elements.classNamed("String"); + stringType = Types.interfaceType(stringElement, Collections.emptyList()); + intElement.setType(intType); + ClassElement functionElement = Elements.classNamed("Function"); + functionType = Types.interfaceType(functionElement, Collections.emptyList()); + ClassElement mapElement = Elements.classNamed("Map"); + mapType = Types.interfaceType(mapElement, Collections.emptyList()); + ClassElement arrayElement = Elements.classNamed("Array"); + arrayType = Types.interfaceType(arrayElement, Collections.emptyList()); + functionElement.setType(functionType); + } + + MockCoreTypeProvider(ClassElement objectElement) { + this.objectElement = objectElement; + } + + @Override + public InterfaceType getIntType() { + return intType; + } + + @Override + public InterfaceType getDoubleType() { + throw new AssertionError(); + } + + @Override + public InterfaceType getBoolType() { + throw new AssertionError(); + } + + @Override + public InterfaceType getStringType() { + return stringType; + } + + @Override + public InterfaceType getFunctionType() { + return functionType; + } + + @Override + public InterfaceType getArrayType(Type elementType) { + return arrayType; + } + + @Override + public Type getNullType() { + throw new AssertionError(); + } + + @Override + public Type getVoidType() { + throw new AssertionError(); + } + + @Override + public DynamicType getDynamicType() { + return Types.newDynamicType(); + } + + @Override + public InterfaceType getFallThroughError() { + throw new AssertionError(); + } + + @Override + public InterfaceType getMapType(Type key, Type value) { + return mapType; + } + + @Override + public InterfaceType getObjectArrayType() { + throw new AssertionError(); + } + + @Override + public InterfaceType getObjectType() { + return objectElement.getType(); + } + + @Override + public InterfaceType getNumType() { + throw new AssertionError(); + } + + @Override + public InterfaceType getArrayLiteralType(Type value) { + throw new AssertionError(); + } + + @Override + public InterfaceType getMapLiteralType(Type key, Type value) { + throw new AssertionError(); + } + + @Override + public InterfaceType getStringImplementationType() { + throw new AssertionError(); + } + + @Override + public InterfaceType getIsolateType() { + throw new AssertionError(); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/ResolverTests.java b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTests.java new file mode 100644 index 00000000000..a72374084f1 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/ResolverTests.java @@ -0,0 +1,28 @@ +// 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. + +package com.google.dart.compiler.resolver; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Tests of the resolver. + */ +public class ResolverTests extends TestSetup { + + public ResolverTests(Test test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart Resolver test suite."); + + suite.addTestSuite(ResolverTest.class); + suite.addTestSuite(NegativeResolverTest.class); + + return suite; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/StaticInstanceCallNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/StaticInstanceCallNegativeTest.dart new file mode 100644 index 00000000000..de5d0a446b0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/StaticInstanceCallNegativeTest.dart @@ -0,0 +1,12 @@ +// 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. + +// Check that an instance method cannot be called as a static method. + +class A { + foo() {} + static bar() { + A.foo(); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperFieldNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperFieldNegativeTest.dart new file mode 100644 index 00000000000..2e445d14a7e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperFieldNegativeTest.dart @@ -0,0 +1,15 @@ +// 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. + +// Check that one cannot access a static field through super. + +class A { + static var x; +} + +class B { + foo() { + return super.x; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperGetterNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperGetterNegativeTest.dart new file mode 100644 index 00000000000..1d613b554c4 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperGetterNegativeTest.dart @@ -0,0 +1,15 @@ +// 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. + +// Check that oen cannot access a static getter through super. + +class A { + static get x() {} +} + +class B { + foo() { + return super.x; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperMethodNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperMethodNegativeTest.dart new file mode 100644 index 00000000000..e8b51ced03d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/StaticSuperMethodNegativeTest.dart @@ -0,0 +1,15 @@ +// 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. + +// Check that one cannot access a static method through super. + +class A { + static x() {} +} + +class B { + foo() { + return super.x; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest1.dart b/compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest1.dart new file mode 100644 index 00000000000..5a81d0ac9a2 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest1.dart @@ -0,0 +1,10 @@ +// 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. + +// case 1 - unqualified - expect failure. + +class A { + static foo() { bar(); } + bar() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest2.dart b/compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest2.dart new file mode 100644 index 00000000000..12d4ec2dced --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/StaticToInstanceInvocationNegativeTest2.dart @@ -0,0 +1,10 @@ +// 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. + +// case 2 - qualified - expect failure. + +class A { + static foo() { this.bar(); } + bar() { } +} diff --git a/compiler/javatests/com/google/dart/compiler/resolver/UnresolvedSuperFieldNegativeTest.dart b/compiler/javatests/com/google/dart/compiler/resolver/UnresolvedSuperFieldNegativeTest.dart new file mode 100644 index 00000000000..2b3ef2dad4a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/resolver/UnresolvedSuperFieldNegativeTest.dart @@ -0,0 +1,14 @@ +// 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. + +// Check that an unresolved super field is an error. + +class A { +} + +class B { + foo() { + return super.x; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/FunctionTypeTest.java b/compiler/javatests/com/google/dart/compiler/type/FunctionTypeTest.java new file mode 100644 index 00000000000..55e367edd7d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/FunctionTypeTest.java @@ -0,0 +1,113 @@ +// 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. + +package com.google.dart.compiler.type; + +import java.util.Arrays; +import java.util.List; + +public class FunctionTypeTest extends TypeTestCase { + private final Types types = Types.getInstance(null); + + private final FunctionType objectsToObject = ftype(function, itype(object), null, itype(object)); + private final FunctionType objectAndObjectsToObject = + ftype(function, itype(object), null, itype(object), itype(object)); + private final FunctionType stringsToObject = ftype(function, itype(object), null, itype(string)); + private final FunctionType namedStringToObject = + ftype(function, itype(object), named(itype(string), "arg"), null); + private final FunctionType namedObjectToObject = + ftype(function, itype(object), named(itype(object), "arg"), null); + private final FunctionType objectAndNamedStringToObject = + ftype(function, itype(object), named(itype(string), "arg"), null, itype(object)); + private final FunctionType manyNames = + ftype(function, itype(object), + named(itype(string), "arg1", itype(intElement), "arg2", itype(object), "arg3"), + null, itype(object)); + + @Override + Types getTypes() { + return types; + } + + public void testToString() { + assertEquals("() -> Object", returnObject.toString()); + assertEquals("() -> String", returnString.toString()); + assertEquals("(Object) -> String", objectToString.toString()); + assertEquals("(String) -> Object", stringToObject.toString()); + assertEquals("(String, int) -> bool", stringAndIntToBool.toString()); + assertEquals("(Object...) -> Object", objectsToObject.toString()); + assertEquals("(Object, Object...) -> Object", objectAndObjectsToObject.toString()); + assertEquals("([String arg]) -> Object", namedStringToObject.toString()); + assertEquals("(Object, [String arg]) -> Object", objectAndNamedStringToObject.toString()); + assertEquals("(Object, [String arg1, int arg2, Object arg3]) -> Object", manyNames.toString()); + } + + public void testAsInstanceOf() { + checkAsInstanceOf(returnObject); + checkAsInstanceOf(returnString); + checkAsInstanceOf(objectToString); + checkAsInstanceOf(stringToObject); + checkAsInstanceOf(stringAndIntToBool); + checkAsInstanceOf(stringAndIntToMap); + checkAsInstanceOf(objectAndNamedStringToObject); + } + + private void checkAsInstanceOf(FunctionType type) { + assertEquals(itype(function), types.asInstanceOf(type, function)); + assertEquals(itype(object), types.asInstanceOf(type, object)); + assertNull(types.asInstanceOf(type, string)); + } + + public void testSubst() { + Type s = typeVar("S", itype(object)); + Type o = typeVar("O", itype(object)); + List vars = Arrays.asList(s, o); + List args = Arrays.asList(itype(string), itype(object)); + Type returnO = ftype(function, o, null, null); + Type returnS = ftype(function, s, null, null); + Type oToO = ftype(function, o, null, null, o); + Type oToS = ftype(function, s, null, null, o); + Type stringAndIntToMapS = ftype(function, itype(map, s, itype(intElement)), + null, null, itype(string), itype(intElement)); + Type sAndIntToBool = ftype(function, itype(bool), null, null, s, itype(intElement)); + assertEquals(returnObject, returnO.subst(args, vars)); + assertEquals(returnString, returnS.subst(args, vars)); + assertEquals(objectToObject, oToO.subst(args, vars)); + assertEquals(objectToString, oToS.subst(args, vars)); + assertEquals(stringAndIntToBool, sAndIntToBool.subst(args, vars)); + assertEquals(stringAndIntToMap, stringAndIntToMapS.subst(args, vars)); + + FunctionType oAndNamedToO = FunctionTypeImplementation.of(function, Arrays.asList(o), + named(itype(string), "arg"), null, o, + null); + assertEquals(objectAndNamedStringToObject, oAndNamedToO.subst(args, vars)); + + Type osToO = FunctionTypeImplementation.of(function, Arrays.asList(), null, o, o, null); + assertEquals(objectsToObject, osToO.subst(args, vars)); + } + + public void testEquals() { + assertEquals(returnObject, ftype(function, itype(object), null, null)); + assertEquals(returnObject, ftype(function, object.getType(), null, null)); + assertFalse(returnObject.equals(returnString)); + assertFalse(returnObject.equals(returnString)); + assertEquals(objectToObject, ftype(function, itype(object), null, null, itype(object))); + assertFalse(objectToObject.equals(objectToString)); + assertFalse(objectToObject.equals(objectsToObject)); + assertEquals(objectsToObject, objectsToObject); + assertEquals(objectAndNamedStringToObject, objectAndNamedStringToObject); + assertFalse(objectsToObject.equals(objectAndNamedStringToObject)); + assertFalse(objectAndNamedStringToObject.equals(objectsToObject)); + } + + public void testIsSubtype() { + checkSubtype(returnObject, returnObject); + checkSubtype(returnString, returnObject); + checkSubtype(objectToObject, stringToObject); + checkSubtype(objectsToObject, objectsToObject); + checkSubtype(objectsToObject, stringsToObject); + checkSubtype(namedObjectToObject, namedObjectToObject); + checkSubtype(namedObjectToObject, namedStringToObject); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerBench.java b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerBench.java new file mode 100644 index 00000000000..2226707373f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerBench.java @@ -0,0 +1,182 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.dart.compiler.CompilerConfiguration; +import com.google.dart.compiler.DartArtifactProvider; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilationPhase; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DartCompilerContext; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DefaultCompilerConfiguration; +import com.google.dart.compiler.DefaultDartArtifactProvider; +import com.google.dart.compiler.DefaultLibrarySource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.UrlLibrarySource; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.resolver.CoreTypeProvider; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; + +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Benchmark for the type analyzer. The benchmark will loop forever to ease profiling. + */ +public class TypeAnalyzerBench { + private static final double SCORE_SCALE = 1000d; + private static final long ROUND_DURATION_MS = 5000L; + + public static void main(String... arguments) throws CmdLineException, IOException { + CompilerOptions compilerOptions = new CompilerOptions(); + CmdLineParser cmdLineParser = new CmdLineParser(compilerOptions); + cmdLineParser.parseArgument(arguments); + final CollectingPhase phase = new CollectingPhase(); + CompilerConfiguration config = new DefaultCompilerConfiguration(compilerOptions) { + @Override + public List getPhases() { + ArrayList phases = new ArrayList(); + phases.addAll(super.getPhases()); + phases.add(phase); + return phases; + } + }; + DartArtifactProvider provider = getArtifactProvider(config.getOutputDirectory()); + DartCompilerListener listener = getListener(); + List sourceFiles = compilerOptions.getSourceFiles(); + if (sourceFiles.size() != 1) { + throw new IllegalArgumentException("incorrect number of source files " + sourceFiles); + } + File sourceFile = new File(sourceFiles.get(0)); + LibrarySource lib; + if (sourceFile.getName().endsWith(".dart")) { + lib = new DefaultLibrarySource(sourceFile, null); + } else { + lib = new UrlLibrarySource(sourceFile); + } + DartCompiler.compileLib(lib, config, provider, listener); + Deque scores = new ArrayDeque(10); + for (int i = 0; i < 10; i++) { + scores.addLast(0d); + } + long start = System.currentTimeMillis(); + int i = 0; + while (true) { + i++; + TypeAnalyzer typeAnalyzer = new TypeAnalyzer(); + for (DartUnit unit : phase.units) { + typeAnalyzer.exec(unit, phase.context, phase.typeProvider); + } + long elapsed = System.currentTimeMillis() - start; + if (elapsed > ROUND_DURATION_MS) { + double score = i * SCORE_SCALE / elapsed; + scores.removeFirst(); + scores.addLast(score); + printScores(scores); + start = System.currentTimeMillis(); + i = 0; + } + } + } + + private static void printScores(Deque scores) { + double xn = 1d; + for (double x : scores) { + xn *= x; + } + double geomean = Math.pow(xn, 1d / scores.size()); + xn = 0d; + for (double x : scores) { + double deviation = x - geomean; + xn += deviation * deviation; + } + double stddev = Math.sqrt(xn / (scores.size() - 1)); + System.out.println(String.format("geomean %.03f std. dev. %.03f", geomean, stddev)); + } + + private static DartArtifactProvider getArtifactProvider(File outputDirectory) { + final DartArtifactProvider provider = new DefaultDartArtifactProvider(outputDirectory); + return new DartArtifactProvider() { + ConcurrentHashMap artifacts = + new ConcurrentHashMap(); + + @Override + public boolean isOutOfDate(Source source, Source base, String extension) { + return true; + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) { + URI uri = getArtifactUri(source, part, extension); + CharArrayWriter writer = new CharArrayWriter(); + CharArrayWriter existing = artifacts.putIfAbsent(uri, writer); + return (existing == null) ? writer : existing; + } + + + @Override + public URI getArtifactUri(Source source, String part, String extension) { + return provider.getArtifactUri(source, part, extension); + } + + @Override + public Reader getArtifactReader(Source source, String part, String extension) + throws IOException { + URI uri = getArtifactUri(source, part, extension); + CharArrayWriter writer = artifacts.get(uri); + if (writer != null) { + return new CharArrayReader(writer.toCharArray()); + } + return provider.getArtifactReader(source, part, extension); + } + }; + } + + private static DartCompilerListener getListener() { + return new DartCompilerListener() { + @Override + public void compilationError(DartCompilationError event) { + } + + @Override + public void compilationWarning(DartCompilationError event) { + } + + @Override + public void typeError(DartCompilationError event) { + } + }; + } + + static class CollectingPhase implements DartCompilationPhase { + List units = new ArrayList(); + DartCompilerContext context; + CoreTypeProvider typeProvider; + + @Override + public synchronized DartUnit exec(DartUnit unit, DartCompilerContext context, + CoreTypeProvider typeProvider) { + units.add(unit); + this.context = context; + this.typeProvider = typeProvider; + return unit; + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java new file mode 100644 index 00000000000..0fd61a1bf72 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/TypeAnalyzerTest.java @@ -0,0 +1,1622 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.common.base.Joiner; +import com.google.common.io.CharStreams; +import com.google.dart.compiler.DartCompilerErrorCode; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.ast.DartClass; +import com.google.dart.compiler.ast.DartExprStmt; +import com.google.dart.compiler.ast.DartExpression; +import com.google.dart.compiler.ast.DartFunctionExpression; +import com.google.dart.compiler.ast.DartFunctionTypeAlias; +import com.google.dart.compiler.ast.DartNode; +import com.google.dart.compiler.ast.DartStatement; +import com.google.dart.compiler.ast.DartUnit; +import com.google.dart.compiler.parser.DartParser; +import com.google.dart.compiler.parser.DartScannerParserContext; +import com.google.dart.compiler.parser.Token; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.TopLevelElementBuilder; +import com.google.dart.compiler.resolver.CoreTypeProvider; +import com.google.dart.compiler.resolver.CyclicDeclarationException; +import com.google.dart.compiler.resolver.DuplicatedInterfaceException; +import com.google.dart.compiler.resolver.Element; +import com.google.dart.compiler.resolver.Elements; +import com.google.dart.compiler.resolver.FunctionAliasElement; +import com.google.dart.compiler.resolver.MemberBuilder; +import com.google.dart.compiler.resolver.ResolutionContext; +import com.google.dart.compiler.resolver.Resolver; +import com.google.dart.compiler.resolver.Resolver.ResolveElementsVisitor; +import com.google.dart.compiler.resolver.Scope; +import com.google.dart.compiler.resolver.SupertypeResolver; +import com.google.dart.compiler.util.DartSourceString; + +import java.io.IOError; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Test of static type analysis. This is mostly a test of {@link TypeAnalyzer}, but this test also + * exercises code in com.google.dart.compiler.resolver. + */ +public class TypeAnalyzerTest extends TypeTestCase { + private final CoreTypeProvider typeProvider = new MockCoreTypeProvider(); + private Resolver resolver = new Resolver(context, getMockScope(""), typeProvider); + private final Types types = Types.getInstance(typeProvider); + private HashSet diagnosedAbstractClasses = new HashSet(); + + @Override + protected void tearDown() { + resolver = null; + diagnosedAbstractClasses = null; + } + + @Override + Types getTypes() { + return types; + } + + private TypeAnalyzer.Analyzer makeTypeAnalyzer(ClassElement element) { + TypeAnalyzer.Analyzer analyzer = + new TypeAnalyzer.Analyzer(context, typeProvider, + new ConcurrentHashMap>(), + diagnosedAbstractClasses); + analyzer.setCurrentClass(element.getType()); + return analyzer; + } + + public void testLabels() { + // Labels should be inside a function or method to be used + + // break + analyze("foo() { L: for (;true;) { break L; } }"); + analyze("foo() { int x; Array c; L: for (x in c) { break L; } }"); + analyze("foo() { Array c; L: for (var x in c) { break L; } }"); + analyze("foo() { L: while (true) { break L; } }"); + analyze("foo() { L: do { break L; } while (true); }"); + + analyze("foo() { L: for (;true;) { for (;true;) { break L; } } }"); + analyze("foo() { int x; Array c; L: for (x in c) { for (;true;) { break L; } } }"); + analyze("foo() { Array c; L: for (var x in c) { for (;true;) { break L; } } }"); + analyze("foo() { L: while (true) { for (;true;) { break L; } } }"); + analyze("foo() { L: do { for (;true;) { break L; } } while (true); }"); + + // continue + analyze("foo() { L: for (;true;) { continue L; } }"); + analyze("foo() { int x; Array c; L: for (x in c) { continue L; } }"); + analyze("foo() { Array c; L: for (var x in c) { continue L; } }"); + analyze("foo() { L: do { continue L; } while (true); }"); + + analyze("foo() { L: for (;true;) { for (;true;) { continue L; } } }"); + analyze( + "foo() { int x; Array c; L: for (x in c) { for (;true;) { continue L; } } }"); + analyze("foo() { Array c; L: for (var x in c) { for (;true;) { continue L; } } }"); + analyze("foo() { L: while (true) { for (;true;) { continue L; } } }"); + analyze("foo() { L: do { for (;true;) { continue L; } } while (true); }"); + + // corner cases + analyze("foo() { L: break L; }"); + + // TODO(zundel): Not type errors, but warnings. + analyze("foo() { L: for (;true;) { } }"); + analyze("foo() { while (true) { L: var a; } }"); + } + + public void testLiterals() { + checkSimpleType(intElement.getType(), "1"); + checkSimpleType(doubleElement.getType(), ".0"); + checkSimpleType(doubleElement.getType(), "1.0"); + checkSimpleType(bool.getType(), "true"); + checkSimpleType(bool.getType(), "false"); + checkSimpleType(string.getType(), "'fisk'"); + checkSimpleType(string.getType(), "'f${null}sk'"); + } + + public void testUnresolvedIdentifier() { + setExpectedTypeErrorCount(3); + checkType(typeProvider.getDynamicType(), "y"); + checkExpectedTypeErrorCount(); + } + + public void testInitializers() { + analyze("int i = 1;"); + analyze("double d1 = .0;"); + analyze("double d2 = 1.0;"); + analyze("int x = null;"); + } + + public void testBadInitializers() { + analyzeFail("int i = .0;", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("int j = 1.0;", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testFunctionTypes() { + checkFunctionStatement("String foo() {};", "() -> String"); + checkFunctionStatement("Object foo() {};", "() -> Object"); + checkFunctionStatement("String foo(int i, bool b) {};", "(int, bool) -> String"); + } + + private void checkFunctionStatement(String statement, String printString) { + DartExprStmt node = (DartExprStmt) analyze(statement); + DartFunctionExpression expression = (DartFunctionExpression) node.getExpression(); + Element element = expression.getSymbol(); + FunctionType type = (FunctionType) element.getType(); + assertEquals(printString, type.toString()); + } + + public void testIdentifiers() { + analyze("{ int i; i = 2; }"); + analyze("{ int j, k; j = 1; k = 3; }"); + analyzeFail("{ int i; i = 'string'; }", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int j, k; k = 'string'; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int j, k; j = 'string'; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testRawTypes() { + loadFile("interfaces.dart"); + + analyze("{ Sub s; }"); + analyze("{ var s = new Sub(); }"); + analyze("{ Sub s = new Sub(); }"); + analyze("{ Sub s = new Sub(); }"); + analyze("{ Sub s; }"); + analyze("{ var s = new Sub(); }"); + analyze("{ Sub s = new Sub(); }"); + analyze("{ Sub s = new Sub(); }"); + + // FYI, this is detected in the resolver, not TypeAnalyzer + analyzeFail("{ Sub s = new Sub(); }", + DartCompilerErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); + analyzeFail("{ Sub s = new Sub(); }", + DartCompilerErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); + analyzeFail("{ Sub s; }", DartCompilerErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); + analyzeFail("{ String s; }", DartCompilerErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); + } + + public void testMethodInvocations() { + loadFile("class_with_methods.dart"); + final String header = "{ ClassWithMethods c; int i, j; Array array; "; + + analyze(header + "int k = c.untypedNoArgumentMethod(); }"); + analyze(header + "ClassWithMethods x = c.untypedNoArgumentMethod(); }"); + + analyze(header + "int k = c.untypedOneArgumentMethod(c); }"); + analyze(header + "ClassWithMethods x = c.untypedOneArgumentMethod(1); }"); + analyze(header + "int k = c.untypedOneArgumentMethod('string'); }"); + analyze(header + "int k = c.untypedOneArgumentMethod(i); }"); + + analyze(header + "int k = c.untypedTwoArgumentMethod(1, 'string'); }"); + analyze(header + "int k = c.untypedTwoArgumentMethod(i, j); }"); + analyze(header + "ClassWithMethods x = c.untypedTwoArgumentMethod(i, c); }"); + + analyze(header + "int k = c.intNoArgumentMethod(); }"); + analyzeFail(header + "ClassWithMethods x = c.intNoArgumentMethod(); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + + analyzeFail(header + "int k = c.intOneArgumentMethod(c); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail(header + "ClassWithMethods x = c.intOneArgumentMethod(1); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail(header + "int k = c.intOneArgumentMethod('string'); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze(header + "int k = c.intOneArgumentMethod(i); }"); + + analyzeFail(header + "int k = c.intTwoArgumentMethod(1, 'string'); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze(header + "int k = c.intTwoArgumentMethod(i, j); }"); + analyzeFail(header + "ClassWithMethods x = c.intTwoArgumentMethod(i, j); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testMethodInvocationArgumentCount() { + loadFile("class_with_methods.dart"); + final String header = "{ ClassWithMethods c; Array array; "; + + analyzeFail(header + "c.untypedNoArgumentMethod(1); }", + DartCompilerErrorCode.EXTRA_ARGUMENT); + analyzeFail(header + "c.untypedOneArgumentMethod(); }", + DartCompilerErrorCode.MISSING_ARGUMENT); + analyzeFail(header + "c.untypedOneArgumentMethod(1, 1); }", + DartCompilerErrorCode.EXTRA_ARGUMENT); + analyzeFail(header + "c.untypedTwoArgumentMethod(); }", + DartCompilerErrorCode.MISSING_ARGUMENT); + analyzeFail(header + "c.untypedTwoArgumentMethod(1, 2, 3); }", + DartCompilerErrorCode.EXTRA_ARGUMENT); + analyzeFail(header + "c.intNoArgumentMethod(1); }", + DartCompilerErrorCode.EXTRA_ARGUMENT); + analyzeFail(header + "c.intOneArgumentMethod(); }", + DartCompilerErrorCode.MISSING_ARGUMENT); + analyzeFail(header + "c.intOneArgumentMethod(1, 1); }", + DartCompilerErrorCode.EXTRA_ARGUMENT); + analyzeFail(header + "c.intTwoArgumentMethod(); }", + DartCompilerErrorCode.MISSING_ARGUMENT); + analyzeFail(header + "c.intTwoArgumentMethod(1, 2, 3); }", + DartCompilerErrorCode.EXTRA_ARGUMENT); + analyze(header + "c.untypedField(); }"); + } + + public void testLoadInterfaces() { + loadFile("interfaces.dart"); + ClassElement superElement = coreElements.get("Super"); + assertNotNull("no element for Super", superElement); + assertEquals(object.getType(), superElement.getSupertype()); + assertEquals(0, superElement.getInterfaces().size()); + ClassElement sub = coreElements.get("Sub"); + assertNotNull("no element for Sub", sub); + assertEquals(object.getType(), sub.getSupertype()); + assertEquals(1, sub.getInterfaces().size()); + assertEquals(superElement, sub.getInterfaces().get(0).getElement()); + InterfaceType superString = itype(superElement, itype(string)); + InterfaceType subString = itype(sub, itype(string)); + assertEquals("Super", String.valueOf(types.asInstanceOf(superString, superElement))); + assertEquals("Super", String.valueOf(types.asInstanceOf(subString, superElement))); + assertEquals("Sub", String.valueOf(types.asInstanceOf(subString, sub))); + assertNull(types.asInstanceOf(superString, sub)); + } + + public void testSuperInterfaces() { + // If this test is failing, first debug any failures in testLoadInterfaces. + loadFile("interfaces.dart"); + analyze("Super s = new Sub();"); + analyze("Super o = new Sub();"); + analyzeFail("Super f1 = new Sub();", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("Sub f2 = new Sub();", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testUnaryOperators() { + Map source = loadSource( + "class Foo {", + " Foo foo;", + " bool b;", + " int i;", + " Foo operator negate() { return this; }", + " Foo operator +(int operand) { return this; }", + " Foo operator -(int operand) { return this; }", + "}", + "class Bar {", + " Bar bar;", + " Bar operator +(Bar operand) { return this; }", + " Bar operator -(Bar operand) { return this; }", + "}", + "class Baz {", + " T baz;", + "}", + "class Qux { ", + " T qux; ", + " void x() { }", + " y() { }", + "}", + "class X {", + " X x;", + " Z operator negate() { return null; }", + " Z operator +(int operand) { return null; }", + " Z operator -(int operand) { return null; }", + "}", + "class Y extends X { Y y; }", + "class Z extends X { Z z; }" + ); + analyzeClasses(source); + ClassElement foo = source.get("Foo"); + ClassElement bar = source.get("Bar"); + ClassElement baz = source.get("Baz"); + ClassElement qux = source.get("Qux"); + ClassElement y = source.get("Y"); + ClassElement z = source.get("Z"); + for (Token op : EnumSet.of(Token.DEC, Token.INC, Token.SUB)) { + analyzeIn(foo, String.format("%sfoo", op), 0); + analyzeIn(foo, String.format("i = %sfoo", op), 1); + analyzeIn(bar, String.format("%sbar", op), 1); + analyzeIn(baz, String.format("%sbaz", op), 0); + analyzeIn(qux, String.format("%squx", op), 1); + } + analyzeIn(z, "z = x++", 0); + analyzeIn(z, "z = ++x", 0); + analyzeIn(z, "z = x--", 0); + analyzeIn(z, "z = --x", 0); + analyzeIn(y, "y = x++", 0); + analyzeIn(y, "y = ++x", 1); + analyzeIn(y, "y = x--", 0); + analyzeIn(y, "y = --x", 1); + + analyzeIn(foo, "b = !b", 0); + analyzeIn(foo, "foo = !foo", 2); + analyzeIn(foo, "b = !i", 1); + analyzeIn(foo, "foo = !b", 1); + analyzeIn(qux, "-x()", 1); + analyzeIn(qux, "-y()", 0); + } + + public void testBinaryOperators() { + ClassElement cls = loadClass("class_with_operators.dart", "ClassWithOperators"); + analyzeIn(cls, "i = o[0]", 0); + analyzeIn(cls, "s = o[0]", 1); + analyzeIn(cls, "o['fisk']", 1); + analyzeIn(cls, "i && o", 2); + analyzeIn(cls, "b && o", 1); + analyzeIn(cls, "i && b", 1); + analyzeIn(cls, "b && b", 0); + analyzeIn(cls, "i || o", 2); + analyzeIn(cls, "b || o", 1); + analyzeIn(cls, "i || b", 1); + analyzeIn(cls, "b || b", 0); + + EnumSet userOperators = EnumSet.of(Token.SHR, + Token.ADD, + Token.SUB, + Token.MUL, + Token.DIV, + Token.TRUNC, + Token.MOD, + Token.LT, + Token.GT, + Token.LTE, + Token.GTE); + for (Token op : userOperators) { + String expression; + expression = String.format("untyped %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("o = untyped %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s = untyped %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("o %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("o = o %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s = o %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("o %s null", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("o = o %s null", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s = o %s null", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("o %s o", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("o = o %s o", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s = o %s o", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("o %s s", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("o %s i", op.getSyntax()); + analyzeIn(cls, expression, 1); + } + + EnumSet equalityOperators = EnumSet.of(Token.EQ, + Token.NE, + Token.EQ_STRICT, + Token.NE_STRICT); + for (Token op : equalityOperators) { + String expression; + expression = String.format("untyped %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("b = untyped %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s = untyped %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("i = untyped %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 1); + + expression = String.format("o %s o", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("b = o %s o", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s = o %s o", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("i = o %s o", op.getSyntax()); + analyzeIn(cls, expression, 1); + + expression = String.format("o %s s", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("b = o %s s", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s = o %s s", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("i = o %s s", op.getSyntax()); + analyzeIn(cls, expression, 1); + } + + EnumSet compoundAssignmentOperators = + EnumSet.of(Token.ASSIGN_ADD, + Token.ASSIGN_SUB, + Token.ASSIGN_MUL, + Token.ASSIGN_DIV, + Token.ASSIGN_MOD, + Token.ASSIGN_TRUNC, + Token.ASSIGN_SHR); + + for (Token op : compoundAssignmentOperators) { + String expression; + expression = String.format("o %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s %s untyped", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("o %s null", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s %s null", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("o %s o", op.getSyntax()); + analyzeIn(cls, expression, 0); + expression = String.format("s %s o", op.getSyntax()); + analyzeIn(cls, expression, 1); + expression = String.format("o %s i", op.getSyntax()); + analyzeIn(cls, expression, 1); + } + + analyzeIn(cls, "untyped is String", 0); + analyzeIn(cls, "b = untyped is String", 0); + analyzeIn(cls, "s = untyped is String", 1); + analyzeIn(cls, "s is String", 0); + analyzeIn(cls, "b = s is String", 0); + analyzeIn(cls, "s = s is String", 1); + + analyzeIn(cls, "untyped is !String", 0); + analyzeIn(cls, "b = untyped is !String", 0); + analyzeIn(cls, "s = untyped is !String", 1); + analyzeIn(cls, "s is !String", 0); + analyzeIn(cls, "b = s is !String", 0); + analyzeIn(cls, "s = s is !String", 1); + + analyzeFail("1 == !'s';", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testBitOperators() { + Map source = loadSource( + "class ClassWithBitops {", + " num n;", + " int i;", + " double d;", + " ClassWithBitops o;", + " num foo() { return 42; }", + " int operator |(int arg) { return arg; }", + " int operator &(int arg) { return arg; }", + " int operator ^(int arg) { return arg; }", + " int operator >>(int arg) { return arg; }", + " int operator >>>(int arg) { return arg; }", + " int operator <<(int arg) { return arg; }", + " int operator ~() { return 1; }", + "}"); + ClassElement cls = source.get("ClassWithBitops"); + analyzeClasses(source); + + EnumSet operators = EnumSet.of(Token.BIT_AND, + Token.BIT_OR, + Token.BIT_XOR, + Token.SHL, + Token.SAR); + for (Token operator : operators) { + analyzeIn(cls, String.format("n %s n" , operator), 0); + analyzeIn(cls, String.format("foo() %s i" , operator), 0); + analyzeIn(cls, String.format("o %s i" , operator), 0); + analyzeIn(cls, String.format("n = d %s i", operator), 1); + analyzeIn(cls, String.format("d = o %s i", operator), 1); + analyzeIn(cls, String.format("d = n %s i", operator), 1); + analyzeIn(cls, String.format("n %s o" , operator), 1); + } + + EnumSet assignOperators = EnumSet.of(Token.ASSIGN_BIT_AND, + Token.ASSIGN_BIT_OR, + Token.ASSIGN_BIT_XOR, + Token.ASSIGN_SHL, + Token.ASSIGN_SAR); + for (Token operator : assignOperators) { + analyzeIn(cls, String.format("n %s n" , operator), 0); + analyzeIn(cls, String.format("n %s i" , operator), 0); + analyzeIn(cls, String.format("d %s i", operator), 1); + analyzeIn(cls, String.format("o %s i", operator), 1); + analyzeIn(cls, String.format("n %s o" , operator), 1); + } + + analyzeIn(cls, "i = ~o", 0); + analyzeIn(cls, "i = ~n", 0); + analyzeIn(cls, "d = ~n", 1); + } + + + 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; }(); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("{ int i = Object _(Object x) { return x; }('fisk'); }"); + analyzeFail("{ int i = String _(Object x) { return x; }(1); }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("Function f = foo() {};"); + } + + public void testAssert() { + analyze("assert(true);"); + analyze("assert(false);"); + analyze("assert(true, 'message');"); + analyze("assert(false, 'message');"); + analyzeFail("assert('message', false);", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("assert('message', true);", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("assert('message');", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("assert(null);"); + analyzeFail("assert(1);", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("assert(foo() {});"); + analyze("assert(bool foo() {});"); + analyze("assert(Object foo() {});"); + analyzeFail("assert(String foo() {});", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testReturn() { + analyzeFail(returnWithType("int", "'string'"), + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze(returnWithType("", "'string'")); + analyze(returnWithType("Object", "'string'")); + analyze(returnWithType("String", "'string'")); + analyze(returnWithType("String", null)); + analyze(returnWithType("int", null)); + analyze(returnWithType("void", "")); + analyzeFail(returnWithType("void", 1), DartCompilerErrorCode.VOID_CANNOT_RETURN_VALUE); + analyzeFail(returnWithType("void", null), DartCompilerErrorCode.VOID_CANNOT_RETURN_VALUE); + analyzeFail(returnWithType("String", ""), DartCompilerErrorCode.MISSING_RETURN_VALUE); + analyze("String foo() {};"); // Should probably fail, http://b/4484060. + } + + public void testNamedFunctionTypeAlias() { + loadFile("named_function_type_alias.dart"); + analyze("VoidFunction f = foo() {};"); + } + + public void testUnresolved() { + ClassElement element = loadClass("class_with_supertypes.dart", "ClassWithSupertypes"); + analyzeIn(element, "null", 0); + analyzeIn(element, "noSuchField", 1); + analyzeIn(element, "noSuchMethod()", 1); + analyzeIn(element, "method()", 0); + analyzeIn(element, "field", 0); + analyzeIn(element, "this.noSuchField", 1); + analyzeIn(element, "this.noSuchMethod()", 1); + analyzeIn(element, "this.method()", 0); + analyzeIn(element, "this.field", 0); + analyzeIn(element, "staticMethod()", 0); + analyzeIn(element, "staticField", 0); + analyzeIn(element, "this.staticMethod()", 1); + analyzeIn(element, "this.staticField", 1); + analyzeIn(element, "ClassWithSupertypes.staticMethod()", 0); + analyzeIn(element, "ClassWithSupertypes.staticField", 0); + analyzeIn(element, "methodInSuperclass()", 0); + analyzeIn(element, "fieldInSuperclass", 0); + analyzeIn(element, "staticMethodInSuperclass()", 0); + analyzeIn(element, "staticFieldInSuperclass", 0); + analyzeIn(element, "this.methodInSuperclass()", 0); + analyzeIn(element, "this.fieldInSuperclass", 0); + analyzeIn(element, "this.staticMethodInSuperclass()", 1); + analyzeIn(element, "this.staticFieldInSuperclass", 1); + analyzeIn(element, "Superclass.staticMethodInSuperclass()", 0); + analyzeIn(element, "Superclass.staticFieldInSuperclass", 0); + analyzeIn(element, "methodInInterface()", 0); + analyzeIn(element, "fieldInInterface", 0); + analyzeIn(element, "this.methodInInterface()", 0); + analyzeIn(element, "this.fieldInInterface", 0); + analyzeIn(element, "staticFieldInInterface", 0); + analyzeIn(element, "Interface.staticFieldInInterface", 0); + analyzeIn(element, "this.staticFieldInInterface", 1); + } + + public void testTypeVariables() { + ClassElement cls = loadFile("class_with_type_parameter.dart").get("ClassWithTypeParameter"); + assertNotNull("unable to locate ClassWithTypeParameter", cls); + analyzeIn(cls, "aField = tField", 0); + analyzeIn(cls, "bField = tField", 0); + analyzeIn(cls, "tField = aField", 0); + analyzeIn(cls, "tField = bField", 0); + analyzeIn(cls, "tField = null", 0); + analyzeIn(cls, "tField = 1", 1); + 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); + } + + public void testFieldAccess() { + ClassElement element = loadFile("class_with_supertypes.dart").get("ClassWithSupertypes"); + assertNotNull("unable to locate ClassWithSupertypes", element); + analyzeIn(element, "field = 1", 0); + analyzeIn(element, "staticField = 1", 0); + analyzeIn(element, "fieldInSuperclass = 1", 0); + analyzeIn(element, "staticFieldInSuperclass = 1", 0); + + analyzeIn(element, "field = field", 0); + analyzeIn(element, "field = staticField", 0); + analyzeIn(element, "field = fieldInSuperclass", 0); + analyzeIn(element, "field = staticFieldInSuperclass", 0); + analyzeIn(element, "field = fieldInInterface", 0); + analyzeIn(element, "field = staticFieldInInterface", 0); + + analyzeIn(element, "field = 1", 0); + analyzeIn(element, "staticField = 1", 0); + analyzeIn(element, "fieldInSuperclass = 1", 0); + analyzeIn(element, "staticFieldInSuperclass = 1", 0); + + analyzeIn(element, "field = ''", 1); + analyzeIn(element, "staticField = ''", 1); + analyzeIn(element, "fieldInSuperclass = ''", 1); + analyzeIn(element, "staticFieldInSuperclass = ''", 1); + + analyzeIn(element, "field.noSuchField", 1); + analyzeIn(element, "staticField.noSuchField", 1); + analyzeIn(element, "fieldInSuperclass.noSuchField", 1); + analyzeIn(element, "staticFieldInSuperclass.noSuchField", 1); + analyzeIn(element, "fieldInInterface.noSuchField", 1); + analyzeIn(element, "staticFieldInInterface.noSuchField", 1); + + analyzeIn(element, "new ClassWithSupertypes()", 2); // Abstract class. + analyzeIn(element, "field = new ClassWithSupertypes().field", 1); + analyzeIn(element, "field = new ClassWithSupertypes().staticField", 2); + analyzeIn(element, "field = new ClassWithSupertypes().fieldInSuperclass", 1); + analyzeIn(element, "field = new ClassWithSupertypes().staticFieldInSuperclass", 2); + analyzeIn(element, "field = new ClassWithSupertypes().fieldInInterface", 1); + analyzeIn(element, "field = new ClassWithSupertypes().staticFieldInInterface", 2); + + analyzeIn(element, "new ClassWithSupertypes().field = 1", 1); + analyzeIn(element, "new ClassWithSupertypes().staticField = 1", 2); + analyzeIn(element, "new ClassWithSupertypes().fieldInSuperclass = 1", 1); + analyzeIn(element, "new ClassWithSupertypes().staticFieldInSuperclass = 1", 2); + // Enable this test when constness is propagated: + // analyzeIn(element, "new ClassWithSupertypes().fieldInInterface = 1", 1); + analyzeIn(element, "new ClassWithSupertypes().staticFieldInInterface = 1", 2); + } + + public void testPropertyAccess() { + ClassElement cls = loadClass("classes_with_properties.dart", "ClassWithProperties"); + analyzeIn(cls, "null", 0); + analyzeIn(cls, "noSuchField", 1); + analyzeIn(cls, "noSuchMethod()", 1); + analyzeIn(cls, "x.noSuchField", 0); + analyzeIn(cls, "x.noSuchMethod()", 0); + analyzeIn(cls, "x.x.noSuchField", 0); + analyzeIn(cls, "x.x.noSuchMethod()", 0); + analyzeIn(cls, "x.a.noSuchField", 0); + analyzeIn(cls, "x.a.noSuchMethod()", 0); + String[] typedFields = { "a", "b", "c"}; + for (String field : typedFields) { + analyzeIn(cls, field + ".noSuchField", 1); + analyzeIn(cls, field + ".noSuchMethod()", 1); + analyzeIn(cls, field + ".a", 0); + analyzeIn(cls, field + ".a()", 1); + } + } + + public void testParameterAccess() { + analyze("{ f(int x) { x = 1; } }"); + analyzeFail("{ f(String x) { x = 1; } }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("{ f(int x, int y) { x = y; } }"); + analyzeFail("{ f(String x, int y) { x = y; } }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("{ f(x, int y) { x = y; } }"); + analyze("{ f(x, int y) { x = y; } }"); + analyzeFail("{ f(String x) { x = 1;} }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testConditionalExpression() { + analyze("true ? 1 : 2;"); + analyze("null ? 1 : 2;"); + analyzeFail("0 ? 1 : 2;", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("'' ? 1 : 2;", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int i; true ? i = 2.7 : 2; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int i; true ? 2 : i = 2.7; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("{ int i; i = true ? 2.7 : 2; }"); + } + + public void testDoWhileStatement() { + analyze("do {} while (true);"); + analyze("do {} while (null);"); + analyzeFail("do {} while (0);", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("do {} while ('');", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("do { int i = 0.5; } while (true);", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("do { int i = 0.5; } while (null);", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testForStatement() { + analyze("for (;true;) {}"); + analyze("for (;null;) {}"); + analyzeFail("for (;0;) {}", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("for (;'';) {}", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testIfStatement() { + analyze("if (true) {}"); + analyze("if (null) {}"); + analyzeFail("if (0) {}", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("if ('') {}", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int i = 27; if (true) { i = 2.7; } else {} }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int i = 27; if (true) {} else { i = 2.7; } }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testWhileStatement() { + analyze("while (true) {}"); + analyze("while (null) {}"); + analyzeFail("while (0) {}", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("while ('') {}", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testThis() { + Map classes = loadFile("class_with_supertypes.dart"); + ClassElement superclass = classes.get("Superclass"); + assertNotNull("unable to locate Superclass", superclass); + ClassElement subclass = classes.get("ClassWithSupertypes"); + assertNotNull("unable to locate ClassWithSupertypes", subclass); + analyzeIn(superclass, "() { String x = this; }", 1); + analyzeIn(superclass, "() { var x = this; }", 0); + analyzeIn(superclass, "() { ClassWithSupertypes x = this; }", 0); + analyzeIn(superclass, "() { Superclass x = this; }", 0); + analyzeIn(superclass, "() { Interface x = this; }", 1); + analyzeIn(subclass, "() { Interface x = this; }", 0); + } + + public void testMapLiteral() { + analyze("{ var x = {\"key\": 42}; }"); + analyze("{ var x = {'key': 42}; }"); + analyze("{ var x = {'key': 42}; }"); + analyze("{ var x = {'key': 42}; }"); + analyze("{ var x = {'key': 0.42}; }"); + analyze("{ var x = {'key': 42}; }"); + analyzeFail("{ var x = {'key': 0.42}; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int i; var x = {'key': i = 0.42}; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("{ var x = const {\"key\": 42}; }"); + analyze("{ var x = const {'key': 42}; }"); + analyze("{ var x = const {'key': 42}; }"); + analyze("{ var x = const {'key': 42}; }"); + analyze("{ var x = const {'key': 0.42}; }"); + analyze("{ var x = const {'key': 42}; }"); + analyzeFail("{ var x = const {'key': 0.42}; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("{ int i; var x = const {'key': i = 0.42}; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("Map map = {'foo':1};", + DartCompilerErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); + analyzeFail("{var x = const {}; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testTryCatchFinally() { + analyze("try { } catch (var _) { } finally { }"); + analyzeFail("try { int i = 4.2; } catch (var _) { } finally { }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("try { } catch (var _) { int i = 4.2; } finally { }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("try { } catch (var _) { } finally { int i = 4.2; }", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testUnqualified() { + ClassElement element = loadClass("class_with_methods.dart", "ClassWithMethods"); + checkAssignIn(element, "var", "intNoArgumentMethod()", 0); + checkAssignIn(element, "var", "intOneArgumentMethod(1)", 0); + checkAssignIn(element, "var", "intOneArgumentMethod('')", 1); + checkAssignIn(element, "int", "intNoArgumentMethod()", 0); + checkAssignIn(element, "int", "intOneArgumentMethod(1)", 0); + checkAssignIn(element, "int", "intOneArgumentMethod('')", 1); + checkAssignIn(element, "String", "intNoArgumentMethod()", 1); + checkAssignIn(element, "String", "intOneArgumentMethod(1)", 1); + checkAssignIn(element, "String", "intOneArgumentMethod('')", 2); + + checkAssignIn(element, "var", "functionField()", 0); + checkAssignIn(element, "int", "functionField()", 0); + checkAssignIn(element, "String", "functionField()", 0); + + checkAssignIn(element, "var", "functionField(1)", 0); + checkAssignIn(element, "int", "functionField('x')", 0); + checkAssignIn(element, "String", "functionField(2.2)", 0); + + + checkAssignIn(element, "var", "untypedField()", 0); + checkAssignIn(element, "int", "untypedField()", 0); + checkAssignIn(element, "String", "untypedField()", 0); + + checkAssignIn(element, "var", "untypedField(1)", 0); + checkAssignIn(element, "int", "untypedField('x')", 0); + checkAssignIn(element, "String", "untypedField(2.2)", 0); + + checkAssignIn(element, "var", "intField()", 1); + checkAssignIn(element, "int", "intField()", 1); + checkAssignIn(element, "String", "intField()", 1); + + checkAssignIn(element, "var", "intField(1)", 1); + 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); + } + + public void testUnqualifiedGeneric() { + ClassElement element = loadClass("generic_class_with_supertypes.dart", + "GenericClassWithSupertypes"); + + checkAssignIn(element, "var", "localField", 0); + checkAssignIn(element, "T1", "localField", 0); + checkAssignIn(element, "T2", "localField", 1); + + checkAssignIn(element, "var", "superField", 0); + checkAssignIn(element, "T1", "superField", 1); + checkAssignIn(element, "T2", "superField", 0); + + checkAssignIn(element, "var", "interfaceField", 0); + checkAssignIn(element, "T1", "interfaceField", 0); + checkAssignIn(element, "T2", "interfaceField", 1); + + checkAssignIn(element, "var", "localMethod(t2)", 0); + checkAssignIn(element, "T1", "localMethod(t2)", 0); + checkAssignIn(element, "T2", "localMethod(t2)", 1); + + checkAssignIn(element, "var", "superMethod(t1)", 0); + checkAssignIn(element, "T1", "superMethod(t1)", 1); + checkAssignIn(element, "T2", "superMethod(t1)", 0); + + checkAssignIn(element, "var", "interfaceMethod(t1)", 0); + checkAssignIn(element, "T1", "interfaceMethod(t1)", 0); + checkAssignIn(element, "T2", "interfaceMethod(t1)", 1); + } + + public void testSuper() { + ClassElement sub = loadClass("covariant_class.dart", "Sub"); + checkAssignIn(sub, "B", "field", 0); + checkAssignIn(sub, "C", "field", 1); + checkAssignIn(sub, "D", "field", 1); + + checkAssignIn(sub, "B", "super.field", 0); + checkAssignIn(sub, "C", "super.field", 0); + checkAssignIn(sub, "D", "super.field", 1); + + checkAssignIn(sub, "B", "accessor", 0); + checkAssignIn(sub, "C", "accessor", 1); + checkAssignIn(sub, "D", "accessor", 1); + + checkAssignIn(sub, "B", "super.accessor", 0); + checkAssignIn(sub, "C", "super.accessor", 0); + checkAssignIn(sub, "D", "super.accessor", 1); + + analyzeIn(sub, "accessor = b", 0); + analyzeIn(sub, "accessor = c", 1); + analyzeIn(sub, "accessor = d", 1); + + analyzeIn(sub, "super.accessor = b", 0); + analyzeIn(sub, "super.accessor = c", 0); + analyzeIn(sub, "super.accessor = d", 1); + + checkAssignIn(sub, "B", "method()", 0); + checkAssignIn(sub, "C", "method()", 1); + checkAssignIn(sub, "D", "method()", 1); + + checkAssignIn(sub, "B", "super.untypedMethod()", 0); + checkAssignIn(sub, "C", "super.untypedMethod()", 0); + checkAssignIn(sub, "D", "super.untypedMethod()", 0); + + checkAssignIn(sub, "B", "super.untypedField", 0); + checkAssignIn(sub, "C", "super.untypedField", 0); + checkAssignIn(sub, "D", "super.untypedField", 0); + + checkAssignIn(sub, "B", "super.untypedAccessor", 0); + checkAssignIn(sub, "C", "super.untypedAccessor", 0); + checkAssignIn(sub, "D", "super.untypedAccessor", 0); + + analyzeIn(sub, "super.untypedAccessor = b", 0); + analyzeIn(sub, "super.untypedAccessor = c", 0); + analyzeIn(sub, "super.untypedAccessor = d", 0); + + checkAssignIn(sub, "B", "super.untypedMethod()", 0); + checkAssignIn(sub, "C", "super.untypedMethod()", 0); + checkAssignIn(sub, "D", "super.untypedMethod()", 0); + } + + public void testSwitch() { + analyze("{ int i = 27; switch(i) { case i: break; } }"); + analyze("{ num i = 27; switch(i) { case i: break; } }"); + analyze("{ switch(true) { case 1: break; case 'foo': break; }}"); + analyzeFail("{ int i = 27; switch(true) { case false: i = 2.7; }}", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testConstructorForwarding() { + Map classes = loadSource( + "class MissingArgument {", + " MissingArgument() : this.bar() {}", + " MissingArgument.bar(int i) {}", + "}", + "class IntArgument {", + " IntArgument() : this.bar(1) {}", + " IntArgument.bar(int i) {}", + "}", + "class ExtraIntArgument {", + " ExtraIntArgument() : this.bar(1, 1) {}", + " ExtraIntArgument.bar(int i) {}", + "}", + "class StringArgument {", + " StringArgument() : this.bar('') {}", + " StringArgument.bar(int i) {}", + "}", + "class NullArgument {", + " NullArgument() : this.bar(null) {}", + " NullArgument.bar(int i) {}", + "}", + "class OptionalParameter {", + " OptionalParameter() : this.bar() {}", + " OptionalParameter.bar(int i = null) {}", + " OptionalParameter.foo() : this.bar('') {}", + "}"); + analyzeClass(classes.get("MissingArgument"), 1); + analyzeClass(classes.get("IntArgument"), 0); + analyzeClass(classes.get("ExtraIntArgument"), 1); + analyzeClass(classes.get("StringArgument"), 1); + analyzeClass(classes.get("NullArgument"), 0); + analyzeClass(classes.get("OptionalParameter"), 1); + } + + public void testSuperConstructorInvocation() { + Map classes = loadSource( + "class Super {", + " Super(int x) {}", + " Super.foo() {}", + " Super.bar(int i = null) {}", + "}", + "class BadSub extends Super {", + " BadSub() : super('x') {}", + " BadSub.foo() : super.foo('x') {}", + " BadSub.bar() : super() {}", + " BadSub.baz() : super.foo(null) {}", + " BadSub.fisk() : super.bar('') {}", + " BadSub.hest() : super.bar(1, 2) {}", + "}", + "class NullSub extends Super {", + " NullSub() : super(null) {}", + " NullSub.foo() : super.bar(null) {}", + " NullSub.bar() : super.bar() {}", + "}", + "class IntSub extends Super {", + " IntSub() : super(1) {}", + " IntSub.foo() : super.bar(1) {}", + "}", + // The following works fine, but was claimed to be a bug: + "class A {", + " int value;", + " A(this.value = 3) {}", + "}", + "class B extends A {", + " B() : super() {}", + "}"); + analyzeClass(classes.get("Super"), 0); + analyzeClass(classes.get("BadSub"), 6); + analyzeClass(classes.get("NullSub"), 0); + analyzeClass(classes.get("IntSub"), 0); + analyzeClass(classes.get("A"), 0); + analyzeClass(classes.get("B"), 0); + } + + public void testNewExpression() { + analyzeClasses(loadSource( + "class Foo {", + " Foo(int x) {}", + " Foo.foo() {}", + " Foo.bar(int i = null) {}", + "}", + "interface Bar factory Baz {", + " Bar.make();", + "}", + "class Baz {", + " factory Bar.make(S x) { return null; }", + "}")); + + analyze("Foo x = new Foo(0);"); + analyzeFail("Foo x = new Foo();", DartCompilerErrorCode.MISSING_ARGUMENT); + analyzeFail("Foo x = new Foo('');", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("Foo x = new Foo(0, null);", DartCompilerErrorCode.EXTRA_ARGUMENT); + + analyze("Foo x = new Foo.foo();"); + analyzeFail("Foo x = new Foo.foo(null);", DartCompilerErrorCode.EXTRA_ARGUMENT); + + analyze("Foo x = new Foo.bar();"); + analyze("Foo x = new Foo.bar(0);"); + analyzeFail("Foo x = new Foo.bar('');", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("Foo x = new Foo.bar(0, null);", DartCompilerErrorCode.EXTRA_ARGUMENT); + + analyze("Bar x = new Bar.make('');"); + } + + public void testFactory() { + analyzeClasses(loadSource( + "interface Foo factory Bar {", + " Foo(argument);", + "}", + "interface Baz {}", + "class Bar implements Foo, Baz {", + " Bar(String argument) {}", + "}")); + + analyzeFail("Baz x = new Foo('');", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testFunctionTypeAlias() { + Map classes = loadSource( + "typedef void VoidFunction();", + "typedef String StringFunction();", + "typedef String IntToStringFunction(int i);", + "class Foo {", + " VoidFunction voidFunction;", + " StringFunction stringFunction;", + " IntToStringFunction intToStringFunction;", + " Foo foo;", + " String string;", + " int i;", + "}"); + analyzeClasses(classes); + ClassElement foo = classes.get("Foo"); + analyzeIn(foo, "voidFunction()", 0); + analyzeIn(foo, "voidFunction(1)", 1); + analyzeIn(foo, "this.voidFunction()", 0); + analyzeIn(foo, "this.voidFunction(1)", 1); + analyzeIn(foo, "foo.voidFunction()", 0); + analyzeIn(foo, "foo.voidFunction(1)", 1); + analyzeIn(foo, "(voidFunction)()", 0); + analyzeIn(foo, "(voidFunction)(1)", 1); + analyzeIn(foo, "(this.voidFunction)()", 0); + analyzeIn(foo, "(this.voidFunction)(1)", 1); + analyzeIn(foo, "(foo.voidFunction)()", 0); + analyzeIn(foo, "(foo.voidFunction)(1)", 1); + + analyzeIn(foo, "string = stringFunction()", 0); + analyzeIn(foo, "i = stringFunction()", 1); + analyzeIn(foo, "string = this.stringFunction()", 0); + analyzeIn(foo, "i = this.stringFunction()", 1); + analyzeIn(foo, "string = foo.stringFunction()", 0); + analyzeIn(foo, "i = foo.stringFunction()", 1); + analyzeIn(foo, "string = (stringFunction)()", 0); + analyzeIn(foo, "i = (stringFunction)()", 1); + analyzeIn(foo, "string = (this.stringFunction)()", 0); + analyzeIn(foo, "i = (this.stringFunction)()", 1); + analyzeIn(foo, "string = (foo.stringFunction)()", 0); + analyzeIn(foo, "i = (foo.stringFunction)()", 1); + + analyzeIn(foo, "voidFunction = stringFunction", 0); + analyzeIn(foo, "stringFunction = intToStringFunction", 1); + analyzeIn(foo, "stringFunction = String foo() { return ''; }", 0); + analyzeIn(foo, "intToStringFunction = String foo() { return ''; }", 1); + } + + public void testVoid() { + // Return a value from a void function. + analyze("void f() { return; }"); + analyzeFail("void f() { return null; }", DartCompilerErrorCode.VOID_CANNOT_RETURN_VALUE); + analyzeFail("void f() { return f(); }", DartCompilerErrorCode.VOID_CANNOT_RETURN_VALUE); + analyzeFail("void f() { return 1; }", DartCompilerErrorCode.VOID_CANNOT_RETURN_VALUE); + analyzeFail("void f() { var x; return x; }", DartCompilerErrorCode.VOID_CANNOT_RETURN_VALUE); + + // No-arg return from non-void function. + analyzeFail("int f() { return; }", DartCompilerErrorCode.MISSING_RETURN_VALUE); + analyze("f() { return; }"); + + // Calling a method on a void expression, property access. + analyzeFail("void f() { f().m(); }", DartCompilerErrorCode.VOID); + analyzeFail("void f() { f().x; }", DartCompilerErrorCode.VOID); + + // Passing a void argument to a method. + analyzeFail("{ void f() {} m(x) {} m(f()); }", DartCompilerErrorCode.VOID); + + // Assigning a void expression to a variable. + analyzeFail("{ void f() {} String x = f(); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} String x; x = f(); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} String x; x += f(); }", DartCompilerErrorCode.VOID); + + // Misc. + analyzeFail("{ void f() {} 1 + f(); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} f() + 1; }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} var x; x && f(); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} !f(); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} -f(); }", DartCompilerErrorCode.VOID); + // We seem to throw away prefix-plus in the parser: + // analyzeFail("{ void f() {} +f(); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} var x; x == f(); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} assert(f()); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} assert(f); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} while (f()); }", DartCompilerErrorCode.VOID); + analyzeFail("{ void f() {} ({ 'x': f() }); }", DartCompilerErrorCode.VOID); + } + + public void testFieldInitializers() { + Map classes = loadSource( + "class Good {", + " String string;", + " int i;", + " Good() : string = '', i = 1;", + " Good.name() : string = null, i = null;", + " Good.untyped(x) : string = x, i = x;", + " Good.string(String s) : string = s, i = 0;", + "}", + "class Bad {", + " String string;", + " int i;", + " Bad() : string = 1, i = '';", + " Bad.string(String s) : string = s, i = s;", + "}"); + analyzeClass(classes.get("Good"), 0); + analyzeClass(classes.get("Bad"), 3); + } + + public void testArrayLiteral() { + analyze("['x'];"); + analyze("['x'];"); + analyzeFail("['x'];", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("['x', 1];", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyze("Array strings = ['x'];"); + analyze("Array strings = ['x'];"); + analyze("Array array = ['x'];"); + analyze("Array array = ['x'];"); + analyze("Array ints = ['x'];"); + analyzeFail("Array ints = ['x'];", + DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + analyzeFail("Array ints = [1];", + DartCompilerErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS); + } + + public void testInitializedLocals() { + analyze("void f(int x = 1) {}"); + analyzeFail("void f(int x = '') {}", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + + analyze("{ int x = 1; }"); + analyzeFail("{ int x = ''; }", DartCompilerErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE); + } + + public void testInitializedFields() { + Map classes = loadSource( + "class GoodField {", + " static final int i = 1;", + "}", + "class BadField {", + " static final int i = '';", + "}"); + analyzeClass(classes.get("GoodField"), 0); + analyzeClass(classes.get("BadField"), 1); + } + + public void testGetAllSupertypes() + throws CyclicDeclarationException, DuplicatedInterfaceException { + Map classes = loadSource( + "class A extends B {", + "}", + "class B extends C> implements I, I1 {", + "}", + "class C {", + "}", + "interface I extends I2 {", + "}", + "class G {", + "}", + "interface I1 {", + "}", + "interface I2 {", + "}", + "class D implements I2 {", + "}", + "class E extends D implements I2 {", + "}"); + analyzeClasses(classes); + assertEquals("[]", object.getAllSupertypes().toString()); + assertEquals("[I, I1, I2, B, C>, Object]", + classes.get("A").getAllSupertypes().toString()); + assertEquals("[I, I1, I2, C>, Object]", + classes.get("B").getAllSupertypes().toString()); + assertEquals("[Object]", classes.get("C").getAllSupertypes().toString()); + assertEquals("[I2, Object]", classes.get("I").getAllSupertypes().toString()); + assertEquals("[Object]", classes.get("G").getAllSupertypes().toString()); + assertEquals("[Object]", classes.get("I1").getAllSupertypes().toString()); + assertEquals("[Object]", classes.get("I2").getAllSupertypes().toString()); + assertEquals("[I2, Object]", classes.get("D").getAllSupertypes().toString()); + assertEquals("[I2, D, Object]", classes.get("E").getAllSupertypes().toString()); + } + + public void testParameterInitializers() { + Map classes = loadSource( + "class C1 { int i; C1(this.i) {} }", + "class C2 { String s; C2(int this.s) {} }", + "class C3 { int i; C3(double this.i) {} }", + "class C4 { int i; C4(num this.i) {} }"); + analyzeClass(classes.get("C1"), 0); + analyzeClass(classes.get("C2"), 1); + analyzeClass(classes.get("C3"), 1); + analyzeClass(classes.get("C4"), 0); + } + + public void testImplementsAndOverrides() { + analyzeClasses(loadSource( + "interface Interface {", + " void foo();", + " void bar();", + "}", + // Abstract class not reported until first instantiation. + "class Class implements Interface {", + " Class() {}", + " String bar() { return null; }", + "}", + // Abstract class not reported until first instantiation. + "class SubClass extends Class {", + " SubClass() : super() {}", + " Object bar() { return null; }", + "}", + "class SubSubClass extends Class {", + " num bar() { return null; }", // TYPE_NOT_ASSIGNMENT_COMPATIBLE. + " void foo(x = null) {}", // TYPE_NOT_ASSIGNMENT_COMPATIBLE. + "}", + "class Usage {", + " m() {", + " new Class();", // CANNOT_INSTATIATE_ABSTRACT_CLASS + // ABSTRACT_CLASS. + " new Class();", // CANNOT_INSTATIATE_ABSTRACT_CLASS. + " new SubClass();", // CANNOT_INSTATIATE_ABSTRACT_CLASS + //ABSTRACT_CLASS. + " }", + "}"), + DartCompilerErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, + DartCompilerErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, + DartCompilerErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, + DartCompilerErrorCode.ABSTRACT_CLASS, + DartCompilerErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, + DartCompilerErrorCode.CANNOT_INSTATIATE_ABSTRACT_CLASS, + DartCompilerErrorCode.ABSTRACT_CLASS); + } + + public void testOddStuff() { + Map classes = analyzeClasses(loadSource( + "class Class {", + " Class() {}", + " var field;", + " void m() {}", + " static void f() {}", + " static g(int i) {}", + "}")); + ClassElement cls = classes.get("Class"); + analyzeIn(cls, "m().foo()", 1); + analyzeIn(cls, "m().x", 1); + analyzeIn(cls, "m()", 0); + analyzeIn(cls, "(m)().foo()", 1); + analyzeIn(cls, "(m)().x", 1); + analyzeIn(cls, "(m)()", 0); + analyzeIn(cls, "field = m()", 1); + analyzeIn(cls, "field = Class.f()", 1); + analyzeIn(cls, "field = (Class.f)()", 1); + analyzeIn(cls, "Class.f()", 0); + analyzeIn(cls, "(Class.f)()", 0); + analyzeIn(cls, "field = Class.g('x')", 1); + analyzeIn(cls, "field = (Class.g)('x')", 1); + analyzeIn(cls, "field = Class.g(0)", 0); + analyzeIn(cls, "field = (Class.g)(0)", 0); + analyzeFail("fisk: while (true) fisk++;", DartCompilerErrorCode.CANNOT_BE_RESOLVED); + analyzeFail("new Class().m().x;", DartCompilerErrorCode.VOID); + analyzeFail("(new Class().m)().x;", DartCompilerErrorCode.VOID); + } + + private Map analyzeClasses(Map classes, + ErrorCode... codes) { + setExpectedTypeErrorCount(codes.length); + for (ClassElement cls : classes.values()) { + analyzeToplevel(cls.getNode()); + } + List errorCodes = context.getErrorCodes(); + assertEquals(Arrays.toString(codes), errorCodes.toString()); + errorCodes.clear(); + checkExpectedTypeErrorCount(); + return classes; + } + + private Type checkAssignIn(ClassElement element, String type, String expression, int errorCount) { + return analyzeIn(element, assign(type, expression), errorCount); + } + + private String assign(String type, String expression) { + return String.format("void foo() { %s x = %s; }", type, expression); + } + + private ClassElement loadClass(String file, String name) { + ClassElement cls = loadFile(file).get(name); + assertNotNull("unable to locate " + name, cls); + return cls; + } + + private String returnWithType(String type, Object expression) { + return String.format("%s foo() { return %s; }", type, String.valueOf(expression)); + } + + private Map loadFile(final String name) { + String source = getResource(name); + return loadSource(source); + } + + private Map loadSource(String firstLine, String secondLine, + String... rest) { + return loadSource(Joiner.on('\n').join(firstLine, secondLine, (Object[]) rest).toString()); + } + + private Map loadSource(String source) { + Map classes = new LinkedHashMap(); + DartUnit unit = parseUnit(source); + TopLevelElementBuilder elementBuilder = new TopLevelElementBuilder(); + elementBuilder.exec(unit, context); + for (DartNode node : unit.getTopLevelNodes()) { + if (node instanceof DartClass) { + DartClass classNode = (DartClass) node; + final ClassElement classElement = classNode.getSymbol(); + String className = classElement.getName(); + coreElements.put(className, classElement); + classes.put(className, classElement); + } else { + DartFunctionTypeAlias alias = (DartFunctionTypeAlias) node; + FunctionAliasElement element = alias.getSymbol(); + coreElements.put(element.getName(), element); + } + } + Scope scope = getMockScope(""); + SupertypeResolver supertypeResolver = new SupertypeResolver(); + supertypeResolver.exec(unit, context, scope, typeProvider); + MemberBuilder memberBuilder = new MemberBuilder(); + memberBuilder.exec(unit, context, scope, typeProvider); + resolver.exec(unit); + return classes; + } + + private String getResource(String name) { + String packageName = getClass().getPackage().getName().replace('.', '/'); + String resouceName = packageName + "/" + name; + InputStream stream = getClass().getClassLoader().getResourceAsStream(resouceName); + if (stream == null) { + throw new AssertionError("Missing resource: " + resouceName); + } + InputStreamReader reader = new InputStreamReader(stream); + try { + return CharStreams.toString(reader); // Also closes the reader. + } catch (IOException e) { + throw new IOError(e); + } + } + + private void analyzeFail(String statement, DartCompilerErrorCode errorCode) { + try { + analyze(statement); + fail("Test unexpectedly passed. Expected ErrorCode: " + errorCode.name()); + } catch (TestTypeError error) { + assertEquals(errorCode, error.getErrorCode()); + } + } + + private void checkSimpleType(Type type, String expression) { + assertSame(type, typeOf(expression)); + setExpectedTypeErrorCount(1); // x is unresolved. + assertSame(type, typeOf("x = " + expression)); + checkExpectedTypeErrorCount(); + } + + private void checkType(Type type, String expression) { + assertEquals(type, typeOf(expression)); + assertEquals(type, typeOf("x = " + expression)); + } + + private Type typeOf(String expression) { + return analyzeNode(parseExpression(expression)); + } + + private DartStatement analyze(String statement) { + DartStatement node = parseStatement(statement); + analyzeNode(node); + return node; + } + + private Type analyzeIn(ClassElement element, String expression, int expectedErrorCount) { + DartExpression node = parseExpression(expression); + ResolutionContext resolutionContext = + new ResolutionContext(getMockScope(""), context, + typeProvider).extend(element); + ResolveElementsVisitor visitor = + resolver.new ResolveElementsVisitor(resolutionContext, element, + Elements.methodElement(null, null)); + setExpectedTypeErrorCount(expectedErrorCount); + node.accept(visitor); + Type type = node.accept(makeTypeAnalyzer(element)); + checkExpectedTypeErrorCount(expression); + return type; + } + + private Type analyzeNode(DartNode node) { + ResolutionContext resolutionContext = + new ResolutionContext(getMockScope(""), context, typeProvider); + ResolveElementsVisitor visitor = + resolver.new ResolveElementsVisitor(resolutionContext, null, + Elements.methodElement(null, null)); + node.accept(visitor); + return node.accept(makeTypeAnalyzer(Elements.dynamicElement())); + } + + private Type analyzeToplevel(DartNode node) { + return node.accept(makeTypeAnalyzer(Elements.dynamicElement())); + } + + private ClassElement analyzeClass(ClassElement cls, int count) { + setExpectedTypeErrorCount(count); + analyzeToplevel(cls.getNode()); + checkExpectedTypeErrorCount(cls.getName()); + return cls; + } + + private DartParser getParser(String string) { + return new DartParser(new DartScannerParserContext(null, string, listener)); + } + + private DartExpression parseExpression(String source) { + return getParser(source).parseExpression(); + } + + private DartStatement parseStatement(String source) { + return getParser(source).parseStatement(); + } + + private DartUnit parseUnit(String string) { + DartSourceString source = new DartSourceString("", string); + return getParser(string).parseUnit(source); + } + + private class MockScope extends Scope { + private MockScope() { + super("test mock scope"); + } + + @Override + public Element findLocalElement(String name) { + return coreElements.get(name); + } + + } + + private Scope getMockScope(String name) { + return new Scope(name, new MockScope()); + } + + private class MockCoreTypeProvider implements CoreTypeProvider { + private final Type voidType = Types.newVoidType(); + private final DynamicType dynamicType = Types.newDynamicType(); + + @Override + public InterfaceType getIntType() { + return intElement.getType(); + } + + @Override + public InterfaceType getDoubleType() { + return doubleElement.getType(); + } + + @Override + public InterfaceType getBoolType() { + return bool.getType(); + } + + @Override + public InterfaceType getStringType() { + return string.getType(); + } + + @Override + public InterfaceType getFunctionType() { + return function.getType(); + } + + @Override + public InterfaceType getArrayType(Type elementType) { + return array.getType().subst(Arrays.asList(elementType), array.getTypeParameters()); + } + + @Override + public Type getNullType() { + return getDynamicType(); + } + + @Override + public Type getVoidType() { + return voidType; + } + + @Override + public DynamicType getDynamicType() { + return dynamicType; + } + + @Override + public InterfaceType getFallThroughError() { + throw new AssertionError(); + } + + @Override + public InterfaceType getMapType(Type key, Type value) { + InterfaceType mapType = map.getType(); + return mapType.subst(Arrays.asList(key, value), + mapType.getElement().getTypeParameters()); + } + + @Override + public InterfaceType getObjectArrayType() { + throw new AssertionError(); + } + + @Override + public InterfaceType getObjectType() { + return object.getType(); + } + + @Override + public InterfaceType getNumType() { + return number.getType(); + } + + @Override + public InterfaceType getArrayLiteralType(Type value) { + throw new AssertionError(); + } + + @Override + public InterfaceType getMapLiteralType(Type key, Type value) { + throw new AssertionError(); + } + + @Override + public InterfaceType getStringImplementationType() { + throw new AssertionError(); + } + + @Override + public InterfaceType getIsolateType() { + throw new AssertionError(); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeTest.java b/compiler/javatests/com/google/dart/compiler/type/TypeTest.java new file mode 100644 index 00000000000..39199f9790f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/TypeTest.java @@ -0,0 +1,123 @@ +// 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. + +package com.google.dart.compiler.type; + + +import org.junit.Assert; + +import java.util.Arrays; +import java.util.List; + +public class TypeTest extends TypeTestCase { + private final Types types = Types.getInstance(null); + + @Override + Types getTypes() { + return types; + } + + public void testToString() { + Assert.assertEquals("Object", itype(object).toString()); + Assert.assertEquals("Array", objectArray.toString()); + Assert.assertEquals("GrowableArray", growableObjectArray.toString()); + Assert.assertEquals("Map", objectMap.toString()); + Assert.assertEquals("ReverseMap", reverseObjectMap.toString()); + + Assert.assertEquals("CLASS Object", object.toString()); + Assert.assertEquals("CLASS Array", array.toString()); + Assert.assertEquals("CLASS GrowableArray", growableArray.toString()); + Assert.assertEquals("CLASS Map", map.toString()); + Assert.assertEquals("CLASS ReverseMap", reverseMap.toString()); + + Assert.assertEquals("Object", object.getType().toString()); + Assert.assertEquals("Array", array.getType().toString()); + Assert.assertEquals("GrowableArray", growableArray.getType().toString()); + Assert.assertEquals("Map", map.getType().toString()); + Assert.assertEquals("ReverseMap", reverseMap.getType().toString()); + } + + public void testRaw() { + Assert.assertFalse(itype(object).isRaw()); + Assert.assertFalse(objectArray.isRaw()); + Assert.assertFalse(growableObjectArray.isRaw()); + Assert.assertFalse(objectMap.isRaw()); + Assert.assertFalse(reverseObjectMap.isRaw()); + + Assert.assertTrue(itype(array).isRaw()); + Assert.assertTrue(itype(array, itype(object), itype(object)).isRaw()); + + Assert.assertFalse(itype(array, objectMap).isRaw()); + } + + public void testAsInstanceOf() { + Assert.assertSame(growableObjectArray, types.asInstanceOf(growableObjectArray, growableArray)); + + Assert.assertEquals(itype(object), types.asInstanceOf(growableObjectArray, object)); + Assert.assertEquals(objectArray, types.asInstanceOf(growableObjectArray, array)); + + Assert.assertNull(types.asInstanceOf(growableObjectArray, map)); + + Assert.assertNull(types.asInstanceOf(itype(object), array)); + + Assert.assertEquals(intStringMap, types.asInstanceOf(stringIntReverseMap, map)); + + Assert.assertFalse(stringIntMap.equals(types.asInstanceOf(stringIntReverseMap, map))); + + Assert.assertEquals(itype(array), types.asInstanceOf(itype(array), array)); + Assert.assertEquals(itype(array), types.asInstanceOf(itype(growableArray), array)); + } + + public void testSubst() { + List vars = Arrays.asList(typeVar("K", itype(object)), typeVar("V", itype(object))); + Type canonMap = map.getType(); + Type substMap = canonMap.subst(vars, map.getTypeParameters()); + checkNotAssignable(canonMap, substMap); + Assert.assertFalse(canonMap.equals(substMap)); + Assert.assertFalse(substMap.equals(canonMap)); + + List args = Arrays.asList(itype(string), itype(intElement)); + Assert.assertTrue(types.isSubtype(canonMap.subst(args, map.getTypeParameters()), stringIntMap)); + Assert.assertTrue(types.isSubtype(substMap.subst(args, vars), stringIntMap)); + + TypeVariable tv = typeVar("T", itype(object)); + Assert.assertSame(tv, tv.subst(vars, args)); + } + + public void testEquals() { + Assert.assertEquals(object.getType(), itype(object)); + Assert.assertNotSame(object.getType(), itype(object)); + Assert.assertFalse(object.getType().equals(map.getTypeParameters().get(0))); + } + + public void testIsSubtype() { + checkSubtype(itype(object), itype(object)); + + checkStrictSubtype(itype(string), itype(object)); + checkStrictSubtype(itype(intElement), itype(object)); + checkNotAssignable(itype(string), itype(intElement)); + + checkStrictSubtype(objectArray, itype(object)); + + checkStrictSubtype(growableObjectArray, itype(object)); + checkStrictSubtype(growableObjectArray, objectArray); + + checkStrictSubtype(objectMap, itype(object)); + + checkStrictSubtype(reverseObjectMap, itype(object)); + checkStrictSubtype(reverseObjectMap, objectMap); + + checkNotAssignable(objectMap, objectArray); + checkNotAssignable(reverseObjectMap, objectArray); + checkNotAssignable(objectMap, growableObjectArray); + checkNotAssignable(reverseObjectMap, growableObjectArray); + + checkSubtype(itype(growableArray), growableObjectArray); + checkSubtype(growableObjectArray, itype(growableArray)); + + checkStrictSubtype(itype(growableArray), itype(object)); + checkStrictSubtype(itype(growableArray), itype(array)); + checkStrictSubtype(itype(growableArray), objectArray); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeTestCase.java b/compiler/javatests/com/google/dart/compiler/type/TypeTestCase.java new file mode 100644 index 00000000000..0792c5be2ee --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/TypeTestCase.java @@ -0,0 +1,179 @@ +// 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. + +package com.google.dart.compiler.type; + +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.ErrorCode; +import com.google.dart.compiler.resolver.ClassElement; +import com.google.dart.compiler.resolver.Elements; +import com.google.dart.compiler.resolver.TypeVariableElement; +import com.google.dart.compiler.testing.TestCompilerContext; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Common superclass for type tests. + */ +abstract class TypeTestCase extends TestCase { + + final Map coreElements = new HashMap(); + final ClassElement object = element("Object", null); + final ClassElement function = element("Function", itype(object)); + final ClassElement number = element("num", itype(object)); + final ClassElement intElement = element("int", itype(number)); + final ClassElement doubleElement = element("double", itype(number)); + final ClassElement bool = element("bool", itype(object)); + final ClassElement string = element("String", itype(object)); + final ClassElement array = element("Array", itype(object), typeVar("E", itype(object))); + final ClassElement growableArray = makeGrowableArray(array); + final ClassElement map = element("Map", itype(object), + typeVar("K", itype(object)), typeVar("V", itype(object))); + final ClassElement stackTrace = element("StackTrace", itype(object)); + final ClassElement reverseMap = makeReverseMap(map); + final InterfaceType objectArray = itype(array, itype(object)); + final InterfaceType growableObjectArray = itype(growableArray, itype(object)); + final InterfaceType objectMap = itype(map, itype(object), itype(object)); + final InterfaceType reverseObjectMap = itype(reverseMap, itype(object), itype(object)); + final InterfaceType stringIntMap = itype(map, itype(string), itype(intElement)); + final InterfaceType intStringMap = itype(map, itype(intElement), itype(string)); + final InterfaceType stringIntReverseMap = itype(reverseMap, itype(string), itype(intElement)); + final FunctionType returnObject = ftype(function, itype(object), null, null); + final FunctionType returnString = ftype(function, itype(string), null, null); + final FunctionType objectToObject = ftype(function, itype(object), null, null, itype(object)); + final FunctionType objectToString = ftype(function, itype(string), null, null, itype(object)); + final FunctionType stringToObject = ftype(function, itype(object), null, null, itype(string)); + final FunctionType stringAndIntToBool = ftype(function, itype(bool), + null, null, itype(string), itype(intElement)); + final FunctionType stringAndIntToMap = ftype(function, stringIntMap, + null, null, itype(string), itype(intElement)); + private int expectedTypeErrors = 0; + + abstract Types getTypes(); + + protected void setExpectedTypeErrorCount(int count) { + checkExpectedTypeErrorCount(); + expectedTypeErrors = count; + } + + protected void checkExpectedTypeErrorCount(String message) { + assertEquals(message, 0, expectedTypeErrors); + } + + protected void checkExpectedTypeErrorCount() { + checkExpectedTypeErrorCount(null); + } + + static TypeVariable typeVar(String name, Type bound) { + TypeVariableElement element = Elements.typeVariableElement(null, name, null); + element.setBound(bound); + return new TypeVariableImplementation(element); + } + + private ClassElement makeGrowableArray(ClassElement array) { + TypeVariable E = typeVar("E", itype(object)); + return element("GrowableArray", itype(array, E), E); + } + + private ClassElement makeReverseMap(ClassElement map) { + TypeVariable K = typeVar("K", itype(object)); + TypeVariable V = typeVar("V", itype(object)); + return element("ReverseMap", itype(map, V, K), K, V); + } + + static InterfaceType itype(ClassElement element, Type... arguments) { + return new InterfaceTypeImplementation(element, Arrays.asList(arguments)); + } + + static FunctionType ftype(ClassElement element, Type returnType, + Map namedParameterTypes, Type rest, Type... arguments) { + return FunctionTypeImplementation.of(element, Arrays.asList(arguments), namedParameterTypes, + rest, returnType, null); + } + + static Map named(Object... pairs) { + Map named = new LinkedHashMap(); + for (int i = 0; i < pairs.length; i++) { + Type type = (Type) pairs[i++]; + String name = (String) pairs[i]; + named.put(name, type); + } + return named; + } + + ClassElement element(String name, InterfaceType supertype, TypeVariable... parameters) { + ClassElement element = Elements.classNamed(name); + element.setSupertype(supertype); + element.setType(itype(element, parameters)); + coreElements.put(name, element); + return element; + } + + void checkSubtype(Type t, Type s) { + Assert.assertTrue(getTypes().isSubtype(t, s)); + } + + void checkStrictSubtype(Type t, Type s) { + checkSubtype(t, s); + checkNotSubtype(s, t); + } + + void checkNotSubtype(Type t, Type s) { + Assert.assertFalse(getTypes().isSubtype(t, s)); + } + + void checkNotAssignable(Type t, Type s) { + checkNotSubtype(t, s); + checkNotSubtype(s, t); + } + + final DartCompilerListener listener = new DartCompilerListener() { + @Override + public void compilationError(DartCompilationError event) { + throw new AssertionError(event); + } + + @Override + public void compilationWarning(DartCompilationError event) { + compilationError(event); + } + + @Override + public void typeError(DartCompilationError event) { + compilationError(event); + } + }; + + final TestCompilerContext context = new TestCompilerContext() { + @Override + public void typeError(DartCompilationError event) { + getErrorCodes().add(event.getErrorCode()); + expectedTypeErrors--; + if (expectedTypeErrors < 0) { + throw new TestTypeError(event); + } + } + }; + + static class TestTypeError extends RuntimeException { + final DartCompilationError event; + + TestTypeError(DartCompilationError event) { + super(String.valueOf(event)); + this.event = event; + } + + ErrorCode getErrorCode() { + return event.getErrorCode(); + } + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/TypeTests.java b/compiler/javatests/com/google/dart/compiler/type/TypeTests.java new file mode 100644 index 00000000000..b14b65140d4 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/TypeTests.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.type; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class TypeTests extends TestSetup { + + public TypeTests(Test test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Type system of dartc"); + + suite.addTestSuite(TypeTest.class); + suite.addTestSuite(FunctionTypeTest.class); + suite.addTestSuite(TypeAnalyzerTest.class); + + return suite; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/class_with_methods.dart b/compiler/javatests/com/google/dart/compiler/type/class_with_methods.dart new file mode 100644 index 00000000000..966b943a87b --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/class_with_methods.dart @@ -0,0 +1,17 @@ +// 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. + +interface ClassWithMethods { + untypedNoArgumentMethod(); + untypedOneArgumentMethod(argument); + untypedTwoArgumentMethod(argument1, argument2); + + int intNoArgumentMethod(); + int intOneArgumentMethod(int argument); + int intTwoArgumentMethod(int argument1, int argument2); + + Function functionField; + var untypedField; + int intField; +} diff --git a/compiler/javatests/com/google/dart/compiler/type/class_with_operators.dart b/compiler/javatests/com/google/dart/compiler/type/class_with_operators.dart new file mode 100644 index 00000000000..2ff55052f07 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/class_with_operators.dart @@ -0,0 +1,61 @@ +// 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. + +class ClassWithOperators { + ClassWithOperators o; + int i; + String s; + bool b; + var untyped; + + ClassWithOperators() {} + + int operator[] (int i) { + return 0; + } + + ClassWithOperators operator >>>(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator +(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator -(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator *(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator /(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator ~/(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator %(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator <(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator >(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator <=(ClassWithOperators operand) { + return this; + } + + ClassWithOperators operator >=(ClassWithOperators operand) { + return this; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/type/class_with_supertypes.dart b/compiler/javatests/com/google/dart/compiler/type/class_with_supertypes.dart new file mode 100644 index 00000000000..b08a61466bc --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/class_with_supertypes.dart @@ -0,0 +1,27 @@ +// 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. + +interface Interface { + void methodInInterface(); + int fieldInInterface; + static final int staticFieldInInterface = 1; +} + +class Superclass { + Superclass() {} + + void methodInSuperclass() {} + int fieldInSuperclass; + static void staticMethodInSuperclass() {} + static int staticFieldInSuperclass; +} + +class ClassWithSupertypes extends Superclass implements Interface { + ClassWithSupertypes() : super() {} + + void method() {} + int field; + static void staticMethod() {} + static int staticField; +} diff --git a/compiler/javatests/com/google/dart/compiler/type/class_with_type_parameter.dart b/compiler/javatests/com/google/dart/compiler/type/class_with_type_parameter.dart new file mode 100644 index 00000000000..48be4d36507 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/class_with_type_parameter.dart @@ -0,0 +1,13 @@ +// 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. + +interface A {} + +interface B extends A {} + +class ClassWithTypeParameter { + A aField; + B bField; + T tField; +} diff --git a/compiler/javatests/com/google/dart/compiler/type/classes_with_properties.dart b/compiler/javatests/com/google/dart/compiler/type/classes_with_properties.dart new file mode 100644 index 00000000000..b126779dd02 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/classes_with_properties.dart @@ -0,0 +1,35 @@ +// 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. + +class A { + A a; + B b; + C c; + void m() {} + var x; +} + +class B { + A a; + B b; + C c; + void m() {} + var x; +} + +class C { + A a; + B b; + C c; + void m() {} + var x; +} + +class ClassWithProperties { + A a; + B b; + C c; + void m() {} + var x; +} diff --git a/compiler/javatests/com/google/dart/compiler/type/covariant_class.dart b/compiler/javatests/com/google/dart/compiler/type/covariant_class.dart new file mode 100644 index 00000000000..b88e9c63a85 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/covariant_class.dart @@ -0,0 +1,43 @@ +// 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. + +interface A { +} + +interface B extends A { +} + +interface C extends A { +} + +interface D { +} + +class Super { + A field; + A get accessor() { return null; } + void set accessor(A newValue) { } + A method() { return null; } + + var untypedField; + get untypedAccessor() { return null; } + set untypedAccessor(newValue) { } + untypedMethod() { return null; } +} + +class Sub extends Super { + B field; + B get accessor() { return null; } + void set accessor(B newValue) { } + B method() { return null; } + + B untypedField; + B get untypedAccessor() { return null; } + set untypedAccessor(B newValue) { } + B untypedMethod() { return null; } + + B b; + C c; + D d; +} diff --git a/compiler/javatests/com/google/dart/compiler/type/generic_class_with_supertypes.dart b/compiler/javatests/com/google/dart/compiler/type/generic_class_with_supertypes.dart new file mode 100644 index 00000000000..b96748fe54a --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/generic_class_with_supertypes.dart @@ -0,0 +1,20 @@ +// 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. + +interface Interface { + I1 interfaceField; + I1 interfaceMethod(I2 arg); +} + +class Superclass { + S1 superField; + S1 superMethod(S2 arg) { return null; } +} + +class GenericClassWithSupertypes extends Superclass implements Interface { + T1 localField; + T1 localMethod(T2 arg) { return null; } + T2 t2; + T1 t1; +} diff --git a/compiler/javatests/com/google/dart/compiler/type/interfaces.dart b/compiler/javatests/com/google/dart/compiler/type/interfaces.dart new file mode 100644 index 00000000000..a8449e51e2c --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/interfaces.dart @@ -0,0 +1,13 @@ +// 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. + +interface Super {} + +interface Sub extends Super factory SubImplementation { + Sub(); +} + +class SubImplementation implements Sub { + SubImplementation() {} +} diff --git a/compiler/javatests/com/google/dart/compiler/type/named_function_type_alias.dart b/compiler/javatests/com/google/dart/compiler/type/named_function_type_alias.dart new file mode 100644 index 00000000000..d9fc3c26123 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/type/named_function_type_alias.dart @@ -0,0 +1,5 @@ +// 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. + +typedef void VoidFunction(); diff --git a/compiler/javatests/com/google/dart/compiler/util/PathsTest.java b/compiler/javatests/com/google/dart/compiler/util/PathsTest.java new file mode 100644 index 00000000000..669f13b927d --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/util/PathsTest.java @@ -0,0 +1,132 @@ +// 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. + +package com.google.dart.compiler.util; + +import junit.framework.TestCase; + +import java.io.File; +import java.net.URI; + +public class PathsTest extends TestCase { + + public void testRelativePathToFile_inChildDir_1() { + testPathToFile("mylib.lib", "childDir/other.lib", "childDir/other.lib"); + } + + public void testRelativePathToFile_inChildDir_2() { + testPathToFile("dir/mylib.lib", "childDir/other.lib", "dir/childDir/other.lib"); + } + + public void testRelativePathToFile_inParentDir_1() { + testPathToFile("mylib.lib", "../other.lib", "../other.lib"); + } + + public void testRelativePathToFile_inParentDir_2() { + testPathToFile("dir/mylib.lib", "../other.lib", "other.lib"); + } + + public void testRelativePathToFile_inSameDir_1() { + testPathToFile("mylib.lib", "other.lib", "other.lib"); + } + + public void testRelativePathToFile_inSameDir_2() { + testPathToFile("dir/mylib.lib", "other.lib", "dir/other.lib"); + } + + public void testRelativePathToFile_inSiblingDir_1() { + testPathToFile("mylib.lib", "../alt/other.lib", "../alt/other.lib"); + } + + public void testRelativePathToFile_inSiblingDir_2() { + testPathToFile("dir/mylib.lib", "../alt/other.lib", "alt/other.lib"); + } + + private void testPathToFile(String baseFilePath, String relPath, + String expectedPath) { + + File baseFile1 = new File(baseFilePath); + File actual1 = Paths.relativePathToFile(baseFile1, relPath); + String expectedPath1 = expectedPath; + assertEquals(expectedPath1, actual1.getPath()); + + File baseFile2 = baseFile1.getAbsoluteFile(); + File actual2 = Paths.relativePathToFile(baseFile2, relPath); + String expectedPath2 = URI.create(actual1.getAbsolutePath()).normalize().getPath(); + assertEquals(expectedPath2, actual2.getPath()); + } + + //========================================================================== + + public void testRelativePathFor_inChildDir_1() { + testPathFor("mylib.lib", "childDir/other.lib", "childDir/other.lib"); + } + + public void testRelativePathFor_inChildDir_2() { + testPathFor("dir/mylib.lib", "dir/childDir/other.lib", "childDir/other.lib"); + } + + public void testRelativePathFor_inParentDir_1() { + testPathFor("mylib.lib", "../other.lib", "../other.lib"); + } + + public void testRelativePathFor_inParentDir_2() { + testPathFor("dir/mylib.lib", "other.lib", "../other.lib"); + } + + public void testRelativePathFor_inParentDir_3() { + testPathFor("grandDir/dir/mylib.lib", "grandDir/other.lib", "../other.lib"); + } + + public void testRelativePathFor_inSameDir_1() { + testPathFor("mylib.lib", "other.lib", "other.lib"); + } + + public void testRelativePathFor_inSameDir_2() { + testPathFor("dir/mylib.lib", "dir/other.lib", "other.lib"); + } + + public void testRelativePathFor_inSameDir_3() { + testPathFor("grandDir/dir/mylib.lib", "grandDir/dir/other.lib", "other.lib"); + } + + public void testRelativePathFor_inSameDir_4() { + testPathFor("grandDir/dir/amylib.lib", "grandDir/dir/aother.lib", "aother.lib"); + } + + public void testRelativePathFor_inSameDir_5() { + testPathFor("grandDir/dir/abmylib.lib", "grandDir/dir/abother.lib", "abother.lib"); + } + + public void testRelativePathFor_inSiblingDir_1() { + testPathFor("mylib.lib", "../otherdir/other.lib", "../otherdir/other.lib"); + } + + public void testRelativePathFor_inSiblingDir_2() { + testPathFor("dir/mylib.lib", "otherdir/other.lib", "../otherdir/other.lib"); + } + + public void testRelativePathFor_inSiblingDir_3() { + testPathFor("grandDir/dir/mylib.lib", "grandDir/otherdir/other.lib", "../otherdir/other.lib"); + } + + public void testRelativePathFor_inSiblingDir_4() { + testPathFor("src/mylib.lib", "src-dir/other.lib", "../src-dir/other.lib"); + } + + private void testPathFor(String baseFilePath, String relFilePath, + String expected) { + + File baseFile1 = new File(baseFilePath); + File relativeFile1 = new File(relFilePath); + String actual1 = Paths.relativePathFor(baseFile1, relativeFile1); + assertEquals(expected, actual1); + + File baseFile2 = baseFile1.getAbsoluteFile(); + File relativeFile2 = relativeFile1.getAbsoluteFile(); + String actual2 = Paths.relativePathFor(baseFile2, relativeFile2); + assertEquals(expected, actual2); + } + +} diff --git a/compiler/javatests/com/google/dart/compiler/util/UtilTests.java b/compiler/javatests/com/google/dart/compiler/util/UtilTests.java new file mode 100644 index 00000000000..c6a01998831 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/util/UtilTests.java @@ -0,0 +1,24 @@ +// 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. + +package com.google.dart.compiler.util; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +public class UtilTests extends TestSetup { + + public UtilTests(Test test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Utilities for dartc"); + + suite.addTestSuite(PathsTest.class); + + return suite; + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/DartOptTests.java b/compiler/javatests/com/google/dart/compiler/vm/DartOptTests.java new file mode 100644 index 00000000000..94af7c70f06 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/DartOptTests.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.vm; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class DartOptTests extends TestSetup { + + public DartOptTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart optimized imported vm test suite."); + + suite.addTestSuite(ImportedDartOptTest.class); + return new DartOptTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/DartTest.java b/compiler/javatests/com/google/dart/compiler/vm/DartTest.java new file mode 100644 index 00000000000..cfce0968ed8 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/DartTest.java @@ -0,0 +1,18 @@ +// 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. + +package com.google.dart.compiler.vm; + +public abstract class DartTest extends VmTest { + + @Override + protected void runNegativeTest(String testName, String... commandArray) { + super.runNegativeTest(testName, commandArray); + } + + @Override + protected void runPositiveTest(String testName, String... commandArray) throws Throwable { + super.runPositiveTest(testName, commandArray); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/DartTests.java b/compiler/javatests/com/google/dart/compiler/vm/DartTests.java new file mode 100644 index 00000000000..d582ce6a8e8 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/DartTests.java @@ -0,0 +1,26 @@ +// 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. + +package com.google.dart.compiler.vm; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @author floitsch@google.com (Florian Loitsch) + */ +public class DartTests extends TestSetup { + + public DartTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart imported vm test suite."); + + suite.addTestSuite(ImportedDartTests.class); + return new DartTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/ImportedDartOptTest.java b/compiler/javatests/com/google/dart/compiler/vm/ImportedDartOptTest.java new file mode 100644 index 00000000000..c21ab1b64b0 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/ImportedDartOptTest.java @@ -0,0 +1,23 @@ +// 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. + +package com.google.dart.compiler.vm; + +public class ImportedDartOptTest extends ImportedDartTests { + + @Override + protected void runNegativeTest(String testName, String... commandArray) { + super.runNegativeTest(testName, addOptimizeOption(commandArray)); + } + + @Override + protected void runPositiveTest(String testName, String... commandArray) throws Throwable { + super.runPositiveTest(testName, addOptimizeOption(commandArray)); + } + + // If necessary, tests expectations can be overridden here, like so: + // public void testUnhandledExceptionNegativeTest() { + // // TODO(johnlenz): http://b/4484716 + // } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/ImportedLibOptTest.java b/compiler/javatests/com/google/dart/compiler/vm/ImportedLibOptTest.java new file mode 100644 index 00000000000..dfaa4c95c4f --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/ImportedLibOptTest.java @@ -0,0 +1,17 @@ +// 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. + +package com.google.dart.compiler.vm; + +public class ImportedLibOptTest extends ImportedLibTests { + @Override + protected void runNegativeTest(String testName, String... commandArray) { + super.runNegativeTest(testName, addOptimizeOption(commandArray)); + } + + @Override + protected void runPositiveTest(String testName, String... commandArray) throws Throwable { + super.runPositiveTest(testName, addOptimizeOption(commandArray)); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/LibOptTests.java b/compiler/javatests/com/google/dart/compiler/vm/LibOptTests.java new file mode 100644 index 00000000000..81cbf57768e --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/LibOptTests.java @@ -0,0 +1,27 @@ +// 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. + +package com.google.dart.compiler.vm; + +import junit.framework.Test; +import junit.framework.TestSuite; +import junit.extensions.TestSetup; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public class LibOptTests extends TestSetup { + + public LibOptTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite( + "Dart optimized imported library test suite."); + + suite.addTestSuite(ImportedLibOptTest.class); + return new LibOptTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/LibTest.java b/compiler/javatests/com/google/dart/compiler/vm/LibTest.java new file mode 100644 index 00000000000..fa268bca346 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/LibTest.java @@ -0,0 +1,18 @@ +// 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. + +package com.google.dart.compiler.vm; + +public abstract class LibTest extends VmTest { + + @Override + protected void runNegativeTest(String testName, String... commandArray) { + super.runNegativeTest(testName, commandArray); + } + + @Override + protected void runPositiveTest(String testName, String... commandArray) throws Throwable { + super.runPositiveTest(testName, commandArray); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/LibTests.java b/compiler/javatests/com/google/dart/compiler/vm/LibTests.java new file mode 100644 index 00000000000..ce0115ac476 --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/LibTests.java @@ -0,0 +1,23 @@ +// 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. + +package com.google.dart.compiler.vm; + +import junit.framework.Test; +import junit.framework.TestSuite; +import junit.extensions.TestSetup; + +public class LibTests extends TestSetup { + + public LibTests(TestSuite test) { + super(test); + } + + public static Test suite() { + TestSuite suite = new TestSuite("Dart imported library test suite."); + + suite.addTestSuite(ImportedLibTests.class); + return new LibTests(suite); + } +} diff --git a/compiler/javatests/com/google/dart/compiler/vm/VmTest.java b/compiler/javatests/com/google/dart/compiler/vm/VmTest.java new file mode 100644 index 00000000000..6520c86c1fe --- /dev/null +++ b/compiler/javatests/com/google/dart/compiler/vm/VmTest.java @@ -0,0 +1,77 @@ +// 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. + +package com.google.dart.compiler.vm; + +import com.google.common.collect.Lists; +import com.google.dart.runner.TestRunner; + +import junit.framework.TestCase; +import org.mozilla.javascript.RhinoException; + +import java.util.Arrays; +import java.util.List; + +/** + * @author floitsch@google.com (Florian Loitsch) + * + * Baseclass to be used for VM-tests. It provides run{Negative|Positive}Test. + */ +abstract class VmTest extends TestCase { + private String createArgumentsString(String[] commandArray) { + StringBuffer buffer = new StringBuffer(); + buffer.append("Command: blaze run //third_party/java_src/dart/compiler:dartc_test --"); + int dartDartCount = 0; + for (String command : commandArray) { + if (command.equals("--")) { + dartDartCount++; + buffer.append(' '); + } else { + // dartDartCount == 0: vm options + // dartDartCount == 1: source files + // dartDartCount == 2: entry point + if (dartDartCount == 1) { + buffer.append(" $(pwd)/"); + } else { + buffer.append(' '); + } + } + buffer.append(command); + } + return buffer.toString(); + } + + String[] addOptimizeOption(String[] commandArray) { + List commands = Lists.newArrayList(Arrays.asList(commandArray)); + commands.add(0, "--optimize"); + return commands.toArray(new String[0]); + } + + protected void runNegativeTest(String testName, String... commandArray) { + try { + TestRunner.throwingMain(commandArray, System.out, System.err); + System.out.println(createArgumentsString(commandArray)); + fail(); + } catch (Exception e) { + // Great. It was supposed to fail. + return; + } + } + + protected void runPositiveTest(String testName, String... commandArray) throws Throwable { + try { + TestRunner.throwingMain(commandArray, System.out, System.err); + } catch (RhinoException e) { + System.out.println(createArgumentsString(commandArray)); + StringBuffer msg = new StringBuffer(); + msg.append(e.sourceName()); + msg.append(" (" + e.lineNumber() + ":" + e.columnNumber() + ")"); + msg.append(" : " + e.details()); + fail(msg.toString()); + } catch (Throwable e) { + System.out.println(createArgumentsString(commandArray)); + throw e; + } + } +} diff --git a/compiler/javatests/com/google/dart/corelib/SharedTestCase.java b/compiler/javatests/com/google/dart/corelib/SharedTestCase.java new file mode 100644 index 00000000000..34fe2724710 --- /dev/null +++ b/compiler/javatests/com/google/dart/corelib/SharedTestCase.java @@ -0,0 +1,294 @@ +// 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. + +package com.google.dart.corelib; + +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; +import com.google.dart.compiler.CompilerConfiguration; +import com.google.dart.compiler.DartArtifactProvider; +import com.google.dart.compiler.DartCompilationError; +import com.google.dart.compiler.DartCompiler; +import com.google.dart.compiler.DartCompilerListener; +import com.google.dart.compiler.DefaultCompilerConfiguration; +import com.google.dart.compiler.DefaultDartArtifactProvider; +import com.google.dart.compiler.DefaultLibrarySource; +import com.google.dart.compiler.LibrarySource; +import com.google.dart.compiler.Source; +import com.google.dart.compiler.UrlLibrarySource; +import com.google.dart.runner.RunnerError; +import com.google.dart.runner.TestRunner; +import com.google.dart.runner.V8Launcher; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; + +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; + +/** + * Wrapper around test that are shared between compiler and runtime (test.py). + *

    + * Sometimes, when fixing a known issue, a test that was previously crashing or failing may start + * passing. This will show up as a test failure because the status file has wrong information. + * Please update the status file. + */ +public class SharedTestCase extends TestCase { + private static final Pattern SEPARATOR = Pattern.compile("\\t"); + + private final Set outcomes; + private final boolean isNegative; + private final String[] arguments; + private final boolean regularCompile; + private final AtomicInteger compilationErrorCount = new AtomicInteger(0); + private final AtomicInteger typeErrorCount = new AtomicInteger(0); + private final AtomicInteger warningCount = new AtomicInteger(0); + + SharedTestCase(String name, Set outcomes, boolean isNegative, boolean regularCompile, + String[] arguments) { + super(name); + this.outcomes = outcomes; + this.isNegative = isNegative; + this.regularCompile = regularCompile; + this.arguments = arguments; + } + + /** + * This constructor is provided for compatibility with Eclipse (for running + * a single test case). + */ + public SharedTestCase(String name) { + super(name = scrubName(name)); + TestSuite suite = SharedTests.suite(); + Enumeration tests = suite.tests(); + SharedTestCase test = null; + while (tests.hasMoreElements()) { + SharedTestCase current = (SharedTestCase) tests.nextElement(); + if (current.getScrubbedName().equals(name)) { + test = current; + break; + } + } + if (test == null) { + throw new IllegalArgumentException("Test '" +name + "' was not found."); + } + this.outcomes = test.outcomes; + this.isNegative = test.isNegative; + this.regularCompile = test.regularCompile; + this.arguments = test.arguments; + } + + private static String scrubName(String name) { + int i = name.indexOf('['); + if (i == -1) { + return name; + } + return name.substring(0, i - 1); + } + + @Override + public String getName() { + List remarks = new ArrayList(); + if (isNegative) { + remarks.add("negative"); + } + for (String outcome : outcomes) { + if (!outcome.equals("pass")) { + remarks.add(outcome); + } + } + if (remarks.isEmpty()) { + return super.getName(); + } else { + return super.getName() + " " + remarks; + } + } + + String getScrubbedName() { + return super.getName(); + } + + @Override + public void runBare() { + assertTrue(V8Launcher.isConfigured()); + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + PrintStream outputStream = new PrintStream(byteOutput); + try { + if (regularCompile) { + invokeCompiler(); + } else { + TestRunner.throwingMain(arguments, outputStream, outputStream); + } + } catch (RunnerError e) { + outputStream.close(); + analyzeError(e, byteOutput.toString()); + return; + } catch (Throwable t) { + outputStream.close(); + analyzeCrash(t); + return; + } + outputStream.close(); + analyzeNormalCompletion(); + } + + private void invokeCompiler() throws CmdLineException, IOException, RunnerError { + CmdLineParser cmdLineParser = null; + CompilerOptions compilerOptions = new CompilerOptions(); + cmdLineParser = new CmdLineParser(compilerOptions); + cmdLineParser.parseArgument(arguments); + CompilerConfiguration config = new DefaultCompilerConfiguration(compilerOptions); + DartArtifactProvider provider = getArtifactProvider(config.getOutputDirectory()); + DartCompilerListener listener = getListener(); + List sourceFiles = compilerOptions.getSourceFiles(); + assertEquals("incorrect number of source files " + sourceFiles, 1, sourceFiles.size()); + File sourceFile = new File(sourceFiles.get(0)); + LibrarySource lib; + if (sourceFile.getName().endsWith(".dart")) { + lib = new DefaultLibrarySource(sourceFile, null); + } else { + lib = new UrlLibrarySource(sourceFile); + } + DartCompiler.compileLib(lib, config, provider, listener); + if (compilationErrorCount.get() != 0 || typeErrorCount.get() != 0 || warningCount.get() != 0) { + throw new RunnerError(sourceFile.getPath()); + } + } + + private DartArtifactProvider getArtifactProvider(File outputDirectory) { + final DartArtifactProvider provider = new DefaultDartArtifactProvider(outputDirectory); + return new DartArtifactProvider() { + ConcurrentHashMap artifacts = + new ConcurrentHashMap(); + + @Override + public boolean isOutOfDate(Source source, Source base, String extension) { + return true; + } + + @Override + public Writer getArtifactWriter(Source source, String part, String extension) { + URI uri = getArtifactUri(source, part, extension); + CharArrayWriter writer = new CharArrayWriter(); + CharArrayWriter existing = artifacts.putIfAbsent(uri, writer); + return (existing == null) ? writer : existing; + } + + + @Override + public URI getArtifactUri(Source source, String part, String extension) { + return provider.getArtifactUri(source, part, extension); + } + + @Override + public Reader getArtifactReader(Source source, String part, String extension) + throws IOException { + URI uri = getArtifactUri(source, part, extension); + CharArrayWriter writer = artifacts.get(uri); + if (writer != null) { + return new CharArrayReader(writer.toCharArray()); + } + return provider.getArtifactReader(source, part, extension); + } + }; + } + + private DartCompilerListener getListener() { + DartCompilerListener listener = new DartCompilerListener() { + @Override + public void compilationError(DartCompilationError event) { + compilationErrorCount.incrementAndGet(); + maybeThrow(event); + } + + private void maybeThrow(DartCompilationError event) { + if (isNegative) { + return; + } + if (outcomes.contains("pass")) { + // It is easier to debug a failing regular test if we throw an exception. + throw new AssertionError(event); + } + } + + @Override + public void compilationWarning(DartCompilationError event) { + warningCount.incrementAndGet(); + maybeThrow(event); + } + + @Override + public void typeError(DartCompilationError event) { + typeErrorCount.incrementAndGet(); + maybeThrow(event); + } + }; + return listener; + } + + private void analyzeNormalCompletion() { + if (isNegative) { + if (!outcomes.contains("fail")) { + fail("Negative test didn't cause an error"); + } + } else { + if (!outcomes.contains("pass")) { + fail("Test passed unexpectly, please update status file"); + } + } + } + + private void analyzeCrash(Throwable t) { + if (outcomes.contains("crash")) { + return; + } + String message = outcomes.contains("fail") ? "Failing test crashed" : "Test crashed unexpectly"; + AssertionFailedError error = new AssertionFailedError(message); + error.initCause(t); + throw error; + } + + private void analyzeError(RunnerError e, String log) { + if (isNegative) { + if (!outcomes.contains("pass")) { + fail("Negative test is passing, please update status file"); + } + } else { + if (!outcomes.contains("fail")) { + fail(log + e.getLocalizedMessage()); + } + } + } + + static Test getInstance(String line, boolean regularCompile) { + String[] fields = SEPARATOR.split(line); + assertTrue(line, fields.length > 3); + String name = fields[0]; + Set outcomes = new HashSet(Arrays.asList(fields[1].split(","))); + boolean isNegative = fields[2].equals("True"); + String[] arguments = new String[fields.length - 3]; + System.arraycopy(fields, 3, arguments, 0, arguments.length); + return new SharedTestCase(name, outcomes, isNegative, regularCompile, arguments); + } +} diff --git a/compiler/javatests/com/google/dart/corelib/SharedTests.java b/compiler/javatests/com/google/dart/corelib/SharedTests.java new file mode 100644 index 00000000000..1ebd382c616 --- /dev/null +++ b/compiler/javatests/com/google/dart/corelib/SharedTests.java @@ -0,0 +1,113 @@ +// 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. + +package com.google.dart.corelib; + +import com.google.common.io.CharStreams; +import com.google.common.io.LineReader; +import com.google.dart.runner.V8Launcher; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * JUnit wrapper around test.py. This wrapper allows you to run most test.py tests from inside your + * favorite IDE, to ease debugging. + *

    + * If you followed the instructions in compiler/eclipse.workspace/README.txt, this test should just + * work inside Eclipse. + *

    + * If you just want to run a single test, launch this class as a JUnit test and stop it once it has + * listed all the tests. Then right click on the desired test and select Run or Debug. + */ +public class SharedTests extends TestSetup { + private final static String TEST_PY = + System.getProperty("com.google.dart.corelib.SharedTests.test_py", "../tools/test.py"); + + private static final String[] listTests = { + TEST_PY, + "--arch=dartc", + "--mode=release", + "--list"}; + + public SharedTests(Test test) { + super(test); + } + + public static TestSuite suite() { + return new SuiteBuilder().buildSuite(); + } + + protected static class SuiteBuilder { + protected TestSuite buildSuite() { + TestSuite suite = new TestSuite("Shared Dart tests"); + + if (!V8Launcher.isConfigured()) { + return configurationProblem(suite, + "Please set the system property com.google.dart.runner.d8"); + } + + File file = new File(listTests[0]); + if (!file.canExecute()) { + return configurationProblem(suite, file.getPath() + " is not executable"); + } + ProcessBuilder builder = new ProcessBuilder(listTests); + try { + Process process = builder.start(); + InputStream inputStream = process.getInputStream(); + StringBuilder sb = new StringBuilder(); + try { + InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + LineReader lineReader = new LineReader(inputStreamReader); + String line; + while ((line = lineReader.readLine()) != null) { + if (!line.startsWith("dartc/")) { + suite.addTest(SharedTestCase.getInstance(line, false)); + } else if (line.startsWith("dartc/client/")) { + suite.addTest(SharedTestCase.getInstance(line, true)); + } + } + } finally { + inputStream.close(); + process.getOutputStream().close(); + InputStreamReader inputStreamReader = new InputStreamReader(process.getErrorStream()); + CharStreams.copy(inputStreamReader, sb); + process.getErrorStream().close(); + } + process.waitFor(); + if (process.exitValue() != 0) { + sb.insert(0, file.getPath()); + sb.insert(0, " returned non-zero exit code.\n"); + return configurationProblem(suite, sb.toString()); + } + } catch (IOException e) { + throw new AssertionError(e); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + return suite; + } + + /** + * Errors reported during suite construction are hard to read. This method creates a test that + * will always fail with an error message that shows up in the Eclipse JUnit UI. + */ + protected TestSuite configurationProblem(TestSuite suite, final String message) { + suite.addTest(new TestCase("Configuration problem") { + @Override + public void runBare() throws Throwable { + fail(message); + } + }); + return suite; + } + } +} diff --git a/compiler/javatests/com/google/dart/corelib/TestSharedTests.java b/compiler/javatests/com/google/dart/corelib/TestSharedTests.java new file mode 100644 index 00000000000..f922ca7dc1b --- /dev/null +++ b/compiler/javatests/com/google/dart/corelib/TestSharedTests.java @@ -0,0 +1,40 @@ +// 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. + +package com.google.dart.corelib; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Test the configuration of SharedTests but without running all the tests. + * This test is designed to be run from test.py which normally skips SharedTests. + */ +public class TestSharedTests extends SharedTests { + + public TestSharedTests(Test test) { + super(test); + } + + public static TestSuite suite() { + final TestSuite suite = new TestSuite("Shared Dart tests configuration"); + + new SuiteBuilder() { + @Override + protected TestSuite configurationProblem(TestSuite ignored, String message) { + return super.configurationProblem(suite, message); + } + }.buildSuite(); + + if (suite.countTestCases() == 0) { + suite.addTest(new TestCase("configuration is fine") { + @Override + public void runBare() throws Throwable { + } + }); + } + return suite; + } +} diff --git a/compiler/javatests/com/google/dart/runner/AllTests.java b/compiler/javatests/com/google/dart/runner/AllTests.java new file mode 100644 index 00000000000..d86bb18429f --- /dev/null +++ b/compiler/javatests/com/google/dart/runner/AllTests.java @@ -0,0 +1,18 @@ +// 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. + +package com.google.dart.runner; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AllTests { + public static Test suite() { + TestSuite suite = new TestSuite("Dartc test runner tests"); + + suite.addTestSuite(TestRunnerTest.class); + + return suite; + } +} diff --git a/compiler/javatests/com/google/dart/runner/TestRunnerTest.java b/compiler/javatests/com/google/dart/runner/TestRunnerTest.java new file mode 100644 index 00000000000..a2bf907e0c5 --- /dev/null +++ b/compiler/javatests/com/google/dart/runner/TestRunnerTest.java @@ -0,0 +1,22 @@ +// 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. + +package com.google.dart.runner; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class TestRunnerTest extends TestCase { + public void testMain() throws Throwable { + try { + PrintStream stream = new PrintStream(new ByteArrayOutputStream()); + TestRunner.throwingMain("NoSuchFile.dart".split(" "), stream, stream); + fail("Expected a compilation failure."); + } catch (RunnerError e) { + // Expected this exception. + } + } +} diff --git a/compiler/lib/clock.dart b/compiler/lib/clock.dart new file mode 100644 index 00000000000..5722b2358f6 --- /dev/null +++ b/compiler/lib/clock.dart @@ -0,0 +1,25 @@ +// 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. + +/** + * The class [Clock] provides access to a monotonically incrementing clock + * device. + */ +class Clock { + + /** + * Returns the current clock tick. + */ + static int now() { + return new DateTime.now().value; + } + + /** + * Returns the frequency of clock ticks in Hz. + */ + static int frequency() { + return 1000; + } + +} diff --git a/compiler/lib/corelib.dart b/compiler/lib/corelib.dart new file mode 100644 index 00000000000..24261a3434c --- /dev/null +++ b/compiler/lib/corelib.dart @@ -0,0 +1,41 @@ +#!/usr/bin/env dart +// 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. + +#library("corelib"); +#import("corelib_impl.dart"); + +#source("clock.dart"); +#source("error.dart"); +#source("object.dart"); +#source("print.dart"); +#source("src/bool.dart"); +#source("src/collection.dart"); +#source("src/comparable.dart"); +#source("src/date.dart"); +#source("src/date_time.dart"); +#source("src/double.dart"); +#source("src/exceptions.dart"); +#source("src/expect.dart"); +#source("src/function.dart"); +#source("src/hashable.dart"); +#source("src/int.dart"); +#source("src/isolate.dart"); +#source("src/iterable.dart"); +#source("src/iterator.dart"); +#source("src/list.dart"); +#source("src/map.dart"); +#source("src/math.dart"); +#source("src/num.dart"); +#source("src/pattern.dart"); +#source("src/promise.dart"); +#source("src/queue.dart"); +#source("src/regexp.dart"); +#source("src/set.dart"); +#source("src/stopwatch.dart"); +#source("src/string.dart"); +#source("src/strings.dart"); +#source("src/string_buffer.dart"); +#source("src/time.dart"); +#source("src/time_zone.dart"); diff --git a/compiler/lib/corelib_impl.dart b/compiler/lib/corelib_impl.dart new file mode 100644 index 00000000000..05af8cf3194 --- /dev/null +++ b/compiler/lib/corelib_impl.dart @@ -0,0 +1,47 @@ +#!/usr/bin/env dart +// 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. + +#library("corelib_impl"); + +#source("implementation/core.dart"); +#source("implementation/array.dart"); +#source("implementation/arrays.dart"); +#source("implementation/bool.dart"); +#source("implementation/collections.dart"); +#source("implementation/date_time_implementation.dart"); +#source("implementation/isolate.dart"); +#source("implementation/isolate_serialization.dart"); +#source("implementation/math_natives.dart"); +#source("implementation/number.dart"); +#source("implementation/regexp.dart"); +#source("implementation/string.dart"); +#source("implementation/string_base.dart"); +#source("implementation/string_buffer.dart"); +#source("implementation/time_zone_implementation.dart"); +#source("implementation/type_token.dart"); +#source("src/implementation/array.dart"); +#source("src/implementation/date_implementation.dart"); +#source("src/implementation/dual_pivot_quicksort.dart"); +#source("src/implementation/exceptions.dart"); +#source("src/implementation/hash_map_set.dart"); +#source("src/implementation/linked_hash_map.dart"); +#source("src/implementation/promise_implementation.dart"); +#source("src/implementation/queue.dart"); +#source("src/implementation/stopwatch_implementation.dart"); +#source("src/implementation/splay_tree.dart"); +#source("src/implementation/time_implementation.dart"); + +#native("implementation/array.js"); +#native("implementation/bool.js"); +#native("implementation/core.js"); +#native("implementation/date_time_implementation.js"); +#native("implementation/isolate.js"); +#native("implementation/math_natives.js"); +#native("implementation/number.js"); +#native("implementation/object.js"); +#native("implementation/print.js"); +#native("implementation/regexp.js"); +#native("implementation/rtt.js"); +#native("implementation/string.js"); diff --git a/compiler/lib/error.dart b/compiler/lib/error.dart new file mode 100644 index 00000000000..09035e2a16e --- /dev/null +++ b/compiler/lib/error.dart @@ -0,0 +1,18 @@ +// 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. + +// Exceptions thrown by the VM. + +class AssertionError { + const AssertionError(); +} + +class TypeError extends AssertionError { + const TypeError() : super(); +} + +class FallThroughError { + const FallThroughError() : super(); +} + diff --git a/compiler/lib/implementation/array.dart b/compiler/lib/implementation/array.dart new file mode 100644 index 00000000000..83468528fbe --- /dev/null +++ b/compiler/lib/implementation/array.dart @@ -0,0 +1,269 @@ +// 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. + +class ArrayFactory { + factory Array.from(Iterable other) { + Array array = new Array(); + for (final e in other) { + array.add(e); + } + return array; + } + + factory Array.fromArray(Array other, int startIndex, int endIndex) { + Array array = new Array(); + if (endIndex > other.length) endIndex = other.length; + if (startIndex < 0) startIndex = 0; + int count = endIndex - startIndex; + if (count > 0) { + array.length = count; + Arrays.copy(other, startIndex, array, 0, count); + } + return array; + } + + factory Array([int length = null]) { + bool isFixed = true; + if (length === null) { + length = 0; + isFixed = false; + } else if (length < 0) { + throw new IllegalArgumentException(length); + } + // TODO(floitsch): make array creation more efficient. Currently we allocate + // a new TypeToken at every allocation. Either we can optimize them away, + // or we need to find other ways to pass type-information from Dart to JS. + ObjectArray array = _new(new TypeToken(), length); + array._isFixed = isFixed; + return array; + } + + static ObjectArray _new(TypeToken typeToken, int length) native; +} + + +class ListFactory { + factory List.from(Iterable other) { + List list = new List(); + for (final e in other) { + list.add(e); + } + return list; + } + + // TODO(bak): Until the final transition Array type is needed for other + factory List.fromList(Array other, int startIndex, int endIndex) { + List list = new List(); + if (endIndex > other.length) endIndex = other.length; + if (startIndex < 0) startIndex = 0; + int count = endIndex - startIndex; + if (count > 0) { + list.length = count; + Arrays.copy(other, startIndex, list, 0, count); + } + return list; + } + + factory List([int length = null]) { + bool isFixed = true; + if (length === null) { + length = 0; + isFixed = false; + } else if (length < 0) { + throw new IllegalArgumentException(length); + } + // TODO(floitsch): make array creation more efficient. Currently we allocate + // a new TypeToken at every allocation. Either we can optimize them away, + // or we need to find other ways to pass type-information from Dart to JS. + ObjectArray list = _new(new TypeToken(), length); + list._isFixed = isFixed; + return list; + } + + static ObjectArray _new(TypeToken typeToken, int length) native; +} + + +class ObjectArray implements Array native "Array" { + // ObjectArray maps directly to a JavaScript array. If the array is + // constructed by the ArrayFactory.Array constructor, it has an + // additional named property for '_isFixed'. If it is a literal, the + // code generator will not add the property. It will be 'undefined' + // and coerce to false. + bool _isFixed; + + T operator[](int index) { + if (0 <= index && index < length) { + return _indexOperator(index); + } + throw new IndexOutOfRangeException(index); + } + + void operator[]=(int index, T value) { + if (index < 0 || length <= index) { + throw new IndexOutOfRangeException(index); + } + _indexAssignOperator(index, value); + } + + Iterator iterator() { + if (_isFixed) { + return new FixedSizeArrayIterator(this); + } else { + return new VariableSizeArrayIterator(this); + } + } + + T _indexOperator(int index) native; + void _indexAssignOperator(int index, T value) native; + int get length() native; + void _setLength(int length) native; + void _add(T value) native; + + void forEach(void f(T element)) { + Collections.forEach(this, f); + } + + Collection filter(bool f(T element)) { + return Collections.filter(this, new Array(), f); + } + + bool every(bool f(T element)) { + return Collections.every(this, f); + } + + bool some(bool f(T element)) { + return Collections.some(this, f); + } + + bool isEmpty() { + return this.length == 0; + } + + void sort(int compare(T a, T b)) { + DualPivotQuicksort.sort(this, compare); + } + + void copyFrom(Array src, int srcStart, int dstStart, int count) { + Arrays.copy(src, srcStart, this, dstStart, count); + } + + int indexOf(T element, int startIndex) { + return Arrays.indexOf(this, element, startIndex, this.length); + } + + int lastIndexOf(T element, int startIndex) { + return Arrays.lastIndexOf(this, element, startIndex); + } + + void add(T element) { + if (_isFixed) { + throw const UnsupportedOperationException( + "Cannot add to a non-extendable array"); + } else { + _add(element); + } + } + + void addLast(T element) { + add(element); + } + + void addAll(Collection elements) { + if (_isFixed) { + throw const UnsupportedOperationException( + "Cannot add to a non-extendable array"); + } else { + for (final e in elements) { + _add(e); + } + } + } + + void clear() { + if (_isFixed) { + throw const UnsupportedOperationException( + "Cannot clear a non-extendable array"); + } else { + length = 0; + } + } + + void set length(int length) { + if (_isFixed) { + throw const UnsupportedOperationException( + "Cannot change the length of a non-extendable array"); + } else { + _setLength(length); + } + } + + T removeLast() { + if (_isFixed) { + throw const UnsupportedOperationException( + "Cannot remove in a non-extendable array"); + } else { + T element = last(); + length = length - 1; + return element; + } + } + + T last() { + return this[length - 1]; + } +} + + +// Iterator for arrays with fixed size. +class FixedSizeArrayIterator extends VariableSizeArrayIterator { + FixedSizeArrayIterator(Array array) + : super(array), + _length = array.length { + } + + bool hasNext() { + return _length > _pos; + } + + final int _length; // Cache array length for faster access. +} + + +// Iterator for arrays with variable size. +class VariableSizeArrayIterator implements Iterator { + VariableSizeArrayIterator(Array array) + : _array = array, + _pos = 0 { + } + + bool hasNext() { + return _array.length > _pos; + } + + T next() { + if (!hasNext()) { + throw const NoMoreElementsException(); + } + return _array[_pos++]; + } + + final Array _array; + int _pos; +} + + +class _ArrayJsUtil { + static int _arrayLength(Array array) native { + return array.length; + } + + static Array _newArray(int len) native { + return new Array(len); + } + + static void _throwIndexOutOfRangeException(int index) native { + throw new IndexOutOfRangeException(index); + } +} diff --git a/compiler/lib/implementation/array.js b/compiler/lib/implementation/array.js new file mode 100644 index 00000000000..0f1c81fcf5e --- /dev/null +++ b/compiler/lib/implementation/array.js @@ -0,0 +1,42 @@ +// 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. + +function native_ArrayFactory__new(typeToken, length) { + return RTT.setTypeInfo( + new Array(length), + Array.$lookupRTT(RTT.getTypeInfo(typeToken).typeArgs)); +} + +function native_ListFactory__new(typeToken, length) { + return RTT.setTypeInfo( + new Array(length), + Array.$lookupRTT(RTT.getTypeInfo(typeToken).typeArgs)); +} + +function native_ObjectArray__indexOperator(index) { + return this[index]; +} + +function native_ObjectArray__indexAssignOperator(index, value) { + this[index] = value; +} + +function native_ObjectArray_get$length() { + return this.length; +} + +function native_ObjectArray__setLength(length) { + this.length = length; +} + +function native_ObjectArray__add(element) { + this.push(element); +} + +function $inlineArrayIndexCheck(array, index) { + if (index >= 0 && index < array.length) { + return index; + } + native__ArrayJsUtil__throwIndexOutOfRangeException(index); +} diff --git a/compiler/lib/implementation/arrays.dart b/compiler/lib/implementation/arrays.dart new file mode 100644 index 00000000000..b73c74dfc62 --- /dev/null +++ b/compiler/lib/implementation/arrays.dart @@ -0,0 +1,66 @@ +// 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. + +class Arrays { + + static void copy(List src, int srcStart, + List dst, int dstStart, int count) { + if (srcStart === null) srcStart = 0; + if (dstStart === null) dstStart = 0; + + if (srcStart < dstStart) { + for (int i = srcStart + count - 1, j = dstStart + count - 1; + i >= srcStart; i--, j--) { + dst[j] = src[i]; + } + } else { + for (int i = srcStart, j = dstStart; i < srcStart + count; i++, j++) { + dst[j] = src[i]; + } + } + } + + /** + * Returns the index in the array [a] of the given [element], starting + * the search at index [startIndex] to [endIndex] (exclusive). + * Returns -1 if [element] is not found. + */ + static int indexOf(List a, + Object element, + int startIndex, + int endIndex) { + if (startIndex >= a.length) { + return -1; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < endIndex; i++) { + if (a[i] == element) { + return i; + } + } + return -1; + } + + /** + * Returns the last index in the array [a] of the given [element], starting + * the search at index [startIndex] to 0. + * Returns -1 if [element] is not found. + */ + static int lastIndexOf(List a, Object element, int startIndex) { + if (startIndex < 0) { + return -1; + } + if (startIndex >= a.length) { + startIndex = a.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (a[i] == element) { + return i; + } + } + return -1; + } +} diff --git a/compiler/lib/implementation/bool.dart b/compiler/lib/implementation/bool.dart new file mode 100644 index 00000000000..e88b509e78c --- /dev/null +++ b/compiler/lib/implementation/bool.dart @@ -0,0 +1,19 @@ +// 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. + +// Adds Dart-methods to the prototype of the JS Boolean function. +// TODO(floitsch): the following comment needs to be updated once we compile +// boolean checks with '=== true'. +// WARNING: 'this' inside this class is always treated as 'true'. +// That is if (this) ... will always pick the 'then' branch. +// The reason is that, once compiled to JS, 'this' is represented by a +// JS Boolean object. However "if (new Boolean(false)) .." picks the then +// branch. +class BoolImplementation implements bool native "Boolean" { + bool operator ==(other) native; + + // TODO(floitsch): we should intercept toString for primitives, to avoid + // creating a wrapper object. + String toString() native; +} diff --git a/compiler/lib/implementation/bool.js b/compiler/lib/implementation/bool.js new file mode 100644 index 00000000000..0eaca5881bb --- /dev/null +++ b/compiler/lib/implementation/bool.js @@ -0,0 +1,27 @@ +// 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. + +/** + * Extend the Boolean prototype with members expected in dart. + * + * TODO(jimhug): Add verification to ! and truth tests + */ +Boolean.$instanceOf = function(obj) { + return typeof obj == 'boolean' || obj instanceof Boolean; +}; + +function native_BoolImplementation_EQ(other) { + if (typeof other == 'boolean') { + return this == other; + } else if (other instanceof Boolean) { + // Must convert other to a primitive for value equality to work + return this == Boolean(other); + } else { + return false; + } +} + +function native_BoolImplementation_toString() { + return (this == true) ? "true" : "false"; +} diff --git a/compiler/lib/implementation/collections.dart b/compiler/lib/implementation/collections.dart new file mode 100644 index 00000000000..d710c603d70 --- /dev/null +++ b/compiler/lib/implementation/collections.dart @@ -0,0 +1,43 @@ +// 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. + +/** + * The [Collections] class implements static methods useful when + * writing a class that implements [Collection] and the [iterator] + * method. + */ +class Collections { + static void forEach(Iterable iterable, void f(Object o)) { + for (final e in iterable) { + f(e); + } + } + + static bool some(Iterable iterable, bool f(Object o)) { + for (final e in iterable) { + if (f(e)) return true; + } + return false; + } + + static bool every(Iterable iterable, bool f(Object o)) { + for (final e in iterable) { + if (!f(e)) return false; + } + return true; + } + + static List filter(Iterable source, + List destination, + bool f(o)) { + for (final e in source) { + if (f(e)) destination.add(e); + } + return destination; + } + + static bool isEmpty(Iterable iterable) { + return !iterable.iterator().hasNext(); + } +} diff --git a/compiler/lib/implementation/core.dart b/compiler/lib/implementation/core.dart new file mode 100644 index 00000000000..10e8f23097f --- /dev/null +++ b/compiler/lib/implementation/core.dart @@ -0,0 +1,46 @@ +// 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. + +class ConstHelper { + static String getConstId(var o) native; + + static String getConstMapId(Map map) native { + StringBuffer sb = new StringBuffer(); + sb.add("m"); + bool first = true; + for (String key in map.getKeys()) { + if (first) { + first = false; + } else { + sb.add(","); + } + sb.add(getConstId(key)); + sb.add(","); + sb.add(getConstId(map[key])); + } + return sb.toString(); + } +} + +class ExceptionHelper { + static NullPointerException createNullPointerException() native { + return new NullPointerException(); + } + + static ObjectNotClosureException createObjectNotClosureException() native { + return new ObjectNotClosureException(); + } + + static NoSuchMethodException createNoSuchMethodException( + receiver, functionName, arguments) native { + return new NoSuchMethodException(receiver, functionName, arguments); + } +} + +class _CoreJsUtil { + static Map _newMapLiteral() native { + return new LinkedHashMap(); + } +} + diff --git a/compiler/lib/implementation/core.js b/compiler/lib/implementation/core.js new file mode 100644 index 00000000000..428ceec91c6 --- /dev/null +++ b/compiler/lib/implementation/core.js @@ -0,0 +1,474 @@ +// 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. + +/** + * Helpers for lazy static initialization. + */ +var static$uninitialized = {}; +var static$initializing = {}; + +// Optimized versions of closure bindings. +// Name convention: $bind_(fn, this, scopes, args) +function $bind0_0(fn, thisObj) { + return function() { + return fn.call(thisObj); + } +} +function $bind0_1(fn, thisObj) { + return function(arg) { + return fn.call(thisObj, arg); + } +} +function $bind0_2(fn, thisObj) { + return function(arg1, arg2) { + return fn.call(thisObj, arg1, arg2); + } +} +function $bind0_3(fn, thisObj) { + return function(arg1, arg2, arg3) { + return fn.call(thisObj, arg1, arg2, arg3); + } +} +function $bind0_4(fn, thisObj) { + return function(arg1, arg2, arg3, arg4) { + return fn.call(thisObj, arg1, arg2, arg3, arg4); + } +} +function $bind0_5(fn, thisObj) { + return function(arg1, arg2, arg3, arg4, arg5) { + return fn.call(thisObj, arg1, arg2, arg3, arg4, arg5); + } +} + +function $bind1_0(fn, thisObj, scope) { + return function() { + return fn.call(thisObj, scope); + } +} +function $bind1_1(fn, thisObj, scope) { + return function(arg) { + return fn.call(thisObj, scope, arg); + } +} +function $bind1_2(fn, thisObj, scope) { + return function(arg1, arg2) { + return fn.call(thisObj, scope, arg1, arg2); + } +} +function $bind1_3(fn, thisObj, scope) { + return function(arg1, arg2, arg3) { + return fn.call(thisObj, scope, arg1, arg2, arg3); + } +} +function $bind1_4(fn, thisObj, scope) { + return function(arg1, arg2, arg3, arg4) { + return fn.call(thisObj, scope, arg1, arg2, arg3, arg4); + } +} +function $bind1_5(fn, thisObj, scope) { + return function(arg1, arg2, arg3, arg4, arg5) { + return fn.call(thisObj, scope, arg1, arg2, arg3, arg4, arg5); + } +} + +function $bind2_0(fn, thisObj, scope1, scope2) { + return function() { + return fn.call(thisObj, scope1, scope2); + } +} +function $bind2_1(fn, thisObj, scope1, scope2) { + return function(arg) { + return fn.call(thisObj, scope1, scope2, arg); + } +} +function $bind2_2(fn, thisObj, scope1, scope2) { + return function(arg1, arg2) { + return fn.call(thisObj, scope1, scope2, arg1, arg2); + } +} +function $bind2_3(fn, thisObj, scope1, scope2) { + return function(arg1, arg2, arg3) { + return fn.call(thisObj, scope1, scope2, arg1, arg2, arg3); + } +} +function $bind2_4(fn, thisObj, scope1, scope2) { + return function(arg1, arg2, arg3, arg4) { + return fn.call(thisObj, scope1, scope2, arg1, arg2, arg3, arg4); + } +} +function $bind2_5(fn, thisObj, scope1, scope2) { + return function(arg1, arg2, arg3, arg4, arg5) { + return fn.call(thisObj, scope1, scope2, arg1, arg2, arg3, arg4, arg5); + } +} + +function $bind3_0(fn, thisObj, scope1, scope2, scope3) { + return function() { + return fn.call(thisObj, scope1, scope2, scope3); + } +} +function $bind3_1(fn, thisObj, scope1, scope2, scope3) { + return function(arg) { + return fn.call(thisObj, scope1, scope2, scope3, arg); + } +} +function $bind3_2(fn, thisObj, scope1, scope2, scope3) { + return function(arg1, arg2) { + return fn.call(thisObj, scope1, scope2, arg1, arg2); + } +} +function $bind3_3(fn, thisObj, scope1, scope2, scope3) { + return function(arg1, arg2, arg3) { + return fn.call(thisObj, scope1, scope2, scope3, arg1, arg2, arg3); + } +} +function $bind3_4(fn, thisObj, scope1, scope2, scope3) { + return function(arg1, arg2, arg3, arg4) { + return fn.call(thisObj, scope1, scope2, scope3, arg1, arg2, arg3, arg4); + } +} +function $bind3_5(fn, thisObj, scope1, scope2, scope3) { + return function(arg1, arg2, arg3, arg4, arg5) { + return fn.call(thisObj, scope1, scope2, scope3, arg1, arg2, arg3, arg4, arg5); + } +} + +/** + * Implements extends for dart classes on javascript prototypes. + * @param {Function} child + * @param {Function} parent + */ +function $inherits(child, parent) { + if (child.prototype.__proto__) { + child.prototype.__proto__ = parent.prototype; + } else { + function tmp() {}; + tmp.prototype = parent.prototype; + child.prototype = new tmp(); + child.prototype.constructor = child; + } +} + +/** + * @param {Function} fn + * @param {Object|undefined} thisObj + * @param {...*} var_args + */ +function $bind(fn, thisObj, var_args) { + if (arguments.length > 2) { + var boundArgs = Array.prototype.slice.call(arguments, 2); + return function() { + // Prepend the bound arguments to the current arguments. + var newArgs = Array.prototype.slice.call(arguments); + Array.prototype.unshift.apply(newArgs, boundArgs); + return fn.apply(thisObj, newArgs); + }; + } else { + return function() { + return fn.apply(thisObj, arguments); + }; + } +} + +/** + * Dart null object that should be used by JS implementation to test for + * Dart null. + * + * TODO(ngeoffray): update dartc to generate this variable instead of + * undefined. + * @const + */ +var $Dart$Null = void 0; + +function assert(expr, msg) { + var val = typeof(expr) == 'function' ? expr() : expr; + if (!val) { + var err = new Error('Assertion failed. ' + (msg || '')); + Error.captureStackTrace && Error.captureStackTrace(err); + throw err; + } +} + +// TODO(jimhug): Remove these functions after updating compiler backend. +function BIT_OR$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 | val2 + : val1.BIT_OR$operator(val2); +} + +function BIT_XOR$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 ^ val2 + : val1.BIT_XOR$operator(val2); +} + +function BIT_AND$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 & val2 + : val1.BIT_AND$operator(val2); +} + +function BIT_NOT$operator(val) { + return (typeof(val) == 'number') ? ~val : val.BIT_NOT$operator(); +} + +function SHL$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 << val2 + : val1.SHL$operator(val2); +} + +function SAR$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 >> val2 + : val1.SAR$operator(val2); +} + +function SHR$operator(val1, val2) { + return val1.SHR$operator(val2); +} + +function ADD$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 + val2 + : val1.ADD$operator(val2); +} + +function SUB$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 - val2 + : val1.SUB$operator(val2); +} + +function MUL$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 * val2 + : val1.MUL$operator(val2); +} + +function DIV$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 / val2 + : val1.DIV$operator(val2); +} + +function MOD$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? number$euclideanModulo(val1, val2) + : val1.MOD$operator(val2); +} + +function TRUNC$operator(val1, val2) { + if (typeof(val1) == 'number' && typeof(val2) == 'number') { + var tmp = val1 / val2; + return (tmp < 0) ? Math.ceil(tmp) : Math.floor(tmp); + } else { + return val1.TRUNC$operator(val2); + } +} + +function negate$operator(val) { + return (typeof(val) == 'number') ? -val : val.negate$operator(); +} + +function LT$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 < val2 + : val1.LT$operator(val2); +} + +function GT$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 > val2 + : val1.GT$operator(val2); +} + +function LTE$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 <= val2 + : val1.LTE$operator(val2); +} + +function GTE$operator(val1, val2) { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 >= val2 + : val1.GTE$operator(val2); +} + + +/** + * These operators need to work correctly with undefined + * so must be functions. + */ +function EQ$operator(val1, val2) { + if (val1 === $Dart$Null) { + return val2 === $Dart$Null; + } else { + return (typeof(val1) == 'number' && typeof(val2) == 'number') + ? val1 == val2 + : val1.EQ$operator(val2); + } +} + +function NE$operator(val1, val2) { + return !EQ$operator(val1, val2); +} + +// The following operator-functions are not called from Dart-generated code, but +// only from handwritten JS code. +function INDEX$operator(obj, index) { + return obj.INDEX$operator(index); +} + +function ASSIGN_INDEX$operator(obj, index, newVal) { + obj.ASSIGN_INDEX$operator(index, newVal); +} + +// Alter the Function object constructor so that all Function objects +// will pass the $instanceOfInterface(..., "$Function$Dart") test. +Function.prototype.$implements$Function$Dart = 1; + +function $Dart$ThrowException(e) { + // If e is not a value, we can use V8's captureStackTrace utility method. + if (e && (typeof e == "object") && Error.captureStackTrace) { + Error.captureStackTrace(e); + } + throw e; +} + +function $toString(x) { + return native__StringJsUtil_toDartString(x); +} + +// Translate a JavaScript exception to a Dart exception +// TODO(zundel): cross browser support. This is Chrome specific. +function $transformBrowserException(e) { + if (e instanceof TypeError) { + switch(e.type) { + case "property_not_function": + case "called_non_callable": + if (e.arguments[0] == "undefined") { + return native_ExceptionHelper_createNullPointerException(); + } + return native_ExceptionHelper_createObjectNotClosureException(); + case "non_object_property_call": + case "non_object_property_load": + return native_ExceptionHelper_createNullPointerException(); + case "undefined_method": + if (e.arguments[0] == "call" || e.arguments[0] == "apply") { + return native_ExceptionHelper_createObjectNotClosureException(); + } + return native_ExceptionHelper_createNoSuchMethodException( + "", e.arguments[0], []); + } + } + return e; +} + +// Throws a NoSuchMethodException (used by named-parameter trampolines). +function $nsme() { + throw native_ExceptionHelper_createNoSuchMethodException("", "", []); +} + +// Shared named-argument object used by call-sites with no named arguments. +/** @const */ +var $noargs = {count:0}; + +// Used for invoking dart functions from js. +function $dartcall(fn, args) { + args.unshift(args.length, $noargs); + fn.apply(null, args); +} + +// +// The following methods are used to create canonical constants. +// + +function native_ConstHelper_getConstId(o) { + return $dart_const_id(o); +} + +// compile time const canonicalization helpers +function $dart_const_id(o) { + if (o === $Dart$Null) return ""; + if (typeof o === "number") return "n" + o; + if (typeof o === "boolean") return "b" + ((o) ? 1 : 0); + if (typeof o === "string") return $dart_const_string_id(o); + if (typeof o === "function") throw "a function is not a constant expression"; + var result = o.$dartConstId; + if (result === undefined) { + throw "internal error: reference to non-canonical constant"; + } + return result; +} + +// Array ids have the form: "aID,ID,ID" +function $dart_const_array_id(o) { + var ids = []; + for (var i=o.length-1; i>=0; i--) { + ids.push($dart_const_id(o[i])); + } + return "a" + ids.join(","); +} + +var $CONST_MAP_PREFIX = ":" + +// String ids have the form "sID" +var $string_id = 0; +var $string_id_cache = {}; +function $dart_const_string_id(s) { + var key = $CONST_MAP_PREFIX + s; + var id = $string_id_cache[key]; + if (!id) { + id = "s" + (++$string_id); + $string_id_cache[key] = id; + } + return id; +} + +// A place to store the canonical consts +var $consts = {}; + +function $isDartMap(o) { + return !!(o && o.$implements$Map$Dart); +} + +// Intern const object "o" +function $intern(o, type_args) { + var id; + // Maps and arrays need special handling + // TODO(johnlenz): This array check may not be sufficient across iframes. + if (o instanceof Array) { + // Dart array literals are implemented as JavaScript native arrays. + id = $dart_const_array_id(o); + } else if ($isDartMap(o)) { + // Dart map literals are currently implemented by a non-const Dart class. + id = native_ConstHelper_getConstMapId(o); + } else { + id = "o" + o.$const_id(); + } + if (type_args != null) { + id += '<'; + for (var i=type_args.length-1; i >= 0; i--) { + id += type_args[i]; + id += "," + } + id += '>'; + } + var key = $CONST_MAP_PREFIX + id; + var match = $consts[key]; + if (match != null) { + return match; + } + o.$dartConstId = id; + $consts[key] = o; + return o; +} + +/** @const */ +var $Dart$MapLiteralType = LinkedHashMapImplementation$Dart; + +function $Dart$MapLiteralFactory() { + return native__CoreJsUtil__newMapLiteral(); +} diff --git a/compiler/lib/implementation/date_time_implementation.dart b/compiler/lib/implementation/date_time_implementation.dart new file mode 100644 index 00000000000..70006c93a71 --- /dev/null +++ b/compiler/lib/implementation/date_time_implementation.dart @@ -0,0 +1,171 @@ +// 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. + +// Dart core library. + +// JavaScript implementation of DateTimeImplementation. +class DateTimeImplementation implements DateTime { + factory DateTimeImplementation(int years, + int month, + int day, + int hours, + int minutes, + int seconds, + int milliseconds) { + return new DateTimeImplementation.withTimeZone( + years, month, day, + hours, minutes, seconds, milliseconds, + new TimeZoneImplementation.local()); + } + + DateTimeImplementation.withTimeZone(int years, + int month, + int day, + int hours, + int minutes, + int seconds, + int milliseconds, + TimeZoneImplementation timeZone) + : this.timeZone = timeZone, + value = _valueFromDecomposed(years, month, day, + hours, minutes, seconds, milliseconds, + timeZone.isUtc) { + } + + factory DateTimeImplementation.fromDateAndTime( + Date date, + Time time, + TimeZoneImplementation timeZone) { + if (timeZone === null) { + timeZone = new TimeZoneImplementation.local(); + } + return new DateTimeImplementation.withTimeZone(date.year, + date.month, + date.day + time.days, + time.hours, + time.minutes, + time.seconds, + time.milliseconds, + timeZone); + } + + DateTimeImplementation.now() + : timeZone = new TimeZone.local(), + value = _now() { + } + + DateTimeImplementation.fromString(String formattedString) + : timeZone = new TimeZone.local(), + value = _valueFromString(formattedString) { + } + + const DateTimeImplementation.fromEpoch(this.value, this.timeZone); + + bool operator ==(other) { + if (!(other is DateTimeImplementation)) return false; + return (value == other.value) && (timeZone == other.timeZone); + } + + int compareTo(DateTime other) { + return value.compareTo(other.value); + } + + DateTime changeTimeZone(TimeZone targetTimeZone) { + if (targetTimeZone == null) { + targetTimeZone = new TimeZoneImplementation.local(); + } + return new DateTime.fromEpoch(value, targetTimeZone); + } + + Date get date() { + return new DateImplementation(year, month, day); + } + + Time get time() { + return new TimeImplementation(0, hours, minutes, seconds, milliseconds); + } + + int get year() { + return _getYear(value, isUtc()); + } + + int get month() { + return _getMonth(value, isUtc()); + } + + int get day() { + return _getDay(value, isUtc()); + } + + int get hours() { + return _getHours(value, isUtc()); + } + + int get minutes() { + return _getMinutes(value, isUtc()); + } + + int get seconds() { + return _getSeconds(value, isUtc()); + } + + int get milliseconds() { + return _getMilliseconds(value, isUtc()); + } + + int get weekday() { + throw "Unimplemented"; + } + + bool isLocalTime() { + return !timeZone.isUtc; + } + + bool isUtc() { + return timeZone.isUtc; + } + + String toString() { + String dateString = date.toString(); + String timeString = time.toString(); + if (timeZone.isUtc) { + return "${dateString} ${timeString}Z"; + } else { + return "${dateString} ${timeString}"; + } + } + + // Adds the duration [time] to this DateTime instance. + DateTime add(Time time) { + return new DateTimeImplementation.fromEpoch(value + time.duration, + timeZone); + } + + // Subtracts the duration [time] from this DateTime instance. + DateTime subtract(Time time) { + return new DateTimeImplementation.fromEpoch(value - time.duration, + timeZone); + } + + // Returns a [Time] with the difference of [this] and [other]. + Time difference(DateTime other) { + return new TimeImplementation.duration(value - other.value); + } + + final int value; + final TimeZoneImplementation timeZone; + + static int _valueFromDecomposed(int years, int month, int day, + int hours, int minutes, int seconds, + int milliseconds, bool isUtc) native; + static int _valueFromString(String str) native; + static int _now() native; + int _getYear(int value, bool isUtc) native; + int _getMonth(int value, bool isUtc) native; + int _getDay(int value, bool isUtc) native; + int _getHours(int value, bool isUtc) native; + int _getMinutes(int value, bool isUtc) native; + int _getSeconds(int value, bool isUtc) native; + int _getMilliseconds(int value, bool isUtc) native; +} diff --git a/compiler/lib/implementation/date_time_implementation.js b/compiler/lib/implementation/date_time_implementation.js new file mode 100644 index 00000000000..c6dc1428cfc --- /dev/null +++ b/compiler/lib/implementation/date_time_implementation.js @@ -0,0 +1,80 @@ +// 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. + +// Dart core library. + +function dateTime$validateValue(value) { + if (isNaN(value)) { + // TODO(floitsch): Use real exception object. + throw "Invalid DateTime"; + } + return value; +} + +function native_DateTimeImplementation__valueFromDecomposed( + years, month, day, hours, minutes, seconds, milliseconds, isUtc) { + // JavaScript has 0-based months. + var jsMonth = month - 1; + var value = isUtc ? + Date.UTC(years, jsMonth, day, + hours, minutes, seconds, milliseconds) : + new Date(years, jsMonth, day, + hours, minutes, seconds, milliseconds).valueOf(); + return dateTime$validateValue(value); +} + +function native_DateTimeImplementation__valueFromString(str) { + return dateTime$validateValue(Date.parse(str)); +} + +function native_DateTimeImplementation__now() { + return new Date().valueOf(); +} + +function dateTime$dateFrom(dartDateTime, value) { + // Lazily keep a JS Date stored in the dart object. + var date = dartDateTime.date; + if (!date) { + date = new Date(value); + dartDateTime.date = date; + } + return date; +} + +function native_DateTimeImplementation__getYear(value, isUtc) { + var date = dateTime$dateFrom(this, value); + return isUtc ? date.getUTCFullYear() : date.getFullYear(); +} + +function native_DateTimeImplementation__getMonth(value, isUtc) { + var date = dateTime$dateFrom(this, value); + var jsMonth = isUtc ? date.getUTCMonth() : date.getMonth(); + // JavaScript has 0-based months. + return jsMonth + 1; +} + +function native_DateTimeImplementation__getDay(value, isUtc) { + var date = dateTime$dateFrom(this, value); + return isUtc ? date.getUTCDate() : date.getDate(); +} + +function native_DateTimeImplementation__getHours(value, isUtc) { + var date = dateTime$dateFrom(this, value); + return isUtc ? date.getUTCHours() : date.getHours(); +} + +function native_DateTimeImplementation__getMinutes(value, isUtc) { + var date = dateTime$dateFrom(this, value); + return isUtc ? date.getUTCMinutes() : date.getMinutes(); +} + +function native_DateTimeImplementation__getSeconds(value, isUtc) { + var date = dateTime$dateFrom(this, value); + return isUtc ? date.getUTCSeconds() : date.getSeconds(); +} + +function native_DateTimeImplementation__getMilliseconds(value, isUtc) { + var date = dateTime$dateFrom(this, value); + return isUtc ? date.getUTCMilliseconds() : date.getMilliseconds(); +} diff --git a/compiler/lib/implementation/isolate.dart b/compiler/lib/implementation/isolate.dart new file mode 100644 index 00000000000..edd94414276 --- /dev/null +++ b/compiler/lib/implementation/isolate.dart @@ -0,0 +1,195 @@ +// 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. + +class SendPortImpl implements SendPort { + + const SendPortImpl(this._workerId, this._isolateId, this._receivePortId); + + void send(var message, SendPort replyTo) { + if (PromiseQueue.isEmpty()) { + this._sendNow(message, replyTo); + } else { + _enqueueSend(message, replyTo); + } + } + + void _enqueueSend(var message, SendPort replyTo) { + PromiseQueue.enqueue(const []).then((ignored) { + this._sendNow(message, replyTo); + }); + } + + void _sendNow(var message, SendPort replyTo) native; + + ReceivePortSingleShotImpl call(var message) { + final result = new ReceivePortSingleShotImpl(); + this.send(message, result.toSendPort()); + return result; + } + + ReceivePortSingleShotImpl _callNow(var message) { + final result = new ReceivePortSingleShotImpl(); + this._sendNow(message, result.toSendPort()); + return result; + } + + bool operator==(var other) { + return (other is SendPortImpl) && + (_workerId == other._workerId) && + (_isolateId == other._isolateId) && + (_receivePortId == other._receivePortId); + } + + int hashCode() { + return (_workerId << 16) ^ (_isolateId << 8) ^ _receivePortId; + } + + final int _receivePortId; + final int _isolateId; + final int _workerId; + + static _create(int workerId, int isolateId, int receivePortId) native { + return new SendPortImpl(workerId, isolateId, receivePortId); + } + static _getReceivePortId(SendPortImpl port) native { + return port._receivePortId; + } + static _getIsolateId(SendPortImpl port) native { + return port._isolateId; + } + static _getWorkerId(SendPortImpl port) native { + return port._workerId; + } +} + + +class ReceivePortFactory { + + factory ReceivePort() { + return new ReceivePortImpl(); + } + + factory ReceivePort.singleShot() { + return new ReceivePortSingleShotImpl(); + } + +} + + +class ReceivePortImpl implements ReceivePort { + ReceivePortImpl() + : _id = _nextFreeId++ { + _register(_id); + } + + void receive(void onMessage(var message, SendPort replyTo)) { + _callback = onMessage; + } + + void close() { + _callback = null; + _unregister(_id); + } + + SendPort toSendPort() { + return new SendPortImpl(_currentWorkerId(), _currentIsolateId(), _id); + } + + int _id; + Function _callback = null; + + static int _nextFreeId = 1; + + void _register(int id) native; + void _unregister(int id) native; + + static int _currentWorkerId() native; + static int _currentIsolateId() native; + + static void _invokeCallback(ReceivePortImpl port, message, replyTo) native { + if (port._callback !== null) (port._callback)(message, replyTo); + } + + static int _getId(ReceivePortImpl port) native { + return port._id; + } + static Function _getCallback(ReceivePortImpl port) native { + return port._callback; + } +} + + +class ReceivePortSingleShotImpl implements ReceivePort { + + ReceivePortSingleShotImpl() : port_ = new ReceivePortImpl() { } + + void receive(void callback(var message, SendPort replyTo)) { + port_.receive((var message, SendPort replyTo) { + port_.close(); + callback(message, replyTo); + }); + } + + void close() { + port_.close(); + } + + SendPort toSendPort() { + return port_.toSendPort(); + } + + final ReceivePortImpl port_; + +} + +final String _SPAWNED_SIGNAL = "spawned"; + +class IsolateNatives { + static Promise spawn(Isolate isolate, bool isLight) { + Promise result = new Promise(); + ReceivePort port = new ReceivePort.singleShot(); + port.receive((msg, SendPort replyPort) { + assert(msg == _SPAWNED_SIGNAL); + result.complete(replyPort); + }); + _spawn(isolate, isLight, port.toSendPort()); + return result; + } + + static SendPort _spawn(Isolate isolate, bool light, SendPort port) native; + static Function bind(Function f) native; +} + + +class _IsolateJsUtil { + static void _promiseQueueProcess() native { + PromiseQueue.process(); + } + + static void _startIsolate(Isolate isolate, SendPort replyTo) native { + ReceivePort port = new ReceivePort(); + replyTo.send(_SPAWNED_SIGNAL, port.toSendPort()); + isolate._run(port); + } + + static SendPort _toSendPort(port) native { + return port.toSendPort(); + } + + static void _print(String msg) native { + print(msg); + } + + static _copyObject(obj) native { + return new Copier().traverse(obj); + } + + static _serializeObject(obj) native { + return new Serializer().traverse(obj); + } + + static _deserializeMessage(message) native { + return new Deserializer().deserialize(message); + } +} diff --git a/compiler/lib/implementation/isolate.js b/compiler/lib/implementation/isolate.js new file mode 100644 index 00000000000..c8d24d06360 --- /dev/null +++ b/compiler/lib/implementation/isolate.js @@ -0,0 +1,536 @@ +// 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. + +var isolate$current = null; +var isolate$rootIsolate = null; // Will only be set in the main worker. +var isolate$inits = []; +var isolate$globalThis = this; + +// These declarations are needed to avoid errors from the Closure Compiler +// optimizer. They are defined in client/dom/generated/dart_dom_wrapping.js. +var __dom_wrap; +var __dom_unwrap; + +var isolate$inWorker = + (typeof isolate$globalThis['importScripts']) != "undefined"; +var isolate$supportsWorkers = + isolate$inWorker || ((typeof isolate$globalThis['Worker']) != 'undefined'); + +var isolate$MAIN_WORKER_ID = 0; +// Non-main workers will update the id variable. +var isolate$thisWorkerId = isolate$MAIN_WORKER_ID; + +// Whether to use web workers when implementing isolates. +var isolate$useWorkers = isolate$supportsWorkers; +// Uncomment this to not use web workers even if they're available. +// isolate$useWorkers = false; + +// Whether to use the web-worker JSON-based message serialization protocol, +// even if not using web workers. +var isolate$useWorkerSerializationProtocol = false; +// Uncomment this to always use the web-worker JSON-based message +// serialization protocol, e.g. for testing purposes. +// isolate$useWorkerSerializationProtocol = true; + + +// ------- SendPort ------- +function isolate$sendMessage(workerId, isolateId, receivePortId, + message, replyTo) { + // Both, the message and the replyTo are already serialized. + if (workerId == isolate$thisWorkerId) { + var isolate = isolate$isolateRegistry.get(isolateId); + if (!isolate) return; // Isolate has been closed. + var receivePort = isolate.getReceivePortForId(receivePortId); + if (!receivePort) return; // ReceivePort has been closed. + isolate$receiveMessage(receivePort, isolate, message, replyTo); + } else { + var worker; + if (isolate$inWorker) { + worker = isolate$mainWorker; + } else { + worker = isolate$workerRegistry.get(workerId); + } + worker.postMessage({ command: 'message', + workerId: workerId, + isolateId: isolateId, + portId: receivePortId, + msg: message, + replyTo: replyTo }); + } +} + +function isolate$receiveMessage(port, isolate, + serializedMessage, serializedReplyTo) { + isolate$IsolateEvent.enqueue(isolate, function() { + var message = isolate$deserializeMessage(serializedMessage); + var replyTo = isolate$deserializeMessage(serializedReplyTo); + native_ReceivePortImpl__invokeCallback(port, message, replyTo); + native__IsolateJsUtil__promiseQueueProcess(); + }); +} + +// ------- ReceivePort ------- + +function native_ReceivePortImpl__register(id) { + isolate$current.registerReceivePort(id, this); +} + +function native_ReceivePortImpl__unregister(id) { + isolate$current.unregisterReceivePort(id); +} + +function native_ReceivePortImpl__currentWorkerId() { + return isolate$thisWorkerId; +} + +function native_ReceivePortImpl__currentIsolateId() { + return isolate$current.id; +} + +// -------- Registry --------- +function isolate$Registry() { + this.map = {}; + this.count = 0; +} + +isolate$Registry.prototype.register = function(id, val) { + if (this.map[id]) { + throw Error("Registry: Elements must be registered only once."); + } + this.map[id] = val; + this.count++; +}; + +isolate$Registry.prototype.unregister = function(id) { + if (id in this.map) { + delete this.map[id]; + this.count--; + } +}; + +isolate$Registry.prototype.get = function(id) { + return this.map[id]; +}; + +isolate$Registry.prototype.isEmpty = function() { + return this.count === 0; +}; + + +// ------- Worker registry ------- +// Only used in the main worker. +var isolate$workerRegistry = new isolate$Registry(); + +// ------- Isolate registry ------- +// 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. +var isolate$isolateRegistry = new isolate$Registry(); + +// ------- Debugging log function ------- +function isolate$log(msg) { + return; + if (isolate$inWorker) { + isolate$mainWorker.postMessage({ command: 'log', msg: msg }); + } else { + try { + isolate$globalThis.console.log(msg); + } catch(e) { + throw String(e.stack); + } + } +} + +function isolate$initializeWorker(workerId) { + isolate$thisWorkerId = workerId; +} + +var isolate$workerPrint = false; +if (isolate$inWorker) { + isolate$workerPrint = function(msg){ + isolate$mainWorker.postMessage({ command: 'print', msg: msg }); + } +} + +// ------- Message handler ------- +function isolate$processWorkerMessage(sender, e) { + var msg = e.data; + switch (msg.command) { + case 'start': + isolate$log("starting worker: " + msg.id + " " + msg.runner); + isolate$initializeWorker(msg.id); + var runnerObject = (isolate$globalThis[msg.runner])(); + var serializedReplyTo = msg.replyTo; + isolate$IsolateEvent.enqueue(new isolate$Isolate(), function() { + var replyTo = isolate$deserializeMessage(serializedReplyTo); + native__IsolateJsUtil__startIsolate(runnerObject, replyTo); + }); + isolate$runEventLoop(); + break; + case 'message': + isolate$sendMessage(msg.workerId, msg.isolateId, msg.portId, + msg.msg, msg.replyTo); + isolate$runEventLoop(); + break; + case 'close': + isolate$log("Closing Worker"); + isolate$workerRegistry.unregister(sender.id); + sender.terminate(); + isolate$runEventLoop(); + break; + case 'log': + isolate$log(msg.msg); + break; + case 'print': + native__IsolateJsUtil__print(msg.msg); + break; + case 'error': + throw msg.msg; + break; + } +} + +if (isolate$supportsWorkers) { + isolate$globalThis.onmessage = function(e) { + isolate$processWorkerMessage(isolate$mainWorker, e); + }; +} + +// ------- Default Worker ------- +function isolate$MainWorker() { + this.id = isolate$MAIN_WORKER_ID; +} + +var isolate$mainWorker = new isolate$MainWorker(); +isolate$mainWorker.postMessage = function(msg) { + isolate$globalThis.postMessage(msg); +}; + +var isolate$nextFreeIsolateId = 1; + +// Native methods for isolate functionality. +/** + * @constructor + */ +function isolate$Isolate() { + // The isolate ids is only unique within the current worker and frame. + this.id = isolate$nextFreeIsolateId++; + // When storing information on DOM nodes the isolate's id is not enough. + // We instead use a token with a hashcode. The token can be stored in the + // DOM node (since it is small and will not keep much data alive). + this.token = new Object(); + this.token.hashCode = (Math.random() * 0xFFFFFFF) >>> 0; + this.receivePorts = new isolate$Registry(); + this.run(function() { + // The Dart-to-JavaScript compiler builds a list of functions that + // need to run for each isolate to setup the state of static + // variables. Run through the list and execute each function. + for (var i = 0, len = isolate$inits.length; i < len; i++) { + isolate$inits[i](); + } + }); +} + +// It is allowed to stack 'run' calls. The stacked isolates can be different. +// That is Isolate1.run could call the DOM which then calls Isolate2.run. +isolate$Isolate.prototype.run = function(code) { + var old = isolate$current; + isolate$current = this; + var result = null; + try { + result = code(); + } finally { + isolate$current = old; + } + return result; +}; + +isolate$Isolate.prototype.registerReceivePort = function(id, port) { + if (this.receivePorts.isEmpty()) { + isolate$isolateRegistry.register(this.id, this); + } + this.receivePorts.register(id, port); +}; + +isolate$Isolate.prototype.unregisterReceivePort = function(id) { + this.receivePorts.unregister(id); + if (this.receivePorts.isEmpty()) { + isolate$isolateRegistry.unregister(this.id); + } +}; + +isolate$Isolate.prototype.getReceivePortForId = function(id) { + return this.receivePorts.get(id); +}; + +var isolate$events = []; + +/** + * @constructor + */ +function isolate$IsolateEvent(isolate, fn) { + this.isolate = isolate; + this.fn = fn; +} + +isolate$IsolateEvent.prototype.process = function() { + this.isolate.run(this.fn); +}; + +isolate$IsolateEvent.enqueue = function(isolate, fn) { + isolate$events.push(new isolate$IsolateEvent(isolate, fn)); +}; + +isolate$IsolateEvent.dequeue = function() { + if (isolate$events.length == 0) return $Dart$Null; + var result = isolate$events[0]; + isolate$events.splice(0, 1); + return result; +}; + +var isolate$lightCount = 0; +var isolate$heavyCount = 0; + +function native_IsolateNatives__spawn(runnable, light, replyPort) { + // TODO(kasperl): Allow mixing heavy and light isolates. + if (light) { + isolate$lightCount++; + if (isolate$heavyCount != 0) { + throw Error("Cannot mix light and heavy isolates."); + } + isolate$useWorkers = false; + } else { + isolate$heavyCount++; + if (isolate$lightCount != 0) { + throw Error("Cannot mix light and heavy isolates."); + } + } + + // TODO(floitsch): throw exception if runnable's class doesn't have a + // default constructor. + if (isolate$useWorkers) { + isolate$startWorker(runnable, replyPort); + } else { + isolate$startNonWorker(runnable, replyPort); + } +} + +function native_IsolateNatives_bind(fn) { + var isolate = isolate$current; + return function() { + var self = this; + var args = arguments; + isolate.run(function() { + fn.apply(self, args); + }); + isolate$runEventLoop(); + }; +} + +function isolate$startNonWorker(runnable, replyTo) { + // Spawn a new isolate and create the receive port in it. + var spawned = new isolate$Isolate(); + + // Instead of just running the provided runnable, we create a + // new cloned instance of it with a fresh state in the spawned + // isolate. This way, we do not get cross-isolate references + // through the runnable. + var factory = runnable.getIsolateFactory(); + isolate$IsolateEvent.enqueue(spawned, function() { + native__IsolateJsUtil__startIsolate(factory(), replyTo); + }); +} + +// This field is only used by the main worker. +var isolate$nextFreeWorkerId = isolate$thisWorkerId + 1; + +var isolate$thisScript = function() { + if (!isolate$supportsWorkers || isolate$inWorker) return null; + + // TODO(5334778): Find a cross-platform non-brittle way of getting the + // currently running script. + var scripts = document.getElementsByTagName('script'); + // The scripts variable only contains the scripts that have already been + // executed. The last one is the currently running script. + var script = scripts[scripts.length - 1]; + var src = script.src; + if (!src) { + // TODO() + src = "FIXME:5407062" + "_" + Math.random().toString(); + script.src = src; + } + return src; +}(); + +function isolate$startWorker(runnable, replyPort) { + if (isolate$inWorker) { + isolate$mainWorker.postMessage("Unimplemented nested spawn."); + throw "Unimplemented"; + } + var factory = runnable.getIsolateFactory(); + var factoryName = factory.name; + var worker = new Worker(isolate$thisScript); + worker.onmessage = function(e) { + isolate$processWorkerMessage(worker, e); + }; + var workerId = isolate$nextFreeWorkerId++; + // We also store the id on the worker itself so that we can unregister it. + worker.id = workerId; + isolate$workerRegistry.register(workerId, worker); + worker.postMessage({ command: 'start', + id: workerId, + replyTo: isolate$serializeMessage(replyPort), + runner: factoryName }); +} + +function native_SendPortImpl__sendNow(message, replyTo) { + if (replyTo !== $Dart$Null && !(replyTo instanceof SendPortImpl$Dart)) { + throw "SendPort::send: Illegal replyTo type."; + } + message = isolate$serializeMessage(message); + replyTo = isolate$serializeMessage(replyTo); + var workerId = native_SendPortImpl__getWorkerId(this); + var isolateId = native_SendPortImpl__getIsolateId(this); + var receivePortId = native_SendPortImpl__getReceivePortId(this); + isolate$sendMessage(workerId, isolateId, receivePortId, message, replyTo); +} + +function isolate$closeWorkerIfNecessary() { + if (!isolate$isolateRegistry.isEmpty()) return; + isolate$mainWorker.postMessage( { command: 'close' } ); +} + +function isolate$doOneEventLoopIteration() { + var CONTINUE_LOOP = true; + var STOP_LOOP = false; + var event = isolate$IsolateEvent.dequeue(); + if (!event) { + if (isolate$inWorker) { + isolate$closeWorkerIfNecessary(); + } else if (!isolate$isolateRegistry.isEmpty() && + isolate$workerRegistry.isEmpty() && + !isolate$supportsWorkers && (typeof(window) == 'undefined')) { + // This should only trigger when running on the command-line. + // We don't want this check to execute in the browser where the isolate + // might still be alive due to DOM callbacks. + throw Error("Program exited with open ReceivePorts."); + } + return STOP_LOOP; + } else { + event.process(); + return CONTINUE_LOOP; + } +} + +function isolate$doRunEventLoop() { + if (typeof window != 'undefined' && window.setTimeout) { + (function next() { + var continueLoop = isolate$doOneEventLoopIteration(); + if (!continueLoop) return; + // TODO(kasperl): It might turn out to be too expensive to call + // setTimeout for every single event. This needs more investigation. + window.setTimeout(next, 0); + })(); + } else { + while (true) { + var continueLoop = isolate$doOneEventLoopIteration(); + if (!continueLoop) break; + } + } +} + +function isolate$runEventLoop() { + if (!isolate$inWorker) { + isolate$doRunEventLoop(); + } else { + try { + isolate$doRunEventLoop(); + } catch(e) { + // TODO(floitsch): try to send stack-trace to the other side. + isolate$mainWorker.postMessage({ command: 'error', msg: "" + e }); + } + } +} + +function RunEntry(entry, args) { + // Don't start the main loop again, if we are in a worker. + if (isolate$inWorker) return; + var isolate = new isolate$Isolate(); + isolate$rootIsolate = isolate; + isolate$IsolateEvent.enqueue(isolate, function() { + entry(args); + }); + isolate$runEventLoop(); + + // BUG(5151491): This 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). + isolate$current = isolate; +} + +// ------- Message Serializing and Deserializing ------- + +function native_MessageTraverser__clearAttachedInfo(o) { + o['__MessageTraverser__attached_info__'] = (void 0); +} + +function native_MessageTraverser__setAttachedInfo(o, info) { + o['__MessageTraverser__attached_info__'] = info; +} + +function native_MessageTraverser__getAttachedInfo(o) { + return o['__MessageTraverser__attached_info__']; +} + +function native_Serializer__newJsArray(len) { + return new Array(len); +} + +function native_Serializer__jsArrayIndexSet(jsArray, index, val) { + jsArray[index] = val; +} + +function native_Serializer__dartListToJsArrayNoCopy(list) { + if (list instanceof Array) { + RTT.removeTypeInfo(list); + return list; + } else { + var len = native__ArrayJsUtil__arrayLength(list); + var array = new Array(len); + for (var i = 0; i < len; i++) { + array[i] = INDEX$operator(list, i); + } + return array; + } +} + +function native_Deserializer__isJsArray(x) { + return x instanceof Array; +} + +function native_Deserializer__jsArrayIndex(x, index) { + return x[index]; +} + +function native_Deserializer__jsArrayLength(x) { + return x.length; +} + +function isolate$serializeMessage(message) { + if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) { + return native__IsolateJsUtil__serializeObject(message); + } else { + return native__IsolateJsUtil__copyObject(message); + } +} + +function isolate$deserializeMessage(message) { + if (isolate$useWorkers || isolate$useWorkerSerializationProtocol) { + return native__IsolateJsUtil__deserializeMessage(message); + } else { + // Nothing more to do. + return message; + } +} diff --git a/compiler/lib/implementation/isolate_serialization.dart b/compiler/lib/implementation/isolate_serialization.dart new file mode 100644 index 00000000000..de40c22c3d9 --- /dev/null +++ b/compiler/lib/implementation/isolate_serialization.dart @@ -0,0 +1,264 @@ +// 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. + +class MessageTraverser { + static bool isPrimitive(x) { + return (x === null) || (x is String) || (x is num) || (x is bool); + } + + MessageTraverser(); + + traverse(var x) { + if (isPrimitive(x)) return visitPrimitive(x); + _taggedObjects = new List(); + var result; + try { + result = _dispatch(x); + } finally { + _cleanup(); + } + return result; + } + + void _cleanup() { + int len = _taggedObjects.length; + for (int i = 0; i < len; i++) { + _clearAttachedInfo(_taggedObjects[i]); + } + _taggedObjects = null; + } + + void _attachInfo(var o, var info) { + _taggedObjects.add(o); + _setAttachedInfo(o, info); + } + + _getInfo(var o) { + return _getAttachedInfo(o); + } + + _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 SendPortImpl) return visitSendPort(x); + if (x is ReceivePortImpl) return visitReceivePort(x); + if (x is ReceivePortSingleShotImpl) return visitReceivePortSingleShot(x); + // TODO(floitsch): make this a real exception. (which one)? + throw "Message serialization: Illegal value $x passed"; + } + + abstract visitPrimitive(x); + abstract visitList(List x); + abstract visitMap(Map x); + abstract visitSendPort(SendPortImpl x); + abstract visitReceivePort(ReceivePortImpl x); + abstract visitReceivePortSingleShot(ReceivePortSingleShotImpl x); + + List _taggedObjects; + + _clearAttachedInfo(var obj) native; + _setAttachedInfo(var o, var info) native; + _getAttachedInfo(var o) native; +} + +class Copier extends MessageTraverser { + Copier() : super(); + + visitPrimitive(x) => x; + + List visitList(List list) { + List copy = _getInfo(list); + if (copy !== null) return copy; + + int len = list.length; + // TODO(floitsch): we loose the generic type of the List. + copy = new List(len); + _attachInfo(list, copy); + for (int i = 0; i < len; i++) { + copy[i] = _dispatch(list[i]); + } + return copy; + } + + Map visitMap(Map map) { + Map copy = _getInfo(map); + if (copy !== null) return copy; + + // TODO(floitsch): we loose the generic type of the map. + copy = new Map(); + _attachInfo(map, copy); + map.forEach((key, val) { + copy[_dispatch(key)] = _dispatch(val); + }); + return copy; + } + + SendPort visitSendPort(SendPortImpl port) { + // No need to copy the sendport. + return port; + } + + SendPort visitReceivePort(ReceivePortImpl port) { + // TODO(floitsch): should we instead call toFreshSendPort? to be certain + // that objects are not shared. + return port.toSendPort(); + } + + SendPort visitReceivePortSingleShot(ReceivePortSingleShotImpl port) { + // TODO(floitsch): should we instead call toFreshSendPort? to be certain + // that objects are not shared. + return port.toSendPort(); + } +} + +class Serializer extends MessageTraverser { + Serializer() : super(); + + visitPrimitive(x) => x; + + visitList(List list) { + int copyId = _getInfo(list); + if (copyId !== null) return _makeRef(copyId); + + int id = _nextFreeRefId++; + _attachInfo(list, id); + var jsArray = _serializeDartListIntoNewJsArray(list); + // TODO(floitsch): we are losing the generic type. + return _dartListToJsArrayNoCopy(['list', id, jsArray]); + } + + visitMap(Map map) { + int copyId = _getInfo(map); + if (copyId !== null) return _makeRef(copyId); + + int id = _nextFreeRefId++; + _attachInfo(map, id); + var keys = _serializeDartListIntoNewJsArray(map.getKeys()); + var values = _serializeDartListIntoNewJsArray(map.getValues()); + // TODO(floitsch): we are losing the generic type. + return _dartListToJsArrayNoCopy(['map', id, keys, values]); + } + + visitSendPort(SendPortImpl port) { + return _dartListToJsArrayNoCopy(['sendport', + port._workerId, + port._isolateId, + port._receivePortId]); + } + + visitReceivePort(ReceivePortImpl port) { + return visitSendPort(port.toSendPort());; + } + + visitReceivePortSingleShot(ReceivePortSingleShotImpl port) { + return visitSendPort(port.toSendPort()); + } + + _serializeDartListIntoNewJsArray(List list) { + int len = list.length; + var jsArray = _newJsArray(len); + for (int i = 0; i < len; i++) { + _jsArrayIndexSet(jsArray, i, _dispatch(list[i])); + } + return jsArray; + } + + _makeRef(int id) { + return _dartListToJsArrayNoCopy(['ref', id]); + } + + int _nextFreeRefId = 0; + + static _newJsArray(int len) native; + static _jsArrayIndexSet(jsArray, int index, val) native; + static _dartListToJsArrayNoCopy(List list) native; +} + +class Deserializer { + 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() + _deserialized = new HashMap(); + return _deserializeHelper(x); + } + + _deserializeHelper(x) { + if (isPrimitive(x)) return x; + assert(_isJsArray(x)); + switch (_jsArrayIndex(x, 0)) { + case 'ref': return _deserializeRef(x); + case 'list': return _deserializeList(x); + case 'map': return _deserializeMap(x); + case 'sendport': return _deserializeSendPort(x); + // TODO(floitsch): Use real exception (which one?). + default: throw "Unexpected serialized object"; + } + } + + _deserializeRef(x) { + int id = _jsArrayIndex(x, 1); + var result = _deserialized[id]; + assert(result !== null); + return result; + } + + List _deserializeList(x) { + int id = _jsArrayIndex(x, 1); + var jsArray = _jsArrayIndex(x, 2); + assert(_isJsArray(jsArray)); + List dartList = _jsArrayToDartListNoCopy(jsArray); + _deserialized[id] = dartList; + int len = dartList.length; + for (int i = 0; i < len; i++) { + dartList[i] = _deserializeHelper(dartList[i]); + } + return dartList; + } + + Map _deserializeMap(x) { + Map result = new Map(); + int id = _jsArrayIndex(x, 1); + _deserialized[id] = result; + var keys = _jsArrayIndex(x, 2); + var values = _jsArrayIndex(x, 3); + assert(_isJsArray(keys)); + assert(_isJsArray(values)); + int len = _jsArrayLength(keys); + assert(len == _jsArrayLength(values)); + for (int i = 0; i < len; i++) { + var key = _deserializeHelper(_jsArrayIndex(keys, i)); + var value = _deserializeHelper(_jsArrayIndex(values, i)); + result[key] = value; + } + return result; + } + + SendPort _deserializeSendPort(x) { + int workerId = _jsArrayIndex(x, 1); + int isolateId = _jsArrayIndex(x, 2); + int receivePortId = _jsArrayIndex(x, 3); + return new SendPortImpl(workerId, isolateId, receivePortId); + } + + List _jsArrayToDartListNoCopy(a) { + // We rely on the fact that Dart-lists are directly mapped to Js-arrays. + // TODO(floitsch): can we do better here? + assert(a is List); + return a; + } + + // TODO(floitsch): this should by Map or Map. + Map _deserialized; + + static bool _isJsArray(x) native; + static _jsArrayIndex(x, int index) native; + static int _jsArrayLength(x) native; +} diff --git a/compiler/lib/implementation/math_natives.dart b/compiler/lib/implementation/math_natives.dart new file mode 100644 index 00000000000..860ca2b121a --- /dev/null +++ b/compiler/lib/implementation/math_natives.dart @@ -0,0 +1,26 @@ +// 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. + +// Dart core library. + +class MathNatives { + static double cos(num d) native; + static double sin(num d) native; + static double tan(num d) native; + static double acos(num d) native; + static double asin(num d) native; + static double atan(num d) native; + static double atan2(num a, num b) native; + static double sqrt(num d) native; + static double exp(num d) native; + static double log(num d) native; + static double pow(num d1, num d2) native; + static double random() native; + static int parseInt(String str) native; + static double parseDouble(String str) native; + + static BadNumberFormatException _newBadNumberFormat(x) native { + return new BadNumberFormatException(x); + } +} diff --git a/compiler/lib/implementation/math_natives.js b/compiler/lib/implementation/math_natives.js new file mode 100644 index 00000000000..4ac5aaffba2 --- /dev/null +++ b/compiler/lib/implementation/math_natives.js @@ -0,0 +1,56 @@ +// 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. + +// Native methods for Math. +var native_Math_ceil = Math.ceil; +var native_Math_floor = Math.floor; +var native_Math_max = Math.max; +var native_Math_min = Math.min; +var native_Math_round = Math.round; + +// A valid integer-string is composed of: +// optional whitespace: \s* +// an optional sign: [+-]? +// either digits (at least one): \d+ +// or a hex-literal: 0[xX][0-9abcdefABCDEF]+ +// optional whitespace: \s* +var math$INT_REGEXP = + /^\s*[+-]?(:?\d+|0[xX][0-9abcdefABCDEF]+)\s*$/; + +// A valid double-string is composed of: +// optional whitespace: \s* +// an optional sign: [+-]? +// either: +// digits* . digits+ exponent? +// digits+ exponent +// Infinity +// NaN +// optional whitespace: \s* +var math$DOUBLE_REGEXP = + /^\s*[+-]?((\d*\.\d+([eE][+-]?\d+)?)|(\d+([eE][+-]?\d+))|Infinity|NaN)\s*$/; + +function native_MathNatives_parseDouble(str) { + if (math$INT_REGEXP.test(str) || math$DOUBLE_REGEXP.test(str)) return +str; + throw native_MathNatives__newBadNumberFormat(str); +} + + + +function native_MathNatives_parseInt(str) { + if (math$INT_REGEXP.test(str)) return +str; + throw native_MathNatives__newBadNumberFormat(str); +} + +function native_MathNatives_random() { return Math.random(); } +function native_MathNatives_sin(x) { return Math.sin(x); } +function native_MathNatives_cos(x) { return Math.cos(x); } +function native_MathNatives_tan(x) { return Math.tan(x); } +function native_MathNatives_asin(x) { return Math.asin(x); } +function native_MathNatives_acos(x) { return Math.acos(x); } +function native_MathNatives_atan(x) { return Math.atan(x); } +function native_MathNatives_atan2(x, y) { return Math.atan2(x, y); } +function native_MathNatives_sqrt(x) { return Math.sqrt(x); } +function native_MathNatives_exp(x) { return Math.exp(x); } +function native_MathNatives_log(x) { return Math.log(x); } +function native_MathNatives_pow(x, y) { return Math.pow(x, y); } diff --git a/compiler/lib/implementation/number.dart b/compiler/lib/implementation/number.dart new file mode 100644 index 00000000000..7d57a5d0b82 --- /dev/null +++ b/compiler/lib/implementation/number.dart @@ -0,0 +1,66 @@ +// 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. + +class NumberImplementation implements int, double native "Number" { + NumberImplementation operator +(NumberImplementation other) native; + NumberImplementation operator -(NumberImplementation other) native; + NumberImplementation operator *(NumberImplementation other) native; + NumberImplementation operator /(NumberImplementation other) native; + NumberImplementation operator ~/(NumberImplementation other) native; + NumberImplementation operator %(NumberImplementation shiftAmount) native; + NumberImplementation operator negate() native; + int operator |(int other) native; + int operator &(int other) native; + int operator ^(int other) native; + int operator <<(int shiftAmount) native; + int operator >>(int shiftAmount) native; + int operator ~() native; + bool operator ==(NumberImplementation other) native; + bool operator <(NumberImplementation other) native; + bool operator <=(NumberImplementation other) native; + bool operator >(NumberImplementation other) native; + bool operator >=(NumberImplementation other) native; + + + NumberImplementation remainder(num other) native; + NumberImplementation abs() native; + NumberImplementation round() native; + NumberImplementation floor() native; + NumberImplementation ceil() native; + NumberImplementation truncate() native; + + // CompareTo has to give a complete order, including -0/+0, NaN and + // Infinities. + NumberImplementation compareTo(NumberImplementation other) { + // TODO(floitsch): NumberImplementation.compareTo is broken, since it + // doesn't take NaNs and -0.0 into account. + return this - other; + } + + bool isNegative() native; + bool isEven() native; + bool isOdd() native; + bool isNaN() native; + bool isInfinite() native; + + int toInt() { + if (isNaN()) throw new BadNumberFormatException("NaN"); + if (isInfinite()) throw new BadNumberFormatException("Infinity"); + NumberImplementation truncated = truncate(); + // If truncated is -0.0 return +0. The test will also trigger for positive + // 0s but that's not a problem. + if (truncated == -0.0) return 0; + return truncated; + } + + NumberImplementation toDouble() { return this; } + + String toString() native; + String toStringAsFixed(int fractionDigits) native; + String toStringAsExponential(int fractionDigits) native; + String toStringAsPrecision(int precision) native; + String toRadixString(int radix) native; + + int hashCode() native; +} diff --git a/compiler/lib/implementation/number.js b/compiler/lib/implementation/number.js new file mode 100644 index 00000000000..0af12f57753 --- /dev/null +++ b/compiler/lib/implementation/number.js @@ -0,0 +1,153 @@ +// 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. + +/** + * Extend the Number prototype with members expected in dart. + * + * TODO(jimhug): Figure out how to map dart's number hierarchy to Number. + */ + +Number.$instanceOf = function(obj) { + return typeof obj == 'number' || obj instanceof Number; +}; + +function native_NumberImplementation_BIT_OR(other) { + return this | other; +} + +function native_NumberImplementation_BIT_XOR(other) { + return this ^ other; +} + +function native_NumberImplementation_BIT_AND(other) { + return this & other; +} + +function native_NumberImplementation_SHL(other) { + return this << other; +} + +function native_NumberImplementation_SAR(other) { + return this >> other; +} + +function native_NumberImplementation_ADD(other) { + return this + other; +} + +function native_NumberImplementation_SUB(other) { + return this - other; +} + +function native_NumberImplementation_MUL(other) { + return this * other; +} + +function native_NumberImplementation_DIV(other) { + return this / other; +} + +function native_NumberImplementation_TRUNC(other) { + var tmp = this / other; + if (tmp < 0) { + return Math.ceil(tmp); + } else { + return Math.floor(tmp); + } +} + +function number$euclideanModulo(a, b) { + var result = a % b; + if (result == 0) { + return 0; // Make sure we don't return -0.0. + } else if (result < 0) { + if (b < 0) { + return result - b; + } else { + return result + b; + } + } + return result; +} + +function native_NumberImplementation_MOD(other) { + return number$euclideanModulo(this, other); +} + +function native_NumberImplementation_LT(other) { + return this < other; +} + +function native_NumberImplementation_GT(other) { + return this > other; +} + +function native_NumberImplementation_LTE(other) { + return this <= other; +} + +function native_NumberImplementation_GTE(other) { + return this >= other; +} + +function native_NumberImplementation_EQ(other) { + if (typeof other == 'number') { + return this == other; + } else if (other instanceof Number) { + // Must convert other to a primitive for value equality to work + return this == Number(other); + } else { + return false; + } +} + +function native_NumberImplementation_BIT_NOT() { + return ~this; +} + +function native_NumberImplementation_negate() { return -this; } + +function native_NumberImplementation_remainder(other) { + return this % other; +} + +function native_NumberImplementation_abs() { return Math.abs(this); } + +function native_NumberImplementation_round() { return Math.round(this); } +function native_NumberImplementation_floor() { return Math.floor(this); } +function native_NumberImplementation_ceil() { return Math.ceil(this); } +function native_NumberImplementation_truncate() { + return (this < 0) ? Math.ceil(this) : Math.floor(this); +} +function native_NumberImplementation_isNegative() { + // TODO(floitsch): is there a faster way to detect -0? + if (this == 0) return (1 / this) < 0; + return this < 0; +} +function native_NumberImplementation_isEven() { return ((this & 1) == 0); } +function native_NumberImplementation_isOdd() { return ((this & 1) == 1); } +function native_NumberImplementation_isNaN() { return isNaN(this); } +function native_NumberImplementation_isInfinite() { + return (this == Infinity) || (this == -Infinity); +} + +function native_NumberImplementation_toString() { + return this.toString(); +} +function native_NumberImplementation_toStringAsFixed(fractionDigits) { + return this.toFixed(fractionDigits); +} +function native_NumberImplementation_toStringAsPrecision(precision) { + return this.toPrecision(precision); +} +function native_NumberImplementation_toStringAsExponential(fractionDigits) { + return this.toExponential(fractionDigits); +} +function native_NumberImplementation_toRadixString(radix) { + return this.toString(radix); +} + +function native_NumberImplementation_hashCode() { + return this & 0xFFFFFFF; +} diff --git a/compiler/lib/implementation/object.js b/compiler/lib/implementation/object.js new file mode 100644 index 00000000000..b8a201daab5 --- /dev/null +++ b/compiler/lib/implementation/object.js @@ -0,0 +1,7 @@ +// 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. + +Object.$instanceOf = function(obj) { + return true; +}; diff --git a/compiler/lib/implementation/print.js b/compiler/lib/implementation/print.js new file mode 100644 index 00000000000..d9334b5da7f --- /dev/null +++ b/compiler/lib/implementation/print.js @@ -0,0 +1,14 @@ +// 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. + +function native__Logger__printString(str) { + if (isolate$workerPrint) { + isolate$workerPrint(str); + } else if (this.console) { + this.console.log(str); + } else if (this.write) { + this.write(str); + this.write('\n'); + } +} diff --git a/compiler/lib/implementation/regexp.dart b/compiler/lib/implementation/regexp.dart new file mode 100644 index 00000000000..7e5ac2a1fe7 --- /dev/null +++ b/compiler/lib/implementation/regexp.dart @@ -0,0 +1,135 @@ +// 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. + +class JSSyntaxRegExp implements RegExp { + const JSSyntaxRegExp(String pattern, String flags) + : this.pattern = pattern, this.flags = flags; + + final String pattern; + final String flags; + + Iterable allMatches(String str) { + return new _LazyAllMatches(this, str); + } + + Match firstMatch(String str) native; + bool hasMatch(String str) native; + String stringMatch(String str) native; + + static String _pattern(JSSyntaxRegExp regexp) native { + return regexp.pattern; + } + static String _flags(JSSyntaxRegExp regexp) native { + return regexp.flags; + } +} + +class JSSyntaxMatch implements Match { + const JSSyntaxMatch(RegExp regexp, String str) + : this.pattern = regexp, this.str = str; + + final String str; + final Pattern pattern; + + String operator[](int group) { + return this.group(group); + } + + Array groups(Array groups) { + Array strings = new Array(); + groups.forEach((int group) { + strings.add(this.group(group)); + }); + return strings; + } + + String group(int nb) native; + + int start() native; + + int end() native; + + groupCount() native; + + static _new(RegExp regexp, String str) native { + return new JSSyntaxMatch(regexp, str); + } +} + +class _LazyAllMatches implements Collection { + JSSyntaxRegExp _regexp; + String _str; + + const _LazyAllMatches(this._regexp, this._str); + + void forEach(void f(Match match)) { + for (Match match in this) { + f(match); + } + } + + Collection filter(bool f(Match match)) { + Array result = new Array(); + for (Match match in this) { + if (f(match)) result.add(match); + } + return result; + } + + bool every(bool f(Match match)) { + for (Match match in this) { + if (!f(match)) return false; + } + return true; + } + + bool some(bool f(Match match)) { + for (Match match in this) { + if (f(match)) return true; + } + return false; + } + + bool isEmpty() { + return _regexp.firstMatch(_str) == null; + } + + int get length() { + int result = 0; + for (Match match in this) { + result++; + } + return result; + } + + Iterator iterator() { + return new _LazyAllMatchesIterator(_regexp, _str); + } +} + +class _LazyAllMatchesIterator implements Iterator { + JSSyntaxRegExp _regexp; + String _str; + Match _nextMatch; + + _LazyAllMatchesIterator(this._regexp, this._str) { + _jsInit(_regexp); + } + + Match next() { + if (!hasNext()) throw const NoMoreElementsException(); + Match result = _nextMatch; + _nextMatch = null; + return result; + } + + bool hasNext() { + if (_nextMatch != null) return true; + _nextMatch = _computeNextMatch(_regexp, _str); + return (_nextMatch != null); + } + + void _jsInit(JSSyntaxRegExp regexp) native; + Match _computeNextMatch(JSSyntaxRegExp regexp, String str) native; +} diff --git a/compiler/lib/implementation/regexp.js b/compiler/lib/implementation/regexp.js new file mode 100644 index 00000000000..23980f43913 --- /dev/null +++ b/compiler/lib/implementation/regexp.js @@ -0,0 +1,65 @@ +// 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. + +function native_JSSyntaxRegExp_firstMatch(str) { + var re = $DartRegExpToJSRegExp(this); + var m = re.exec(str); + if (m != null) { + var match = native_JSSyntaxMatch__new(this, str); + match.match_ = m; + match.lastIndex_ = re.lastIndex; + return match; + } + return $Dart$Null; +} + +function native_JSSyntaxRegExp_hasMatch(str) { + return $DartRegExpToJSRegExp(this).test(str); +} + +function native_JSSyntaxRegExp_stringMatch(str) { + var m = $DartRegExpToJSRegExp(this).exec(str); + return (m != null ? m[0] : $Dart$Null); +} + +function native_JSSyntaxMatch_group(nb) { + return this.match_[nb]; +} + +function native_JSSyntaxMatch_groupCount() { + return this.match_.length; +} + +function native_JSSyntaxMatch_start() { + return this.match_.index; +} + +function native_JSSyntaxMatch_end() { + return this.lastIndex_; +} + +function native__LazyAllMatchesIterator__jsInit(regExp) { + this.re = $DartRegExpToJSRegExp(regExp); +} + +// The given RegExp is only used to initialize a new Match. We use the +// cached JS regexp to compute the next match. +function native__LazyAllMatchesIterator__computeNextMatch(regExp, str) { + var re = this.re; + if (re === null) return $Dart$Null; + var m = re.exec(str); + if (m == null) { + this.re = null; + return $Dart$Null; + } + var match = native_JSSyntaxMatch__new(regExp, str); + match.match_ = m; + match.lastIndex_ = re.lastIndex; + return match; +} + +function $DartRegExpToJSRegExp(exp) { + return new RegExp(native_JSSyntaxRegExp__pattern(exp), + native_JSSyntaxRegExp__flags(exp) + 'g'); +} diff --git a/compiler/lib/implementation/rtt.js b/compiler/lib/implementation/rtt.js new file mode 100644 index 00000000000..68ecb5b3f8d --- /dev/null +++ b/compiler/lib/implementation/rtt.js @@ -0,0 +1,200 @@ +// 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. + +// The following methods are used to handle type information +// + +/** + * @constructor + * @param {string} classkey + * @param {string=} typekey + * @param {Array.=} typeargs + */ +function RTT(classkey, typekey, typeargs) { + this.classKey = classkey; + this.typeKey = typekey ? typekey : classkey; + this.typeArgs = typeargs; + this.implementedTypes = {}; + // Add self + this.implementedTypes[classkey] = this; + // Add Object + if (classkey != 'Object') { + this.implementedTypes['Object'] = RTT.objectType; + } +} + +/** @type {Object.} */ +RTT.types = {}; + +/** @type {Array.} */ +RTT.prototype.derivedTypes = []; + +/** @return {string} */ +RTT.prototype.toString = function() { return this.typeKey; } + +/** + * @param {*} value + * @return {boolean} Whether this type is implemented by the value + */ +RTT.prototype.implementedBy = function(value){ + return (value == null) ? RTT.nullInstanceOf(this) : + this.implementedByType(RTT.getTypeInfo(value)); +}; + +/** + * @param {!RTT} other + * @return {boolean} Whether this type is implement by other + */ +RTT.prototype.implementedByType = function(otherType) { + if (otherType === this || otherType === RTT.dynamicType) { + return true; + } + var targetTypeInfo = otherType.implementedTypes[this.classKey]; + if (targetTypeInfo == null) { + return false; + } + if (targetTypeInfo.typeArgs && this.typeArgs) { + for(var i = this.typeArgs.length - 1; i >= 0; i--) { + if (!this.typeArgs[i].implementedByType(targetTypeInfo.typeArgs[i])) { + return false; + } + } + } + return true; +}; + +/** + * @param {RTT} + * @return {boolean} + */ +RTT.nullInstanceOf = function(type) { + return type === RTT.objectType || type === RTT.dynamicType; +}; + +/** + * @param {*} value The value to retrieve type information for + * @return {RTT} + */ +RTT.getNativeTypeInfo = function(value) { + if (value instanceof Array) return Array.$lookupRTT(); + switch (typeof value) { + case 'string': return String.$lookupRTT(); + case 'number': return Number.$lookupRTT(); + case 'boolean': return Boolean.$lookupRTT(); + } + return RTT.placeholderType; +}; + +/** + * @param {string} name + * @param {function(RTT,Array.)=} implementsSupplier + * @param {Array.=} typeArgs + * @return {RTT} The RTT information object + */ +RTT.create = function(name, implementsSupplier, typeArgs) { + if (name == "Object") return RTT.objectType; + var typekey = RTT.getTypeKey(name, typeArgs); + var rtt = RTT.types[typekey]; + if (rtt) { + return rtt; + } + var classkey = RTT.getTypeKey(name); + rtt = new RTT(classkey, typekey, typeArgs); + RTT.types[typekey] = rtt; + if (implementsSupplier) { + implementsSupplier(rtt, typeArgs); + } + return rtt; +}; + +/** + * @param {string} classkey + * @param {Array.<(RTT|string)>=} typeargs + * @return {string} + */ +RTT.getTypeKey = function(classkey, typeargs) { + var key = classkey; + if (typeargs) { + key += "<" + typeargs.join(",") + ">"; + } + return key; +}; + +/** + * @return {*} value + * @return {RTT} return the RTT information object for the value + */ +RTT.getTypeInfo = function(value) { + return (value.$typeInfo) ? value.$typeInfo : RTT.getNativeTypeInfo(value); +}; + +/** + * @param {Object} o + * @param {RTT} rtt + * Sets the RTT on the object and returns the object itself. + */ +RTT.setTypeInfo = function(o, rtt) { + o.$typeInfo = rtt; + return o; +}; + +/** + * @param {Object} o + * Removes any RTT from the object and returns the object itself. + */ +RTT.removeTypeInfo = function(o) { + o.$typeInfo = null; + return o; +}; + +/** + * The typeArg array is optional + * @param {Array.=} typeArgs + * @param {number} i + * @return {RTT} + */ +RTT.getTypeArg = function(typeArgs,i) { + if (typeArgs) { + if (typeArgs.length > i) { + return typeArgs[i]; + } else { + throw new Error("Missing type arg"); + } + } + return RTT.dynamicType; +}; + +/** + * The typeArg array is optional + * @param {*} o + * @param {string} classkey + * @param {number} i + * @return {Array.} + */ +RTT.getTypeArgsFor = function(o, classkey) { + var rtt = RTT.getTypeInfo(o).implementedTypes[classkey]; + if (!rtt) { + throw new Error("internal error: can not find " + classkey + " in " + JSON.stringify(o)); + } + return rtt.typeArgs; +}; + +// Base types for runtime type information + +/** @type {!RTT} */ +RTT.objectType = new RTT('Object'); +RTT.objectType.implementedBy = function(o) {return true}; +RTT.objectType.implementedByType = function(o) {return true}; + +/** @type {!RTT} */ +RTT.dynamicType = new RTT('Dynamic'); +RTT.dynamicType.implementedBy = function(o) {return true}; +RTT.dynamicType.implementedByType = function(o) {return true}; + +/** @type {!RTT} */ +RTT.placeholderType = new RTT(''); +RTT.placeholderType.implementedBy = function(o) {return true}; +RTT.placeholderType.implementedByType = function(o) {return true}; + + diff --git a/compiler/lib/implementation/string.dart b/compiler/lib/implementation/string.dart new file mode 100644 index 00000000000..9aa63e91a43 --- /dev/null +++ b/compiler/lib/implementation/string.dart @@ -0,0 +1,230 @@ +// 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. + +class StringImplementation implements String native "String" { + factory StringImplementation.fromValues(Array values) { + return _newFromValues(values); + } + + String operator[](int index) { + if (0 <= index && index < length) { + return _indexOperator(index); + } + throw new IndexOutOfRangeException(index); + } + + int charCodeAt(int index) { + if (0 <= index && index < length) { + return _charCodeAt(index); + } + throw new IndexOutOfRangeException(index); + } + + int get length() native; + + bool operator ==(var other) native; + + bool substringMatches(int start, String other) { + int len = length; + int otherLen = other.length; + if (otherLen == 0) return true; + if ((start < 0) || (start >= len)) return false; + if (start + otherLen > len) return false; + StringImplementation otherImpl = other; + for (int i = 0; i < otherLen; i++) { + // We can use the unsafe _charCodeAt. + if (_charCodeAt(start + i) != otherImpl._charCodeAt(i)) return false; + } + return true; + } + + bool endsWith(String other) { + return substringMatches(length - other.length, other); + } + + bool startsWith(String other) { + return substringMatches(0, other); + } + + int indexOf(String other, int startIndex) native; + int lastIndexOf(String other, int fromIndex) native; + + bool isEmpty() { + return length == 0; + } + + String concat(String other) native; + + String operator +(Object obj) { + return this.concat(obj.toString()); + } + + String substring(int startIndex, int endIndex) { + if ((startIndex < 0) || (startIndex > this.length)) { + throw new IndexOutOfRangeException(startIndex); + } + if ((endIndex < 0) || (endIndex > this.length)) { + throw new IndexOutOfRangeException(endIndex); + } + if (startIndex > endIndex) { + throw new IndexOutOfRangeException(startIndex); + } + return _substringUnchecked(startIndex, endIndex); + } + + // TODO(terry): Temporary workaround until substring can support a default + // argument for endIndex (when the VM supports default args). + // This method is a place holder to flag breakage for apps + // that depend on this behavior of substring. + String substringToEnd(int startIndex) { + return this.substring(startIndex, this.length); + } + + String trim() native; + + bool contains(Pattern pattern, int startIndex) { + if (startIndex == null) startIndex = 0; + if (startIndex < 0 || startIndex > length) { + throw new IndexOutOfRangeException(startIndex); + } + if (pattern is String) { + return this.indexOf(pattern, startIndex) != -1; + } else if (pattern is JSSyntaxRegExp) { + JSSyntaxRegExp regExp = pattern; + return regExp.hasMatch(_substringUnchecked(startIndex, length)); + } else { + String substr = _substringUnchecked(startIndex, length); + return !pattern.allMatches(substr).iterator().hasNext(); + } + } + + String replaceFirst(Pattern from, String to) { + if (from is String || from is JSSyntaxRegExp) { + return _replace(from, to); + } else { + // TODO(floitsch): implement generic String.replace (with patterns). + throw "StringImplementation.replace(Pattern) UNIMPLEMENTED"; + } + } + + String replaceAll(Pattern from, String to) { + if (from is String) { + if (from == "") { + if (this == "") { + return to; + } else { + StringBuffer result = new StringBuffer(); + int len = length; + result.add(to); + for (int i = 0; i < len; i++) { + result.add(this[i]); + result.add(to); + } + return result.toString(); + } + } else { + return _replaceAll(from, to); + } + } else if (from is JSSyntaxRegExp) { + return _replaceAll(from, to); + } else { + // TODO(floitsch): implement generic String.replace (with patterns). + throw "StringImplementation.replaceAll(Pattern) UNIMPLEMENTED"; + } + } + + Array split(Pattern pattern) { + if (pattern is String || pattern is JSSyntaxRegExp) { + return _split(pattern); + } else { + throw "StringImplementation.split(Pattern) UNIMPLEMENTED"; + } + } + + Iterable allMatches(String str) { + List result = []; + if (this.isEmpty()) return result; + int length = this.length; + + int ix = 0; + while (ix < str.length) { + int foundIx = str.indexOf(this, ix); + if (foundIx < 0) break; + result.add(new _StringMatch(foundIx, str, this)); + ix = foundIx + length; + } + return result; + } + + Array splitChars() { + return _split(""); + } + + Array charCodes() { + int len = length; + Array result = new Array(len); + for (int i = 0; i < len; i++) { + // It is safe to call the private function (which doesn't do + // range-checks). + result[i] = _charCodeAt(i); + } + return result; + } + + String toLowerCase() native; + String toUpperCase() native; + + int hashCode() native; + + // Note: we can't just return 'this', because we want the primitive string + // and not the wrapped String object. + String toString() native; + + int compareTo(String other) native; + + static String _newFromValues(Array values) native; + String _indexOperator(int index) native; + int _charCodeAt(int index) native; + String _substringUnchecked(int startIndex, int endIndex) native; + String _replace(Pattern from, String to) native; + String _replaceAll(Pattern from, String to) native; + Array _split(Pattern pattern) native; +} + +class _StringJsUtil { + static String toDartString(o) native { + if (o === null) return "null"; + return o.toString(); + } +} + +class _StringMatch implements Match { + const _StringMatch(int this._start, + String this.str, + String this.pattern); + + int start() => _start; + int end() => _start + pattern.length; + String operator[](int g) => group(g); + int groupCount() => 0; + + String group(int group) { + if (group != 0) { + throw new IndexOutOfRangeException(group); + } + return pattern; + } + + Array groups(Array groups) { + Array result = new Array(); + for (int g in groups) { + result.add(group(g)); + } + return result; + } + + final int _start; + final String str; + final String pattern; +} diff --git a/compiler/lib/implementation/string.js b/compiler/lib/implementation/string.js new file mode 100644 index 00000000000..73c49abe64a --- /dev/null +++ b/compiler/lib/implementation/string.js @@ -0,0 +1,141 @@ +// 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. + +/** + * Extend the String prototype with members expected in dart. + */ + +String.$instanceOf = function(obj) { + return typeof obj == 'string' || obj instanceof String; +}; + +function native_StringImplementation__indexOperator(index) { + return this[index]; +} + +function native_StringImplementation__charCodeAt(index) { + return this.charCodeAt(index); +} + +function native_StringImplementation_get$length() { + return this.length; +} + +function native_StringImplementation_EQ(other) { + if (typeof other == 'string') { + return this == other; + } else if (other instanceof String) { + // Must convert other to a primitive for value equality to work. + return this == String(other); + } else { + return false; + } +} + +function native_StringImplementation_indexOf(other, startIndex) { + return this.indexOf(other, startIndex); +} + +function native_StringImplementation_lastIndexOf(other, fromIndex) { + if (other == "") { + return Math.min(this.length, fromIndex); + } + return this.lastIndexOf(other, fromIndex); +} + +function native_StringImplementation_concat(other) { + return this.concat(other); +} + +function native_StringImplementation__substringUnchecked(startIndex, endIndex) { + return this.substring(startIndex, endIndex); +} + +function native_StringImplementation_trim() { + if (this.trim) return this.trim(); + return this.replace(new RegExp("^[\s]+|[\s]+$", "g"), ""); +} + +function native_StringImplementation__replace(from, to) { + if (String.$instanceOf(from)) { + return this.replace(from, to); + } else { + return this.replace($DartRegExpToJSRegExp(from), to); + } +} + +function native_StringImplementation__replaceAll(from, to) { + if (String.$instanceOf(from)) { + var regexp = new RegExp( + from.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'g'); + return this.replace(regexp, to); + } else { + var regexp = $DartRegExpToJSRegExp(from); + return this.replace(regexp, to); + } +} + +function native_StringImplementation__split(pattern) { + if (String.$instanceOf(pattern)) { + return this.split(pattern); + } else { + return this.split($DartRegExpToJSRegExp(pattern)); + } +} + +function native_StringImplementation_toLowerCase() { + return this.toLowerCase(); +} + +function native_StringImplementation_toUpperCase() { + return this.toUpperCase(); +} + +// Inherited from Hashable. +function native_StringImplementation_hashCode() { + if (this.hash_ === undefined) { + for (var i = 0; i < this.length; i++) { + var ch = this.charCodeAt(i); + this.hash_ += ch; + this.hash_ += this.hash_ << 10; + this.hash_ ^= this.hash_ >> 6; + } + + this.hash_ += this.hash_ << 3; + this.hash_ ^= this.hash_ >> 11; + this.hash_ += this.hash_ << 15; + this.hash_ = this.hash_ & ((1 << 29) - 1); + } + return this.hash_; +} + +function native_StringImplementation_toString() { + // Return the primitive string of this String object. + return String(this); +} + +// TODO(floitsch): If we allow comparison operators on the String class we +// should move this function into dart world. +function native_StringImplementation_compareTo(other) { + if (this == other) return 0; + if (this < other) return -1; + return 1; +} + +function native_StringImplementation__newFromValues(array) { + if (!(array instanceof Array)) { + var length = native__ArrayJsUtil__arrayLength(array); + var tmp = new Array(length); + for (var i = 0; i < length; i++) { + tmp[i] = INDEX$operator(array, i); + } + array = tmp; + } + return String.fromCharCode.apply(this, array); +} + +// Deprecated old name of new String.fromValues(..). +function native_StringBase_createFromCharCodes(array) { + return native_StringImplementation__newFromValues(array); +} diff --git a/compiler/lib/implementation/string_base.dart b/compiler/lib/implementation/string_base.dart new file mode 100644 index 00000000000..b75ef1c3344 --- /dev/null +++ b/compiler/lib/implementation/string_base.dart @@ -0,0 +1,23 @@ +// 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. + +class StringBase { + static String createFromCharCodes(Array charCodes) native; + + static String join(Array strings, String separator) { + String s = ""; + for (int i = 0; i < strings.length; i++) { + if (i > 0) { + s = s.concat(separator); + } + s = s.concat(strings[i]); + } + return s; + } + + static String concatAll(Array strings) { + return join(strings, ""); + } + +} diff --git a/compiler/lib/implementation/string_buffer.dart b/compiler/lib/implementation/string_buffer.dart new file mode 100644 index 00000000000..eba52382355 --- /dev/null +++ b/compiler/lib/implementation/string_buffer.dart @@ -0,0 +1,84 @@ +// 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. + +/** + * The StringBuffer class is useful for concatenating strings + * efficiently. Only on a call to [toString] are the strings + * concatenated to a single String. + */ +class StringBufferImpl implements StringBuffer { + /** + * Creates the string buffer with an initial content. + */ + StringBufferImpl([Object content = ""]) { + clear(); + add(content); + } + + /** + * Returns the length of the buffer. + */ + int get length() { + return _length; + } + + bool isEmpty() { + return _length == 0; + } + + /** + * Adds [obj] to the buffer. Returns [this]. + */ + StringBuffer add(Object obj) { + String str = obj.toString(); + if (str === null || str.isEmpty()) return this; + _buffer.add(str); + _length += str.length; + return this; + } + + /** + * Adds all items in [objects] to the buffer. Returns [this]. + */ + StringBuffer addAll(Collection objects) { + for (Object obj in objects) { + add(obj); + } + return this; + } + + /** + * Adds the string representation of [charCode] to the buffer. + * Returns [this]. + */ + StringBuffer addCharCode(int charCode) { + return add(new String.fromCharCodes([charCode])); + } + + /** + * Clears the string buffer. Returns [this]. + */ + StringBuffer clear() { + _buffer = new Array(); + _length = 0; + return this; + } + + /** + * Returns the contents of buffer as a concatenated string. + */ + String toString() { + if (_buffer.length == 0) return ""; + if (_buffer.length == 1) return _buffer[0]; + String result = StringBase.concatAll(_buffer); + _buffer.clear(); + _buffer.add(result); + // Since we track the length at each add operation, there is no + // need to update it in this function. + return result; + } + + Array _buffer; + int _length; +} diff --git a/compiler/lib/implementation/time_zone_implementation.dart b/compiler/lib/implementation/time_zone_implementation.dart new file mode 100644 index 00000000000..8f48b5e8b07 --- /dev/null +++ b/compiler/lib/implementation/time_zone_implementation.dart @@ -0,0 +1,35 @@ +// 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. + +// Dart core library. + +// JavaScript implementation of TimeZoneImplementation. +class TimeZoneImplementation implements TimeZone { + Time get offset() { + if (isUtc) return const Time.duration(0); + throw "Unimplemented"; + } + factory TimeZoneImplementation(Time offset) { + if (offset.duration == 0) { + return const TimeZoneImplementation.utc(); + } else { + throw "Unimplemented"; + } + } + + const TimeZoneImplementation.utc() : this.isUtc = true; + const TimeZoneImplementation.local() : this.isUtc = false; + + bool operator ==(other) { + if (!(other is TimeZoneImplementation)) return false; + return isUtc == other.isUtc; + } + + String toString() { + if (isUtc) return "TimeZone (UTC)"; + return "TimeZone (Local)"; + } + + final bool isUtc; +} diff --git a/compiler/lib/implementation/type_token.dart b/compiler/lib/implementation/type_token.dart new file mode 100644 index 00000000000..6d12be7afb9 --- /dev/null +++ b/compiler/lib/implementation/type_token.dart @@ -0,0 +1,11 @@ +// 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. + +/* + * This class is generic. It's main use is to transfer type information from + * Dart to JS. + */ +class TypeToken { + const TypeToken(); +} diff --git a/compiler/lib/object.dart b/compiler/lib/object.dart new file mode 100644 index 00000000000..1531bd9d385 --- /dev/null +++ b/compiler/lib/object.dart @@ -0,0 +1,23 @@ +// 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. + +// Dart core library. + +class Object native "Object" { + + const Object(); + + bool operator ==(Object other) { + return this === other; + } + + String toString() { + return "Object"; + } + + /** + * Return this object without type information. + */ + get dynamic() { return this; } +} diff --git a/compiler/lib/print.dart b/compiler/lib/print.dart new file mode 100644 index 00000000000..f12083159c2 --- /dev/null +++ b/compiler/lib/print.dart @@ -0,0 +1,19 @@ +// 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. + +// TODO(ngeoffray): define native top-level methods once top-level +// methods work with the resolver. +typedef void _PrintType(arg); + +_PrintType get print() { + return _Logger.print; +} + +class _Logger { + static print(arg) { + _printString((arg === null) ? "null" : arg.toString()); + } + + static void _printString(String str) native; +} diff --git a/compiler/scripts/build_dartc_for_perf_metrics b/compiler/scripts/build_dartc_for_perf_metrics new file mode 100755 index 00000000000..243e0197344 --- /dev/null +++ b/compiler/scripts/build_dartc_for_perf_metrics @@ -0,0 +1,65 @@ +#!/bin/sh +# +# Produce a build of dartc that can be used to measure its performance over +# time. This script needs to be backwards compatible with earlier +# revisions. +# +# TODO: Still need to handle revisions between r2694 and r2764. +# +DARTC_REVISION=$1 +USERNAME=$2 +PASSWORD=$3 + +# Assume that revisions prior to 2694 are handled by the metrics system +# and do not need to be handled here. +if [ $DARTC_REVISION -lt 2694 ] +then + echo "Exiting; don't know how to build revisions prior to r2694" + exit 1 +fi + +# Checkout the depot tools +svn checkout --quiet http://src.chromium.org/svn/trunk/tools/depot_tools depot_tools +GCLIENT_CMD=./depot_tools/gclient + +# Make a gclient with the compiler deps only +$GCLIENT_CMD config svn://svn.chromium.org/dash/trunk/deps/compiler.deps + +# Make sure that we have access +svn ls --username $USERNAME --password "$PASSWORD" svn://svn.chromium.org/dash + +# Try to sync to a particular revision transitively; muffle the output +$GCLIENT_CMD sync -r $DARTC_REVISION -t > gclient.sync.txt + +# Build dartc +cd compiler +../tools/build.py --mode release --arch dartc +rc=$? +if [ $rc -ne 0 ]; then + exit $rc +fi + +# Give the metrics system a backwards compatible way of getting to the +# artifacts that it needs. +cd .. +mkdir -p prebuilt +cd prebuilt +COMPILER_OUTDIR=../compiler/out/Release_dartc +cp -r $COMPILER_OUTDIR/compiler ./compiler + +if [ $DARTC_REVISION -lt 2773 ] +then + GENERATED_SCRIPT_DIR=obj.target/geni +else + # Path for revisions 2773 and later. + GENERATED_SCRIPT_DIR=obj.target/geni/dartc +fi + +PATH_TO_METRICS_SCRIPT=$COMPILER_OUTDIR/$GENERATED_SCRIPT_DIR +cp -r $PATH_TO_METRICS_SCRIPT/dartc_run.sh . +cp -r $PATH_TO_METRICS_SCRIPT/dartc_size.sh . +cp $COMPILER_OUTDIR/d8 . +if [ -e $PATH_TO_METRICS_SCRIPT/dartc_metrics.sh ]; then + cp $PATH_TO_METRICS_SCRIPT/dartc_metrics.sh . +fi + diff --git a/compiler/scripts/compiler_compare.sh b/compiler/scripts/compiler_compare.sh new file mode 100755 index 00000000000..799e829e43c --- /dev/null +++ b/compiler/scripts/compiler_compare.sh @@ -0,0 +1,191 @@ +#!/bin/bash +# +# 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. + +# Compare the current working copy of the repository to either a clean copy +# or the specified revision. +# Along with the files created by compiler_metrics.sh, creates +# compiler_compare[_stats_[12]].log in the working direcetory and a +# out_compile_samples/ directory at the root of the repository, +# which are left around for later examination. +# These files will be destroyed on script re-run. + +APP="" +REV="" +ONE_DELTA="" +BASE_PATH=$(pwd) +SCRIPT_PATH=$(dirname $0) +SCRIPT_PATH=$(cd $SCRIPT_PATH; pwd) +RUNS=10 + +function printHelp() { + exitValue=${1:1} + echo "Compare current changes as they relate to performance with another SVN revision" + echo "" + echo " Usage:" + echo " -a=, --app= The dart app file to test (required)." + echo " -d=, --one-delta= The filename, relative to app, to touch in order to trigger a one-delta compile." + echo " -r=, --revision= The compiler revision to compare against (default to current repository revision)." + echo " --runs= Number of runs to test with, default $RUNS" + echo " --stats Non-destructive display of previous stats" + echo " -h, --help What you see is what you get." + exit $exitValue +} + +function failTest() { + if [ ! $1 -eq 0 ]; then + echo $2 + exit $1 + fi +} + +if [ $# -eq 0 ]; then + printHelp; +fi + +for i in $* +do + case $i in + --one-delta=*|-d=*) + ONE_DELTA=${i#*=} + COMPARE_OPTIONS+="--one-delta=$ONE_DELTA ";; + --app=*|-a=*) + APP=${i#*=} + APP=$( cd "$( dirname "$APP" )" && pwd )/$( basename "$APP");; + --revision=*|-r=*) + REV=${i#*=};; + --help|-h) + printHelp 0;; + --runs=*) + RUNS=${i#*=};; + --stats) + calcStats + exit 0;; + *) + echo "Invalid parameter: $i" + printhelp 1;; + esac +done + +COMPARE_OPTIONS+="-r=$RUNS " + +if [ "" = "$APP" ] || [ ! -r $APP ]; then + echo "Required --app" + printHelp +fi + +RESPONSE[0]="Performance: Better" +RESPONSE[1]="Performance: No Change" +RESPONSE[2]="Performance: Worse" + +# Passed: MAX deviation, Changed Amount +# Returns: Index of RESPONSE +function responseValue() { + ABS=$2 + (( ABS = ABS < 0 ? ABS * -1 : ABS )) + if [ $ABS -gt $1 ]; then + if [ $2 -lt 0 ]; then + return 2 + else + return 0 + fi + fi + return 1 +} + +function calcStat() { + # Assume we're always making improvments, S2 will be presented as a larger value + LINEMATCH_DEV="s/${1}: .*stdev: \([0-9]*\).*/\1/p" + LINEMATCH_VALUE="s/${1}: average: \([0-9]*\).*/\1/p" + S1_DEV=$(sed -n -e "$LINEMATCH_DEV" $STAT1) + if [ "" != "$S1_DEV" ]; then + S2_DEV=$(sed -n -e "$LINEMATCH_DEV" $STAT2) + DEV_MAX=$(( S1_DEV < S2_DEV ? S2_DEV : S1_DEV )) + S1_VALUE=$(sed -n -e "$LINEMATCH_VALUE" $STAT1) + S2_VALUE=$(sed -n -e "$LINEMATCH_VALUE" $STAT2) + DIFF=$(( S2_VALUE - S1_VALUE )) + DIFF_PERCENT=`echo "$DIFF*100/$S2_VALUE" | bc -l | sed 's/\([0-9]*\.[0-9][0-9]\)[0-9]*/\1/'` + responseValue $DEV_MAX $DIFF + echo "$1: Before/After-ms(${S2_VALUE} / ${S1_VALUE}), stdev(${S2_DEV} / ${S1_DEV}), Difference: ${DIFF}ms (${DIFF_PERCENT}%), " ${RESPONSE[$?]} + fi + return 0 +} + +function calcStats() { + calcStat full-compile + calcStat zero-delta-compile + calcStat one-delta-compile +} + +ROOT_OF_REPO=$BASE_PATH +TEST_DIR=$BASE_PATH +while true; do + ls -d .gclient > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "Root found: $ROOT_OF_REPO" + break; + fi + if [ "$TEST_DIR" = "/" ]; then + failTest 1 "Hit the root directory; no .git/ found?!" + fi + ROOT_OF_REPO=$TEST_DIR + cd .. + TEST_DIR=$(pwd) +done + +# Make a temporary directory in the current path and checkout the revision +TMP_DIR=$ROOT_OF_REPO/compiler/tmp_performance_comparisons +mkdir -p $TMP_DIR + +LOG_FILE=$TMP_DIR/compiler_compare.log +STAT1=$TMP_DIR/compiler_compare_stats_1.log +STAT2=$TMP_DIR/compiler_compare_stats_2.log +COMPARE_OPTIONS+="--output=$TMP_DIR " + +# zero out files +echo "" > $LOG_FILE +echo "" > $STAT1 +echo "" > $STAT2 + +# Do the first test +echo "Compiling dartc with your changes (mode=release)" +cd $ROOT_OF_REPO/compiler +gclient runhooks >> $LOG_FILE 2>&1 +../tools/build.py --mode release --arch dartc >> $LOG_FILE 2>&1 +failTest $? "Error compiling your location changes, check $LOG_FILE" + +echo "Running first test against current working copy" +echo $SCRIPT_PATH/compiler_metrics.sh --stats-prefix=yours --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP >> $LOG_FILE +$SCRIPT_PATH/compiler_metrics.sh --stats-prefix=yours --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP > $STAT1 +failTest $? "Error collecting statistics from working copy" + +# switch to tmp for remainder of building +cd $TMP_DIR + +gclient config svn://svn.chromium.org/dash/trunk/deps/compiler.deps >> $LOG_FILE 2>&1 +failTest $? "Error calling gclient config" + +GCLIENT_SYNC="-t " +if [ "" != "$REV" ]; then + GCLIENT_SYNC+="--revision=$REV" +fi + +echo "Checking out clean version of $REV; will take some time. Look at $LOG_FILE for progress" +gclient sync $GCLIENT_SYNC >> $LOG_FILE 2>&1 +failTest $? "Error calling gclient sync" + +echo "Compiler clean version; may take some time" +cd compiler +gclient runhooks >> $LOG_FILE 2>&1 +../tools/build.py --mode release --arch dartc >> $LOG_FILE 2>&1 +failTest $? "Error compiling comparison revision" + +# Do the second test +echo "Running second test against clean copy" +echo $SCRIPT_PATH/compiler_metrics.sh --stats-prefix=clean --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP >> $LOG_FILE +$SCRIPT_PATH/compiler_metrics.sh --stats-prefix=clean --dartc=./out/Release_dartc/dartc $COMPARE_OPTIONS --app=$APP > $STAT2 +failTest $? "Error collecting statistics from clean copy" + +calcStats diff --git a/compiler/scripts/compiler_metrics.sh b/compiler/scripts/compiler_metrics.sh new file mode 100755 index 00000000000..8104d772fc9 --- /dev/null +++ b/compiler/scripts/compiler_metrics.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# +# 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. + +# Collects compiler statics for a given Dart app and spits them out +# Creates sample-full.txt, sample-incr-zero.txt, sample-incr-one.txt, +# and out_compile_samples/ in the working direcetory, which are left +# around for later examination. These files will be destroyed on +# script re-run + +DART_PARAMS="-metrics " +RUNS=3 +ONE_DELTA="" +DARTC=$(which dartc) +SAMPLE_DIR="." +PREFIX="sample" +SCRIPT_DIR=$(dirname $0) + +source $SCRIPT_DIR/metrics_math.sh + +function printHelp() { + exitValue=${1:1} + echo "Generate average and standard deviation compiler stats on a full compile, zero-delta compile," + echo "and optional one-delta compile." + echo "" + echo " Usage:" + echo " -a=, --app= The dart app file to test (required)." + echo " -r=, --runs= Set the number of compile samples to be executed. Defaults to $RUNS" + echo " -d=, --one-delta= The filename, relative to DART_APP, to touch in order to trigger a one-delta compile." + echo " -o=, --output= Directory location of sample storage" + echo " --dartc= Override PATH location for dartc script" + echo " -p, --stats-prefix= Adds prefix to each output file (default: sample-[full|incri-[one|zero]]).txt)" + echo " -h, --help What you see is what you get." + echo " DART_APP Path to dart .app file to compile (depricated)" + exit $exitValue +} + +if [ $# -eq 0 ]; then + printHelp; +fi + +for i in $* +do + case $i in + --runs=*|-r=*) + RUNS=${i#*=};; + --one-delta=*|-d=*) + ONE_DELTA=${i#*=};; + --dartc=*) + DARTC=${i#*=};; + --output=*|-o=*) + SAMPLE_DIR=${i#*=};; + --stats-prefix=*|-p=*) + if [ "" = "${i#*=}" ]; then + echo "prefix cannot be empty" + printHelp 1; + fi + PREFIX=${i#*=};; + --app=*|-a=*) + APP=${i#*=};; + --help|-h) + printHelp 0;; + -*) + echo "Parameter $i not recognized" + printHelp 1;; + *) + break;; + esac +done + +if [ "" = "$DARTC" ] || [ ! -x $DARTC ]; then + echo "Error: Location of 'dartc' not found." + printHelp 1 +fi + +if [ "" = "$SAMPLE_DIR" ] || [ ! -d $SAMPLE_DIR ]; then + echo "Error: Invalid directory for samples location: $SAMPLE_DIR" + printHelp 1 +fi + +OUT_DIR=$SAMPLE_DIR/out_compile_samples +DART_PARAMS+="-out $OUT_DIR " + +if [ "" = "$APP" ]; then + APP=$(echo $@ | sed -n 's/.*\s\(\S*\.app\).*/\1/p') +fi +if [ "" = "$APP" ] || [ ! -r $APP ]; then + echo "Error: Must specify app file, got: $APP" + printHelp 1 +fi + +APP_RELATIVE=`dirname $APP` +if [ "" != "$ONE_DELTA" ]; then + ONE_DELTA="$APP_RELATIVE/$ONE_DELTA" + if [ ! -r $ONE_DELTA ]; then + echo "Error, one_delta file, $ONE_DELTA, does not exist" + printHelp + fi +fi + +SAMPLE_FULL=$SAMPLE_DIR/$PREFIX-full.txt +SAMPLE_INCR_ZERO=$SAMPLE_DIR/$PREFIX-incr-zero.txt +SAMPLE_INCR_ONE=$SAMPLE_DIR/$PREFIX-incr-one.txt + +#clean up +rm -Rf $SAMPLE_FULL $SAMPLE_INCR_ZERO $SAMPLE_INCR_ONE + +for ((i=0;i<$RUNS;i++)) do + echo "Run $i" + rm -Rf $OUT_DIR + $DARTC $DART_PARAMS $APP >> $SAMPLE_FULL + $DARTC $DART_PARAMS $APP >> $SAMPLE_INCR_ZERO + if [ -e $ONE_DELTA ] && [ "" != "$ONE_DELTA" ]; then + touch $ONE_DELTA + $DARTC $DART_PARAMS $APP >> $SAMPLE_INCR_ONE + fi +done + +sample_file "full-compile" "Compile-time-total-ms" "$SAMPLE_FULL" +sample_file "zero-delta-compile" "Compile-time-total-ms" "$SAMPLE_INCR_ZERO" +if [ -e $ONE_DELTA ] && [ "" != "$ONE_DELTA" ]; then + sample_file "one-delta-compile" "Compile-time-total-ms" "$SAMPLE_INCR_ONE" +fi + + diff --git a/compiler/scripts/compiler_series_test.sh b/compiler/scripts/compiler_series_test.sh new file mode 100755 index 00000000000..8d8fdff9074 --- /dev/null +++ b/compiler/scripts/compiler_series_test.sh @@ -0,0 +1,226 @@ +#!/bin/bash +# +# 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. + +# Compares a series of compiler reivsions against a target application for +# statistic collection. Outputs a gnuplot consumable table. + +APP="" +REV="" +ONE_DELTA="" +BASE_PATH=$(pwd) +SCRIPT_PATH=$(dirname $0) +SCRIPT_PATH=$(cd $SCRIPT_PATH; pwd) +RUNS=10 +COUNT=50 +LOW_REV="" + +function printHelp() { + exitValue=${1:1} + echo "Compare performance of multiple compiler revisions against a given target application." + echo "Creates a cache of pre-built compiler revisions in compiler/revs/ for later comparison." + echo "The target output of this script is a gnuplot consumable list of stats (for now), located" + echo "in tmp_performance_comparisons/compiler_plots.dat." + echo "" + echo " Usage:" + echo " -a=, --app= The dart app file to test (required)." + echo " -d=, --one-delta= The filename, relative to app, to touch in order to trigger a one-delta compile." + echo " -r=, --revision= The compiler revision to start comparing against (default to current repository revision)." + echo " -c=, --count= The number of compiler revisions test against. Default 50." + echo " -l=, --low-rev= Alternative to --count; set the lowest revision to run to" + echo " -n=, --runs= How many times each compiler is run against the target application." + echo " -h, --help What you see is what you get." + exit $exitValue +} + +function failTest() { + if [ ! $1 -eq 0 ]; then + echo $2 + exit $1 + fi +} + +RESPONSE[0]="Performance: Better" +RESPONSE[1]="Performance: No Change" +RESPONSE[2]="Performance: Worse" + +function calcStat() { + # Assume we're always making improvments, S2 will be presented as a larger value + LINEMATCH_DEV="s/${1}: .*stdev: \([0-9]*\).*/\1/p" + LINEMATCH_VALUE="s/${1}: average: \([0-9]*\).*/\1/p" + S1_DEV=$(sed -n -e "$LINEMATCH_DEV" $STAT1) + if [ "" != "$S1_DEV" ]; then + S1_VALUE=$(sed -n -e "$LINEMATCH_VALUE" $STAT1) + echo -ne "\t"$S1_VALUE"\t"$S1_DEV >> $PLOTS + else + echo -ne "\t-1\t-1" >> $PLOTS + fi + return 0 +} + +function calcStats() { + echo -n $1 >> $PLOTS + calcStat full-compile + calcStat zero-delta-compile + calcStat one-delta-compile + echo "" >> $PLOTS +} + +if [ $# -eq 0 ]; then + printHelp; +fi + +for i in $* +do + case $i in + --one-delta=*|-d=*) + ONE_DELTA=${i#*=} + COMPARE_OPTIONS+="--one-delta=$ONE_DELTA ";; + --app=*|-a=*) + APP=${i#*=};; + --revision=*|-r=*) + REV=${i#*=};; + --count=*|-c=*) + COUNT=${i#*=} + LOW_REV="";; + --runs=*|-n=*) + RUNS=${i#*=};; + --low-rev=*|-l=*) + LOW_REV=${i#*=} + COUNT=0;; + --help|-h) + printHelp 0;; + *) + echo "Parameter $i not recognized" + printHelp 1;; + esac +done + +COMPARE_OPTIONS+="-r=$RUNS " + +if ((RUNS > 0)); then + if [ "" = "$APP" ] || [ ! -r $APP ]; then + echo "Required --app" " got: $APP" + printHelp 1 + fi + APP=$( cd "$( dirname "$APP" )" && pwd )/$( basename "$APP") + COMPARE_OPTIONS+="--app=$APP " +else + echo "Building up compiler cache" + APP="Compiler cache" +fi + +ROOT_OF_REPO=$BASE_PATH +TEST_DIR=$BASE_PATH +while true; do + ls -d .gclient > /dev/null 2>&1 + if [ $? -eq 0 ]; then + echo "Root found: $ROOT_OF_REPO" + break; + fi + if [ "$TEST_DIR" = "/" ]; then + failTest 1 "Hit the root directory; no .git/ found?!" + fi + ROOT_OF_REPO=$TEST_DIR + cd .. + TEST_DIR=$(pwd) +done + +# Make a temporary directory in the current path and checkout the revision +TMP_DIR=$ROOT_OF_REPO/compiler/tmp_performance_comparisons +mkdir -p $TMP_DIR + +LOG_FILE=$TMP_DIR/compiler_compare.log +PLOTS=$TMP_DIR/compiler_plots.dat +STAT1=$TMP_DIR/compiler_metrics.txt +COMPARE_OPTIONS+="--output=$TMP_DIR " + +# zero out files +echo "" > $LOG_FILE + +# switch to tmp for remainder of building +cd $TMP_DIR +gclient config svn://svn.chromium.org/dash/trunk/deps/compiler.deps >> $LOG_FILE 2>&1 +failTest $? "Error calling gclient config" + +if [ "" == "$REV" ]; then + echo "No revision specified; checking out head for test" + REV=`svn info svn://svn.chromium.org/dash/trunk/deps/compiler.deps | sed -n -e 's/Revision: \([0-9]*\)/\1/p'` + echo "Head revision = $REV" +fi + +function failStats() { + echo -e "$1\t-1\t0\t-1\t0\t-1\t0" >> $PLOTS + return 0; +} + +function compileRevision() { + REVISION=$1 + PREBUILT_DIR=$ROOT_OF_REPO/compiler/revs/$REVISION/prebuilt + PREBUILT_BIN=$PREBUILT_DIR/compiler/bin/dartc + if [ ! -x $PREBUILT_BIN ]; then + echo "No prebuilt, building and caching" + echo "Checking out clean version of $REVISION; will take some time. Look at $LOG_FILE for progress" + date + cd $TMP_DIR + gclient sync -t --revision=$REVISION >> $LOG_FILE 2>&1 + failTest $? "Error calling gclient sync" + echo "Run hooks" + gclient runhooks >> $LOG_FILE 2>&1 + + echo "Compiling clean version of dartc; may take some time" + date + cd compiler + ../tools/build.py --mode release --arch dartc >> $LOG_FILE 2>&1 + if [ ! $? -eq 0 ]; then + echo "error compiling" + failStats $REVISION + return 1; + fi + + # Give the metrics system a backwards compatible way of getting to the + # artifacts that it needs. + cd .. + mkdir -p $ROOT_OF_REPO/compiler/revs/$REVISION/prebuilt + cd $ROOT_OF_REPO/compiler/revs/$REVISION/prebuilt + COMPILER_OUTDIR=$TMP_DIR/compiler/out/Release_dartc + cp -r $COMPILER_OUTDIR/compiler ./compiler + else + echo "Cached prebuilt of $REVISION!" + fi + + # Short circuit if we're just filling in the build cache + if [ $RUNS -eq 0 ]; then + echo "run in compile only mode, no stats generating" + return 0; + fi + + # Do the second test + echo "Running test with dartc $REVISION!" + date + echo $SCRIPT_PATH/compiler_metrics.sh --stats-prefix=$REVISION --dartc=$PREBUILT_DIR/compiler/bin/dartc $COMPARE_OPTIONS >> $LOG_FILE 2>&1 + $SCRIPT_PATH/compiler_metrics.sh --stats-prefix=$REVISION --dartc=$PREBUILT_DIR/compiler/bin/dartc $COMPARE_OPTIONS > $STAT1 + if [ ! $? -eq 0 ]; then + echo "error sampling" + failStats $REVISION + return 2; + fi + + # Output the reivision to the PLOTS file; newline added after stats + calcStats $REVISION +} + +echo -e "#Rev\tFull-ms\tdev\tZeroD\tdev\tOneD\tdev" > $PLOTS +if [ "$LOW_REV" ]; then + COUNT=$(( REV - LOW_REV + 1 )) +else + LOW_REV=$(( REV - COUNT + 1 )) +fi +for (( i = REV ; i >= LOW_REV ; i-- )) +do + echo "["$( basename "$APP")": "$((REV - i + 1))"/"$COUNT", rev:$i]" + compileRevision $i +done + diff --git a/compiler/scripts/dartc.sh b/compiler/scripts/dartc.sh new file mode 100755 index 00000000000..82438929780 --- /dev/null +++ b/compiler/scripts/dartc.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# 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. + +SCRIPT_DIR=$(dirname $0) +DARTC_HOME=$(dirname $SCRIPT_DIR) +DARTC_LIBS=$DARTC_HOME/lib + +if [ -x /usr/libexec/java_home ]; then + export JAVA_HOME=$(/usr/libexec/java_home -v '1.6+') +fi + +exec java $DART_JVMARGS -ea -classpath "@CLASSPATH@" \ + com.google.dart.compiler.DartCompiler $@ diff --git a/compiler/scripts/dartc_build_wrapper.py b/compiler/scripts/dartc_build_wrapper.py new file mode 100644 index 00000000000..e2be328ad00 --- /dev/null +++ b/compiler/scripts/dartc_build_wrapper.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# 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. + +"""Wrapper around dartc for use from GYP.""" + +import optparse +import os +from os import path +import shutil +import subprocess +import sys + + +def _BuildOptions(): + op = optparse.OptionParser(usage='usage: %prog [options] FILE') + op.add_option('--out') + op.add_option('--dartc') + op.add_option('--dartc-option', action='append', dest='dartc_options', + default=[]) + op.add_option('--incremental', action='store_false', + default=os.getenv('DARTC_INCREMENTAL', default=False)) + return op + + +def _ParseOptions(cmd_line): + op = _BuildOptions() + (options, args) = op.parse_args(args=cmd_line) + if not options.out: + op.error('Flag "--out" not provided') + if not options.dartc: + op.error('Flag "--dartc" not provided') + return (options, args) + + +def main(): + (options, args) = _ParseOptions(sys.argv[1:]) + if path.exists(options.out): + is_out_of_date = path.getmtime(options.dartc) > path.getmtime(options.out) + if is_out_of_date or not options.incremental: + print 'Deleting %r.' % options.out + shutil.rmtree(options.out) + command_array = [options.dartc] + command_array.extend(['-out', options.out]) + command_array.extend(options.dartc_options) + command_array.extend(args) + print ' '.join([repr(a) for a in command_array]) + proc = subprocess.Popen(command_array) + proc.communicate() + sys.exit(proc.wait()) + + +if __name__ == '__main__': + main() diff --git a/compiler/scripts/dartc_metrics.sh b/compiler/scripts/dartc_metrics.sh new file mode 100755 index 00000000000..af3e581ab6c --- /dev/null +++ b/compiler/scripts/dartc_metrics.sh @@ -0,0 +1,71 @@ +#!/bin/bash --posix +# +# 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. + +# Compiles either TotalDart or Thump based on benchmark and reports +# the metrics back to the collector. +# Removes 'out' directory if one exists. + +# Determine where the libs are +DARTC_HOME=`readlink -f .` +DIST_DIR=$DARTC_HOME/compiler +DARTC_BIN=$DIST_DIR/bin/dartc + +# A word about directories +# The project directories are now copied before this script runs and we just have to change +# in to the correct sub-directory to compile. We'll send the output of compiles and metrics +# to the script directory instead of poluting the cache. +LAST_ARG=`readlink -f ${!#}` +BENCH_DIR=`dirname $LAST_ARG` + +# Big hack. We assume that the benchmark argument in the list: +# x/y/dart/BenchmarkName.dart +BENCH_NAME=`basename $LAST_ARG .dart` + +# Currently we only benchmark the compilation of two applications; +# Redpill's Thump and Dart's Total. +if [ $BENCH_NAME == "Total" ]; then + cd $BENCH_DIR/samples/total/src/ + APP_FILE=Total.dart + DART_MAIN_FILE=Total.dart +else + if [ $BENCH_NAME == "Thump" ]; then + cd $BENCH_DIR/samples/swarm + APP_FILE=swarm.dart + DART_MAIN_FILE=SwarmApp.dart + else + echo "ERROR: Compilation failed - benchmark ${BENCH_NAME} != Total | Thump" 1>&2 + exit 1 + fi +fi + +DARTC_FLAGS="-metrics -out $DARTC_HOME/out " + +# Warmup period, run the compiler a few times to warm up the os/filesystem/etc +# before collecting metrics +$DARTC_BIN $DARTC_FLAGS -noincremental $APP_FILE > /dev/null 2>&1 +rm -Rf $DARTC_HOME/out + +# Full compile metrics +$DARTC_BIN $DARTC_FLAGS -noincremental $APP_FILE > $DARTC_HOME/build.full.txt + +# Single file delta incremental metrics +touch $DART_MAIN_FILE +$DARTC_BIN $DARTC_FLAGS $APP_FILE > $DARTC_HOME/build.incr.txt + +# Generate output for the metrics collection +SED_FULL_CMD="s/^[^#].*/${BENCH_NAME}-full-&/p" +SED_INCR_CMD="s/^[^#].*/${BENCH_NAME}-incr-&/p" +sed -ne $SED_FULL_CMD $DARTC_HOME/build.full.txt +sed -ne $SED_INCR_CMD $DARTC_HOME/build.incr.txt + +# Cleanup compiled output and metrics captures +rm -rf $DARTC_HOME/out $DARTC_HOME/build.full.txt $DARTC_HOME/build.incr.txt + +if [ ! "$? " = "0 " ]; then + echo "ERROR: Compilation failed." 1>&2 + exit 1 +fi + diff --git a/compiler/scripts/dartc_run.sh b/compiler/scripts/dartc_run.sh new file mode 100755 index 00000000000..1a5d859d222 --- /dev/null +++ b/compiler/scripts/dartc_run.sh @@ -0,0 +1,64 @@ +#!/bin/bash --posix +# +# 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. + + +# Prevent problems where the caller has exported CLASSPATH, causing our +# computed value to be copied into the environment and double-counted +# against the argv limit. +unset CLASSPATH + +# Figure out where the dartc home is +SCRIPT_DIR=`dirname $0` +DARTC_HOME=`cd $SCRIPT_DIR; pwd` +DIST_DIR=$DARTC_HOME/compiler +DARTC_LIBS=$DIST_DIR/lib + +D8_EXEC=${D8_EXEC:-$DARTC_HOME/d8} + +DARTC_FLAGS="--optimize" + +# Make it easy to insert 'set -x' or similar commands when debugging problems with this script. +eval "$JAVA_STUB_DEBUG" + +JVM_FLAGS=${JVM_FLAGS:-"-Dcom.google.dart.runner.d8=$D8_EXEC"} +JVM_FLAGS_CMDLINE="" + +while [ ! -z "$1" ]; do + case "$1" in + --prof) + # Ensure the preset -optimize flag is gone when profiling. + DARTC_FLAGS="--prof" + shift ;; + --debug) + JVM_DEBUG_PORT=${DEFAULT_JVM_DEBUG_PORT:-"5005"} + shift ;; + --debug=*) + JVM_DEBUG_PORT=${1/--debug=/} + shift ;; + --jvm_flags=*) + JVM_FLAGS_CMDLINE="$JVM_FLAGS_CMDLINE ${1/--jvm_flags=/}" + shift ;; + *) break ;; + esac +done + +if [ "$JVM_DEBUG_PORT" != "" ]; then + JVM_DEBUG_SUSPEND=${DEFAULT_JVM_DEBUG_SUSPEND:-"y"} + JVM_DEBUG_FLAGS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=${JVM_DEBUG_SUSPEND},address=${JVM_DEBUG_PORT}" +fi + +shopt -s execfail # Return control to this script if exec fails. +exec $JAVABIN -ea -classpath @CLASSPATH@ \ + ${JVM_DEBUG_FLAGS} \ + ${JVM_FLAGS} \ + ${JVM_FLAGS_CMDLINE} \ + com.google.dart.runner.TestRunner \ + $DARTC_FLAGS \ + "$@" + +echo "ERROR: couldn't exec ${JAVABIN}." 1>&2 + +exit 1 diff --git a/compiler/scripts/dartc_size.sh b/compiler/scripts/dartc_size.sh new file mode 100755 index 00000000000..8dc0571c170 --- /dev/null +++ b/compiler/scripts/dartc_size.sh @@ -0,0 +1,92 @@ +#!/bin/bash --posix +# +# 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. + +# Compiles the given benchmark and reports its size (zipped and unzipped). +# Removes 'out' directory if one exists. + +# Prevent problems where the caller has exported CLASSPATH, causing our +# computed value to be copied into the environment and double-counted +# against the argv limit. +unset CLASSPATH + +# Determine where the libs are +SCRIPT_DIR=`dirname $0` +DARTC_HOME=`cd $SCRIPT_DIR; pwd` +DIST_DIR=$DARTC_HOME/compiler +DARTC_LIBS=$DIST_DIR/lib + +OUT_DIR=out-size +DARTC_FLAGS="-optimize -out $OUT_DIR" + +# Make it easy to insert 'set -x' or similar commands when debugging problems with this script. +eval "$JAVA_STUB_DEBUG" + +JVM_FLAGS=${JVM_FLAGS:-""} +JVM_FLAGS_CMDLINE="" + +while [ ! -z "$1" ]; do + case "$1" in + --debug) + JVM_DEBUG_PORT=${DEFAULT_JVM_DEBUG_PORT:-"5005"} + shift ;; + --debug=*) + JVM_DEBUG_PORT=${1/--debug=/} + shift ;; + --jvm_flags=*) + JVM_FLAGS_CMDLINE="$JVM_FLAGS_CMDLINE ${1/--jvm_flags=/}" + shift ;; + *) break ;; + esac +done + +if [ "$JVM_DEBUG_PORT" != "" ]; then + JVM_DEBUG_SUSPEND=${DEFAULT_JVM_DEBUG_SUSPEND:-"y"} + JVM_DEBUG_FLAGS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=${JVM_DEBUG_SUSPEND},address=${JVM_DEBUG_PORT}" +fi + +# Delete existing out directory. +rm -rf $OUT_DIR + +mkdir $OUT_DIR + +# Big hack. We assume that the benchmark file is the last one in the list and +# lives in a directory called 'dart': +# x/y/dart/BenchmarkName.dart +# We remove everything (including other files) before the benchmark name, and +# then remove the extension. +BENCH_NAME=`echo $@ | sed 's/.*dart\///' | sed 's/.dart//'` + +APP_FILE=`readlink -f $1`; +APP_PATH=$(dirname $APP_FILE) + +$JAVABIN -classpath @CLASSPATH@ \ + ${JVM_DEBUG_FLAGS} \ + ${JVM_FLAGS} \ + ${JVM_FLAGS_CMDLINE} \ + com.google.dart.compiler.DartCompiler \ + $DARTC_FLAGS \ + "$APP_FILE" + +if [ ! "$? " = "0 " ]; then + echo "ERROR: Compilation failed." 1>&2 + exit 1 +fi + +OUT_FILE=`ls $OUT_DIR/file/$APP_PATH/*.opt.js` + +if [ ! "$? " = "0 " ]; then + echo "ERROR: couldn't find generated javascript file." 1>&2 + exit 3 +fi + +NB_APP_JS=`ls $OUT_DIR/file/$APP_PATH/*.opt.js | wc -l` +if [ "$NB_APP_JS" != "1" ]; then + echo "ERROR: more than one *.app.opt.js file." 1>&2 + exit 4 +fi + +echo "$BENCH_NAME-size: " `cat "$OUT_FILE" | wc -c` +echo "$BENCH_NAME-zip-size: " `cat "$OUT_FILE" | gzip | wc -c` diff --git a/compiler/scripts/dartc_test.sh b/compiler/scripts/dartc_test.sh new file mode 100755 index 00000000000..a9d6122d2a9 --- /dev/null +++ b/compiler/scripts/dartc_test.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# 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. + +case $(uname -s) in + *Linux*|*linux*) + OS="linux" + ;; + Darwin) + OS="mac" + ;; + *) + OS="generic" + ;; +esac + +SCRIPT_DIR=$(dirname $0) +DARTC_HOME=$(dirname $SCRIPT_DIR) +DARTC_LIBS=$DARTC_HOME/lib +D8_EXEC=${D8_EXEC:-@D8_EXEC@} +DART_SCRIPT_NAME=${DART_SCRIPT_NAME:-$(basename $0)} + +if [ -x /usr/libexec/java_home ]; then + export JAVA_HOME=$(/usr/libexec/java_home -v '1.6+') +fi + +exec java -ea -Dcom.google.dart.runner.d8="$D8_EXEC" \ + -Dcom.google.dart.runner.progname="$DART_SCRIPT_NAME" \ + -classpath "@CLASSPATH@" \ + com.google.dart.runner.TestRunner $@ diff --git a/compiler/scripts/dartc_wrapper.py b/compiler/scripts/dartc_wrapper.py new file mode 100755 index 00000000000..96ebdf1cb58 --- /dev/null +++ b/compiler/scripts/dartc_wrapper.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# 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. + +import os +import sys + + +SCRIPT_MAP = { + 'dart': 'dartc_test', + 'dartc': 'dartc', +} + + +def Main(): + script_name = os.path.basename(sys.argv[0]) + script_dir = os.path.dirname(sys.argv[0]) + dartc_script = os.path.join(script_dir, 'compiler', 'bin', + SCRIPT_MAP[script_name]) + os.putenv("D8_EXEC", script_dir + "/d8") + os.putenv("DART_SCRIPT_NAME", script_name) + + return os.execv(dartc_script, [dartc_script] + sys.argv[1:]) + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/compiler/scripts/generate_my_projects.py b/compiler/scripts/generate_my_projects.py new file mode 100755 index 00000000000..8846ba14dfc --- /dev/null +++ b/compiler/scripts/generate_my_projects.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# 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. + +import os +import sys + + +def Main(): + def normjoin(*args): + return os.path.normpath(os.path.join(*args)) + + compiler = normjoin(sys.argv[0], os.pardir, os.pardir) + tools = normjoin(compiler, os.pardir, 'tools') + locations = { + 'compiler': compiler, + 'tools': tools, + } + + exit_code = os.system("python %(compiler)s/generate_source_list.py " + "java %(compiler)s/sources java" % locations) + if exit_code: + return exit_code + + + exit_code = os.system("python %(compiler)s/generate_source_list.py " + "javatests %(compiler)s/test_sources javatests" + % locations) + if exit_code: + return exit_code + + exit_code = os.system("python %(compiler)s/generate_source_list.py " + "corelib %(compiler)s/corelib_sources ../corelib/src" + % locations) + if exit_code: + return exit_code + + exit_code = os.system("python %(compiler)s/generate_source_list.py " + "compiler_corelib " + "%(compiler)s/compiler_corelib_sources " + "lib" % locations) + if exit_code: + return exit_code + + exit_code = os.system("python %(compiler)s/generate_source_list.py " + "closure_compiler_src %(compiler)s/closure_compiler_sources " + "../third_party/closure_compiler_src " + "build javadoc test" + % locations) + if exit_code: + return exit_code + + if '--no-gyp' in sys.argv: + print '--no-gyp is deprecated.' + + return exit_code + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/compiler/scripts/metrics_math.sh b/compiler/scripts/metrics_math.sh new file mode 100644 index 00000000000..5bf11047dfe --- /dev/null +++ b/compiler/scripts/metrics_math.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# +# 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. + +# This file is repsonsible for performing the averag and standard deviation +# for a specific metrics over a given set of metrics in a file. +# This should be included in other compiler scripts, or used through +# sample_metrics.sh + +AVERAGE=0 +STDEV=0 +COUNT=0 + +function do_math_return() { + local sum=0 + local count=0 + local time + for time in $2 + do + sum=$(echo "$sum + $time" | bc -l) + (( count++ )) + done + average=$(echo "$sum / $count" | bc -l) + #echo "count=$count, sum=$sum, average=$average" + + #go over the numbers, take the difference from the average and squart, then square root / count. + sum_dev=0 + for time in $2 + do + step=$(echo "$time - $average" | bc -l) + sum_dev=$(echo "$sum_dev + $step^2" | bc -l) + done + + step=$(echo "$sum_dev / $count" | bc -l) + OUT="sqrt(${step})" + standard_dev=$(echo $OUT | bc -l | sed 's/\([0-9]*\.[0-9][0-9]\)[0-9]*/\1/') + average=$(echo $average | sed 's/\([0-9]*\.[0-9][0-9]\)[0-9]*/\1/') + AVERAGE=$average + STDEV=$standard_dev + COUNT=$count +} + +function do_math() { + do_math_return "$1" "$2" + echo "$1: average: $AVERAGE stdev: $STDEV count: $COUNT" +} + +function sample_file_return() { + TIMES=$(sed -n "s/$2\S*\s*:\s*\(\([0-9]*\)\(\.[0-9]*\)\?\)/\1/p" $3) + do_math_return "$1" "$TIMES" + SAMPLE_LINE="$1: average: $AVERAGE stdev: $STDEV count: $COUNT" +} + +function sample_file() { + TIMES=$(sed -n "s/$2\S*\s*:\s*\(\([0-9]*\)\(\.[0-9]*\)\?\)/\1/p" $3) + do_math "$1" "$TIMES" +} diff --git a/compiler/scripts/sample_metrics.sh b/compiler/scripts/sample_metrics.sh new file mode 100755 index 00000000000..11702ff0af1 --- /dev/null +++ b/compiler/scripts/sample_metrics.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. + +# Given a file that contains one or more of DartC metrics, check the +# for a specific metric, compute its average and deviation, and print +# it to the stdout. + +source metrics_math.sh + +if [ ! $# -eq 3 ]; then + echo $(basename $0) "\"OutputHeader\" \"CompilerStatToMatch\" metrics.txt"; + exit 1; +fi + +sample_file "$1" "$2" "$3" diff --git a/compiler/tests/dart/dart.status b/compiler/tests/dart/dart.status new file mode 100644 index 00000000000..28c5f990243 --- /dev/null +++ b/compiler/tests/dart/dart.status @@ -0,0 +1,31 @@ +# 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. + +# These tests are intended for testing dartc specific variants of the language tests. + +prefix dart + + +[ $arch == dartc || $arch == chromium ] +# Legacy test cases superceded by a test in langauage/tests. +# Remove in favor of the language test when that test passes. + + +[ $arch == ia32 || $arch == dartium ] +*: Skip + + +[ $arch == x64 ] +*: Skip + + +[ $arch == simarm ] +*: Skip + + +[ $arch == arm ] +*: Skip + + + diff --git a/compiler/tests/dart/src/TemplateTest.dart b/compiler/tests/dart/src/TemplateTest.dart new file mode 100644 index 00000000000..1f58073dcf1 --- /dev/null +++ b/compiler/tests/dart/src/TemplateTest.dart @@ -0,0 +1,9 @@ +// 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 template file. + +main() { + Expect.equals(true, true); +} diff --git a/compiler/tests/dart/testcfg.py b/compiler/tests/dart/testcfg.py new file mode 100644 index 00000000000..2df39433e47 --- /dev/null +++ b/compiler/tests/dart/testcfg.py @@ -0,0 +1,8 @@ +# 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. + +import testing + +def GetConfiguration(context, root): + return testing.StandardTestConfiguration(context, root) diff --git a/compiler/tests/dartc/dartc.status b/compiler/tests/dartc/dartc.status new file mode 100644 index 00000000000..1ac64403478 --- /dev/null +++ b/compiler/tests/dartc/dartc.status @@ -0,0 +1,35 @@ +# 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. +prefix dartc + + +compiler/vm/*: Skip + + +# Already tested through test.py. +corelib/SharedTests: Skip + + +[ $arch == ia32 ] +*: Skip + + +[ $arch == x64 ] +*: Skip + + +[ $arch == simarm ] +*: Skip + + +[ $arch == arm ] +*: Skip + + +[ $arch == dartium ] +*: Skip + + +[ $arch == chromium ] +*: Skip diff --git a/compiler/tests/dartc/testcfg.py b/compiler/tests/dartc/testcfg.py new file mode 100644 index 00000000000..3597ccffa8c --- /dev/null +++ b/compiler/tests/dartc/testcfg.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# 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. + +import os +import re +import test +import utils + +from os.path import join, exists + +class JUnitTestCase(test.TestCase): + def __init__(self, path, context, classnames, mode, arch): + super(JUnitTestCase, self).__init__(context, path) + self.classnames = classnames + self.mode = mode + self.arch = arch + + def IsBatchable(self): + return False + + def IsNegative(self): + return False + + def GetLabel(self): + return "%s/%s %s" % (self.mode, self.arch, '/'.join(self.path)) + + def GetClassPath(self): + third_party = join(self.context.workspace, 'third_party') + jars = ['args4j/2.0.12/args4j-2.0.12.jar', + 'guava/r09/guava-r09.jar', + 'json/r2_20080312/json.jar', + 'rhino/1_7R3/js.jar', + 'hamcrest/v1_3/hamcrest-core-1.3.0RC2.jar', + 'hamcrest/v1_3/hamcrest-generator-1.3.0RC2.jar', + 'hamcrest/v1_3/hamcrest-integration-1.3.0RC2.jar', + 'hamcrest/v1_3/hamcrest-library-1.3.0RC2.jar', + 'junit/v4_8_2/junit.jar'] + jars = [ join(third_party, jar) for jar in jars ] + buildroot = utils.GetBuildRoot(self.context.os, self.mode, self.arch) + dartc_classes = [ os.path.join(buildroot, 'compiler', 'lib', 'dartc.jar'), + os.path.join(buildroot, 'compiler', 'lib', 'corelib.jar') ] + test_classes = os.path.join(buildroot, 'compiler-tests.jar') + closure_jar = os.path.sep.join([buildroot, 'closure_out', 'compiler.jar']) + return os.path.pathsep.join( + dartc_classes + [test_classes] + [closure_jar] + jars) + + def GetCommand(self): + test_py = join(join(self.context.workspace, 'tools'), 'test.py') + d8 = self.context.GetExecutable(self.mode, self.arch, 'd8') + # Note that it is important to run all the JUnit tests in the same process. + # This way we have a chance of causing problems with static state early. + return ['java', '-ea', '-classpath', self.GetClassPath(), + '-Dcom.google.dart.runner.d8=' + d8, + '-Dcom.google.dart.corelib.SharedTests.test_py=' + test_py, + 'org.junit.runner.JUnitCore'] + self.classnames + + def GetName(self): + return self.path[-1] + + +class JUnitTestConfiguration(test.TestConfiguration): + def __init__(self, context, root): + super(JUnitTestConfiguration, self).__init__(context, root) + + def ListTests(self, current_path, path, mode, arch): + test_path = current_path + ['junit_tests'] + if not self.Contains(path, test_path): + return [] + classes = [] + javatests_path = join(join(join(self.root, '..'), '..'), 'javatests') + javatests_path = os.path.normpath(javatests_path) + for root, dirs, files in os.walk(javatests_path): + if root.endswith('com/google/dart/compiler/vm'): + continue + for f in [x for x in files if self.IsTest(x)]: + classname = [] + classname.extend(root[len(javatests_path) + 1:].split(os.path.sep)) + classname.append(f[:-5]) # Remove .java suffix. + classname = '.'.join(classname) + if classname == 'com.google.dart.corelib.SharedTests': + continue + classes.append(classname) + return [JUnitTestCase(test_path, self.context, classes, mode, arch)] + + def IsTest(self, name): + return name.endswith('Tests.java') + + def GetTestStatus(self, sections, defs): + status = join(self.root, 'dartc.status') + if exists(status): + test.ReadConfigurationInto(status, sections, defs) + + +def GetConfiguration(context, root): + return JUnitTestConfiguration(context, root)