From 845189ca287c96b864bd1442fe31e591a9c6f883 Mon Sep 17 00:00:00 2001 From: develar Date: Sun, 30 Sep 2012 12:36:34 +0400 Subject: [PATCH] move from my kotlin fork --- .idea/.name | 1 + .idea/artifacts/dart_ast_jar.xml | 10 + .idea/compiler.xml | 23 + .idea/encodings.xml | 5 + .idea/inspectionProfiles/Project_Default.xml | 58 + .../inspectionProfiles/profiles_settings.xml | 7 + .idea/misc.xml | 10 + .idea/modules.xml | 9 + .idea/scopes/scope_settings.xml | 5 + .idea/uiDesigner.xml | 125 ++ .idea/vcs.xml | 7 + former-dast-ast.iml | 27 + src/com/google/dart/compiler/License.txt | 5 + src/com/google/dart/compiler/Source.java | 45 + .../js/JsConstructExpressionVisitor.java | 104 ++ .../backend/js/JsFirstExpressionVisitor.java | 134 ++ .../backend/js/JsPrecedenceVisitor.java | 280 ++++ .../backend/js/JsRequiresSemiVisitor.java | 158 ++ .../backend/js/JsReservedIdentifiers.java | 151 ++ .../backend/js/JsSourceGenerationVisitor.java | 37 + .../js/JsToStringGenerationVisitor.java | 1306 +++++++++++++++++ .../backend/js/ast/CanBooleanEval.java | 13 + .../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 | 71 + .../backend/js/ast/JsBinaryOperation.java | 100 ++ .../backend/js/ast/JsBinaryOperator.java | 115 ++ .../dart/compiler/backend/js/ast/JsBlock.java | 58 + .../dart/compiler/backend/js/ast/JsBreak.java | 33 + .../dart/compiler/backend/js/ast/JsCase.java | 39 + .../dart/compiler/backend/js/ast/JsCatch.java | 66 + .../compiler/backend/js/ast/JsCatchScope.java | 36 + .../backend/js/ast/JsConditional.java | 77 + .../compiler/backend/js/ast/JsContext.java | 27 + .../compiler/backend/js/ast/JsContinue.java | 42 + .../compiler/backend/js/ast/JsDebugger.java | 25 + .../compiler/backend/js/ast/JsDefault.java | 28 + .../compiler/backend/js/ast/JsDoWhile.java | 31 + .../dart/compiler/backend/js/ast/JsEmpty.java | 25 + .../compiler/backend/js/ast/JsExprStmt.java | 32 + .../compiler/backend/js/ast/JsExpression.java | 25 + .../backend/js/ast/JsExpressionImpl.java | 27 + .../dart/compiler/backend/js/ast/JsFor.java | 99 ++ .../dart/compiler/backend/js/ast/JsForIn.java | 71 + .../compiler/backend/js/ast/JsFunction.java | 112 ++ .../backend/js/ast/JsGlobalBlock.java | 19 + .../dart/compiler/backend/js/ast/JsIf.java | 69 + .../compiler/backend/js/ast/JsInvocation.java | 83 ++ .../dart/compiler/backend/js/ast/JsLabel.java | 52 + .../compiler/backend/js/ast/JsLiteral.java | 100 ++ .../dart/compiler/backend/js/ast/JsName.java | 53 + .../compiler/backend/js/ast/JsNameRef.java | 125 ++ .../dart/compiler/backend/js/ast/JsNew.java | 66 + .../dart/compiler/backend/js/ast/JsNode.java | 9 + .../compiler/backend/js/ast/JsNodeImpl.java | 41 + .../backend/js/ast/JsNullLiteral.java | 41 + .../backend/js/ast/JsNumberLiteral.java | 78 + .../backend/js/ast/JsObjectLiteral.java | 83 ++ .../compiler/backend/js/ast/JsOperator.java | 28 + .../compiler/backend/js/ast/JsParameter.java | 39 + .../backend/js/ast/JsPostfixOperation.java | 42 + .../backend/js/ast/JsPrefixOperation.java | 69 + .../compiler/backend/js/ast/JsProgram.java | 128 ++ .../backend/js/ast/JsProgramFragment.java | 34 + .../backend/js/ast/JsPropertyInitializer.java | 54 + .../compiler/backend/js/ast/JsRegExp.java | 64 + .../compiler/backend/js/ast/JsReturn.java | 43 + .../compiler/backend/js/ast/JsRootScope.java | 42 + .../dart/compiler/backend/js/ast/JsScope.java | 176 +++ .../compiler/backend/js/ast/JsStatement.java | 8 + .../backend/js/ast/JsStringLiteral.java | 53 + .../compiler/backend/js/ast/JsSwitch.java | 47 + .../backend/js/ast/JsSwitchMember.java | 24 + .../dart/compiler/backend/js/ast/JsThrow.java | 37 + .../dart/compiler/backend/js/ast/JsTry.java | 66 + .../backend/js/ast/JsUnaryOperation.java | 53 + .../backend/js/ast/JsUnaryOperator.java | 78 + .../backend/js/ast/JsValueLiteral.java | 23 + .../dart/compiler/backend/js/ast/JsVars.java | 137 ++ .../compiler/backend/js/ast/JsVisitable.java | 18 + .../compiler/backend/js/ast/JsVisitor.java | 435 ++++++ .../dart/compiler/backend/js/ast/JsWhile.java | 51 + .../compiler/backend/js/ast/NodeKind.java | 50 + .../dart/compiler/common/AbstractNode.java | 57 + .../dart/compiler/common/HasSourceInfo.java | 55 + .../dart/compiler/common/HasSymbol.java | 16 + .../dart/compiler/common/SourceInfo.java | 59 + .../dart/compiler/common/SourceInfoImpl.java | 44 + .../google/dart/compiler/common/Symbol.java | 11 + .../google/dart/compiler/util/AstUtil.java | 49 + src/com/google/dart/compiler/util/Lists.java | 267 ++++ src/com/google/dart/compiler/util/Maps.java | 41 + .../google/dart/compiler/util/TextOutput.java | 38 + .../dart/compiler/util/TextOutputImpl.java | 174 +++ 96 files changed, 7200 insertions(+) create mode 100644 .idea/.name create mode 100644 .idea/artifacts/dart_ast_jar.xml create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 former-dast-ast.iml create mode 100644 src/com/google/dart/compiler/License.txt create mode 100644 src/com/google/dart/compiler/Source.java create mode 100644 src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java create mode 100644 src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java create mode 100644 src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java create mode 100644 src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java create mode 100644 src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java create mode 100644 src/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java create mode 100644 src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/HasArguments.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/HasCondition.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/HasName.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsBlock.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsBreak.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsCase.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsCatch.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsConditional.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsContext.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsContinue.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsDebugger.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsDefault.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsEmpty.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsExprStmt.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsExpression.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsExpressionImpl.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsFor.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsForIn.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsFunction.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsIf.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsInvocation.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsLabel.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsLiteral.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsName.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsNameRef.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsNew.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsNode.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsNodeImpl.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsOperator.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsParameter.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsProgram.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsRegExp.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsReturn.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsRootScope.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsScope.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsStatement.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsSwitch.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsThrow.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsTry.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsVars.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsVisitable.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsVisitor.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/JsWhile.java create mode 100644 src/com/google/dart/compiler/backend/js/ast/NodeKind.java create mode 100644 src/com/google/dart/compiler/common/AbstractNode.java create mode 100644 src/com/google/dart/compiler/common/HasSourceInfo.java create mode 100644 src/com/google/dart/compiler/common/HasSymbol.java create mode 100644 src/com/google/dart/compiler/common/SourceInfo.java create mode 100644 src/com/google/dart/compiler/common/SourceInfoImpl.java create mode 100644 src/com/google/dart/compiler/common/Symbol.java create mode 100644 src/com/google/dart/compiler/util/AstUtil.java create mode 100644 src/com/google/dart/compiler/util/Lists.java create mode 100644 src/com/google/dart/compiler/util/Maps.java create mode 100644 src/com/google/dart/compiler/util/TextOutput.java create mode 100644 src/com/google/dart/compiler/util/TextOutputImpl.java diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000000..a02436dd37e --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +former-dast-ast \ No newline at end of file diff --git a/.idea/artifacts/dart_ast_jar.xml b/.idea/artifacts/dart_ast_jar.xml new file mode 100644 index 00000000000..501533125dd --- /dev/null +++ b/.idea/artifacts/dart_ast_jar.xml @@ -0,0 +1,10 @@ + + + $PROJECT_DIR$/out/artifacts + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000000..217af471a9e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 00000000000..e206d70d859 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000000..9ff76d8c843 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,58 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000000..3b312839bf2 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000000..97320410eec --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000000..9d41cd5a8e0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 00000000000..922003b8433 --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 00000000000..3b000203088 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000000..275077f8255 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/former-dast-ast.iml b/former-dast-ast.iml new file mode 100644 index 00000000000..9d6c93e3d36 --- /dev/null +++ b/former-dast-ast.iml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/google/dart/compiler/License.txt b/src/com/google/dart/compiler/License.txt new file mode 100644 index 00000000000..eb4e1abc135 --- /dev/null +++ b/src/com/google/dart/compiler/License.txt @@ -0,0 +1,5 @@ +Google Dart Js backend was removed - https://code.google.com/p/dart/source/detail?r=4771 + +According to http://www.apache.org/legal/3party.html we can include "Google Dart Js backend" in source form, because code license is "New BSD License" (Authorized License). + +This part of code will be removed when kotlin will be rewritten on kotlin. diff --git a/src/com/google/dart/compiler/Source.java b/src/com/google/dart/compiler/Source.java new file mode 100644 index 00000000000..f0ff5797f73 --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java b/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java new file mode 100644 index 00000000000..cb3df42ca9b --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.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.dart.compiler.backend.js.ast.*; + +/** + * 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/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java b/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java new file mode 100644 index 00000000000..0ae823c60e5 --- /dev/null +++ b/src/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:
+ *

