diff --git a/js/js.dart-ast/.idea/.name b/js/js.dart-ast/.idea/.name new file mode 100644 index 00000000000..a02436dd37e --- /dev/null +++ b/js/js.dart-ast/.idea/.name @@ -0,0 +1 @@ +former-dast-ast \ No newline at end of file diff --git a/js/js.dart-ast/.idea/artifacts/dart_ast_jar.xml b/js/js.dart-ast/.idea/artifacts/dart_ast_jar.xml new file mode 100644 index 00000000000..22aab8d51c0 --- /dev/null +++ b/js/js.dart-ast/.idea/artifacts/dart_ast_jar.xml @@ -0,0 +1,10 @@ + + + $PROJECT_DIR$/out/artifacts + + + + + + + \ No newline at end of file diff --git a/js/js.dart-ast/.idea/artifacts/dart_ast_src.xml b/js/js.dart-ast/.idea/artifacts/dart_ast_src.xml new file mode 100644 index 00000000000..97361010d7c --- /dev/null +++ b/js/js.dart-ast/.idea/artifacts/dart_ast_src.xml @@ -0,0 +1,10 @@ + + + $PROJECT_DIR$/out/artifacts + + + + + + + \ No newline at end of file diff --git a/js/js.dart-ast/.idea/codeStyleSettings.xml b/js/js.dart-ast/.idea/codeStyleSettings.xml new file mode 100644 index 00000000000..1129f00105d --- /dev/null +++ b/js/js.dart-ast/.idea/codeStyleSettings.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/js/js.dart-ast/.idea/compiler.xml b/js/js.dart-ast/.idea/compiler.xml new file mode 100644 index 00000000000..217af471a9e --- /dev/null +++ b/js/js.dart-ast/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/js/js.dart-ast/.idea/encodings.xml b/js/js.dart-ast/.idea/encodings.xml new file mode 100644 index 00000000000..e206d70d859 --- /dev/null +++ b/js/js.dart-ast/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/js/js.dart-ast/.idea/inspectionProfiles/Project_Default.xml b/js/js.dart-ast/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000000..3674c63d88d --- /dev/null +++ b/js/js.dart-ast/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,62 @@ + + + + \ No newline at end of file diff --git a/js/js.dart-ast/.idea/inspectionProfiles/profiles_settings.xml b/js/js.dart-ast/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000000..3b312839bf2 --- /dev/null +++ b/js/js.dart-ast/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/js/js.dart-ast/.idea/misc.xml b/js/js.dart-ast/.idea/misc.xml new file mode 100644 index 00000000000..97320410eec --- /dev/null +++ b/js/js.dart-ast/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/js/js.dart-ast/.idea/modules.xml b/js/js.dart-ast/.idea/modules.xml new file mode 100644 index 00000000000..9d41cd5a8e0 --- /dev/null +++ b/js/js.dart-ast/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/js/js.dart-ast/.idea/scopes/scope_settings.xml b/js/js.dart-ast/.idea/scopes/scope_settings.xml new file mode 100644 index 00000000000..922003b8433 --- /dev/null +++ b/js/js.dart-ast/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/js/js.dart-ast/.idea/uiDesigner.xml b/js/js.dart-ast/.idea/uiDesigner.xml new file mode 100644 index 00000000000..3b000203088 --- /dev/null +++ b/js/js.dart-ast/.idea/uiDesigner.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/js.dart-ast/.idea/vcs.xml b/js/js.dart-ast/.idea/vcs.xml new file mode 100644 index 00000000000..275077f8255 --- /dev/null +++ b/js/js.dart-ast/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/js/js.dart-ast/License.txt b/js/js.dart-ast/License.txt new file mode 100644 index 00000000000..eb4e1abc135 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/former-dast-ast.iml b/js/js.dart-ast/former-dast-ast.iml new file mode 100644 index 00000000000..9d6c93e3d36 --- /dev/null +++ b/js/js.dart-ast/former-dast-ast.iml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/js.dart-ast/src/com/google/dart/compiler/Source.java b/js/js.dart-ast/src/com/google/dart/compiler/Source.java new file mode 100644 index 00000000000..f0ff5797f73 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java new file mode 100644 index 00000000000..4cfd42b55c7 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsConstructExpressionVisitor.java @@ -0,0 +1,95 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.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 RecursiveJsVisitor { + 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; + + private JsConstructExpressionVisitor() { + } + + /** + * We only look at the array expression since the index has its own scope. + */ + @Override + public void visitArrayAccess(JsArrayAccess x) { + accept(x.getArrayExpression()); + } + + /** + * Array literals have their own scoping. + */ + @Override + public void visitArray(JsArrayLiteral x) { + } + + /** + * Functions have their own scoping. + */ + @Override + public void visitFunction(JsFunction x) { + } + + @Override + public void visitInvocation(JsInvocation invocation) { + containsInvocation = true; + } + + @Override + public void visitNameRef(JsNameRef nameRef) { + if (!nameRef.isLeaf()) { + accept(nameRef.getQualifier()); + } + } + + /** + * New constructs bind to the nearest set of parentheses. + */ + @Override + public void visitNew(JsNew x) { + } + + /** + * Object literals have their own scope. + */ + @Override + public void visitObjectLiteral(JsObjectLiteral x) { + } + + /** + * We only look at nodes that would not normally be surrounded by parentheses. + */ + @Override + public void accept(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. + if (node instanceof JsExpression) { + JsExpression expression = (JsExpression) node; + int precedence = JsPrecedenceVisitor.exec(expression); + // Only visit expressions that won't automatically be surrounded by + // parentheses + if (precedence < JsPrecedenceVisitor.PRECEDENCE_NEW) { + return; + } + } + super.accept(node); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.java new file mode 100644 index 00000000000..6789cd2bd71 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsFirstExpressionVisitor.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.*; +import com.google.dart.compiler.backend.js.ast.JsExpressionStatement; + +/** + * 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 RecursiveJsVisitor { + public static boolean exec(JsExpressionStatement 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 void visitArrayAccess(JsArrayAccess x) { + accept(x.getArrayExpression()); + } + + @Override + public void visitArray(JsArrayLiteral x) { + } + + @Override + public void visitBinaryExpression(JsBinaryOperation x) { + accept(x.getArg1()); + } + + @Override + public void visitConditional(JsConditional x) { + accept(x.getTestExpression()); + } + + @Override + public void visitFunction(JsFunction x) { + needsParentheses = true; + } + + @Override + public void visitInvocation(JsInvocation invocation) { + accept(invocation.getQualifier()); + } + + @Override + public void visitNameRef(JsNameRef nameRef) { + if (!nameRef.isLeaf()) { + accept(nameRef.getQualifier()); + } + } + + @Override + public void visitNew(JsNew x) { + } + + @Override + public void visitObjectLiteral(JsObjectLiteral x) { + needsParentheses = true; + } + + @Override + public void visitPostfixOperation(JsPostfixOperation x) { + accept(x.getArg()); + } + + @Override + public void visitPrefixOperation(JsPrefixOperation x) { + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.java new file mode 100644 index 00000000000..3a2d8115da9 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsPrecedenceVisitor.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 com.google.dart.compiler.backend.js.ast.*; + +/** + * 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; + + private int answer = -1; + + private JsPrecedenceVisitor() { + } + + 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; + } + + @Override + public void visitArrayAccess(JsArrayAccess x) { + answer = 16; + } + + @Override + public void visitArray(JsArrayLiteral x) { + answer = 17; // primary + } + + @Override + public void visitBinaryExpression(JsBinaryOperation x) { + answer = x.getOperator().getPrecedence(); + } + + @Override + public void visitBoolean(JsLiteral.JsBooleanLiteral x) { + answer = 17; // primary + } + + @Override + public void visitConditional(JsConditional x) { + answer = 3; + } + + @Override + public void visitFunction(JsFunction x) { + answer = 17; // primary + } + + @Override + public void visitInvocation(JsInvocation invocation) { + answer = 16; + } + + @Override + public void visitNameRef(JsNameRef nameRef) { + if (nameRef.isLeaf()) { + answer = 17; // primary + } + else { + answer = 16; // property access + } + } + + @Override + public void visitNew(JsNew x) { + answer = PRECEDENCE_NEW; + } + + @Override + public void visitNull(JsNullLiteral x) { + answer = 17; // primary + } + + @Override + public void visitInt(JsNumberLiteral.JsIntLiteral x) { + answer = 17; // primary + } + + @Override + public void visitDouble(JsNumberLiteral.JsDoubleLiteral x) { + answer = 17; // primary + } + + @Override + public void visitObjectLiteral(JsObjectLiteral x) { + answer = 17; // primary + } + + @Override + public void visitPostfixOperation(JsPostfixOperation x) { + answer = x.getOperator().getPrecedence(); + } + + @Override + public void visitPrefixOperation(JsPrefixOperation x) { + answer = x.getOperator().getPrecedence(); + } + + @Override + public void visitPropertyInitializer(JsPropertyInitializer x) { + answer = 17; // primary + } + + @Override + public void visitRegExp(JsRegExp x) { + answer = 17; // primary + } + + @Override + public void visitString(JsStringLiteral x) { + answer = 17; // primary + } + + @Override + public void visitThis(JsLiteral.JsThisRef x) { + answer = 17; // primary + } + + @Override + protected void visitElement(JsNode node) { + throw new RuntimeException("Only expressions have precedence."); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java new file mode 100644 index 00000000000..b5a13be9400 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsRequiresSemiVisitor.java @@ -0,0 +1,80 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js; + +import com.google.dart.compiler.backend.js.ast.*; + +/** + * 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 { + private boolean needsSemicolon; + + private JsRequiresSemiVisitor() { + } + + public static boolean exec(JsStatement lastStatement) { + JsRequiresSemiVisitor visitor = new JsRequiresSemiVisitor(); + visitor.accept(lastStatement); + return visitor.needsSemicolon; + } + + @Override + public void visitFor(JsFor x) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + } + + @Override + public void visitForIn(JsForIn x) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + } + + @Override + public void visitIf(JsIf x) { + 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); + } + } + + @Override + public void visitLabel(JsLabel x) { + if (x.getStatement() instanceof JsEmpty) { + needsSemicolon = true; + } + } + + @Override + public void visitWhile(JsWhile x) { + if (x.getBody() instanceof JsEmpty) { + needsSemicolon = true; + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsReservedIdentifiers.java new file mode 100644 index 00000000000..da462ef29f4 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java new file mode 100644 index 00000000000..6977040853b --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/JsToStringGenerationVisitor.java @@ -0,0 +1,1193 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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.THashSet; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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 THashSet(); + protected final TextOutput p; + + public JsToStringGenerationVisitor(TextOutput out) { + p = out; + } + + @Override + public void visitArrayAccess(JsArrayAccess x) { + printPair(x, x.getArrayExpression()); + leftSquare(); + accept(x.getIndexExpression()); + rightSquare(); + } + + @Override + public void visitArray(JsArrayLiteral x) { + leftSquare(); + printExpressions(x.getExpressions()); + rightSquare(); + } + + private void printExpressions(List expressions) { + boolean notFirst = false; + for (JsExpression expression : expressions) { + notFirst = sepCommaOptSpace(notFirst) && !(expression instanceof JsDocComment); + boolean isEnclosed = parenPushIfCommaExpression(expression); + accept(expression); + if (isEnclosed) { + rightParen(); + } + } + } + + @Override + public void visitBinaryExpression(JsBinaryOperation binaryOperation) { + JsBinaryOperator operator = binaryOperation.getOperator(); + JsExpression arg1 = binaryOperation.getArg1(); + boolean isExpressionEnclosed = parenPush(binaryOperation, arg1, !operator.isLeftAssociative()); + + accept(arg1); + if (operator.isKeyword()) { + _parenPopOrSpace(binaryOperation, arg1, !operator.isLeftAssociative()); + } + else if (operator != JsBinaryOperator.COMMA) { + if (isExpressionEnclosed) { + rightParen(); + } + spaceOpt(); + } + + p.print(operator.getSymbol()); + + JsExpression arg2 = binaryOperation.getArg2(); + boolean isParenOpened; + if (operator == JsBinaryOperator.COMMA) { + isParenOpened = false; + spaceOpt(); + } + else if (arg2 instanceof JsBinaryOperation && ((JsBinaryOperation) arg2).getOperator() == JsBinaryOperator.AND) { + spaceOpt(); + leftParen(); + isParenOpened = true; + } + else { + if (spaceCalc(operator, arg2)) { + isParenOpened = _parenPushOrSpace(binaryOperation, arg2, operator.isLeftAssociative()); + } + else { + spaceOpt(); + isParenOpened = parenPush(binaryOperation, arg2, operator.isLeftAssociative()); + } + } + accept(arg2); + if (isParenOpened) { + rightParen(); + } + } + + @Override + public void visitBlock(JsBlock x) { + printJsBlock(x, true, true); + } + + @Override + public void visitBoolean(JsLiteral.JsBooleanLiteral x) { + if (x.getValue()) { + p.print(CHARS_TRUE); + } + else { + p.print(CHARS_FALSE); + } + } + + @Override + public void visitBreak(JsBreak x) { + p.print(CHARS_BREAK); + continueOrBreakLabel(x); + } + + @Override + public void visitContinue(JsContinue x) { + p.print(CHARS_CONTINUE); + continueOrBreakLabel(x); + } + + private void continueOrBreakLabel(JsContinue x) { + String label = x.getLabel(); + if (label != null) { + space(); + p.print(label); + } + } + + @Override + public void visitCase(JsCase x) { + p.print(CHARS_CASE); + space(); + accept(x.getCaseExpression()); + _colon(); + newlineOpt(); + + printSwitchMemberStatements(x); + } + + private void printSwitchMemberStatements(JsSwitchMember x) { + p.indentIn(); + for (JsStatement stmt : x.getStatements()) { + needSemi = true; + accept(stmt); + if (needSemi) { + semi(); + } + newlineOpt(); + } + p.indentOut(); + needSemi = false; + } + + @Override + public void visitCatch(JsCatch x) { + spaceOpt(); + p.print(CHARS_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()); + } + + @Override + public void visitConditional(JsConditional x) { + // 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(); + p.print('?'); + spaceOpt(); + printPair(x, x.getThenExpression()); + spaceOpt(); + _colon(); + spaceOpt(); + printPair(x, x.getElseExpression()); + } + + private void printPair(JsExpression parent, JsExpression expression) { + boolean isNeedParen = parenCalc(parent, expression, false); + if (isNeedParen) { + leftParen(); + } + accept(expression); + if (isNeedParen) { + rightParen(); + } + } + + @Override + public void visitDebugger(JsDebugger x) { + p.print(CHARS_DEBUGGER); + } + + @Override + public void visitDefault(JsDefault x) { + p.print(CHARS_DEFAULT); + _colon(); + + printSwitchMemberStatements(x); + } + + @Override + public void visitWhile(JsWhile x) { + _while(); + spaceOpt(); + leftParen(); + accept(x.getCondition()); + rightParen(); + nestedPush(x.getBody()); + accept(x.getBody()); + nestedPop(x.getBody()); + } + + @Override + public void visitDoWhile(JsDoWhile x) { + p.print(CHARS_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(); + } + + @Override + public void visitEmpty(JsEmpty x) { + } + + @Override + public void visitExpressionStatement(JsExpressionStatement x) { + boolean surroundWithParentheses = JsFirstExpressionVisitor.exec(x); + if (surroundWithParentheses) { + leftParen(); + } + accept(x.getExpression()); + if (surroundWithParentheses) { + rightParen(); + } + } + + @Override + public void visitFor(JsFor x) { + _for(); + spaceOpt(); + leftParen(); + + // The init expressions or var decl. + // + if (x.getInitExpression() != null) { + accept(x.getInitExpression()); + } + 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.getIncrementExpression() != null) { + spaceOpt(); + accept(x.getIncrementExpression()); + } + + rightParen(); + nestedPush(x.getBody()); + accept(x.getBody()); + nestedPop(x.getBody()); + } + + @Override + public void visitForIn(JsForIn x) { + _for(); + spaceOpt(); + leftParen(); + + if (x.getIterVarName() != null) { + var(); + space(); + nameDef(x.getIterVarName()); + + if (x.getIterExpression() != null) { + spaceOpt(); + assignment(); + spaceOpt(); + accept(x.getIterExpression()); + } + } + else { + // Just a name ref. + // + accept(x.getIterExpression()); + } + + space(); + p.print(CHARS_IN); + space(); + accept(x.getObjectExpression()); + + rightParen(); + nestedPush(x.getBody()); + accept(x.getBody()); + nestedPop(x.getBody()); + } + + @Override + public void visitFunction(JsFunction x) { + p.print(CHARS_FUNCTION); + space(); + if (x.getName() != null) { + nameOf(x); + } + + leftParen(); + boolean notFirst = false; + for (Object element : x.getParameters()) { + JsParameter param = (JsParameter) element; + notFirst = sepCommaOptSpace(notFirst); + accept(param); + } + rightParen(); + space(); + + lineBreakAfterBlock = false; + accept(x.getBody()); + needSemi = true; + } + + @Override + public void visitIf(JsIf x) { + _if(); + spaceOpt(); + leftParen(); + accept(x.getIfExpression()); + rightParen(); + JsStatement thenStmt = x.getThenStatement(); + nestedPush(thenStmt); + accept(thenStmt); + nestedPop(thenStmt); + JsStatement elseStatement = x.getElseStatement(); + if (elseStatement != null) { + if (needSemi) { + semi(); + newlineOpt(); + } + else { + spaceOpt(); + needSemi = true; + } + p.print(CHARS_ELSE); + boolean elseIf = elseStatement instanceof JsIf; + if (!elseIf) { + nestedPush(elseStatement); + } + else { + space(); + } + accept(elseStatement); + if (!elseIf) { + nestedPop(elseStatement); + } + } + } + + @Override + public void visitInvocation(JsInvocation invocation) { + printPair(invocation, invocation.getQualifier()); + + leftParen(); + printExpressions(invocation.getArguments()); + rightParen(); + } + + @Override + public void visitLabel(JsLabel x) { + nameOf(x); + _colon(); + spaceOpt(); + accept(x.getStatement()); + } + + @Override + public void visitNameRef(JsNameRef nameRef) { + JsExpression qualifier = nameRef.getQualifier(); + if (qualifier != null) { + final boolean enclose; + if (qualifier instanceof JsLiteral.JsValueLiteral) { + // "42.foo" is not allowed, but "(42).foo" is. + enclose = qualifier instanceof JsNumberLiteral; + } + else { + enclose = parenCalc(nameRef, qualifier, false); + } + + if (enclose) { + leftParen(); + } + accept(qualifier); + if (enclose) { + rightParen(); + } + p.print('.'); + } + + p.maybeIndent(); + beforeNodePrinted(nameRef); + p.print(nameRef.getIdent()); + } + + protected void beforeNodePrinted(JsNode node) { + } + + @Override + public void visitNew(JsNew x) { + p.print(CHARS_NEW); + space(); + + JsExpression constructorExpression = x.getConstructorExpression(); + boolean needsParens = JsConstructExpressionVisitor.exec(constructorExpression); + if (needsParens) { + leftParen(); + } + accept(constructorExpression); + if (needsParens) { + rightParen(); + } + + leftParen(); + printExpressions(x.getArguments()); + rightParen(); + } + + @Override + public void visitNull(JsNullLiteral x) { + p.print(CHARS_NULL); + } + + @Override + public void visitInt(JsIntLiteral x) { + p.print(x.value); + } + + @Override + public void visitDouble(JsDoubleLiteral x) { + p.print(x.value); + } + + @Override + public void visitObjectLiteral(JsObjectLiteral objectLiteral) { + p.print('{'); + if (objectLiteral.isMultiline()) { + p.indentIn(); + } + + boolean notFirst = false; + for (JsPropertyInitializer item : objectLiteral.getPropertyInitializers()) { + if (notFirst) { + p.print(','); + } + + if (objectLiteral.isMultiline()) { + newlineOpt(); + } + else if (notFirst) { + spaceOpt(); + } + + notFirst = 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(); + } + + p.print('}'); + } + + @Override + public void visitParameter(JsParameter x) { + nameOf(x); + } + + @Override + public void visitPostfixOperation(JsPostfixOperation x) { + JsUnaryOperator op = x.getOperator(); + JsExpression arg = x.getArg(); + // unary operators always associate correctly (I think) + printPair(x, arg); + p.print(op.getSymbol()); + } + + @Override + public void visitPrefixOperation(JsPrefixOperation x) { + 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); + } + + @Override + public void visitProgram(JsProgram x) { + p.print(""); + } + + @Override + public void visitProgramFragment(JsProgramFragment x) { + p.print(""); + } + + @Override + public void visitRegExp(JsRegExp x) { + slash(); + p.print(x.getPattern()); + slash(); + String flags = x.getFlags(); + if (flags != null) { + p.print(flags); + } + } + + @Override + public void visitReturn(JsReturn x) { + p.print(CHARS_RETURN); + JsExpression expr = x.getExpression(); + if (expr != null) { + space(); + accept(expr); + } + } + + @Override + public void visitString(JsStringLiteral x) { + p.print(javaScriptString(x.getValue())); + } + + @Override + public void visit(JsSwitch x) { + p.print(CHARS_SWITCH); + spaceOpt(); + leftParen(); + accept(x.getExpression()); + rightParen(); + spaceOpt(); + blockOpen(); + acceptList(x.getCases()); + blockClose(); + } + + @Override + public void visitThis(JsLiteral.JsThisRef x) { + p.print(CHARS_THIS); + } + + @Override + public void visitThrow(JsThrow x) { + p.print(CHARS_THROW); + space(); + accept(x.getExpression()); + } + + @Override + public void visitTry(JsTry x) { + p.print(CHARS_TRY); + spaceOpt(); + accept(x.getTryBlock()); + + acceptList(x.getCatches()); + + JsBlock finallyBlock = x.getFinallyBlock(); + if (finallyBlock != null) { + p.print(CHARS_FINALLY); + spaceOpt(); + accept(finallyBlock); + } + } + + @Override + public void visit(JsVar var) { + nameOf(var); + JsExpression initExpr = var.getInitExpression(); + if (initExpr != null) { + spaceOpt(); + assignment(); + spaceOpt(); + boolean isEnclosed = parenPushIfCommaExpression(initExpr); + accept(initExpr); + if (isEnclosed) { + rightParen(); + } + } + } + + @Override + public void visitVars(JsVars vars) { + var(); + space(); + boolean sep = false; + for (JsVar var : vars) { + if (sep) { + if (vars.isMultiline()) { + newlineOpt(); + } + p.print(','); + spaceOpt(); + } + else { + sep = true; + } + + accept(var); + } + } + + @Override + public void visitDocComment(JsDocComment comment) { + boolean asSingleLine = comment.getTags().size() == 1; + if (!asSingleLine) { + newlineOpt(); + } + p.print("/**"); + if (asSingleLine) { + space(); + } + else { + p.newline(); + } + + boolean notFirst = false; + for (Map.Entry entry : comment.getTags().entrySet()) { + if (notFirst) { + p.newline(); + p.print(' '); + p.print('*'); + } + else { + notFirst = true; + } + + p.print('@'); + p.print(entry.getKey()); + Object value = entry.getValue(); + if (value != null) { + space(); + if (value instanceof CharSequence) { + p.print((CharSequence) value); + } + else { + visitNameRef((JsNameRef) value); + } + } + + if (!asSingleLine) { + p.newline(); + } + } + + if (asSingleLine) { + space(); + } + else { + newlineOpt(); + } + + p.print('*'); + p.print('/'); + if (asSingleLine) { + spaceOpt(); + } + } + + protected final void newlineOpt() { + if (!p.isCompact()) { + p.newline(); + } + } + + 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 stmtIsGlobalBlock = false; + if (isGlobal) { + if (statement instanceof JsBlock) { + // A block inside a global block is still considered global + stmtIsGlobalBlock = true; + globalBlocks.add((JsBlock) statement); + } + } + + 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 JsExpressionStatement && ((JsExpressionStatement) 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 { + p.newline(); + } + } + else { + if (lastStatement) { + p.printOpt(';'); + } + else { + semi(); + } + newlineOpt(); + } + } + ++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 _colon() { + p.print(':'); + } + + private void _for() { + p.print(CHARS_FOR); + } + + private void _if() { + p.print(CHARS_IF); + } + + private void leftParen() { + p.print('('); + } + + private void leftSquare() { + p.print('['); + } + + private void nameDef(JsName name) { + p.print(name.getIdent()); + } + + private void nameOf(HasName hasName) { + nameDef(hasName.getName()); + } + + 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 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 _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 rightParen() { + p.print(')'); + } + + private void rightSquare() { + p.print(']'); + } + + private void semi() { + p.print(';'); + } + + 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. + */ + return binary.getOperator().getPrecedence() > op.getPrecedence() && spaceCalc(op, binary.getArg1()); + } + 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 var() { + p.print(CHARS_VAR); + } + + private void _while() { + p.print(CHARS_WHILE); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/AbstractNode.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/AbstractNode.java new file mode 100644 index 00000000000..d2d906be326 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/AbstractNode.java @@ -0,0 +1,13 @@ +package com.google.dart.compiler.backend.js.ast; + +import com.google.dart.compiler.backend.js.JsToStringGenerationVisitor; +import com.google.dart.compiler.util.TextOutputImpl; + +abstract class AbstractNode implements JsNode { + @Override + public String toString() { + TextOutputImpl out = new TextOutputImpl(); + new JsToStringGenerationVisitor(out).accept(this); + return out.toString(); + } +} \ No newline at end of file diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/ChameleonJsExpression.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/ChameleonJsExpression.java new file mode 100644 index 00000000000..c50d40e8166 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/ChameleonJsExpression.java @@ -0,0 +1,51 @@ +package com.google.dart.compiler.backend.js.ast; + +public class ChameleonJsExpression implements JsExpression { + private JsExpression expression; + + public ChameleonJsExpression(JsExpression initialExpression) { + expression = initialExpression; + } + + public ChameleonJsExpression() { + } + + public void resolve(JsExpression expression) { + this.expression = expression; + } + + @Override + public boolean isLeaf() { + return expression.isLeaf(); + } + + @Override + public JsStatement makeStmt() { + return expression.makeStmt(); + } + + @Override + public void accept(JsVisitor visitor) { + expression.accept(visitor); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + expression.acceptChildren(visitor); + } + + @Override + public Object getSource() { + return expression.getSource(); + } + + @Override + public void setSource(Object info) { + expression.setSource(info); + } + + @Override + public JsExpression source(Object info) { + return expression.source(info); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasArguments.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasArguments.java new file mode 100644 index 00000000000..29b12859174 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasArguments.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 java.util.List; + +/** + * Implemented by JavaScript objects that accept arguments. + */ +public interface HasArguments extends JsExpression { + List getArguments(); +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasCondition.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasCondition.java new file mode 100644 index 00000000000..082f4e15eb3 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasName.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasName.java new file mode 100644 index 00000000000..bdd87970929 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.java new file mode 100644 index 00000000000..4a0475f224a --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsArrayAccess.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.backend.js.ast; + +/** + * Represents a javascript expression for array access. + */ +public final class JsArrayAccess extends JsExpressionImpl { + private JsExpression arrayExpression; + private JsExpression indexExpression; + + public JsArrayAccess() { + super(); + } + + public JsArrayAccess(JsExpression arrayExpression, JsExpression indexExpression) { + this.arrayExpression = arrayExpression; + this.indexExpression = indexExpression; + } + + public JsExpression getArrayExpression() { + return arrayExpression; + } + + public JsExpression getIndexExpression() { + return indexExpression; + } + + public void setArrayExpression(JsExpression arrayExpression) { + this.arrayExpression = arrayExpression; + } + + public void setIndexExpression(JsExpression indexExpression) { + this.indexExpression = indexExpression; + } + + @Override + public void accept(JsVisitor v) { + v.visitArrayAccess(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(arrayExpression); + visitor.accept(indexExpression); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.java new file mode 100644 index 00000000000..3398d8ee345 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsArrayLiteral.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.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 void accept(JsVisitor v) { + v.visitArray(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.acceptWithInsertRemove(expressions); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java new file mode 100644 index 00000000000..d13cd077aab --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperation.java @@ -0,0 +1,51 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import org.jetbrains.annotations.Nullable; + +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 void accept(JsVisitor v) { + v.visitBinaryExpression(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + if (op.isAssignment()) { + visitor.acceptLvalue(arg1); + } + else { + visitor.accept(arg1); + } + visitor.accept(arg2); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBinaryOperator.java new file mode 100644 index 00000000000..54ed2b7ca97 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBlock.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBlock.java new file mode 100644 index 00000000000..f27a4cae049 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBlock.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.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 SourceInfoAwareJsNode 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 accept(JsVisitor v) { + v.visitBlock(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.acceptWithInsertRemove(statements); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBreak.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBreak.java new file mode 100644 index 00000000000..82f53548696 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsBreak.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; + +/** + * Represents the JavaScript break statement. + */ +public final class JsBreak extends JsContinue { + public JsBreak() { + super(null); + } + + public JsBreak(String label) { + super(label); + } + + @Override + public void accept(JsVisitor v) { + v.visitBreak(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCase.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCase.java new file mode 100644 index 00000000000..0f9cf3270de --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCase.java @@ -0,0 +1,35 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents the JavaScript case statement. + */ +public final class JsCase extends JsSwitchMember { + private JsExpression caseExpression; + + public JsCase() { + super(); + } + + public JsExpression getCaseExpression() { + return caseExpression; + } + + public void setCaseExpression(JsExpression caseExpression) { + this.caseExpression = caseExpression; + } + + @Override + public void accept(JsVisitor v) { + v.visitCase(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(caseExpression); + super.acceptChildren(visitor); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java new file mode 100644 index 00000000000..41d431cb2d3 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java @@ -0,0 +1,63 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript catch clause. + */ +public class JsCatch extends SourceInfoAwareJsNode 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 accept(JsVisitor v) { + v.visitCatch(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(param); + if (condition != null) { + visitor.accept(condition); + } + visitor.accept(body); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java new file mode 100644 index 00000000000..20f0add6790 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsConditional.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsConditional.java new file mode 100644 index 00000000000..597891e1e7d --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsConditional.java @@ -0,0 +1,56 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +public final class JsConditional extends JsExpressionImpl { + private JsExpression testExpression; + private JsExpression elseExpression; + private JsExpression thenExpression; + + public JsConditional() { + } + + public JsConditional(JsExpression testExpression, JsExpression thenExpression, JsExpression elseExpression) { + this.testExpression = testExpression; + this.thenExpression = thenExpression; + this.elseExpression = elseExpression; + } + + public JsExpression getElseExpression() { + return elseExpression; + } + + public JsExpression getTestExpression() { + return testExpression; + } + + public JsExpression getThenExpression() { + return thenExpression; + } + + public void setElseExpression(JsExpression elseExpression) { + this.elseExpression = elseExpression; + } + + public void setTestExpression(JsExpression testExpression) { + this.testExpression = testExpression; + } + + public void setThenExpression(JsExpression thenExpression) { + this.thenExpression = thenExpression; + } + + @Override + public void accept(JsVisitor v) { + v.visitConditional(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(testExpression); + visitor.accept(thenExpression); + visitor.accept(elseExpression); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsContext.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsContext.java new file mode 100644 index 00000000000..35b8c74769e --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsContext.java @@ -0,0 +1,26 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * 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(JsNode node); + + void insertBefore(JsNode node); + + boolean isLvalue(); + + void removeMe(); + + void replaceMe(JsNode node); +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsContinue.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsContinue.java new file mode 100644 index 00000000000..d5b5d60ab60 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsContinue.java @@ -0,0 +1,29 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import org.jetbrains.annotations.Nullable; + +public class JsContinue extends SourceInfoAwareJsNode implements JsStatement { + protected final String label; + + public JsContinue() { + this(null); + } + + public JsContinue(@Nullable String label) { + super(); + this.label = label; + } + + public String getLabel() { + return label; + } + + @Override + public void accept(JsVisitor v) { + v.visitContinue(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDebugger.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDebugger.java new file mode 100644 index 00000000000..cbf6063e986 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDebugger.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; + +/** + * Represents a JavaScript debugger statement. + */ +public class JsDebugger extends SourceInfoAwareJsNode implements JsStatement { + public JsDebugger() { + } + + @Override + public void accept(JsVisitor v) { + v.visitDebugger(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDefault.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDefault.java new file mode 100644 index 00000000000..330005ddd57 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDefault.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; + +/** + * Represents the default option in a JavaScript swtich statement. + */ +public final class JsDefault extends JsSwitchMember { + @Override + public void accept(JsVisitor v) { + v.visitDefault(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java new file mode 100644 index 00000000000..f23b08c26e4 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDoWhile.java @@ -0,0 +1,22 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * Represents a JavaScript do..while statement. + */ +public class JsDoWhile extends JsWhile { + public JsDoWhile() { + } + + public JsDoWhile(JsExpression condition, JsStatement body) { + super(condition, body); + } + + @Override + public void accept(JsVisitor v) { + v.visitDoWhile(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDocComment.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDocComment.java new file mode 100644 index 00000000000..292c9070ab8 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsDocComment.java @@ -0,0 +1,29 @@ +package com.google.dart.compiler.backend.js.ast; + +import java.util.Collections; +import java.util.Map; + +public class JsDocComment extends JsExpressionImpl { + private final Map tags; + + public JsDocComment(Map tags) { + this.tags = tags; + } + + public Map getTags() { + return tags; + } + + public JsDocComment(String tagName, JsNameRef tagValue) { + tags = Collections.singletonMap(tagName, tagValue); + } + + public JsDocComment(String tagName, String tagValue) { + tags = Collections.singletonMap(tagName, tagValue); + } + + @Override + public void accept(JsVisitor v) { + v.visitDocComment(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsEmpty.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsEmpty.java new file mode 100644 index 00000000000..602e1e63308 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsEmpty.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; + +public class JsEmpty extends SourceInfoAwareJsNode implements JsStatement { + JsEmpty() { + } + + @Override + public void accept(JsVisitor v) { + v.visitEmpty(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpression.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpression.java new file mode 100644 index 00000000000..5209a21eb64 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpression.java @@ -0,0 +1,10 @@ +package com.google.dart.compiler.backend.js.ast; + +public interface JsExpression extends JsNode { + boolean isLeaf(); + + JsStatement makeStmt(); + + @Override + JsExpression source(Object info); +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpressionImpl.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpressionImpl.java new file mode 100644 index 00000000000..f1f5bed3399 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpressionImpl.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.backend.js.ast; + +import java.util.List; + +abstract class JsExpressionImpl extends SourceInfoAwareJsNode implements JsExpression { + /** + * 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 JsExpressionStatement(this); + } + + protected abstract static class JsExpressionHasArguments extends JsExpressionImpl implements HasArguments { + protected final List arguments; + + public JsExpressionHasArguments(List arguments) { + this.arguments = arguments; + } + + @Override + public List getArguments() { + return arguments; + } + } + + @Override + public JsExpression source(Object info) { + setSource(info); + return this; + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpressionStatement.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpressionStatement.java new file mode 100644 index 00000000000..85a7fe6bee4 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsExpressionStatement.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; + +public final class JsExpressionStatement extends AbstractNode implements JsStatement { + private JsExpression expression; + + public JsExpressionStatement(JsExpression expression) { + this.expression = expression; + } + + public JsExpression getExpression() { + return expression; + } + + @Override + public void accept(JsVisitor v) { + v.visitExpressionStatement(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(expression); + } + + @Override + public Object getSource() { + return null; + } + + @Override + public void setSource(Object info) { + throw new IllegalStateException("You must not set source info for JsExpressionStatement, set for expression"); + } + + @Override + public JsNode source(Object info) { + throw new IllegalStateException("You must not set source info for JsExpressionStatement, set for expression"); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFor.java new file mode 100644 index 00000000000..b7387ec0101 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFor.java @@ -0,0 +1,98 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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 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 #getInitExpression()} 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 SourceInfoAwareJsNode implements JsStatement { + private JsStatement body; + private JsExpression condition; + private JsExpression incrementExpression; + private JsExpression initExpression; + private JsVars initVars; + + public JsFor(JsVars initVars, JsExpression condition, JsExpression incrementExpression) { + this(initVars, condition, incrementExpression, null); + } + + public JsFor(JsVars initVars, JsExpression condition, JsExpression incrementExpression, JsStatement body) { + this.initVars = initVars; + this.incrementExpression = incrementExpression; + this.condition = condition; + this.body = body; + initExpression = null; + } + + public JsFor(JsExpression initExpression, JsExpression condition, JsExpression incrementExpression) { + this(initExpression, condition, incrementExpression, null); + } + + public JsFor(JsExpression initExpression, JsExpression condition, JsExpression incrementExpression, JsStatement body) { + this.initExpression = initExpression; + this.incrementExpression = incrementExpression; + this.condition = condition; + this.body = body; + initVars = null; + } + + public JsStatement getBody() { + return body; + } + + public JsExpression getCondition() { + return condition; + } + + public JsExpression getIncrementExpression() { + return incrementExpression; + } + + public JsExpression getInitExpression() { + return initExpression; + } + + public JsVars getInitVars() { + return initVars; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + @Override + public void accept(JsVisitor v) { + v.visitFor(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + assert (!(initExpression != null && initVars != null)); + + if (initExpression != null) { + visitor.accept(initExpression); + } + else if (initVars != null) { + visitor.accept(initVars); + } + + if (condition != null) { + visitor.accept(condition); + } + + if (incrementExpression != null) { + visitor.accept(incrementExpression); + } + visitor.accept(body); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsForIn.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsForIn.java new file mode 100644 index 00000000000..3fc4a915abf --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsForIn.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; + +public class JsForIn extends SourceInfoAwareJsNode implements JsStatement { + private JsStatement body; + private JsExpression iterExpression; + private JsExpression objectExpression; + + // 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 getIterExpression() { + return iterExpression; + } + + public JsName getIterVarName() { + return iterVarName; + } + + public JsExpression getObjectExpression() { + return objectExpression; + } + + public void setBody(JsStatement body) { + this.body = body; + } + + public void setIterExpression(JsExpression iterExpression) { + this.iterExpression = iterExpression; + } + + public void setObjectExpression(JsExpression objectExpression) { + this.objectExpression = objectExpression; + } + + @Override + public void accept(JsVisitor v) { + v.visitForIn(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + if (iterExpression != null) { + visitor.acceptLvalue(iterExpression); + } + visitor.accept(objectExpression); + visitor.accept(body); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java new file mode 100644 index 00000000000..1bc7b301b8a --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java @@ -0,0 +1,76 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +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; + 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; + } + + public void setBody(JsBlock body) { + this.body = body; + } + + public void setName(@Nullable JsName name) { + this.name = name; + } + + @Override + public void accept(JsVisitor v) { + v.visitFunction(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.acceptWithInsertRemove(params); + visitor.accept(body); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsGlobalBlock.java new file mode 100644 index 00000000000..11bbd10d86e --- /dev/null +++ b/js/js.dart-ast/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; + +/** + * Represents a JavaScript block in the global scope. + */ +public class JsGlobalBlock extends JsBlock { + + public JsGlobalBlock() { + } + + @Override + public boolean isGlobalBlock() { + return true; + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java new file mode 100644 index 00000000000..18af5ffb414 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsIf.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 if statement. + */ +public final class JsIf extends SourceInfoAwareJsNode 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 elseStatement) { + this.elseStatement = elseStatement; + } + + public void setIfExpression(JsExpression ifExpression) { + this.ifExpression = ifExpression; + } + + public void setThenStatement(JsStatement thenStatement) { + this.thenStatement = thenStatement; + } + + @Override + public void accept(JsVisitor v) { + v.visitIf(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(ifExpression); + visitor.accept(thenStatement); + if (elseStatement != null) { + visitor.accept(elseStatement); + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java new file mode 100644 index 00000000000..d7569b12f4b --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsInvocation.java @@ -0,0 +1,61 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.intellij.util.SmartList; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public final class JsInvocation extends JsExpressionImpl.JsExpressionHasArguments { + private JsExpression qualifier; + + public JsInvocation() { + super(new SmartList()); + } + + public JsInvocation(JsExpression qualifier, List arguments) { + super(arguments); + this.qualifier = qualifier; + } + + 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; + } + + public void setQualifier(JsExpression qualifier) { + this.qualifier = qualifier; + } + + @Override + public void accept(JsVisitor v) { + v.visitInvocation(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(qualifier); + visitor.acceptList(arguments); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java new file mode 100644 index 00000000000..a3abb3392bc --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.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; + +/** + * Represents a JavaScript label statement. + */ +public class JsLabel extends SourceInfoAwareJsNode implements JsStatement, HasName { + private final JsName label; + + private JsStatement statement; + + public JsLabel(JsName label) { + this.label = label; + } + + public JsLabel(JsName label, JsStatement statement) { + this.label = label; + this.statement = statement; + } + + @Override + public JsName getName() { + return label; + } + + @Override + public Symbol getSymbol() { + return label; + } + + public JsStatement getStatement() { + return statement; + } + + public void setStatement(JsStatement statement) { + this.statement = statement; + } + + @Override + public void accept(JsVisitor v) { + v.visitLabel(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(statement); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java new file mode 100644 index 00000000000..8823d12b557 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLiteral.java @@ -0,0 +1,61 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +public abstract class JsLiteral extends JsExpressionImpl { + 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 void accept(JsVisitor v) { + v.visitThis(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 void accept(JsVisitor v) { + v.visitBoolean(this); + } + } + + /** + * A JavaScript string literal expression. + */ + public abstract static class JsValueLiteral extends JsLiteral { + protected JsValueLiteral() { + } + + @Override + public final boolean isLeaf() { + return true; + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsName.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsName.java new file mode 100644 index 00000000000..44da16f6610 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java new file mode 100644 index 00000000000..6fb7bc3e5ae --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java @@ -0,0 +1,82 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.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 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 isLeaf() { + return qualifier == null; + } + + public void resolve(JsName name) { + this.name = name; + ident = null; + } + + public void setQualifier(JsExpression qualifier) { + this.qualifier = qualifier; + } + + @Override + public void accept(JsVisitor v) { + v.visitNameRef(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + if (qualifier != null) { + visitor.accept(qualifier); + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNew.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNew.java new file mode 100644 index 00000000000..a5283f284c0 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNew.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; + +import com.intellij.util.SmartList; + +import java.util.List; + +public final class JsNew extends JsExpressionImpl.JsExpressionHasArguments { + private JsExpression constructorExpression; + + public JsNew(JsExpression constructorExpression) { + this(constructorExpression, new SmartList()); + } + + public JsNew(JsExpression constructorExpression, List arguments) { + super(arguments); + this.constructorExpression = constructorExpression; + } + + public JsExpression getConstructorExpression() { + return constructorExpression; + } + + @Override + public void accept(JsVisitor v) { + v.visitNew(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(constructorExpression); + visitor.acceptList(arguments); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNode.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNode.java new file mode 100644 index 00000000000..6d2ad279985 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNode.java @@ -0,0 +1,30 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +public interface JsNode { + /** + * Causes this object to have the visitor visit itself and its children. + * + * @param visitor the visitor that should traverse this node + */ + void accept(JsVisitor visitor); + + void acceptChildren(JsVisitor visitor); + + /** + * Return the source info associated with this object. + */ + Object getSource(); + + /** + * Set the source info associated with this object. + * + * @param info + */ + void setSource(Object info); + + JsNode source(Object info); +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.java new file mode 100644 index 00000000000..8aec6b41e16 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNullLiteral.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; + +public final class JsNullLiteral extends JsLiteral.JsValueLiteral { + JsNullLiteral() { + } + + @Override + public void accept(JsVisitor v) { + v.visitNull(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.java new file mode 100644 index 00000000000..6dc3d156754 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNumberLiteral.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 abstract class JsNumberLiteral extends JsLiteral.JsValueLiteral { + public static final class JsDoubleLiteral extends JsNumberLiteral { + public final double value; + + JsDoubleLiteral(double value) { + this.value = value; + } + + @Override + public void accept(JsVisitor v) { + v.visitDouble(this); + } + + 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 void accept(JsVisitor v) { + v.visitInt(this); + } + + public String toString() { + return String.valueOf(value); + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java new file mode 100644 index 00000000000..efa53053914 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsObjectLiteral.java @@ -0,0 +1,50 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import com.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 void accept(JsVisitor v) { + v.visitObjectLiteral(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.acceptWithInsertRemove(properties); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsOperator.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsOperator.java new file mode 100644 index 00000000000..844805fcc73 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java new file mode 100644 index 00000000000..9167a30ece9 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.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; + +import com.google.dart.compiler.common.Symbol; + +/** + * A JavaScript parameter. + */ +public final class JsParameter extends SourceInfoAwareJsNode 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 accept(JsVisitor v) { + v.visitParameter(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java new file mode 100644 index 00000000000..df59b0d1f5a --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPostfixOperation.java @@ -0,0 +1,20 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +public final class JsPostfixOperation extends JsUnaryOperation { + public JsPostfixOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsPostfixOperation(JsUnaryOperator op, JsExpression arg) { + super(op, arg); + } + + @Override + public void accept(JsVisitor v) { + v.visitPostfixOperation(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java new file mode 100644 index 00000000000..552d2e9eec2 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPrefixOperation.java @@ -0,0 +1,20 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +public final class JsPrefixOperation extends JsUnaryOperation { + public JsPrefixOperation(JsUnaryOperator op) { + this(op, null); + } + + public JsPrefixOperation(JsUnaryOperator op, JsExpression arg) { + super(op, arg); + } + + @Override + public void accept(JsVisitor v) { + v.visitPrefixOperation(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java new file mode 100644 index 00000000000..2954c33321d --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java @@ -0,0 +1,121 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import gnu.trove.TDoubleObjectHashMap; +import gnu.trove.THashMap; +import gnu.trove.TIntObjectHashMap; + +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 SourceInfoAwareJsNode { + private final JsEmpty emptyStatement; + + 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; + + public JsProgram(String unitId) { + rootScope = new JsRootScope(this); + topScope = new JsScope(rootScope, "Global", unitId); + setFragmentCount(1); + + emptyStatement = new JsEmpty(); + } + + public JsEmpty getEmptyStatement() { + return emptyStatement; + } + + 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 accept(JsVisitor v) { + v.visitProgram(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + for (JsProgramFragment fragment : fragments) { + visitor.accept(fragment); + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java new file mode 100644 index 00000000000..e9691878c11 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgramFragment.java @@ -0,0 +1,30 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * One independently loadable fragment of a {@link JsProgram}. + */ +public class JsProgramFragment extends SourceInfoAwareJsNode { + private final JsGlobalBlock globalBlock; + + public JsProgramFragment() { + globalBlock = new JsGlobalBlock(); + } + + public JsBlock getGlobalBlock() { + return globalBlock; + } + + @Override + public void accept(JsVisitor v) { + v.visitProgramFragment(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(globalBlock); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.java new file mode 100644 index 00000000000..f38885b7316 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsPropertyInitializer.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 org.jetbrains.annotations.NotNull; + +/** + * Used in object literals to specify property values by name. + */ +public class JsPropertyInitializer extends SourceInfoAwareJsNode { + 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 void setValueExpr(@NotNull JsExpression valueExpr) { + this.valueExpr = valueExpr; + } + + @Override + public void accept(JsVisitor v) { + v.visitPropertyInitializer(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(labelExpr); + visitor.accept(valueExpr); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsRegExp.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsRegExp.java new file mode 100644 index 00000000000..e958b368f39 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsRegExp.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; + +public final class JsRegExp extends JsLiteral.JsValueLiteral { + private String flags; + private String pattern; + + public JsRegExp() { + } + + public String getFlags() { + return flags; + } + + public String getPattern() { + return pattern; + } + + public void setFlags(String suffix) { + flags = suffix; + } + + public void setPattern(String re) { + pattern = re; + } + + @Override + public void accept(JsVisitor v) { + v.visitRegExp(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsReturn.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsReturn.java new file mode 100644 index 00000000000..93c0106d802 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsReturn.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; + +/** + * A JavaScript return statement. + */ +public final class JsReturn extends SourceInfoAwareJsNode implements JsStatement { + private JsExpression expression; + + public JsReturn() { + } + + public JsReturn(JsExpression expression) { + this.expression = expression; + } + + public JsExpression getExpression() { + return expression; + } + + public void setExpression(JsExpression expression) { + this.expression = expression; + } + + @Override + public void accept(JsVisitor v) { + v.visitReturn(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + if (expression != null) { + visitor.accept(expression); + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsRootScope.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsRootScope.java new file mode 100644 index 00000000000..e808eb0f576 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java new file mode 100644 index 00000000000..abe81550df7 --- /dev/null +++ b/js/js.dart-ast/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; + parent = null; + 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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsStatement.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsStatement.java new file mode 100644 index 00000000000..a3ea11cec92 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.java new file mode 100644 index 00000000000..08d9d40fbf7 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsStringLiteral.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; + +public final class JsStringLiteral extends JsLiteral.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 void accept(JsVisitor v) { + v.visitString(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java new file mode 100644 index 00000000000..763c6c1feeb --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsSwitch.java @@ -0,0 +1,44 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * A JavaScript switch statement. + */ +public class JsSwitch extends SourceInfoAwareJsNode implements JsStatement { + + private final List cases = new ArrayList(); + private JsExpression expression; + + public JsSwitch() { + super(); + } + + public List getCases() { + return cases; + } + + public JsExpression getExpression() { + return expression; + } + + public void setExpression(JsExpression expression) { + this.expression = expression; + } + + @Override + public void accept(JsVisitor v) { + v.visit(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(expression); + visitor.acceptWithInsertRemove(cases); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java new file mode 100644 index 00000000000..225369525ff --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsSwitchMember.java @@ -0,0 +1,29 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.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 SourceInfoAwareJsNode { + protected final List statements = new SmartList(); + + protected JsSwitchMember() { + super(); + } + + public List getStatements() { + return statements; + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.acceptWithInsertRemove(statements); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsThrow.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsThrow.java new file mode 100644 index 00000000000..c7814d73c58 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsThrow.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; + +public class JsThrow extends SourceInfoAwareJsNode 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 accept(JsVisitor v) { + v.visitThrow(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(expression); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsTry.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsTry.java new file mode 100644 index 00000000000..f559b340fd6 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsTry.java @@ -0,0 +1,63 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.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 SourceInfoAwareJsNode 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) { + finallyBlock = block; + } + + public void setTryBlock(JsBlock block) { + tryBlock = block; + } + + @Override + public void accept(JsVisitor v) { + v.visitTry(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(tryBlock); + visitor.acceptWithInsertRemove(catches); + if (finallyBlock != null) { + visitor.accept(finallyBlock); + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java new file mode 100644 index 00000000000..9e5d1b360c3 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperation.java @@ -0,0 +1,46 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +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; + } + + public void setArg(JsExpression arg) { + this.arg = arg; + } + + @Override + public void acceptChildren(JsVisitor visitor) { + if (op.isModifying()) { + // The delete operator is practically like an assignment of undefined, so + // for practical purposes we're treating it as an lvalue. + visitor.acceptLvalue(arg); + } + else { + visitor.accept(arg); + } + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsUnaryOperator.java new file mode 100644 index 00000000000..2b854d8313e --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java new file mode 100644 index 00000000000..3e9de8bf4ec --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java @@ -0,0 +1,124 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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; +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 SourceInfoAwareJsNode 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 SourceInfoAwareJsNode 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 accept(JsVisitor v) { + v.visit(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + if (initExpression != null) { + visitor.accept(initExpression); + } + } + } + + 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 accept(JsVisitor v) { + v.visitVars(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.acceptWithInsertRemove(vars); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java new file mode 100644 index 00000000000..6d66d655981 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVisitor.java @@ -0,0 +1,210 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use 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; + +public abstract class JsVisitor { + public void accept(T node) { + node.accept(this); + } + + public final void acceptList(List collection) { + for (T node : collection) { + accept(node); + } + } + + public void acceptLvalue(JsExpression expression) { + accept(expression); + } + + public final void acceptWithInsertRemove(List collection) { + for (T node : collection) { + accept(node); + } + } + + public void visitArrayAccess(JsArrayAccess x) { + visitElement(x); + } + + public void visitArray(JsArrayLiteral x) { + visitElement(x); + } + + public void visitBinaryExpression(JsBinaryOperation x) { + visitElement(x); + } + + public void visitBlock(JsBlock x) { + visitElement(x); + } + + public void visitBoolean(JsLiteral.JsBooleanLiteral x) { + visitElement(x); + } + + public void visitBreak(JsBreak x) { + visitElement(x); + } + + public void visitCase(JsCase x) { + visitElement(x); + } + + public void visitCatch(JsCatch x) { + visitElement(x); + } + + public void visitConditional(JsConditional x) { + visitElement(x); + } + + public void visitContinue(JsContinue x) { + visitElement(x); + } + + public void visitDebugger(JsDebugger x) { + visitElement(x); + } + + public void visitDefault(JsDefault x) { + visitElement(x); + } + + public void visitDoWhile(JsDoWhile x) { + visitElement(x); + } + + public void visitEmpty(JsEmpty x) { + visitElement(x); + } + + public void visitExpressionStatement(JsExpressionStatement x) { + visitElement(x); + } + + public void visitFor(JsFor x) { + visitElement(x); + } + + public void visitForIn(JsForIn x) { + visitElement(x); + } + + public void visitFunction(JsFunction x) { + visitElement(x); + } + + public void visitIf(JsIf x) { + visitElement(x); + } + + public void visitInvocation(JsInvocation invocation) { + visitElement(invocation); + } + + public void visitLabel(JsLabel x) { + visitElement(x); + } + + public void visitNameRef(JsNameRef nameRef) { + visitElement(nameRef); + } + + public void visitNew(JsNew x) { + visitElement(x); + } + + public void visitNull(JsNullLiteral x) { + visitElement(x); + } + + public void visitInt(JsNumberLiteral.JsIntLiteral x) { + visitElement(x); + } + + public void visitDouble(JsNumberLiteral.JsDoubleLiteral x) { + visitElement(x); + } + + public void visitObjectLiteral(JsObjectLiteral x) { + visitElement(x); + } + + public void visitParameter(JsParameter x) { + visitElement(x); + } + + public void visitPostfixOperation(JsPostfixOperation x) { + visitElement(x); + } + + public void visitPrefixOperation(JsPrefixOperation x) { + visitElement(x); + } + + public void visitProgram(JsProgram x) { + visitElement(x); + } + + public void visitProgramFragment(JsProgramFragment x) { + visitElement(x); + } + + public void visitPropertyInitializer(JsPropertyInitializer x) { + visitElement(x); + } + + public void visitRegExp(JsRegExp x) { + visitElement(x); + } + + public void visitReturn(JsReturn x) { + visitElement(x); + } + + public void visitString(JsStringLiteral x) { + visitElement(x); + } + + public void visit(JsSwitch x) { + visitElement(x); + } + + public void visitThis(JsLiteral.JsThisRef x) { + visitElement(x); + } + + public void visitThrow(JsThrow x) { + visitElement(x); + } + + public void visitTry(JsTry x) { + visitElement(x); + } + + public void visit(JsVar x) { + visitElement(x); + } + + public void visitVars(JsVars x) { + visitElement(x); + } + + public void visitWhile(JsWhile x) { + visitElement(x); + } + + public void visitDocComment(JsDocComment comment) { + visitElement(comment); + } + + protected void visitElement(JsNode node) { + } +} \ No newline at end of file diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsWhile.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsWhile.java new file mode 100644 index 00000000000..aeaba3921e7 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsWhile.java @@ -0,0 +1,48 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.backend.js.ast; + +/** + * A JavaScript while statement. + */ +public class JsWhile extends SourceInfoAwareJsNode 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 accept(JsVisitor v) { + v.visitWhile(this); + } + + @Override + public void acceptChildren(JsVisitor visitor) { + visitor.accept(condition); + visitor.accept(body); + } +} \ No newline at end of file diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/RecursiveJsVisitor.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/RecursiveJsVisitor.java new file mode 100644 index 00000000000..56cb331b789 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/RecursiveJsVisitor.java @@ -0,0 +1,8 @@ +package com.google.dart.compiler.backend.js.ast; + +public abstract class RecursiveJsVisitor extends JsVisitor { + @Override + protected void visitElement(JsNode node) { + node.acceptChildren(this); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/SourceInfoAwareJsNode.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/SourceInfoAwareJsNode.java new file mode 100644 index 00000000000..bfed21105a7 --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/SourceInfoAwareJsNode.java @@ -0,0 +1,25 @@ +package com.google.dart.compiler.backend.js.ast; + +abstract class SourceInfoAwareJsNode extends AbstractNode { + private Object source; + + @Override + public Object getSource() { + return source; + } + + @Override + public void setSource(Object info) { + source = info; + } + + @Override + public void acceptChildren(JsVisitor visitor) { + } + + @Override + public JsNode source(Object info) { + source = info; + return this; + } +} \ No newline at end of file diff --git a/js/js.dart-ast/src/com/google/dart/compiler/common/HasSymbol.java b/js/js.dart-ast/src/com/google/dart/compiler/common/HasSymbol.java new file mode 100644 index 00000000000..0128679bed1 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/common/SourceInfo.java b/js/js.dart-ast/src/com/google/dart/compiler/common/SourceInfo.java new file mode 100644 index 00000000000..6e0efc8e505 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/common/SourceInfoImpl.java b/js/js.dart-ast/src/com/google/dart/compiler/common/SourceInfoImpl.java new file mode 100644 index 00000000000..450f873e3e5 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/common/Symbol.java b/js/js.dart-ast/src/com/google/dart/compiler/common/Symbol.java new file mode 100644 index 00000000000..6e786358883 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/util/AstUtil.java b/js/js.dart-ast/src/com/google/dart/compiler/util/AstUtil.java new file mode 100644 index 00000000000..0be554fb7af --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/util/AstUtil.java @@ -0,0 +1,32 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +import com.google.dart.compiler.backend.js.ast.*; + +/** + * @author johnlenz@google.com (John Lenz) + */ +public final class AstUtil { + private AstUtil() { + } + + /** + * 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/js/js.dart-ast/src/com/google/dart/compiler/util/Maps.java b/js/js.dart-ast/src/com/google/dart/compiler/util/Maps.java new file mode 100644 index 00000000000..5ca74c3a370 --- /dev/null +++ b/js/js.dart-ast/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/js/js.dart-ast/src/com/google/dart/compiler/util/TextOutput.java b/js/js.dart-ast/src/com/google/dart/compiler/util/TextOutput.java new file mode 100644 index 00000000000..9b6772551db --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/util/TextOutput.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.util; + +/** + * Interface used for printing text output. + */ +public interface TextOutput { + int getPosition(); + + int getLine(); + + int getColumn(); + + void indentIn(); + + void indentOut(); + + void newline(); + + 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); + + boolean isCompact(); + + boolean isJustNewlined(); + + void setOutListener(OutListener outListener); + + void maybeIndent(); + + public interface OutListener { + void newLined(); + + void indentedAfterNewLine(); + } +} diff --git a/js/js.dart-ast/src/com/google/dart/compiler/util/TextOutputImpl.java b/js/js.dart-ast/src/com/google/dart/compiler/util/TextOutputImpl.java new file mode 100644 index 00000000000..2f1d041056f --- /dev/null +++ b/js/js.dart-ast/src/com/google/dart/compiler/util/TextOutputImpl.java @@ -0,0 +1,181 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.google.dart.compiler.util; + +import java.util.Arrays; + +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; + + private OutListener outListener; + + public TextOutputImpl() { + this(false); + } + + public boolean isCompact() { + return compact; + } + + public TextOutputImpl(boolean compact) { + this.compact = compact; + 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; + if (outListener != null) { + outListener.newLined(); + } + } + + @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; + } + + @Override + public void print(char[] s) { + maybeIndent(); + printAndCount(s); + } + + @Override + public void print(CharSequence s) { + maybeIndent(); + printAndCount(s); + } + + @Override + public void printOpt(char c) { + if (!compact) { + print(c); + } + } + + @Override + public void printOpt(char[] s) { + if (!compact) { + maybeIndent(); + printAndCount(s); + } + } + + @Override + public void printOpt(String s) { + if (!compact) { + maybeIndent(); + printAndCount(s); + } + } + + @Override + public void maybeIndent() { + if (justNewlined && !compact) { + printAndCount(indents[identLevel]); + justNewlined = false; + if (outListener != null) { + outListener.indentedAfterNewLine(); + } + } + } + + 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); + } + + @Override + public boolean isJustNewlined() { + return justNewlined && !compact; + } + + @Override + public void setOutListener(OutListener outListener) { + this.outListener = outListener; + } +} diff --git a/js/js.dart-ast/src/org/jetbrains/js/compiler/SourceMapBuilder.java b/js/js.dart-ast/src/org/jetbrains/js/compiler/SourceMapBuilder.java new file mode 100644 index 00000000000..72b6c36f59c --- /dev/null +++ b/js/js.dart-ast/src/org/jetbrains/js/compiler/SourceMapBuilder.java @@ -0,0 +1,17 @@ +package org.jetbrains.js.compiler; + +import java.io.File; + +public interface SourceMapBuilder { + void newLine(); + + void addMapping(String source, int sourceLine, int sourceColumn); + + void processSourceInfo(Object info); + + void addLink(); + + File getOutFile(); + + String build(); +}