+ *

+ * The following do not require parentheses:
+ *

+ */ +public class JsFirstExpressionVisitor extends JsVisitor { + public static boolean exec(JsExprStmt statement) { + JsExpression expression = statement.getExpression(); + // Pure function declarations do not need parentheses + if (expression instanceof JsFunction) { + return false; + } + + JsFirstExpressionVisitor visitor = new JsFirstExpressionVisitor(); + 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/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java b/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java new file mode 100644 index 00000000000..a9632298819 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java @@ -0,0 +1,280 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.*; +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(JsLiteral.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.JsIntLiteral x, JsContext ctx) { + answer = 17; // primary + return false; + } + + @Override + public boolean visit(JsNumberLiteral.JsDoubleLiteral 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(JsLiteral.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/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java b/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java new file mode 100644 index 00000000000..bd13ea3af72 --- /dev/null +++ b/src/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:
+ * + * + * The following do not require semicolons:
+ * + */ +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.getThenStatement(); + JsStatement elseStmt = x.getElseStatement(); + 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/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java b/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java new file mode 100644 index 00000000000..da462ef29f4 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.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.backend.js; + +import gnu.trove.THashSet; + +import java.util.Collections; +import java.util.Set; + +/** + * Determines whether or not a particular string is a JavaScript keyword or not. + */ +public class JsReservedIdentifiers { + public static final Set reservedGlobalSymbols; + + static { + 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", + }; + + reservedGlobalSymbols = new THashSet(commonBuiltins.length); + Collections.addAll(reservedGlobalSymbols, commonBuiltins); + } + + private JsReservedIdentifiers() { + } +} diff --git a/src/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java b/src/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.java new file mode 100644 index 00000000000..de88bdb7b74 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/JsSourceGenerationVisitor.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.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 program, JsContext ctx) { + 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/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java b/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java new file mode 100644 index 00000000000..50d29b64e08 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java @@ -0,0 +1,1306 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.*; +import com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import com.google.dart.compiler.util.TextOutput; +import gnu.trove.TIntArrayList; + +import java.util.*; + +import static com.google.dart.compiler.backend.js.ast.JsNumberLiteral.JsDoubleLiteral; +import static com.google.dart.compiler.backend.js.ast.JsNumberLiteral.JsIntLiteral; + +/** + * 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; + + public static CharSequence javaScriptString(String value) { + return javaScriptString(value, false); + } + + /** + * Generate JavaScript code that evaluates to the supplied string. Adapted + * from {@link org.mozilla.javascript.ScriptRuntime#escapeString(String)} + * . The difference is that we quote with either " or ' depending on + * which one is used less inside the string. + */ + @SuppressWarnings({"ConstantConditions", "UnnecessaryFullyQualifiedName", "JavadocReference"}) + public static CharSequence javaScriptString(CharSequence chars, boolean forceDoubleQuote) { + final int n = chars.length(); + int quoteCount = 0; + int aposCount = 0; + + for (int i = 0; i < n; i++) { + switch (chars.charAt(i)) { + case '"': + ++quoteCount; + break; + case '\'': + ++aposCount; + break; + } + } + + StringBuilder result = new StringBuilder(n + 16); + + char quoteChar = (quoteCount < aposCount || forceDoubleQuote) ? '"' : '\''; + result.append(quoteChar); + + for (int i = 0; i < n; i++) { + char c = chars.charAt(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.charAt(i + 1) < '0' || chars.charAt(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); + return result; + } + + /** + * 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(StringBuilder str) { + if (str == null) { + return; + } + + int index = 0; + while ((index = str.indexOf(" globalBlocks = new HashSet(); + private final TextOutput p; + private TIntArrayList statementEnds = new TIntArrayList(); + private TIntArrayList statementStarts = new TIntArrayList(); + + public JsToStringGenerationVisitor(TextOutput out) { + this.p = out; + } + + @Override + public boolean visit(JsArrayAccess x, JsContext ctx) { + JsExpression arrayExpr = x.getArrayExpr(); + printPair(x, arrayExpr); + _lsquare(); + accept(x.getIndexExpr()); + _rsquare(); + return false; + } + + @Override + public boolean visit(JsArrayLiteral x, JsContext ctx) { + _lsquare(); + printExpressions(x.getExpressions()); + _rsquare(); + return false; + } + + private void printExpressions(List expressions) { + boolean sep = false; + for (JsExpression arg : expressions) { + sep = sepCommaOptSpace(sep); + boolean isEnclosed = parenPushIfCommaExpression(arg); + accept(arg); + if (isEnclosed) { + rightParen(); + } + } + } + + @Override + public boolean visit(JsBinaryOperation binaryOperation, JsContext ctx) { + JsBinaryOperator op = binaryOperation.getOperator(); + JsExpression arg1 = binaryOperation.getArg1(); + boolean isExpressionEnclosed = _parenPush(binaryOperation, arg1, !op.isLeftAssociative()); + + accept(arg1); + if (op.isKeyword()) { + _parenPopOrSpace(binaryOperation, arg1, !op.isLeftAssociative()); + } + else if (op != JsBinaryOperator.COMMA) { + if (isExpressionEnclosed) { + rightParen(); + } + spaceOpt(); + } + + p.print(op.getSymbol()); + + JsExpression arg2 = binaryOperation.getArg2(); + boolean isParenOpened; + if (op == JsBinaryOperator.COMMA) { + isParenOpened = false; + spaceOpt(); + } + else if (arg2 instanceof JsBinaryOperation && isBinaryOperationShouldBeEnclosed(((JsBinaryOperation) arg2).getOperator())) { + spaceOpt(); + leftParen(); + isParenOpened = true; + } + else { + if (spaceCalc(op, arg2)) { + isParenOpened = _parenPushOrSpace(binaryOperation, arg2, op.isLeftAssociative()); + } + else { + spaceOpt(); + isParenOpened = _parenPush(binaryOperation, arg2, op.isLeftAssociative()); + } + } + accept(arg2); + if (isParenOpened) { + rightParen(); + } + + return false; + } + + private static boolean isBinaryOperationShouldBeEnclosed(JsBinaryOperator operator) { + return operator == JsBinaryOperator.AND; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + printJsBlock(x, true, true); + return false; + } + + @Override + public boolean visit(JsLiteral.JsBooleanLiteral x, JsContext ctx) { + if (x.getValue()) { + p.print(CHARS_TRUE); + } + else { + _false(); + } + return false; + } + + @Override + public boolean visit(JsBreak x, JsContext ctx) { + _break(); + continueOrBreakLabel(x); + return false; + } + + @Override + public boolean visit(JsContinue x, JsContext ctx) { + _continue(); + continueOrBreakLabel(x); + return false; + } + + private void continueOrBreakLabel(JsContinue x) { + JsNameRef label = x.getLabel(); + if (label != null) { + space(); + _nameRef(label); + } + } + + @Override + public boolean visit(JsCase x, JsContext ctx) { + _case(); + space(); + accept(x.getCaseExpr()); + _colon(); + newlineOpt(); + + printSwitchMemberStatements(x); + return false; + } + + private void printSwitchMemberStatements(JsSwitchMember x) { + indent(); + for (JsStatement stmt : x.getStatements()) { + needSemi = true; + accept(stmt); + if (needSemi) { + semi(); + } + newlineOpt(); + } + outdent(); + needSemi = false; + } + + @Override + public boolean visit(JsCatch x, JsContext ctx) { + spaceOpt(); + _catch(); + spaceOpt(); + leftParen(); + nameDef(x.getParameter().getName()); + + // Optional catch condition. + // + JsExpression catchCond = x.getCondition(); + if (catchCond != null) { + space(); + _if(); + space(); + accept(catchCond); + } + + rightParen(); + 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. + printPair(x, x.getTestExpression()); + spaceOpt(); + questionMark(); + spaceOpt(); + printPair(x, x.getThenExpression()); + spaceOpt(); + _colon(); + spaceOpt(); + printPair(x, x.getElseExpression()); + return false; + } + + private void printPair(JsExpression parent, JsExpression expression) { + boolean isNeedParen = parenCalc(parent, expression, false); + if (isNeedParen) { + leftParen(); + } + accept(expression); + if (isNeedParen) { + rightParen(); + } + } + + @Override + public boolean visit(JsDebugger x, JsContext ctx) { + _debugger(); + return false; + } + + @Override + public boolean visit(JsDefault x, JsContext ctx) { + _default(); + _colon(); + + printSwitchMemberStatements(x); + return false; + } + + @Override + public boolean visit(JsWhile x, JsContext ctx) { + _while(); + spaceOpt(); + leftParen(); + accept(x.getCondition()); + rightParen(); + _nestedPush(x.getBody()); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + @Override + public boolean visit(JsDoWhile x, JsContext ctx) { + _do(); + _nestedPush(x.getBody()); + accept(x.getBody()); + _nestedPop(x.getBody()); + if (needSemi) { + semi(); + newlineOpt(); + } + else { + spaceOpt(); + needSemi = true; + } + _while(); + spaceOpt(); + leftParen(); + accept(x.getCondition()); + rightParen(); + 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) { + leftParen(); + } + accept(x.getExpression()); + if (surroundWithParentheses) { + rightParen(); + } + return false; + } + + @Override + public boolean visit(JsFor x, JsContext ctx) { + _for(); + spaceOpt(); + leftParen(); + + // 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()); + } + + rightParen(); + _nestedPush(x.getBody()); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + @Override + public boolean visit(JsForIn x, JsContext ctx) { + _for(); + spaceOpt(); + leftParen(); + + 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()); + + rightParen(); + _nestedPush(x.getBody()); + accept(x.getBody()); + _nestedPop(x.getBody()); + return false; + } + + @Override + public boolean visit(JsFunction x, JsContext ctx) { + _function(); + space(); + if (x.getName() != null) { + nameOf(x); + } + + leftParen(); + boolean sep = false; + for (Object element : x.getParameters()) { + JsParameter param = (JsParameter) element; + sep = sepCommaOptSpace(sep); + accept(param); + } + rightParen(); + space(); + + lineBreakAfterBlock = false; + accept(x.getBody()); + needSemi = true; + return false; + } + + @Override + public boolean visit(JsIf x, JsContext ctx) { + _if(); + spaceOpt(); + leftParen(); + accept(x.getIfExpression()); + rightParen(); + JsStatement thenStmt = x.getThenStatement(); + _nestedPush(thenStmt); + accept(thenStmt); + _nestedPop(thenStmt); + JsStatement elseStmt = x.getElseStatement(); + if (elseStmt != null) { + if (needSemi) { + semi(); + newlineOpt(); + } + else { + spaceOpt(); + needSemi = true; + } + _else(); + boolean elseIf = elseStmt instanceof JsIf; + if (!elseIf) { + _nestedPush(elseStmt); + } + else { + space(); + } + accept(elseStmt); + if (!elseIf) { + _nestedPop(elseStmt); + } + } + return false; + } + + @Override + public boolean visit(JsInvocation x, JsContext ctx) { + JsExpression qualifier = x.getQualifier(); + printPair(x, qualifier); + + leftParen(); + printExpressions(x.getArguments()); + rightParen(); + 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. + */ + leftParen(); + } + accept(q); + if (q instanceof JsNumberLiteral) { + rightParen(); + } + 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) { + leftParen(); + } + accept(ctorExpr); + if (needsParens) { + rightParen(); + } + + leftParen(); + printExpressions(x.getArguments()); + rightParen(); + + return false; + } + + @Override + public boolean visit(JsNullLiteral x, JsContext ctx) { + _null(); + return false; + } + + @Override + public boolean visit(JsIntLiteral x, JsContext ctx) { + p.print(x.value); + return false; + } + + @Override + public boolean visit(JsDoubleLiteral x, JsContext ctx) { + p.print(x.value); + return false; + } + + @Override + public boolean visit(JsObjectLiteral objectLiteral, JsContext context) { + _lbrace(); + if (objectLiteral.isMultiline()) { + p.indentIn(); + } + + boolean isNotFirst = false; + for (JsPropertyInitializer item : objectLiteral.getPropertyInitializers()) { + if (isNotFirst) { + p.print(','); + } + + if (objectLiteral.isMultiline()) { + newlineOpt(); + } + else if (isNotFirst) { + spaceOpt(); + } + + isNotFirst = true; + + JsExpression labelExpr = item.getLabelExpr(); + // labels can be either string, integral, or decimal literals + if (labelExpr instanceof JsNameRef) { + p.print(((JsNameRef) labelExpr).getIdent()); + } + else if (labelExpr instanceof JsStringLiteral) { + p.print(((JsStringLiteral) labelExpr).getValue()); + } + else { + accept(labelExpr); + } + + _colon(); + space(); + JsExpression valueExpr = item.getValueExpr(); + boolean wasEnclosed = parenPushIfCommaExpression(valueExpr); + accept(valueExpr); + if (wasEnclosed) { + rightParen(); + } + } + + if (objectLiteral.isMultiline()) { + p.indentOut(); + newlineOpt(); + } + + _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) + printPair(x, arg); + 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) + printPair(x, arg); + 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(); + leftParen(); + accept(x.getExpr()); + rightParen(); + spaceOpt(); + _blockOpen(); + acceptList(x.getCases()); + _blockClose(); + return false; + } + + @Override + public boolean visit(JsLiteral.JsThisRef x, JsContext ctx) { + p.print(CHARS_THIS); + return false; + } + + @Override + public boolean visit(JsThrow x, JsContext ctx) { + p.print(CHARS_THROW); + space(); + accept(x.getExpression()); + return false; + } + + @Override + public boolean visit(JsTry x, JsContext ctx) { + p.print(CHARS_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 var, JsContext ctx) { + nameOf(var); + JsExpression initExpr = var.getInitExpression(); + if (initExpr != null) { + spaceOpt(); + assignment(); + spaceOpt(); + boolean isEnclosed = parenPushIfCommaExpression(initExpr); + accept(initExpr); + if (isEnclosed) { + rightParen(); + } + } + return false; + } + + @Override + public boolean visit(JsVars vars, JsContext context) { + var(); + space(); + boolean sep = false; + for (JsVar var : vars) { + if (sep) { + if (vars.isMultiline()) { + newlineOpt(); + } + p.print(','); + spaceOpt(); + } + else { + sep = true; + } + + accept(var); + } + return false; + } + + protected void _newline() { + p.newline(); + } + + protected void newlineOpt() { + p.newlineOpt(); + } + + protected void printJsBlock(JsBlock x, boolean truncate, boolean finalNewline) { + if (!lineBreakAfterBlock) { + finalNewline = false; + lineBreakAfterBlock = true; + } + + boolean needBraces = !x.isGlobalBlock(); + if (needBraces) { + _blockOpen(); + } + + int count = 0; + Iterator iterator = x.getStatements().iterator(); + while (iterator.hasNext()) { + boolean isGlobal = x.isGlobalBlock() || globalBlocks.contains(x); + + if (truncate && count > JSBLOCK_LINES_TO_PRINT) { + p.print("[...]"); + newlineOpt(); + break; + } + JsStatement statement = iterator.next(); + if (statement instanceof JsEmpty) { + continue; + } + + needSemi = true; + boolean shouldRecordPositions = isGlobal && !(statement instanceof JsBlock); + boolean stmtIsGlobalBlock = false; + if (isGlobal) { + if (statement instanceof JsBlock) { + // A block inside a global block is still considered global + stmtIsGlobalBlock = true; + globalBlocks.add((JsBlock) statement); + } + } + if (shouldRecordPositions) { + statementStarts.add(p.getPosition()); + } + accept(statement); + if (stmtIsGlobalBlock) { + //noinspection SuspiciousMethodCalls + globalBlocks.remove(statement); + } + if (needSemi) { + /* + * Special treatment of function declarations: 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 = + statement instanceof JsExprStmt && ((JsExprStmt) statement).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 = !iterator.hasNext() && needBraces && !JsRequiresSemiVisitor.exec(statement); + 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()); + } + ++count; + } + + 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 leftParen() { + p.print('('); + } + + private void _lsquare() { + p.print('['); + } + + private void nameDef(JsName name) { + p.print(name.getIdent()); + } + + private void nameOf(HasName hasName) { + nameDef(hasName.getName()); + } + + private void _nameRef(JsNameRef nameRef) { + p.print(nameRef.getIdent()); + } + + private boolean _nestedPop(JsStatement statement) { + boolean pop = !(statement instanceof JsBlock); + if (pop) { + p.indentOut(); + } + return pop; + } + + private boolean _nestedPush(JsStatement statement) { + boolean push = !(statement instanceof JsBlock); + if (push) { + newlineOpt(); + p.indentIn(); + } + else { + spaceOpt(); + } + return push; + } + + private void _new() { + p.print(CHARS_NEW); + } + + private void _null() { + p.print(CHARS_NULL); + } + + private static 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) { + rightParen(); + } + return doPop; + } + + private boolean _parenPopOrSpace(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPop = parenCalc(parent, child, wrongAssoc); + if (doPop) { + rightParen(); + } + else { + space(); + } + return doPop; + } + + private boolean _parenPush(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPush = parenCalc(parent, child, wrongAssoc); + if (doPush) { + leftParen(); + } + return doPush; + } + + private boolean parenPushIfCommaExpression(JsExpression x) { + boolean doPush = x instanceof JsBinaryOperation && ((JsBinaryOperation) x).getOperator() == JsBinaryOperator.COMMA; + if (doPush) { + leftParen(); + } + return doPush; + } + + private boolean _parenPushOrSpace(JsExpression parent, JsExpression child, boolean wrongAssoc) { + boolean doPush = parenCalc(parent, child, wrongAssoc); + if (doPush) { + leftParen(); + } + else { + space(); + } + return doPush; + } + + private void questionMark() { + p.print('?'); + } + + private void _rbrace() { + p.print('}'); + } + + private void _return() { + p.print(CHARS_RETURN); + } + + private void rightParen() { + 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 static 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 && (op == JsBinaryOperator.SUB || op == JsUnaryOperator.NEG)) { + if (arg instanceof JsIntLiteral) { + return ((JsIntLiteral) arg).value < 0; + } + else { + assert arg instanceof JsDoubleLiteral; + //noinspection CastConflictsWithInstanceof + return ((JsDoubleLiteral) arg).value < 0; + } + } + return false; + } + + private void spaceOpt() { + p.printOpt(' '); + } + + private void _switch() { + p.print(CHARS_SWITCH); + } + + private void var() { + p.print(CHARS_VAR); + } + + private void _while() { + p.print(CHARS_WHILE); + } + + private void indent() { + p.indentIn(); + } + + private void outdent() { + p.indentOut(); + } + + private void printStringLiteral(String value) { + p.print(javaScriptString(value)); + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java b/src/com/google/dart/compiler/backend/js/ast/CanBooleanEval.java new file mode 100644 index 00000000000..4213394da22 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/CanBooleanEval.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.backend.js.ast; + +/** + * An interface that describes the boolean evaluation of an expression. + */ +public interface CanBooleanEval { + boolean isBooleanFalse(); + boolean isBooleanTrue(); +} diff --git a/src/com/google/dart/compiler/backend/js/ast/HasArguments.java b/src/com/google/dart/compiler/backend/js/ast/HasArguments.java new file mode 100644 index 00000000000..814189f2846 --- /dev/null +++ b/src/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 extends JsExpression { + + List getArguments(); +} diff --git a/src/com/google/dart/compiler/backend/js/ast/HasCondition.java b/src/com/google/dart/compiler/backend/js/ast/HasCondition.java new file mode 100644 index 00000000000..082f4e15eb3 --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/HasName.java b/src/com/google/dart/compiler/backend/js/ast/HasName.java new file mode 100644 index 00000000000..bdd87970929 --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java b/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java new file mode 100644 index 00000000000..c85c088d1f9 --- /dev/null +++ b/src/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 JsExpressionImpl { + + 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/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java b/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java new file mode 100644 index 00000000000..558a02245eb --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.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; + +import com.intellij.util.SmartList; + +import java.util.List; + +/** + * Represents a JavaScript expression for array literals. + */ +public final class JsArrayLiteral extends JsLiteral { + private final List expressions; + + public JsArrayLiteral() { + expressions = new SmartList(); + } + + public JsArrayLiteral(List expressions) { + this.expressions = expressions; + } + + public List getExpressions() { + return expressions; + } + + @Override + public boolean hasSideEffects() { + for (JsExpression expr : expressions) { + 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(expressions); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.ARRAY; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java b/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java new file mode 100644 index 00000000000..fc1513a74eb --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.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.backend.js.ast; + +import org.jetbrains.annotations.Nullable; + +/** + * Represents a JavaScript binary operation. + */ +public final class JsBinaryOperation extends JsExpressionImpl { + private JsExpression arg1; + private JsExpression arg2; + private final JsBinaryOperator op; + + public JsBinaryOperation(JsBinaryOperator op) { + this(op, null, null); + } + + public JsBinaryOperation(JsBinaryOperator op, @Nullable JsExpression arg1, @Nullable 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; + } + + @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/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java b/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java new file mode 100644 index 00000000000..54ed2b7ca97 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.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; + +/** + * 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() { + 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/src/com/google/dart/compiler/backend/js/ast/JsBlock.java b/src/com/google/dart/compiler/backend/js/ast/JsBlock.java new file mode 100644 index 00000000000..cad730b4b5c --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsBlock.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.backend.js.ast; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Represents a JavaScript block statement. + */ +public class JsBlock extends JsNodeImpl implements JsStatement { + private final List statements; + + public JsBlock() { + this(new ArrayList()); + } + + public JsBlock(JsStatement statement) { + this(Collections.singletonList(statement)); + } + + public JsBlock(JsStatement... statements) { + this(Arrays.asList(statements)); + } + + public JsBlock(List statements) { + this.statements = statements; + } + + public List getStatements() { + return statements; + } + + public boolean isEmpty() { + return statements.isEmpty(); + } + + public boolean isGlobalBlock() { + return false; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(statements); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.BLOCK; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsBreak.java b/src/com/google/dart/compiler/backend/js/ast/JsBreak.java new file mode 100644 index 00000000000..ce1c9e66f2d --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsBreak.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.backend.js.ast; + +/** + * Represents the JavaScript break statement. + */ +public final class JsBreak extends JsContinue { + public JsBreak() { + super(null); + } + + public JsBreak(JsNameRef label) { + super(label); + } + + @Override + public NodeKind getKind() { + return NodeKind.BREAK; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (label != null) { + v.accept(label); + } + } + v.endVisit(this, ctx); + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsCase.java b/src/com/google/dart/compiler/backend/js/ast/JsCase.java new file mode 100644 index 00000000000..a8b881b41cc --- /dev/null +++ b/src/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(statements); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.CASE; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsCatch.java b/src/com/google/dart/compiler/backend/js/ast/JsCatch.java new file mode 100644 index 00000000000..fc97ad4df4c --- /dev/null +++ b/src/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 JsNodeImpl 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.findName(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/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java b/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java new file mode 100644 index 00000000000..20f0add6790 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.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; + +import org.jetbrains.annotations.NotNull; + +/** + * 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"); + name = new JsName(this, ident); + } + + @Override + public JsName declareName(String identifier) { + // Declare into parent scope! + return getParent().declareName(identifier); + } + + @Override + public boolean hasOwnName(@NotNull String name) { + return findOwnName(name) != null; + } + + @Override + protected JsName findOwnName(String ident) { + return name.getIdent().equals(ident) ? name : null; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsConditional.java b/src/com/google/dart/compiler/backend/js/ast/JsConditional.java new file mode 100644 index 00000000000..5b98b278b5e --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsConditional.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.backend.js.ast; + +/** + * Represents a JavaScript conditional expression. + */ +public final class JsConditional extends JsExpressionImpl { + private JsExpression testExpr; + private JsExpression elseExpr; + 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/src/com/google/dart/compiler/backend/js/ast/JsContext.java b/src/com/google/dart/compiler/backend/js/ast/JsContext.java new file mode 100644 index 00000000000..ddb170052d3 --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsContinue.java b/src/com/google/dart/compiler/backend/js/ast/JsContinue.java new file mode 100644 index 00000000000..89396dc89a8 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsContinue.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; + +import org.jetbrains.annotations.Nullable; + +/** + * Represents the JavaScript continue statement. + */ +public class JsContinue extends JsNodeImpl implements JsStatement { + protected final JsNameRef label; + + public JsContinue() { + this(null); + } + + public JsContinue(@Nullable 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 NodeKind getKind() { + return NodeKind.CONTINUE; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsDebugger.java b/src/com/google/dart/compiler/backend/js/ast/JsDebugger.java new file mode 100644 index 00000000000..fe9900e134a --- /dev/null +++ b/src/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 JsNodeImpl implements 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/src/com/google/dart/compiler/backend/js/ast/JsDefault.java b/src/com/google/dart/compiler/backend/js/ast/JsDefault.java new file mode 100644 index 00000000000..89624ddd683 --- /dev/null +++ b/src/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(statements); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.DEFAULT; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java b/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java new file mode 100644 index 00000000000..44f25010e07 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.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.backend.js.ast; + +/** + * Represents a JavaScript do..while statement. + */ +public class JsDoWhile extends JsWhile { + public JsDoWhile() { + } + + public JsDoWhile(JsExpression condition, JsStatement body) { + super(condition, body); + } + + @Override + public NodeKind getKind() { + return NodeKind.DO; + } + + @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); + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsEmpty.java b/src/com/google/dart/compiler/backend/js/ast/JsEmpty.java new file mode 100644 index 00000000000..188676e0ddd --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsEmpty.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 an empty statement in JavaScript. + */ +public class JsEmpty extends JsNodeImpl implements 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/src/com/google/dart/compiler/backend/js/ast/JsExprStmt.java b/src/com/google/dart/compiler/backend/js/ast/JsExprStmt.java new file mode 100644 index 00000000000..0ad3a6c74b5 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsExprStmt.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; + +public final class JsExprStmt extends JsNodeImpl implements 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.EXPRESSION_STMT; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsExpression.java b/src/com/google/dart/compiler/backend/js/ast/JsExpression.java new file mode 100644 index 00000000000..5fb9a7e41f1 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsExpression.java @@ -0,0 +1,25 @@ +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.common.HasSourceInfo; +import com.google.dart.compiler.common.SourceInfo; + +public interface JsExpression extends JsNode, SourceInfo, HasSourceInfo, JsVisitable { + /** + * Determines whether the expression can cause side effects. + */ + boolean hasSideEffects(); + + /** + * True if the target expression is definitely not null. + */ + boolean isDefinitelyNotNull(); + + /** + * True if the target expression is definitely null. + */ + boolean isDefinitelyNull(); + + boolean isLeaf(); + + JsStatement makeStmt(); +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsExpressionImpl.java b/src/com/google/dart/compiler/backend/js/ast/JsExpressionImpl.java new file mode 100644 index 00000000000..f42ae1c26da --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsExpressionImpl.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; + +abstract class JsExpressionImpl extends JsNodeImpl implements JsExpression { + protected JsExpressionImpl() { + } + + /** + * Determines whether or not this expression is a leaf, such as a + * {@link JsNameRef}, {@link JsLiteral.JsBooleanLiteral}, and so on. Leaf expressions + * never need to be parenthesized. + */ + @Override + public boolean isLeaf() { + // Conservatively say that it isn't a leaf. + // Individual subclasses can speak for themselves if they are a leaf. + return false; + } + + @Override + public JsStatement makeStmt() { + return new JsExprStmt(this); + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsFor.java b/src/com/google/dart/compiler/backend/js/ast/JsFor.java new file mode 100644 index 00000000000..ccf61b77954 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsFor.java @@ -0,0 +1,99 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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 JsNodeImpl implements JsStatement { + private JsStatement body; + private JsExpression condition; + private JsExpression incrExpr; + private JsExpression initExpr; + private JsVars initVars; + + public JsFor(JsVars initVars, JsExpression condition, JsExpression incrementExpr) { + this.initVars = initVars; + this.incrExpr = incrementExpr; + this.condition = condition; + initExpr = null; + } + + public JsFor(JsExpression initExpr, JsExpression condition, JsExpression incrementExpr) { + this.initExpr = initExpr; + this.incrExpr = incrementExpr; + this.condition = condition; + initVars = null; + } + + 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; + } + + @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/src/com/google/dart/compiler/backend/js/ast/JsForIn.java b/src/com/google/dart/compiler/backend/js/ast/JsForIn.java new file mode 100644 index 00000000000..048fb02b25c --- /dev/null +++ b/src/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 JsNodeImpl implements 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; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsFunction.java b/src/com/google/dart/compiler/backend/js/ast/JsFunction.java new file mode 100644 index 00000000000..8a55f90b79d --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsFunction.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.ast; + +import com.google.dart.compiler.common.SourceInfo; +import com.google.dart.compiler.common.Symbol; +import com.intellij.util.SmartList; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public final class JsFunction extends JsLiteral implements HasName { + private JsBlock body; + private List params; + private final JsScope scope; + private JsName name; + + public JsFunction(JsScope parentScope) { + this(parentScope, (JsName) null); + } + + public JsFunction(JsScope parentScope, JsBlock body) { + this(parentScope, (JsName) null); + this.body = body; + } + + private JsFunction(JsScope parentScope, @Nullable JsName name) { + this.name = name; + this.scope = new JsScope(parentScope, name == null ? null : name.getIdent()); + } + + public JsBlock getBody() { + return body; + } + + @Override + public JsName getName() { + return name; + } + + @Override + public Symbol getSymbol() { + return name; + } + + public List getParameters() { + if (params == null) { + params = new SmartList(); + } + return params; + } + + public JsScope getScope() { + return scope; + } + + @Override + public boolean hasSideEffects() { + // If there's a name, the name is assigned to. + return name != null; + } + + @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 setBody(JsBlock body) { + this.body = body; + } + + public void setName(@Nullable JsName name) { + this.name = name; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + v.acceptWithInsertRemove(params); + body = v.accept(body); + } + v.endVisit(this, ctx); + } + + @Override + public JsFunction setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + @Override + public NodeKind getKind() { + return NodeKind.FUNCTION; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java b/src/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java new file mode 100644 index 00000000000..9a14004e14c --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsIf.java b/src/com/google/dart/compiler/backend/js/ast/JsIf.java new file mode 100644 index 00000000000..1722bc525c7 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsIf.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; + +/** + * Represents a JavaScript if statement. + */ +public final class JsIf extends JsNodeImpl implements JsStatement { + private JsExpression ifExpression; + private JsStatement thenStatement; + private JsStatement elseStatement; + + public JsIf() { + } + + public JsIf(JsExpression ifExpression, JsStatement thenStatement, JsStatement elseStatement) { + this.ifExpression = ifExpression; + this.thenStatement = thenStatement; + this.elseStatement = elseStatement; + } + + public JsIf(JsExpression ifExpression, JsStatement thenStatement) { + this.ifExpression = ifExpression; + this.thenStatement = thenStatement; + } + + public JsStatement getElseStatement() { + return elseStatement; + } + + public JsExpression getIfExpression() { + return ifExpression; + } + + public JsStatement getThenStatement() { + return thenStatement; + } + + public void setElseStatement(JsStatement elseStmt) { + this.elseStatement = elseStmt; + } + + public void setIfExpression(JsExpression ifExpr) { + this.ifExpression = ifExpr; + } + + public void setThenStatement(JsStatement thenStmt) { + this.thenStatement = thenStmt; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + ifExpression = v.accept(ifExpression); + thenStatement = v.accept(thenStatement); + if (elseStatement != null) { + elseStatement = v.accept(elseStatement); + } + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.IF; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java b/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java new file mode 100644 index 00000000000..1856dcc6dad --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsInvocation.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.backend.js.ast; + +import com.intellij.util.SmartList; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Represents a JavaScript invocation. + */ +public final class JsInvocation extends JsExpressionImpl implements HasArguments { + private final List arguments; + private JsExpression qualifier; + + public JsInvocation() { + arguments = new SmartList(); + } + + public JsInvocation(JsExpression qualifier, List arguments) { + this.qualifier = qualifier; + this.arguments = arguments; + } + + public JsInvocation(JsExpression qualifier, JsExpression arg) { + this(qualifier, Collections.singletonList(arg)); + } + + public JsInvocation(JsExpression qualifier, JsExpression... arguments) { + this(qualifier, Arrays.asList(arguments)); + } + + public JsInvocation(JsExpression qualifier) { + this(); + this.qualifier = qualifier; + } + + @Override + public List getArguments() { + return arguments; + } + + 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(arguments); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.INVOKE; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsLabel.java b/src/com/google/dart/compiler/backend/js/ast/JsLabel.java new file mode 100644 index 00000000000..f9817ee3390 --- /dev/null +++ b/src/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 JsNodeImpl implements JsStatement, 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/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java b/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java new file mode 100644 index 00000000000..eb00aab6e36 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsLiteral.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.backend.js.ast; + +public abstract class JsLiteral extends JsExpressionImpl implements CanBooleanEval { + public static final JsValueLiteral THIS = new JsThisRef(); + public static final JsNameRef UNDEFINED = new JsNameRef("undefined"); + + public static final JsNullLiteral NULL = new JsNullLiteral(); + + public static final JsBooleanLiteral TRUE = new JsBooleanLiteral(true); + public static final JsBooleanLiteral FALSE = new JsBooleanLiteral(false); + + public static JsBooleanLiteral getBoolean(boolean truth) { + return truth ? TRUE : FALSE; + } + + public static final class JsThisRef extends JsValueLiteral { + private JsThisRef() { + super(); + } + + @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) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.THIS; + } + } + + public static final class JsBooleanLiteral extends JsValueLiteral { + private final boolean value; + + // Should be interned by JsProgram + private JsBooleanLiteral(boolean value) { + this.value = value; + } + + public boolean getValue() { + return value; + } + + @Override + public boolean isBooleanFalse() { + return !value; + } + + @Override + public boolean isBooleanTrue() { + return value; + } + + @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/src/com/google/dart/compiler/backend/js/ast/JsName.java b/src/com/google/dart/compiler/backend/js/ast/JsName.java new file mode 100644 index 00000000000..44da16f6610 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsName.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; + +import com.google.dart.compiler.common.Symbol; + +/** + * An abstract base class for named JavaScript objects. + */ +public class JsName implements Symbol { + private final JsScope enclosing; + private final String ident; + + /** + * @param ident the unmangled ident to use for this name + */ + JsName(JsScope enclosing, String ident) { + this.enclosing = enclosing; + this.ident = ident; + } + + public String getIdent() { + return ident; + } + + public JsNameRef makeRef() { + return new JsNameRef(this); + } + + @Override + public String toString() { + return ident; + } + + @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; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java b/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java new file mode 100644 index 00000000000..54d1a923aa1 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsNameRef.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.backend.js.ast; + +import com.google.dart.compiler.common.Symbol; + +/** + * Represents a JavaScript expression that references a name. + */ +public final class JsNameRef extends JsExpressionImpl 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 JsNameRef(String ident, JsExpression qualifier) { + this.ident = ident; + this.qualifier = qualifier; + } + + public JsNameRef(String ident, String qualifier) { + this(ident, new JsNameRef(qualifier)); + } + + public JsNameRef(JsName name, JsExpression qualifier) { + this.name = name; + this.qualifier = qualifier; + } + + 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; + } + + @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() { + return false; + } + + @Override + public boolean isDefinitelyNull() { + if (name != null) { + return (JsLiteral.UNDEFINED.getName() == name); + } + return false; + } + + @Override + public boolean isLeaf() { + if (qualifier == null) { + return true; + } + else { + return false; + } + } + + 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/src/com/google/dart/compiler/backend/js/ast/JsNew.java b/src/com/google/dart/compiler/backend/js/ast/JsNew.java new file mode 100644 index 00000000000..68ffb540ca5 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsNew.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 com.intellij.util.SmartList; + +import java.util.List; + +/** + * Represents the JavaScript new expression. + */ +public final class JsNew extends JsExpressionImpl implements HasArguments { + private final List arguments; + private JsExpression ctorExpression; + + public JsNew(JsExpression ctorExpression) { + this(ctorExpression, new SmartList()); + } + + public JsNew(JsExpression ctorExpression, List arguments) { + this.ctorExpression = ctorExpression; + this.arguments = arguments; + } + + @Override + public List getArguments() { + return arguments; + } + + public JsExpression getConstructorExpression() { + return ctorExpression; + } + + @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)) { + ctorExpression = v.accept(ctorExpression); + v.acceptList(arguments); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.NEW; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsNode.java b/src/com/google/dart/compiler/backend/js/ast/JsNode.java new file mode 100644 index 00000000000..78e78af6434 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsNode.java @@ -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. + +package com.google.dart.compiler.backend.js.ast; + +public interface JsNode extends JsVisitable { + NodeKind getKind(); +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsNodeImpl.java b/src/com/google/dart/compiler/backend/js/ast/JsNodeImpl.java new file mode 100644 index 00000000000..e96a902c178 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsNodeImpl.java @@ -0,0 +1,41 @@ +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.TextOutputImpl; + +abstract class JsNodeImpl extends AbstractNode implements JsNode { + protected JsNodeImpl() { + } + + // Causes source generation to delegate to the one visitor + public final String toSource() { + TextOutputImpl out = new TextOutputImpl(); + JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out); + v.accept(this); + return out.toString(); + } + + // Causes source generation to delegate to the one visitor + @Override + public String toString() { + TextOutputImpl out = new TextOutputImpl(); + 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; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java b/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java new file mode 100644 index 00000000000..0514fb0df89 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.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; + +public final class JsNullLiteral extends JsValueLiteral { + 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/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java b/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java new file mode 100644 index 00000000000..e191e7e8954 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.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; + +public abstract class JsNumberLiteral extends JsValueLiteral { + @Override + public boolean isDefinitelyNotNull() { + return true; + } + + @Override + public boolean isDefinitelyNull() { + return false; + } + + @Override + public NodeKind getKind() { + return NodeKind.NUMBER; + } + + public static final class JsDoubleLiteral extends JsNumberLiteral { + public final double value; + + JsDoubleLiteral(double value) { + this.value = value; + } + + @Override + public boolean isBooleanFalse() { + return value == 0.0; + } + + @Override + public boolean isBooleanTrue() { + return value != 0.0; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + public String toString() { + return String.valueOf(value); + } + } + + public static final class JsIntLiteral extends JsNumberLiteral { + public final int value; + + JsIntLiteral(int value) { + this.value = value; + } + + @Override + public boolean isBooleanFalse() { + return value == 0; + } + + @Override + public boolean isBooleanTrue() { + return value != 0; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + v.visit(this, ctx); + v.endVisit(this, ctx); + } + + public String toString() { + return String.valueOf(value); + } + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java b/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java new file mode 100644 index 00000000000..b0353a3d3bd --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.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.backend.js.ast; + +import com.intellij.util.SmartList; + +import java.util.List; + +public final class JsObjectLiteral extends JsLiteral { + private final List properties; + + private final boolean multiline; + + public JsObjectLiteral() { + this(new SmartList()); + } + + public JsObjectLiteral(boolean multiline) { + this(new SmartList(), multiline); + } + + public boolean isMultiline() { + return multiline; + } + + public JsObjectLiteral(List properties) { + this(properties, false); + } + + public JsObjectLiteral(List properties, boolean multiline) { + this.properties = properties; + this.multiline = multiline; + } + + public List getPropertyInitializers() { + return properties; + } + + @Override + public boolean hasSideEffects() { + for (JsPropertyInitializer prop : properties) { + 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(properties); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.OBJECT; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsOperator.java b/src/com/google/dart/compiler/backend/js/ast/JsOperator.java new file mode 100644 index 00000000000..844805fcc73 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsOperator.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; + +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/src/com/google/dart/compiler/backend/js/ast/JsParameter.java b/src/com/google/dart/compiler/backend/js/ast/JsParameter.java new file mode 100644 index 00000000000..fbb0908e69f --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsParameter.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; + +import com.google.dart.compiler.common.Symbol; + +/** + * A JavaScript parameter. + */ +public final class JsParameter extends JsNodeImpl implements HasName { + private final JsName name; + + public JsParameter(JsName name) { + this.name = name; + } + + @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/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java b/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java new file mode 100644 index 00000000000..a33536f6c0d --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java b/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java new file mode 100644 index 00000000000..8ce12c7bacd --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsProgram.java b/src/com/google/dart/compiler/backend/js/ast/JsProgram.java new file mode 100644 index 00000000000..afea24a93ca --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsProgram.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.backend.js.ast; + +import gnu.trove.TDoubleObjectHashMap; +import gnu.trove.THashMap; +import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +import static com.google.dart.compiler.backend.js.ast.JsNumberLiteral.JsDoubleLiteral; +import static com.google.dart.compiler.backend.js.ast.JsNumberLiteral.JsIntLiteral; + +/** + * A JavaScript program. + */ +public final class JsProgram extends JsNodeImpl { + private final JsEmpty emptyStmt; + + private JsProgramFragment[] fragments; + + private final TDoubleObjectHashMap doubleLiteralMap = new TDoubleObjectHashMap(); + private final TIntObjectHashMap intLiteralMap = new TIntObjectHashMap(); + + private final JsRootScope rootScope; + private final Map stringLiteralMap = new THashMap(); + private final JsScope topScope; + + /** + * Constructs a JavaScript program object. + */ + public JsProgram(String unitId) { + rootScope = new JsRootScope(this); + topScope = new JsScope(rootScope, "Global", unitId); + setFragmentCount(1); + + emptyStmt = new JsEmpty(); + } + + public JsEmpty getEmptyStmt() { + return emptyStmt; + } + + public JsBlock getFragmentBlock(int fragment) { + if (fragment < 0 || fragment >= fragments.length) { + throw new IllegalArgumentException("Invalid fragment: " + fragment); + } + return fragments[fragment].getGlobalBlock(); + } + + public JsBlock getGlobalBlock() { + return getFragmentBlock(0); + } + + public JsNumberLiteral getNumberLiteral(double value) { + JsDoubleLiteral literal = doubleLiteralMap.get(value); + if (literal == null) { + literal = new JsDoubleLiteral(value); + doubleLiteralMap.put(value, literal); + } + + return literal; + } + + public JsNumberLiteral getNumberLiteral(int value) { + JsIntLiteral literal = intLiteralMap.get(value); + if (literal == null) { + literal = new JsIntLiteral(value); + intLiteralMap.put(value, literal); + } + + return literal; + } + + /** + * 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 literal = stringLiteralMap.get(value); + if (literal == null) { + literal = new JsStringLiteral(value); + stringLiteralMap.put(value, literal); + } + return literal; + } + + public void setFragmentCount(int fragments) { + this.fragments = new JsProgramFragment[fragments]; + for (int i = 0; i < fragments; i++) { + this.fragments[i] = new JsProgramFragment(); + } + } + + @Override + public void traverse(JsVisitor v, @Nullable 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/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java b/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java new file mode 100644 index 00000000000..3a291556f66 --- /dev/null +++ b/src/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 JsNodeImpl { + + 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/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java b/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java new file mode 100644 index 00000000000..0a8d3e2467d --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.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; + +import org.jetbrains.annotations.NotNull; + +/** + * Used in object literals to specify property values by name. + */ +public class JsPropertyInitializer extends JsNodeImpl { + private JsExpression labelExpr; + private JsExpression valueExpr; + + public JsPropertyInitializer(@NotNull JsExpression labelExpr) { + this.labelExpr = labelExpr; + } + + public JsPropertyInitializer(@NotNull JsExpression labelExpr, @NotNull JsExpression valueExpr) { + this(labelExpr); + this.valueExpr = valueExpr; + } + + public JsExpression getLabelExpr() { + return labelExpr; + } + + public JsExpression getValueExpr() { + return valueExpr; + } + + public boolean hasSideEffects() { + return labelExpr.hasSideEffects() || valueExpr.hasSideEffects(); + } + + public void setValueExpr(@NotNull 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/src/com/google/dart/compiler/backend/js/ast/JsRegExp.java b/src/com/google/dart/compiler/backend/js/ast/JsRegExp.java new file mode 100644 index 00000000000..158c7a5e092 --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsReturn.java b/src/com/google/dart/compiler/backend/js/ast/JsReturn.java new file mode 100644 index 00000000000..b8806c82bab --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsReturn.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.backend.js.ast; + +/** + * A JavaScript return statement. + */ +public final class JsReturn extends JsNodeImpl implements 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 NodeKind getKind() { + return NodeKind.RETURN; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsRootScope.java b/src/com/google/dart/compiler/backend/js/ast/JsRootScope.java new file mode 100644 index 00000000000..e808eb0f576 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsRootScope.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; + +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 findOwnName(String ident) { + JsName name = super.findOwnName(ident); + if (name == null) { + if (JsReservedIdentifiers.reservedGlobalSymbols.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); + } + } + return name; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsScope.java b/src/com/google/dart/compiler/backend/js/ast/JsScope.java new file mode 100644 index 00000000000..406069e6890 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsScope.java @@ -0,0 +1,176 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.Maps; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Map; + +/** + * A scope is a factory for creating and allocating + * {@link 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 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 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 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 { + @Nullable + private final String description; + private Map names = Collections.emptyMap(); + private final JsScope parent; + protected int tempIndex = 0; + private final String scopeId; + + public JsScope(JsScope parent, @Nullable String description) { + this(parent, description, null); + } + + public JsScope(JsScope parent) { + this(parent, null); + } + + public JsScope(JsScope parent, @Nullable String description, @Nullable String scopeId) { + assert (parent != null); + this.scopeId = scopeId; + this.description = description; + this.parent = parent; + } + + @NotNull + public JsScope innerScope(@Nullable String scopeName) { + return new JsScope(this, scopeName); + } + + protected JsScope(@Nullable String description) { + this.description = description; + this.parent = null; + this.scopeId = null; + } + + /** + * Gets a name object associated with the specified identifier in this scope, + * creating it if necessary.
+ * If the JsName does not exist yet, a new JsName is created. The identifier, + * short name, and original name of the newly created JsName are equal to + * the given identifier. + * + * @param identifier An identifier that is unique within this scope. + */ + public JsName declareName(String identifier) { + JsName name = findOwnName(identifier); + return name != null ? name : doCreateName(identifier); + } + + /** + * Creates a new variable with an unique ident in this scope. + * The generated JsName is guaranteed to have an identifier 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). + */ + @NotNull + public JsName declareFreshName(String suggestedName) { + String name = suggestedName; + int counter = 0; + while (hasOwnName(name)) { + name = suggestedName + '_' + counter++; + } + return doCreateName(name); + } + + private String getNextTempName() { + // 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()); + } + + /** + * 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 + */ + @Nullable + public final JsName findName(String ident) { + JsName name = findOwnName(ident); + if (name == null && parent != null) { + return parent.findName(ident); + } + return name; + } + + protected boolean hasOwnName(@NotNull String name) { + return names.containsKey(name); + } + + /** + * Returns the parent scope of this scope, or null if this is the + * root scope. + */ + public final JsScope getParent() { + return parent; + } + + 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; + } + } + + protected JsName doCreateName(String ident) { + JsName name = new JsName(this, ident); + names = Maps.put(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 findOwnName(String ident) { + return names.get(ident); + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsStatement.java b/src/com/google/dart/compiler/backend/js/ast/JsStatement.java new file mode 100644 index 00000000000..a3ea11cec92 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsStatement.java @@ -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. + +package com.google.dart.compiler.backend.js.ast; + +public interface JsStatement extends JsNode { +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java b/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java new file mode 100644 index 00000000000..77e60c9b89a --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java b/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java new file mode 100644 index 00000000000..4e76da8d939 --- /dev/null +++ b/src/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 JsNodeImpl implements 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/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java b/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java new file mode 100644 index 00000000000..08265369888 --- /dev/null +++ b/src/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 com.intellij.util.SmartList; + +import java.util.List; + +/** + * A member/case in a JavaScript switch object. + */ +public abstract class JsSwitchMember extends JsNodeImpl { + protected final List statements = new SmartList(); + + protected JsSwitchMember() { + super(); + } + + public List getStatements() { + return statements; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsThrow.java b/src/com/google/dart/compiler/backend/js/ast/JsThrow.java new file mode 100644 index 00000000000..f6b8f1987f8 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsThrow.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.ast; + +public class JsThrow extends JsNodeImpl implements JsStatement { + private JsExpression expression; + + public JsThrow() { + } + + public JsThrow(JsExpression expression) { + this.expression = expression; + } + + public JsExpression getExpression() { + return expression; + } + + public void setExpression(JsExpression expression) { + this.expression = expression; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + expression = v.accept(expression); + } + v.endVisit(this, ctx); + } + + @Override + public NodeKind getKind() { + return NodeKind.THROW; + } +} diff --git a/src/com/google/dart/compiler/backend/js/ast/JsTry.java b/src/com/google/dart/compiler/backend/js/ast/JsTry.java new file mode 100644 index 00000000000..2a3b78607b6 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsTry.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 com.intellij.util.SmartList; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * A JavaScript try statement. + */ +public class JsTry extends JsNodeImpl implements JsStatement { + private final List catches; + private JsBlock finallyBlock; + private JsBlock tryBlock; + + public JsTry() { + catches = new SmartList(); + } + + public JsTry(JsBlock tryBlock, List catches, @Nullable JsBlock finallyBlock) { + this.tryBlock = tryBlock; + this.catches = catches; + this.finallyBlock = finallyBlock; + } + + 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/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java b/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java new file mode 100644 index 00000000000..6f3ee93273d --- /dev/null +++ b/src/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 JsExpressionImpl { + + 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/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java b/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java new file mode 100644 index 00000000000..2b854d8313e --- /dev/null +++ b/src/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/src/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java b/src/com/google/dart/compiler/backend/js/ast/JsValueLiteral.java new file mode 100644 index 00000000000..f64eb794ad6 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsValueLiteral.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.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/src/com/google/dart/compiler/backend/js/ast/JsVars.java b/src/com/google/dart/compiler/backend/js/ast/JsVars.java new file mode 100644 index 00000000000..d02c932715a --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsVars.java @@ -0,0 +1,137 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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 com.intellij.util.SmartList; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * A JavaScript var statement. + */ +public class JsVars extends JsNodeImpl implements JsStatement, Iterable { + private final List vars; + + private final boolean multiline; + + public JsVars() { + this(new SmartList(), false); + } + + public JsVars(boolean multiline) { + this(new SmartList(), multiline); + } + + public JsVars(List vars, boolean multiline) { + this.vars = vars; + this.multiline = multiline; + } + + public JsVars(JsVar var) { + this(Collections.singletonList(var), false); + } + + public JsVars(JsVar... vars) { + this(Arrays.asList(vars), false); + } + + public boolean isMultiline() { + return multiline; + } + + /** + * A var declared using the JavaScript var statement. + */ + public static class JsVar extends JsNodeImpl implements HasName { + private final JsName name; + private JsExpression initExpression; + + public JsVar(JsName name) { + this.name = name; + } + + public JsVar(JsName name, @Nullable JsExpression initExpression) { + this.name = name; + this.initExpression = initExpression; + } + + public JsExpression getInitExpression() { + return initExpression; + } + + @Override + public JsName getName() { + return name; + } + + @Override + public Symbol getSymbol() { + return name; + } + + public void setInitExpression(JsExpression initExpression) { + this.initExpression = initExpression; + } + + @Override + public void traverse(JsVisitor v, JsContext ctx) { + if (v.visit(this, ctx)) { + if (initExpression != null) { + initExpression = v.accept(initExpression); + } + } + v.endVisit(this, ctx); + } + + @Override + public JsVar setSourceRef(SourceInfo info) { + super.setSourceRef(info); + return this; + } + + @Override + public NodeKind getKind() { + return NodeKind.VAR; + } + } + + public void add(JsVar var) { + vars.add(var); + } + + public void addIfHasInitializer(JsVar var) { + if (var.getInitExpression() != null) { + add(var); + } + } + + public boolean isEmpty() { + return vars.isEmpty(); + } + + @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/src/com/google/dart/compiler/backend/js/ast/JsVisitable.java b/src/com/google/dart/compiler/backend/js/ast/JsVisitable.java new file mode 100644 index 00000000000..bd68f477d90 --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsVisitable.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.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/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java b/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java new file mode 100644 index 00000000000..df5be84598f --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java @@ -0,0 +1,435 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.List; + +/** + * Implemented by nodes that will visit child nodes. + */ +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(JsLiteral.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(JsLiteral.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(JsLiteral.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.JsIntLiteral x, JsContext ctx) { + return true; + } + + public boolean visit(JsNumberLiteral.JsDoubleLiteral 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(JsLiteral.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/src/com/google/dart/compiler/backend/js/ast/JsWhile.java b/src/com/google/dart/compiler/backend/js/ast/JsWhile.java new file mode 100644 index 00000000000..c3639154d0d --- /dev/null +++ b/src/com/google/dart/compiler/backend/js/ast/JsWhile.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 while statement. + */ +public class JsWhile extends JsNodeImpl implements JsStatement { + protected JsStatement body; + protected 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/src/com/google/dart/compiler/backend/js/ast/NodeKind.java b/src/com/google/dart/compiler/backend/js/ast/NodeKind.java new file mode 100644 index 00000000000..0c47f2877f0 --- /dev/null +++ b/src/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; + +enum NodeKind { + ARRAY_ACCESS, + ARRAY, + BINARY_OP, + BLOCK, + BOOLEAN, + BREAK, + CASE, + CATCH, + CONDITIONAL, + CONTINUE, + DEBUGGER, + DEFAULT, + DO, + EMPTY, + EXPRESSION_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/src/com/google/dart/compiler/common/AbstractNode.java b/src/com/google/dart/compiler/common/AbstractNode.java new file mode 100644 index 00000000000..6d9eaf40090 --- /dev/null +++ b/src/com/google/dart/compiler/common/AbstractNode.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.common; + +import com.google.dart.compiler.Source; + +public class AbstractNode implements SourceInfo, HasSourceInfo { + protected SourceInfo sourceInfo; + + @Override + public Source getSource() { + return sourceInfo.getSource(); + } + + @Override + public int getLine() { + return sourceInfo.getLine(); + } + + @Override + public int getColumn() { + return sourceInfo.getColumn(); + } + + @Override + public int getStart() { + return sourceInfo.getStart(); + } + + @Override + public int getLength() { + return sourceInfo.getLength(); + } + + @Override + public SourceInfo getSourceInfo() { + return this; + } + + @Override + public void setSourceInfo(SourceInfo info) { + sourceInfo = info; + } + + @Override + public final void setSourceLocation(Source source, int line, int column, int startPosition, int length) { + sourceInfo = new SourceInfoImpl(source, line, column, startPosition, length); + } + + public final void setSourceRange(int startPosition, int length) { + //Preconditions.checkArgument(startPosition != -1 && length >= 0 || startPosition == -1 && length == 0); + //sourceInfo.sourceStart = startPosition; + //sourceInfo.sourceLength = length; + } +} diff --git a/src/com/google/dart/compiler/common/HasSourceInfo.java b/src/com/google/dart/compiler/common/HasSourceInfo.java new file mode 100644 index 00000000000..c7780f6acee --- /dev/null +++ b/src/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#getStart() + * @see SourceInfo#getLength() + */ + void setSourceLocation(Source source, int line, int column, int startPosition, int length); +} diff --git a/src/com/google/dart/compiler/common/HasSymbol.java b/src/com/google/dart/compiler/common/HasSymbol.java new file mode 100644 index 00000000000..0128679bed1 --- /dev/null +++ b/src/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/src/com/google/dart/compiler/common/SourceInfo.java b/src/com/google/dart/compiler/common/SourceInfo.java new file mode 100644 index 00000000000..6e0efc8e505 --- /dev/null +++ b/src/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 getLine(); + + /** + * @return A 1-based column number into the original source file indicating + * where the source fragment begins. + */ + int getColumn(); + + /** + * 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 #getLength() + * @see HasSourceInfo#setSourceLocation(Source, int, int, int, int) + */ + int getStart(); + + /** + * 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 #getStart() + * @see HasSourceInfo#setSourceLocation(Source, int, int, int, int) + */ + int getLength(); +} diff --git a/src/com/google/dart/compiler/common/SourceInfoImpl.java b/src/com/google/dart/compiler/common/SourceInfoImpl.java new file mode 100644 index 00000000000..450f873e3e5 --- /dev/null +++ b/src/com/google/dart/compiler/common/SourceInfoImpl.java @@ -0,0 +1,44 @@ +package com.google.dart.compiler.common; + +import com.google.dart.compiler.Source; + +public class SourceInfoImpl implements SourceInfo { + protected Source source = null; + protected int line = -1; + protected int column = -1; + protected int start = -1; + protected int length = -1; + + public SourceInfoImpl(Source source, int line, int column, int start, int length) { + this.source = source; + this.line = line; + this.column = column; + this.start = start; + this.length = length; + } + + @Override + public Source getSource() { + return source; + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getColumn() { + return column; + } + + @Override + public int getStart() { + return start; + } + + @Override + public int getLength() { + return length; + } +} diff --git a/src/com/google/dart/compiler/common/Symbol.java b/src/com/google/dart/compiler/common/Symbol.java new file mode 100644 index 00000000000..6e786358883 --- /dev/null +++ b/src/com/google/dart/compiler/common/Symbol.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.common; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public interface Symbol { +} diff --git a/src/com/google/dart/compiler/util/AstUtil.java b/src/com/google/dart/compiler/util/AstUtil.java new file mode 100644 index 00000000000..d2abe9123d6 --- /dev/null +++ b/src/com/google/dart/compiler/util/AstUtil.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.util; + +import com.google.dart.compiler.backend.js.ast.*; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public final class AstUtil { + private AstUtil() { + } + + public static JsNameRef newQualifiedNameRef(String name) { + JsNameRef node = null; + int endPos; + int startPos = 0; + do { + endPos = name.indexOf('.', startPos); + String part = (endPos == -1 + ? name.substring(startPos) + : name.substring(startPos, endPos)); + node = new JsNameRef(part, node); + startPos = endPos + 1; + } + while (endPos != -1); + + return node; + } + + /** + * 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 RuntimeException("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; + } +} diff --git a/src/com/google/dart/compiler/util/Lists.java b/src/com/google/dart/compiler/util/Lists.java new file mode 100644 index 00000000000..69507ed3197 --- /dev/null +++ b/src/com/google/dart/compiler/util/Lists.java @@ -0,0 +1,267 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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 Lists() { + } + + 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 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/src/com/google/dart/compiler/util/Maps.java b/src/com/google/dart/compiler/util/Maps.java new file mode 100644 index 00000000000..5ca74c3a370 --- /dev/null +++ b/src/com/google/dart/compiler/util/Maps.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.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * 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 Maps() { + } + + 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 Collections.singletonMap(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; + } + } +} diff --git a/src/com/google/dart/compiler/util/TextOutput.java b/src/com/google/dart/compiler/util/TextOutput.java new file mode 100644 index 00000000000..3b42663d7dc --- /dev/null +++ b/src/com/google/dart/compiler/util/TextOutput.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.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(int v); + void print(double v); + + void print(char[] s); + + void print(CharSequence s); + + void printOpt(char c); + + void printOpt(char[] s); + + void printOpt(String s); +} diff --git a/src/com/google/dart/compiler/util/TextOutputImpl.java b/src/com/google/dart/compiler/util/TextOutputImpl.java new file mode 100644 index 00000000000..84c126da023 --- /dev/null +++ b/src/com/google/dart/compiler/util/TextOutputImpl.java @@ -0,0 +1,174 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.Arrays; + +/** + * An abstract base type to build TextOutput implementations. + */ +public class TextOutputImpl implements TextOutput { + private final boolean compact; + private int identLevel = 0; + private final static int indentGranularity = 2; + private char[][] indents = new char[][] {new char[0]}; + private boolean justNewlined; + private final StringBuilder out; + private int position = 0; + private int line = 0; + private int column = 0; + + public TextOutputImpl() { + this(false); + } + + public TextOutputImpl(boolean compact) { + this.compact = compact; + this.out = new StringBuilder(); + } + + @Override + public String toString() { + return out.toString(); + } + + @Override + public int getPosition() { + return position; + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getColumn() { + return column; + } + + @Override + 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; + } + } + + @Override + public void indentOut() { + --identLevel; + } + + @Override + public void newline() { + out.append('\n'); + position++; + line++; + column = 0; + justNewlined = true; + } + + @Override + public void newlineOpt() { + if (!compact) { + newline(); + } + } + + @Override + public void print(double value) { + maybeIndent(); + int oldLength = out.length(); + out.append(value); + movePosition(out.length() - oldLength); + } + + @Override + public void print(int value) { + maybeIndent(); + int oldLength = out.length(); + out.append(value); + movePosition(out.length() - oldLength); + } + + @Override + public void print(char c) { + maybeIndent(); + out.append(c); + movePosition(1); + } + + private void movePosition(int l) { + position += l; + column += l; + justNewlined = false; + } + + @Override + public void print(char[] s) { + maybeIndent(); + printAndCount(s); + justNewlined = false; + } + + @Override + public void print(CharSequence s) { + maybeIndent(); + printAndCount(s); + justNewlined = false; + } + + @Override + public void printOpt(char c) { + if (!compact) { + maybeIndent(); + out.append(c); + position++; + column++; + } + } + + @Override + public void printOpt(char[] s) { + if (!compact) { + maybeIndent(); + printAndCount(s); + } + } + + @Override + public void printOpt(String s) { + if (!compact) { + maybeIndent(); + printAndCount(s); + } + } + + private void maybeIndent() { + if (justNewlined && !compact) { + printAndCount(indents[identLevel]); + justNewlined = false; + } + } + + private void printAndCount(CharSequence charSequence) { + position += charSequence.length(); + column += charSequence.length(); + out.append(charSequence); + } + + private void printAndCount(char[] chars) { + position += chars.length; + column += chars.length; + out.append(chars); + } +}